diff --git a/pkg/api/pod/util.go b/pkg/api/pod/util.go index 36f2e869f9f..621438091b2 100644 --- a/pkg/api/pod/util.go +++ b/pkg/api/pod/util.go @@ -416,6 +416,8 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po } opts.AllowPodLifecycleSleepActionZeroValue = opts.AllowPodLifecycleSleepActionZeroValue || podLifecycleSleepActionZeroValueInUse(podSpec) + // If oldPod has resize policy set on the restartable init container, we must allow it + opts.AllowSidecarResizePolicy = hasRestartableInitContainerResizePolicy(oldPodSpec) } if oldPodMeta != nil && !opts.AllowInvalidPodDeletionCost { // This is an update, so validate only if the existing object was valid. @@ -1373,3 +1375,17 @@ func useOnlyRecursiveSELinuxChangePolicy(oldPodSpec *api.PodSpec) bool { // No feature gate + no value in the old object -> only Recursive is allowed return true } + +// hasRestartableInitContainerResizePolicy returns true if the pod spec is non-nil and +// it has any init container with ContainerRestartPolicyAlways and non-nil ResizePolicy. +func hasRestartableInitContainerResizePolicy(podSpec *api.PodSpec) bool { + if podSpec == nil { + return false + } + for _, c := range podSpec.InitContainers { + if IsRestartableInitContainer(&c) && len(c.ResizePolicy) > 0 { + return true + } + } + return false +} diff --git a/pkg/api/pod/util_test.go b/pkg/api/pod/util_test.go index 2f2f58e3f81..2f53c8fdc6e 100644 --- a/pkg/api/pod/util_test.go +++ b/pkg/api/pod/util_test.go @@ -4036,3 +4036,171 @@ func TestDropSELinuxChangePolicy(t *testing.T) { }) } } + +func TestValidateAllowSidecarResizePolicy(t *testing.T) { + restartPolicyAlways := api.ContainerRestartPolicyAlways + testCases := []struct { + name string + oldPodSpec *api.PodSpec + wantOption bool + }{ + { + name: "old pod spec is nil", + wantOption: false, + }, + { + name: "one sidecar container + one regular init container, no resize policy set on any of them", + oldPodSpec: &api.PodSpec{ + InitContainers: []api.Container{ + { + Name: "c1-restartable-init", + Image: "image", + RestartPolicy: &restartPolicyAlways, + }, + { + Name: "c1-init", + Image: "image", + }, + }, + }, + wantOption: false, + }, + { + name: "one sidecar container + one regular init container, resize policy set on regular init container", + oldPodSpec: &api.PodSpec{ + InitContainers: []api.Container{ + { + Name: "c1-restartable-init", + Image: "image", + RestartPolicy: &restartPolicyAlways, + }, + { + Name: "c1-init", + Image: "image", + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + }, + }, + }, + }, + wantOption: false, + }, + { + name: "one sidecar container + one regular init container, resize policy set on sidecar container", + oldPodSpec: &api.PodSpec{ + InitContainers: []api.Container{ + { + Name: "c1-restartable-init", + Image: "image", + RestartPolicy: &restartPolicyAlways, + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + }, + }, + { + Name: "c1-init", + Image: "image", + }, + }, + }, + wantOption: true, + }, + { + name: "one sidecar container + one regular init container, resize policy set on both of them", + oldPodSpec: &api.PodSpec{ + InitContainers: []api.Container{ + { + Name: "c1-restartable-init", + Image: "image", + RestartPolicy: &restartPolicyAlways, + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + }, + }, + { + Name: "c1-init", + Image: "image", + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + }, + }, + }, + }, + wantOption: true, + }, + { + name: "two sidecar containers, resize policy set on one of them", + oldPodSpec: &api.PodSpec{ + InitContainers: []api.Container{ + { + Name: "c1-restartable-init", + Image: "image", + RestartPolicy: &restartPolicyAlways, + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + }, + }, + { + Name: "c2-restartable-init", + Image: "image", + RestartPolicy: &restartPolicyAlways, + }, + }, + }, + wantOption: true, + }, + { + name: "two regular init containers, resize policy set on both of them", + oldPodSpec: &api.PodSpec{ + InitContainers: []api.Container{ + { + Name: "c1-init", + Image: "image", + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + }, + }, + { + Name: "c2-init", + Image: "image", + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + }, + }, + }, + }, + wantOption: false, + }, + { + name: "two non-init containers, resize policy set on both of them", + oldPodSpec: &api.PodSpec{ + Containers: []api.Container{ + { + Name: "c1", + Image: "image", + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + }, + }, + { + Name: "c2", + Image: "image", + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + }, + }, + }, + }, + wantOption: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + gotOptions := GetValidationOptionsFromPodSpecAndMeta(&api.PodSpec{}, tc.oldPodSpec, nil, nil) + if tc.wantOption != gotOptions.AllowSidecarResizePolicy { + t.Errorf("Got AllowSidecarResizePolicy=%t, want %t", gotOptions.AllowSidecarResizePolicy, tc.wantOption) + } + }) + } +} diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index d53d7fc010b..dfebbf43daf 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -3543,7 +3543,7 @@ func validateInitContainers(containers []core.Container, regularContainers []cor } } - if len(ctr.ResizePolicy) > 0 { + if !opts.AllowSidecarResizePolicy && len(ctr.ResizePolicy) > 0 { allErrs = append(allErrs, field.Invalid(idxPath.Child("resizePolicy"), ctr.ResizePolicy, "must not be set for init containers")) } } @@ -4051,6 +4051,8 @@ type PodValidationOptions struct { AllowOnlyRecursiveSELinuxChangePolicy bool // Indicates whether PodLevelResources feature is enabled or disabled. PodLevelResourcesEnabled bool + // Allow sidecar containers resize policy for backward compatibility + AllowSidecarResizePolicy bool } // validatePodMetadataAndSpec tests if required fields in the pod.metadata and pod.spec are set,