feature(pkg/api): warning for Pod with null labelSelector in PodAffinity and TopologySpread
This commit is contained in:
		@@ -112,6 +112,11 @@ func warningsForPodSpecAndMeta(fieldPath *field.Path, podSpec *api.PodSpec, meta
 | 
			
		||||
				msg,
 | 
			
		||||
			))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// warn if labelSelector is empty which is no-match.
 | 
			
		||||
		if t.LabelSelector == nil {
 | 
			
		||||
			warnings = append(warnings, fmt.Sprintf("%s: LabelSelector mustn't be empty; it will result in matching with no object", fieldPath.Child("spec", "topologySpreadConstraints").Index(i).Child("labelSelector")))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// use of deprecated annotations
 | 
			
		||||
@@ -254,5 +259,38 @@ func warningsForPodSpecAndMeta(fieldPath *field.Path, podSpec *api.PodSpec, meta
 | 
			
		||||
	if podSpec.TerminationGracePeriodSeconds != nil && *podSpec.TerminationGracePeriodSeconds < 0 {
 | 
			
		||||
		warnings = append(warnings, fmt.Sprintf("%s: must be >= 0; negative values are invalid and will be treated as 1", fieldPath.Child("spec", "terminationGracePeriodSeconds")))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if podSpec.Affinity != nil {
 | 
			
		||||
		if affinity := podSpec.Affinity.PodAffinity; affinity != nil {
 | 
			
		||||
			warnings = append(warnings, warningsForPodAffinityTerms(affinity.RequiredDuringSchedulingIgnoredDuringExecution, fieldPath.Child("spec", "affinity", "podAffinity", "requiredDuringSchedulingIgnoredDuringExecution"))...)
 | 
			
		||||
			warnings = append(warnings, warningsForWeightedPodAffinityTerms(affinity.PreferredDuringSchedulingIgnoredDuringExecution, fieldPath.Child("spec", "affinity", "podAffinity", "preferredDuringSchedulingIgnoredDuringExecution"))...)
 | 
			
		||||
		}
 | 
			
		||||
		if affinity := podSpec.Affinity.PodAntiAffinity; affinity != nil {
 | 
			
		||||
			warnings = append(warnings, warningsForPodAffinityTerms(affinity.RequiredDuringSchedulingIgnoredDuringExecution, fieldPath.Child("spec", "affinity", "podAntiAffinity", "requiredDuringSchedulingIgnoredDuringExecution"))...)
 | 
			
		||||
			warnings = append(warnings, warningsForWeightedPodAffinityTerms(affinity.PreferredDuringSchedulingIgnoredDuringExecution, fieldPath.Child("spec", "affinity", "podAntiAffinity", "preferredDuringSchedulingIgnoredDuringExecution"))...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return warnings
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func warningsForPodAffinityTerms(terms []api.PodAffinityTerm, fieldPath *field.Path) []string {
 | 
			
		||||
	var warnings []string
 | 
			
		||||
	for i, t := range terms {
 | 
			
		||||
		if t.LabelSelector == nil {
 | 
			
		||||
			warnings = append(warnings, fmt.Sprintf("%s: LabelSelector mustn't be empty; it will result in matching with no object", fieldPath.Index(i).Child("labelSelector")))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return warnings
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func warningsForWeightedPodAffinityTerms(terms []api.WeightedPodAffinityTerm, fieldPath *field.Path) []string {
 | 
			
		||||
	var warnings []string
 | 
			
		||||
	for i, t := range terms {
 | 
			
		||||
		// warn if labelSelector is empty which is no-match.
 | 
			
		||||
		if t.PodAffinityTerm.LabelSelector == nil {
 | 
			
		||||
			warnings = append(warnings, fmt.Sprintf("%s: LabelSelector mustn't be empty; it will result in matching with no object", fieldPath.Index(i).Child("podAffinityTerm", "labelSelector")))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return warnings
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -401,12 +401,30 @@ func TestWarnings(t *testing.T) {
 | 
			
		||||
			template: &api.PodTemplateSpec{
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					TopologySpreadConstraints: []api.TopologySpreadConstraint{
 | 
			
		||||
						{TopologyKey: `foo`},
 | 
			
		||||
						{TopologyKey: `beta.kubernetes.io/arch`},
 | 
			
		||||
						{TopologyKey: `beta.kubernetes.io/os`},
 | 
			
		||||
						{TopologyKey: `failure-domain.beta.kubernetes.io/region`},
 | 
			
		||||
						{TopologyKey: `failure-domain.beta.kubernetes.io/zone`},
 | 
			
		||||
						{TopologyKey: `beta.kubernetes.io/instance-type`},
 | 
			
		||||
						{
 | 
			
		||||
							TopologyKey:   `foo`,
 | 
			
		||||
							LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							TopologyKey:   `beta.kubernetes.io/arch`,
 | 
			
		||||
							LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							TopologyKey:   `beta.kubernetes.io/os`,
 | 
			
		||||
							LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							TopologyKey:   `failure-domain.beta.kubernetes.io/region`,
 | 
			
		||||
							LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							TopologyKey:   `failure-domain.beta.kubernetes.io/zone`,
 | 
			
		||||
							LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							TopologyKey:   `beta.kubernetes.io/instance-type`,
 | 
			
		||||
							LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -502,6 +520,85 @@ func TestWarnings(t *testing.T) {
 | 
			
		||||
				`spec.terminationGracePeriodSeconds: must be >= 0; negative values are invalid and will be treated as 1`,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty LabelSelector in topologySpreadConstraints",
 | 
			
		||||
			template: &api.PodTemplateSpec{
 | 
			
		||||
				ObjectMeta: metav1.ObjectMeta{},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					TopologySpreadConstraints: []api.TopologySpreadConstraint{
 | 
			
		||||
						{
 | 
			
		||||
							LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							LabelSelector: nil,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []string{
 | 
			
		||||
				`spec.topologySpreadConstraints[1].labelSelector: LabelSelector mustn't be empty; it will result in matching with no object`,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty LabelSelector in PodAffinity",
 | 
			
		||||
			template: &api.PodTemplateSpec{
 | 
			
		||||
				ObjectMeta: metav1.ObjectMeta{},
 | 
			
		||||
				Spec: api.PodSpec{
 | 
			
		||||
					Affinity: &api.Affinity{
 | 
			
		||||
						PodAffinity: &api.PodAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
 | 
			
		||||
								{
 | 
			
		||||
									LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
								},
 | 
			
		||||
								{
 | 
			
		||||
									LabelSelector: nil,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
 | 
			
		||||
								{
 | 
			
		||||
									PodAffinityTerm: api.PodAffinityTerm{
 | 
			
		||||
										LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								{
 | 
			
		||||
									PodAffinityTerm: api.PodAffinityTerm{
 | 
			
		||||
										LabelSelector: nil,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						PodAntiAffinity: &api.PodAntiAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
 | 
			
		||||
								{
 | 
			
		||||
									LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
								},
 | 
			
		||||
								{
 | 
			
		||||
									LabelSelector: nil,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
 | 
			
		||||
								{
 | 
			
		||||
									PodAffinityTerm: api.PodAffinityTerm{
 | 
			
		||||
										LabelSelector: &metav1.LabelSelector{},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
								{
 | 
			
		||||
									PodAffinityTerm: api.PodAffinityTerm{
 | 
			
		||||
										LabelSelector: nil,
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []string{
 | 
			
		||||
				`spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[1].labelSelector: LabelSelector mustn't be empty; it will result in matching with no object`,
 | 
			
		||||
				`spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[1].podAffinityTerm.labelSelector: LabelSelector mustn't be empty; it will result in matching with no object`,
 | 
			
		||||
				`spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[1].labelSelector: LabelSelector mustn't be empty; it will result in matching with no object`,
 | 
			
		||||
				`spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[1].podAffinityTerm.labelSelector: LabelSelector mustn't be empty; it will result in matching with no object`,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range testcases {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user