Merge pull request #47491 from derekwaynecarr/fix-image-name-validation
Automatic merge from submit-queue image name must not have leading or trailing whitespace **What this PR does / why we need it**: verifies that an image name can not have leading or trailing whitespace **Which issue this PR fixes** fixes https://github.com/kubernetes/kubernetes/issues/47490 **Special notes for your reviewer**: i was surprised we had not caught this, so if there is an image spec that says leading and trailing whitespace is a good thing, i am open to correction. i was made aware of downstream users of validate pod template spec that used " " as a special token. as a result, i only do the validation of image name " " in the `Pod` only. **Release note**: ```release-note NONE ```
This commit is contained in:
		@@ -1972,6 +1972,9 @@ func validateContainers(containers []api.Container, volumes sets.String, fldPath
 | 
				
			|||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			allNames.Insert(ctr.Name)
 | 
								allNames.Insert(ctr.Name)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// TODO: do not validate leading and trailing whitespace to preserve backward compatibility.
 | 
				
			||||||
 | 
							// for example: https://github.com/openshift/origin/issues/14659 image = " " is special token in pod template
 | 
				
			||||||
 | 
							// others may have done similar
 | 
				
			||||||
		if len(ctr.Image) == 0 {
 | 
							if len(ctr.Image) == 0 {
 | 
				
			||||||
			allErrs = append(allErrs, field.Required(idxPath.Child("image"), ""))
 | 
								allErrs = append(allErrs, field.Required(idxPath.Child("image"), ""))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -2211,12 +2214,33 @@ func ValidateTolerations(tolerations []api.Toleration, fldPath *field.Path) fiel
 | 
				
			|||||||
	return allErrors
 | 
						return allErrors
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// validateContainersOnlyForPod does additional validation for containers on a pod versus a pod template
 | 
				
			||||||
 | 
					// it only does additive validation of fields not covered in validateContainers
 | 
				
			||||||
 | 
					func validateContainersOnlyForPod(containers []api.Container, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
 | 
						for i, ctr := range containers {
 | 
				
			||||||
 | 
							idxPath := fldPath.Index(i)
 | 
				
			||||||
 | 
							if len(ctr.Image) != len(strings.TrimSpace(ctr.Image)) {
 | 
				
			||||||
 | 
								allErrs = append(allErrs, field.Invalid(idxPath.Child("image"), ctr.Image, "must not have leading or trailing whitespace"))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return allErrs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidatePod tests if required fields in the pod are set.
 | 
					// ValidatePod tests if required fields in the pod are set.
 | 
				
			||||||
func ValidatePod(pod *api.Pod) field.ErrorList {
 | 
					func ValidatePod(pod *api.Pod) field.ErrorList {
 | 
				
			||||||
	fldPath := field.NewPath("metadata")
 | 
						fldPath := field.NewPath("metadata")
 | 
				
			||||||
	allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath)
 | 
						allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath)
 | 
				
			||||||
	allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"))...)
 | 
						allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"))...)
 | 
				
			||||||
	allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, field.NewPath("spec"))...)
 | 
						allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, field.NewPath("spec"))...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// we do additional validation only pertinent for pods and not pod templates
 | 
				
			||||||
 | 
						// this was done to preserve backwards compatibility
 | 
				
			||||||
 | 
						specPath := field.NewPath("spec")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.Containers, specPath.Child("containers"))...)
 | 
				
			||||||
 | 
						allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.InitContainers, specPath.Child("initContainers"))...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2605,6 +2629,10 @@ func ValidateContainerUpdates(newContainers, oldContainers []api.Container, fldP
 | 
				
			|||||||
		if len(ctr.Image) == 0 {
 | 
							if len(ctr.Image) == 0 {
 | 
				
			||||||
			allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("image"), ""))
 | 
								allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("image"), ""))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// this is only called from ValidatePodUpdate so its safe to check leading/trailing whitespace.
 | 
				
			||||||
 | 
							if len(strings.TrimSpace(ctr.Image)) != len(ctr.Image) {
 | 
				
			||||||
 | 
								allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("image"), ctr.Image, "must not have leading or trailing whitespace"))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return allErrs, false
 | 
						return allErrs, false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3080,6 +3080,9 @@ func TestValidateContainers(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	successCase := []api.Container{
 | 
						successCase := []api.Container{
 | 
				
			||||||
		{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
 | 
							{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
 | 
				
			||||||
 | 
							// backwards compatibility to ensure containers in pod template spec do not check for this
 | 
				
			||||||
 | 
							{Name: "def", Image: " ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
 | 
				
			||||||
 | 
							{Name: "ghi", Image: " some  ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
 | 
				
			||||||
		{Name: "123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
 | 
							{Name: "123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
 | 
				
			||||||
		{Name: "abc-123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
 | 
							{Name: "abc-123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -3234,6 +3237,7 @@ func TestValidateContainers(t *testing.T) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
	errorCases := map[string][]api.Container{
 | 
						errorCases := map[string][]api.Container{
 | 
				
			||||||
		"zero-length name":     {{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
 | 
							"zero-length name":     {{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
 | 
				
			||||||
 | 
							"zero-length-image":    {{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
 | 
				
			||||||
		"name > 63 characters": {{Name: strings.Repeat("a", 64), Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
 | 
							"name > 63 characters": {{Name: strings.Repeat("a", 64), Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
 | 
				
			||||||
		"name not a DNS label": {{Name: "a.b.c", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
 | 
							"name not a DNS label": {{Name: "a.b.c", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
 | 
				
			||||||
		"name not unique": {
 | 
							"name not unique": {
 | 
				
			||||||
@@ -4214,6 +4218,28 @@ func TestValidatePod(t *testing.T) {
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"image whitespace": {
 | 
				
			||||||
 | 
								expectedError: "spec.containers[0].image",
 | 
				
			||||||
 | 
								spec: api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "ns"},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
 | 
										DNSPolicy:     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										Containers:    []api.Container{{Name: "ctr", Image: " ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"image leading and trailing whitespace": {
 | 
				
			||||||
 | 
								expectedError: "spec.containers[0].image",
 | 
				
			||||||
 | 
								spec: api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "ns"},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
 | 
										DNSPolicy:     api.DNSClusterFirst,
 | 
				
			||||||
 | 
										Containers:    []api.Container{{Name: "ctr", Image: " something ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"bad namespace": {
 | 
							"bad namespace": {
 | 
				
			||||||
			expectedError: "metadata.namespace",
 | 
								expectedError: "metadata.namespace",
 | 
				
			||||||
			spec: api.Pod{
 | 
								spec: api.Pod{
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user