Fix update validation for pods w/init containers.
This commit is contained in:
		@@ -1960,6 +1960,23 @@ func ValidatePodSecurityContext(securityContext *api.PodSecurityContext, spec *a
 | 
				
			|||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func validateContainerUpdates(newContainers, oldContainers []api.Container, fldPath *field.Path) (allErrs field.ErrorList, stop bool) {
 | 
				
			||||||
 | 
						allErrs = field.ErrorList{}
 | 
				
			||||||
 | 
						if len(newContainers) != len(oldContainers) {
 | 
				
			||||||
 | 
							//TODO: Pinpoint the specific container that causes the invalid error after we have strategic merge diff
 | 
				
			||||||
 | 
							allErrs = append(allErrs, field.Forbidden(fldPath, "pod updates may not add or remove containers"))
 | 
				
			||||||
 | 
							return allErrs, true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// validate updated container images
 | 
				
			||||||
 | 
						for i, ctr := range newContainers {
 | 
				
			||||||
 | 
							if len(ctr.Image) == 0 {
 | 
				
			||||||
 | 
								allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("image"), ""))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return allErrs, false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
 | 
					// ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
 | 
				
			||||||
// that cannot be changed.
 | 
					// that cannot be changed.
 | 
				
			||||||
func ValidatePodUpdate(newPod, oldPod *api.Pod) field.ErrorList {
 | 
					func ValidatePodUpdate(newPod, oldPod *api.Pod) field.ErrorList {
 | 
				
			||||||
@@ -1967,21 +1984,21 @@ func ValidatePodUpdate(newPod, oldPod *api.Pod) field.ErrorList {
 | 
				
			|||||||
	allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
 | 
						allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
 | 
				
			||||||
	allErrs = append(allErrs, ValidatePodSpecificAnnotations(newPod.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
 | 
						allErrs = append(allErrs, ValidatePodSpecificAnnotations(newPod.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
 | 
				
			||||||
	specPath := field.NewPath("spec")
 | 
						specPath := field.NewPath("spec")
 | 
				
			||||||
	if len(newPod.Spec.Containers) != len(oldPod.Spec.Containers) {
 | 
					 | 
				
			||||||
		//TODO: Pinpoint the specific container that causes the invalid error after we have strategic merge diff
 | 
					 | 
				
			||||||
		allErrs = append(allErrs, field.Forbidden(specPath.Child("containers"), "pod updates may not add or remove containers"))
 | 
					 | 
				
			||||||
		return allErrs
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// validate updateable fields:
 | 
						// validate updateable fields:
 | 
				
			||||||
	// 1.  containers[*].image
 | 
						// 1.  containers[*].image
 | 
				
			||||||
	// 2.  spec.activeDeadlineSeconds
 | 
						// 2.  initContainers[*].image
 | 
				
			||||||
 | 
						// 3.  spec.activeDeadlineSeconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// validate updated container images
 | 
						containerErrs, stop := validateContainerUpdates(newPod.Spec.Containers, oldPod.Spec.Containers, specPath.Child("containers"))
 | 
				
			||||||
	for i, ctr := range newPod.Spec.Containers {
 | 
						allErrs = append(allErrs, containerErrs...)
 | 
				
			||||||
		if len(ctr.Image) == 0 {
 | 
						if stop {
 | 
				
			||||||
			allErrs = append(allErrs, field.Required(specPath.Child("containers").Index(i).Child("image"), ""))
 | 
							return allErrs
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						containerErrs, stop = validateContainerUpdates(newPod.Spec.InitContainers, oldPod.Spec.InitContainers, specPath.Child("initContainers"))
 | 
				
			||||||
 | 
						allErrs = append(allErrs, containerErrs...)
 | 
				
			||||||
 | 
						if stop {
 | 
				
			||||||
 | 
							return allErrs
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// validate updated spec.activeDeadlineSeconds.  two types of updates are allowed:
 | 
						// validate updated spec.activeDeadlineSeconds.  two types of updates are allowed:
 | 
				
			||||||
@@ -2013,6 +2030,13 @@ func ValidatePodUpdate(newPod, oldPod *api.Pod) field.ErrorList {
 | 
				
			|||||||
		newContainers = append(newContainers, container)
 | 
							newContainers = append(newContainers, container)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	mungedPod.Spec.Containers = newContainers
 | 
						mungedPod.Spec.Containers = newContainers
 | 
				
			||||||
 | 
						// munge initContainers[*].image
 | 
				
			||||||
 | 
						var newInitContainers []api.Container
 | 
				
			||||||
 | 
						for ix, container := range mungedPod.Spec.InitContainers {
 | 
				
			||||||
 | 
							container.Image = oldPod.Spec.InitContainers[ix].Image
 | 
				
			||||||
 | 
							newInitContainers = append(newInitContainers, container)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mungedPod.Spec.InitContainers = newInitContainers
 | 
				
			||||||
	// munge spec.activeDeadlineSeconds
 | 
						// munge spec.activeDeadlineSeconds
 | 
				
			||||||
	mungedPod.Spec.ActiveDeadlineSeconds = nil
 | 
						mungedPod.Spec.ActiveDeadlineSeconds = nil
 | 
				
			||||||
	if oldPod.Spec.ActiveDeadlineSeconds != nil {
 | 
						if oldPod.Spec.ActiveDeadlineSeconds != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1908,6 +1908,7 @@ func TestValidatePodSpec(t *testing.T) {
 | 
				
			|||||||
				{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
 | 
									{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Containers:     []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
 | 
								Containers:     []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
 | 
				
			||||||
 | 
								InitContainers: []api.Container{{Name: "ictr", Image: "iimage", ImagePullPolicy: "IfNotPresent"}},
 | 
				
			||||||
			RestartPolicy:  api.RestartPolicyAlways,
 | 
								RestartPolicy:  api.RestartPolicyAlways,
 | 
				
			||||||
			NodeSelector: map[string]string{
 | 
								NodeSelector: map[string]string{
 | 
				
			||||||
				"key": "value",
 | 
									"key": "value",
 | 
				
			||||||
@@ -1999,6 +2000,12 @@ func TestValidatePodSpec(t *testing.T) {
 | 
				
			|||||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
								RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
								DNSPolicy:     api.DNSClusterFirst,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"bad init container": {
 | 
				
			||||||
 | 
								Containers:     []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
 | 
				
			||||||
 | 
								InitContainers: []api.Container{{}},
 | 
				
			||||||
 | 
								RestartPolicy:  api.RestartPolicyAlways,
 | 
				
			||||||
 | 
								DNSPolicy:      api.DNSClusterFirst,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"bad DNS policy": {
 | 
							"bad DNS policy": {
 | 
				
			||||||
			DNSPolicy:     api.DNSPolicy("invalid"),
 | 
								DNSPolicy:     api.DNSPolicy("invalid"),
 | 
				
			||||||
			RestartPolicy: api.RestartPolicyAlways,
 | 
								RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
@@ -3022,6 +3029,35 @@ func TestValidatePodUpdate(t *testing.T) {
 | 
				
			|||||||
			false,
 | 
								false,
 | 
				
			||||||
			"more containers",
 | 
								"more containers",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
 | 
										Name: "foo",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										InitContainers: []api.Container{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												Image: "foo:V1",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										InitContainers: []api.Container{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												Image: "foo:V2",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												Image: "bar:V2",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								false,
 | 
				
			||||||
 | 
								"more init containers",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			api.Pod{
 | 
								api.Pod{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
				
			||||||
@@ -3070,6 +3106,30 @@ func TestValidatePodUpdate(t *testing.T) {
 | 
				
			|||||||
			true,
 | 
								true,
 | 
				
			||||||
			"image change",
 | 
								"image change",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										InitContainers: []api.Container{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												Image: "foo:V1",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										InitContainers: []api.Container{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												Image: "foo:V2",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								true,
 | 
				
			||||||
 | 
								"init container image change",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			api.Pod{
 | 
								api.Pod{
 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
				
			||||||
@@ -3092,6 +3152,28 @@ func TestValidatePodUpdate(t *testing.T) {
 | 
				
			|||||||
			false,
 | 
								false,
 | 
				
			||||||
			"image change to empty",
 | 
								"image change to empty",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										InitContainers: []api.Container{
 | 
				
			||||||
 | 
											{},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										InitContainers: []api.Container{
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												Image: "foo:V2",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								false,
 | 
				
			||||||
 | 
								"init container image change to empty",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			api.Pod{
 | 
								api.Pod{
 | 
				
			||||||
				Spec: api.PodSpec{},
 | 
									Spec: api.PodSpec{},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -131,7 +131,6 @@ func addGrouplessTypes() {
 | 
				
			|||||||
	api.Scheme.AddKnownTypes(grouplessGroupVersion,
 | 
						api.Scheme.AddKnownTypes(grouplessGroupVersion,
 | 
				
			||||||
		&apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{},
 | 
							&apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{},
 | 
				
			||||||
		&api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
 | 
							&api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
 | 
				
			||||||
	api.Scheme.AddKnownTypes(grouplessGroupVersion, &api.Pod{})
 | 
					 | 
				
			||||||
	api.Scheme.AddKnownTypes(grouplessInternalGroupVersion,
 | 
						api.Scheme.AddKnownTypes(grouplessInternalGroupVersion,
 | 
				
			||||||
		&apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &api.ListOptions{},
 | 
							&apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &api.ListOptions{},
 | 
				
			||||||
		&apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
 | 
							&apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/api/rest"
 | 
						"k8s.io/kubernetes/pkg/api/rest"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/testapi"
 | 
						"k8s.io/kubernetes/pkg/api/testapi"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/unversioned"
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/v1"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/types"
 | 
						"k8s.io/kubernetes/pkg/types"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/diff"
 | 
						"k8s.io/kubernetes/pkg/util/diff"
 | 
				
			||||||
@@ -186,12 +187,7 @@ func (tc *patchTestCase) Run(t *testing.T) {
 | 
				
			|||||||
	namer := &testNamer{namespace, name}
 | 
						namer := &testNamer{namespace, name}
 | 
				
			||||||
	copier := runtime.ObjectCopier(api.Scheme)
 | 
						copier := runtime.ObjectCopier(api.Scheme)
 | 
				
			||||||
	resource := unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
 | 
						resource := unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
 | 
				
			||||||
 | 
						versionedObj := &v1.Pod{}
 | 
				
			||||||
	versionedObj, err := api.Scheme.ConvertToVersion(&api.Pod{}, unversioned.GroupVersion{Version: "v1"})
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("%s: unexpected error: %v", tc.name, err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, patchType := range []api.PatchType{api.JSONPatchType, api.MergePatchType, api.StrategicMergePatchType} {
 | 
						for _, patchType := range []api.PatchType{api.JSONPatchType, api.MergePatchType, api.StrategicMergePatchType} {
 | 
				
			||||||
		// TODO SUPPORT THIS!
 | 
							// TODO SUPPORT THIS!
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user