Commit 165add86 authored by k8s-merge-robot's avatar k8s-merge-robot Committed by GitHub

Merge pull request #29034 from ivan4th/fix-init-container-update-validation

Automatic merge from submit-queue Fix init container update validation for pods Partial fix #26840 The remaining issues with `kubectl apply` on pods with init containers are caused by temporary annotation-based representation and will resolve themselves once init containers leave alpha state. Also, this PR makes sure internal and external objects don't get mixed up by the PATCH handler (see related issue #25106). This PR is an alternative for #28557 which met criticism from @smarterclayton and @liggitt for working around the temporary issue with annotations. #28557 is a full fix for #26840 and contains an e2e test that cannot pass without the `VolumeMounts` workaround. As there appears to be no good way to include an e2e test that's known to be failing in k8s source, I've removed it from this PR. Either this PR or #28557 should be applied, but not both.
parents 8ead63f1 02baa449
......@@ -1960,6 +1960,23 @@ func ValidatePodSecurityContext(securityContext *api.PodSecurityContext, spec *a
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
// that cannot be changed.
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 = append(allErrs, ValidatePodSpecificAnnotations(newPod.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
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:
// 1. containers[*].image
// 2. spec.activeDeadlineSeconds
// 2. initContainers[*].image
// 3. spec.activeDeadlineSeconds
// validate updated container images
for i, ctr := range newPod.Spec.Containers {
if len(ctr.Image) == 0 {
allErrs = append(allErrs, field.Required(specPath.Child("containers").Index(i).Child("image"), ""))
}
containerErrs, stop := validateContainerUpdates(newPod.Spec.Containers, oldPod.Spec.Containers, specPath.Child("containers"))
allErrs = append(allErrs, containerErrs...)
if stop {
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:
......@@ -2013,6 +2030,13 @@ func ValidatePodUpdate(newPod, oldPod *api.Pod) field.ErrorList {
newContainers = append(newContainers, container)
}
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
mungedPod.Spec.ActiveDeadlineSeconds = nil
if oldPod.Spec.ActiveDeadlineSeconds != nil {
......
......@@ -1907,8 +1907,9 @@ func TestValidatePodSpec(t *testing.T) {
Volumes: []api.Volume{
{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
},
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
InitContainers: []api.Container{{Name: "ictr", Image: "iimage", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
NodeSelector: map[string]string{
"key": "value",
},
......@@ -1999,6 +2000,12 @@ func TestValidatePodSpec(t *testing.T) {
RestartPolicy: api.RestartPolicyAlways,
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": {
DNSPolicy: api.DNSPolicy("invalid"),
RestartPolicy: api.RestartPolicyAlways,
......@@ -3024,6 +3031,35 @@ func TestValidatePodUpdate(t *testing.T) {
},
{
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{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Spec: api.PodSpec{Containers: []api.Container{{Image: "foo:V1"}}},
},
......@@ -3074,6 +3110,30 @@ func TestValidatePodUpdate(t *testing.T) {
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{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Spec: api.PodSpec{
Containers: []api.Container{
{},
},
......@@ -3094,6 +3154,28 @@ func TestValidatePodUpdate(t *testing.T) {
},
{
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{
Spec: api.PodSpec{},
},
api.Pod{
......
......@@ -131,7 +131,6 @@ func addGrouplessTypes() {
api.Scheme.AddKnownTypes(grouplessGroupVersion,
&apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{},
&api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
api.Scheme.AddKnownTypes(grouplessGroupVersion, &api.Pod{})
api.Scheme.AddKnownTypes(grouplessInternalGroupVersion,
&apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &api.ListOptions{},
&apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
......
......@@ -31,6 +31,7 @@ import (
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/diff"
......@@ -186,12 +187,7 @@ func (tc *patchTestCase) Run(t *testing.T) {
namer := &testNamer{namespace, name}
copier := runtime.ObjectCopier(api.Scheme)
resource := unversioned.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
versionedObj, err := api.Scheme.ConvertToVersion(&api.Pod{}, unversioned.GroupVersion{Version: "v1"})
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
return
}
versionedObj := &v1.Pod{}
for _, patchType := range []api.PatchType{api.JSONPatchType, api.MergePatchType, api.StrategicMergePatchType} {
// TODO SUPPORT THIS!
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment