mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 18:54:06 +00:00
Validation
This commit is contained in:
parent
70540c9f43
commit
0380f2c41c
@ -270,6 +270,12 @@ func SetContainerImage(image string) TweakContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetContainerLifecycle(lifecycle api.Lifecycle) TweakContainer {
|
||||||
|
return func(cnr *api.Container) {
|
||||||
|
cnr.Lifecycle = &lifecycle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func MakeResourceRequirements(requests, limits map[string]string) api.ResourceRequirements {
|
func MakeResourceRequirements(requests, limits map[string]string) api.ResourceRequirements {
|
||||||
rr := api.ResourceRequirements{Requests: api.ResourceList{}, Limits: api.ResourceList{}}
|
rr := api.ResourceRequirements{Requests: api.ResourceList{}, Limits: api.ResourceList{}}
|
||||||
for k, v := range requests {
|
for k, v := range requests {
|
||||||
|
@ -678,6 +678,51 @@ func dropDisabledFields(
|
|||||||
dropPodLifecycleSleepAction(podSpec, oldPodSpec)
|
dropPodLifecycleSleepAction(podSpec, oldPodSpec)
|
||||||
dropImageVolumes(podSpec, oldPodSpec)
|
dropImageVolumes(podSpec, oldPodSpec)
|
||||||
dropSELinuxChangePolicy(podSpec, oldPodSpec)
|
dropSELinuxChangePolicy(podSpec, oldPodSpec)
|
||||||
|
dropContainerStopSignals(podSpec, oldPodSpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dropContainerStopSignals(podSpec, oldPodSpec *api.PodSpec) {
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.ContainerStopSignals) || containerStopSignalsInUse(oldPodSpec) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wipeLifecycle := func(ctr *api.Container) {
|
||||||
|
if ctr.Lifecycle == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ctr.Lifecycle.StopSignal != nil {
|
||||||
|
ctr.Lifecycle.StopSignal = nil
|
||||||
|
if *ctr.Lifecycle == (api.Lifecycle{}) {
|
||||||
|
ctr.Lifecycle = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool {
|
||||||
|
if c.Lifecycle == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
wipeLifecycle(c)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func containerStopSignalsInUse(podSpec *api.PodSpec) bool {
|
||||||
|
if podSpec == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var inUse bool
|
||||||
|
VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool {
|
||||||
|
if c.Lifecycle == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c.Lifecycle.StopSignal != nil {
|
||||||
|
inUse = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return inUse
|
||||||
}
|
}
|
||||||
|
|
||||||
func dropDisabledPodLevelResources(podSpec, oldPodSpec *api.PodSpec) {
|
func dropDisabledPodLevelResources(podSpec, oldPodSpec *api.PodSpec) {
|
||||||
@ -713,7 +758,7 @@ func dropPodLifecycleSleepAction(podSpec, oldPodSpec *api.PodSpec) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
adjustLifecycle(podSpec.Containers[i].Lifecycle)
|
adjustLifecycle(podSpec.Containers[i].Lifecycle)
|
||||||
if podSpec.Containers[i].Lifecycle.PreStop == nil && podSpec.Containers[i].Lifecycle.PostStart == nil {
|
if podSpec.Containers[i].Lifecycle.PreStop == nil && podSpec.Containers[i].Lifecycle.PostStart == nil && podSpec.Containers[i].Lifecycle.StopSignal == nil {
|
||||||
podSpec.Containers[i].Lifecycle = nil
|
podSpec.Containers[i].Lifecycle = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -723,7 +768,7 @@ func dropPodLifecycleSleepAction(podSpec, oldPodSpec *api.PodSpec) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
adjustLifecycle(podSpec.InitContainers[i].Lifecycle)
|
adjustLifecycle(podSpec.InitContainers[i].Lifecycle)
|
||||||
if podSpec.InitContainers[i].Lifecycle.PreStop == nil && podSpec.InitContainers[i].Lifecycle.PostStart == nil {
|
if podSpec.InitContainers[i].Lifecycle.PreStop == nil && podSpec.InitContainers[i].Lifecycle.PostStart == nil && podSpec.InitContainers[i].Lifecycle.StopSignal == nil {
|
||||||
podSpec.InitContainers[i].Lifecycle = nil
|
podSpec.InitContainers[i].Lifecycle = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -733,7 +778,7 @@ func dropPodLifecycleSleepAction(podSpec, oldPodSpec *api.PodSpec) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
adjustLifecycle(podSpec.EphemeralContainers[i].Lifecycle)
|
adjustLifecycle(podSpec.EphemeralContainers[i].Lifecycle)
|
||||||
if podSpec.EphemeralContainers[i].Lifecycle.PreStop == nil && podSpec.EphemeralContainers[i].Lifecycle.PostStart == nil {
|
if podSpec.EphemeralContainers[i].Lifecycle.PreStop == nil && podSpec.EphemeralContainers[i].Lifecycle.PostStart == nil && podSpec.EphemeralContainers[i].Lifecycle.StopSignal == nil {
|
||||||
podSpec.EphemeralContainers[i].Lifecycle = nil
|
podSpec.EphemeralContainers[i].Lifecycle = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3436,6 +3436,148 @@ func TestDropPodLifecycleSleepAction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDropContainerStopSignals(t *testing.T) {
|
||||||
|
makeContainer := func(lifecycle *api.Lifecycle) api.Container {
|
||||||
|
container := api.Container{Name: "foo"}
|
||||||
|
if lifecycle != nil {
|
||||||
|
container.Lifecycle = lifecycle
|
||||||
|
}
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
|
||||||
|
makeEphemeralContainer := func(lifecycle *api.Lifecycle) api.EphemeralContainer {
|
||||||
|
container := api.EphemeralContainer{
|
||||||
|
EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "foo"},
|
||||||
|
}
|
||||||
|
if lifecycle != nil {
|
||||||
|
container.Lifecycle = lifecycle
|
||||||
|
}
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
|
||||||
|
makePod := func(os api.OSName, containers []api.Container, initContainers []api.Container, ephemeralContainers []api.EphemeralContainer) *api.PodSpec {
|
||||||
|
return &api.PodSpec{
|
||||||
|
OS: &api.PodOS{Name: os},
|
||||||
|
Containers: containers,
|
||||||
|
InitContainers: initContainers,
|
||||||
|
EphemeralContainers: ephemeralContainers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
featuregateEnabled bool
|
||||||
|
oldLifecycle *api.Lifecycle
|
||||||
|
newLifecycle *api.Lifecycle
|
||||||
|
expectedLifecycle *api.Lifecycle
|
||||||
|
}{
|
||||||
|
// feature gate is turned on and stopsignal is not in use - Lifecycle stays nil
|
||||||
|
{
|
||||||
|
featuregateEnabled: true,
|
||||||
|
oldLifecycle: nil,
|
||||||
|
newLifecycle: nil,
|
||||||
|
expectedLifecycle: nil,
|
||||||
|
},
|
||||||
|
// feature gate is turned off and StopSignal is in use - StopSignal is not dropped
|
||||||
|
{
|
||||||
|
featuregateEnabled: false,
|
||||||
|
oldLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM)},
|
||||||
|
newLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM)},
|
||||||
|
expectedLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM)},
|
||||||
|
},
|
||||||
|
// feature gate is turned off and StopSignal is not in use - Entire lifecycle is dropped
|
||||||
|
{
|
||||||
|
featuregateEnabled: false,
|
||||||
|
oldLifecycle: &api.Lifecycle{StopSignal: nil},
|
||||||
|
newLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM)},
|
||||||
|
expectedLifecycle: nil,
|
||||||
|
},
|
||||||
|
// feature gate is turned on and StopSignal is in use - StopSignal is not dropped
|
||||||
|
{
|
||||||
|
featuregateEnabled: true,
|
||||||
|
oldLifecycle: &api.Lifecycle{StopSignal: nil},
|
||||||
|
newLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM)},
|
||||||
|
expectedLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM)},
|
||||||
|
},
|
||||||
|
// feature gate is turned off and PreStop is in use - StopSignal alone is dropped
|
||||||
|
{
|
||||||
|
featuregateEnabled: false,
|
||||||
|
oldLifecycle: &api.Lifecycle{StopSignal: nil, PreStop: &api.LifecycleHandler{
|
||||||
|
Exec: &api.ExecAction{Command: []string{"foo"}},
|
||||||
|
}},
|
||||||
|
newLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM), PreStop: &api.LifecycleHandler{
|
||||||
|
Exec: &api.ExecAction{Command: []string{"foo"}},
|
||||||
|
}},
|
||||||
|
expectedLifecycle: &api.Lifecycle{StopSignal: nil, PreStop: &api.LifecycleHandler{
|
||||||
|
Exec: &api.ExecAction{Command: []string{"foo"}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
// feature gate is turned on and PreStop is in use - StopSignal is not dropped
|
||||||
|
{
|
||||||
|
featuregateEnabled: true,
|
||||||
|
oldLifecycle: &api.Lifecycle{StopSignal: nil, PreStop: &api.LifecycleHandler{
|
||||||
|
Exec: &api.ExecAction{Command: []string{"foo"}},
|
||||||
|
}},
|
||||||
|
newLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM), PreStop: &api.LifecycleHandler{
|
||||||
|
Exec: &api.ExecAction{Command: []string{"foo"}},
|
||||||
|
}},
|
||||||
|
expectedLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM), PreStop: &api.LifecycleHandler{
|
||||||
|
Exec: &api.ExecAction{Command: []string{"foo"}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
// feature gate is turned off and PreStop and StopSignal are in use - nothing is dropped
|
||||||
|
{
|
||||||
|
featuregateEnabled: true,
|
||||||
|
oldLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM), PreStop: &api.LifecycleHandler{
|
||||||
|
Exec: &api.ExecAction{Command: []string{"foo"}},
|
||||||
|
}},
|
||||||
|
newLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM), PreStop: &api.LifecycleHandler{
|
||||||
|
Exec: &api.ExecAction{Command: []string{"foo"}},
|
||||||
|
}},
|
||||||
|
expectedLifecycle: &api.Lifecycle{StopSignal: ptr.To(api.SIGTERM), PreStop: &api.LifecycleHandler{
|
||||||
|
Exec: &api.ExecAction{Command: []string{"foo"}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testCases {
|
||||||
|
t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ContainerStopSignals, tc.featuregateEnabled)
|
||||||
|
// Containers
|
||||||
|
{
|
||||||
|
oldPod := makePod(api.Linux, []api.Container{makeContainer(tc.oldLifecycle.DeepCopy())}, nil, nil)
|
||||||
|
newPod := makePod(api.Linux, []api.Container{makeContainer(tc.newLifecycle.DeepCopy())}, nil, nil)
|
||||||
|
expectedPod := makePod(api.Linux, []api.Container{makeContainer(tc.expectedLifecycle.DeepCopy())}, nil, nil)
|
||||||
|
dropDisabledFields(newPod, nil, oldPod, nil)
|
||||||
|
|
||||||
|
if diff := cmp.Diff(expectedPod, newPod); diff != "" {
|
||||||
|
t.Fatalf("Unexpected modification to new pod; diff (-got +want)\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// InitContainers
|
||||||
|
{
|
||||||
|
oldPod := makePod(api.Linux, nil, []api.Container{makeContainer(tc.oldLifecycle.DeepCopy())}, nil)
|
||||||
|
newPod := makePod(api.Linux, nil, []api.Container{makeContainer(tc.newLifecycle.DeepCopy())}, nil)
|
||||||
|
expectPod := makePod(api.Linux, nil, []api.Container{makeContainer(tc.expectedLifecycle.DeepCopy())}, nil)
|
||||||
|
dropDisabledFields(newPod, nil, oldPod, nil)
|
||||||
|
if diff := cmp.Diff(expectPod, newPod); diff != "" {
|
||||||
|
t.Fatalf("Unexpected modification to new pod; diff (-got +want)\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// EphemeralContainers
|
||||||
|
{
|
||||||
|
oldPod := makePod(api.Linux, nil, nil, []api.EphemeralContainer{makeEphemeralContainer(tc.oldLifecycle.DeepCopy())})
|
||||||
|
newPod := makePod(api.Linux, nil, nil, []api.EphemeralContainer{makeEphemeralContainer(tc.newLifecycle.DeepCopy())})
|
||||||
|
expectPod := makePod(api.Linux, nil, nil, []api.EphemeralContainer{makeEphemeralContainer(tc.expectedLifecycle.DeepCopy())})
|
||||||
|
dropDisabledFields(newPod, nil, oldPod, nil)
|
||||||
|
if diff := cmp.Diff(expectPod, newPod); diff != "" {
|
||||||
|
t.Fatalf("Unexpected modification to new pod; diff (-got +want)\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDropSupplementalGroupsPolicy(t *testing.T) {
|
func TestDropSupplementalGroupsPolicy(t *testing.T) {
|
||||||
supplementalGroupsPolicyMerge := api.SupplementalGroupsPolicyMerge
|
supplementalGroupsPolicyMerge := api.SupplementalGroupsPolicyMerge
|
||||||
podWithSupplementalGroupsPolicy := func() *api.Pod {
|
podWithSupplementalGroupsPolicy := func() *api.Pod {
|
||||||
|
@ -3343,7 +3343,48 @@ func validateHandler(handler commonHandler, gracePeriod *int64, fldPath *field.P
|
|||||||
return allErrors
|
return allErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateLifecycle(lifecycle *core.Lifecycle, gracePeriod *int64, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
var supportedStopSignalsLinux = sets.New(
|
||||||
|
core.SIGABRT, core.SIGALRM, core.SIGBUS, core.SIGCHLD,
|
||||||
|
core.SIGCLD, core.SIGCONT, core.SIGFPE, core.SIGHUP,
|
||||||
|
core.SIGILL, core.SIGINT, core.SIGIO, core.SIGIOT,
|
||||||
|
core.SIGKILL, core.SIGPIPE, core.SIGPOLL, core.SIGPROF,
|
||||||
|
core.SIGPWR, core.SIGQUIT, core.SIGSEGV, core.SIGSTKFLT,
|
||||||
|
core.SIGSTOP, core.SIGSYS, core.SIGTERM, core.SIGTRAP,
|
||||||
|
core.SIGTSTP, core.SIGTTIN, core.SIGTTOU, core.SIGURG,
|
||||||
|
core.SIGUSR1, core.SIGUSR2, core.SIGVTALRM, core.SIGWINCH,
|
||||||
|
core.SIGXCPU, core.SIGXFSZ, core.SIGRTMIN, core.SIGRTMINPLUS1,
|
||||||
|
core.SIGRTMINPLUS2, core.SIGRTMINPLUS3, core.SIGRTMINPLUS4,
|
||||||
|
core.SIGRTMINPLUS5, core.SIGRTMINPLUS6, core.SIGRTMINPLUS7,
|
||||||
|
core.SIGRTMINPLUS8, core.SIGRTMINPLUS9, core.SIGRTMINPLUS10,
|
||||||
|
core.SIGRTMINPLUS11, core.SIGRTMINPLUS12, core.SIGRTMINPLUS13,
|
||||||
|
core.SIGRTMINPLUS14, core.SIGRTMINPLUS15, core.SIGRTMAXMINUS14,
|
||||||
|
core.SIGRTMAXMINUS13, core.SIGRTMAXMINUS12, core.SIGRTMAXMINUS11,
|
||||||
|
core.SIGRTMAXMINUS10, core.SIGRTMAXMINUS9, core.SIGRTMAXMINUS8,
|
||||||
|
core.SIGRTMAXMINUS7, core.SIGRTMAXMINUS6, core.SIGRTMAXMINUS5,
|
||||||
|
core.SIGRTMAXMINUS4, core.SIGRTMAXMINUS3, core.SIGRTMAXMINUS2,
|
||||||
|
core.SIGRTMAXMINUS1, core.SIGRTMAX)
|
||||||
|
|
||||||
|
var supportedStopSignalsWindows = sets.New(core.SIGKILL, core.SIGTERM)
|
||||||
|
|
||||||
|
func validateStopSignal(stopSignal *core.Signal, fldPath *field.Path, os *core.PodOS) field.ErrorList {
|
||||||
|
allErrors := field.ErrorList{}
|
||||||
|
|
||||||
|
if os == nil {
|
||||||
|
allErrors = append(allErrors, field.Forbidden(fldPath, "may not be set for containers with empty `spec.os.name`"))
|
||||||
|
} else if os.Name == core.Windows {
|
||||||
|
if !supportedStopSignalsWindows.Has(*stopSignal) {
|
||||||
|
allErrors = append(allErrors, field.NotSupported(fldPath, stopSignal, sets.List(supportedStopSignalsWindows)))
|
||||||
|
}
|
||||||
|
} else if os.Name == core.Linux {
|
||||||
|
if !supportedStopSignalsLinux.Has(*stopSignal) {
|
||||||
|
allErrors = append(allErrors, field.NotSupported(fldPath, stopSignal, sets.List(supportedStopSignalsLinux)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateLifecycle(lifecycle *core.Lifecycle, gracePeriod *int64, fldPath *field.Path, opts PodValidationOptions, os *core.PodOS) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
if lifecycle.PostStart != nil {
|
if lifecycle.PostStart != nil {
|
||||||
allErrs = append(allErrs, validateHandler(handlerFromLifecycle(lifecycle.PostStart), gracePeriod, fldPath.Child("postStart"), opts)...)
|
allErrs = append(allErrs, validateHandler(handlerFromLifecycle(lifecycle.PostStart), gracePeriod, fldPath.Child("postStart"), opts)...)
|
||||||
@ -3351,6 +3392,9 @@ func validateLifecycle(lifecycle *core.Lifecycle, gracePeriod *int64, fldPath *f
|
|||||||
if lifecycle.PreStop != nil {
|
if lifecycle.PreStop != nil {
|
||||||
allErrs = append(allErrs, validateHandler(handlerFromLifecycle(lifecycle.PreStop), gracePeriod, fldPath.Child("preStop"), opts)...)
|
allErrs = append(allErrs, validateHandler(handlerFromLifecycle(lifecycle.PreStop), gracePeriod, fldPath.Child("preStop"), opts)...)
|
||||||
}
|
}
|
||||||
|
if lifecycle.StopSignal != nil {
|
||||||
|
allErrs = append(allErrs, validateStopSignal(lifecycle.StopSignal, fldPath.Child("stopSignal"), os)...)
|
||||||
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3494,7 +3538,7 @@ func validateFieldAllowList(value interface{}, allowedFields map[string]bool, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validateInitContainers is called by pod spec and template validation to validate the list of init containers
|
// validateInitContainers is called by pod spec and template validation to validate the list of init containers
|
||||||
func validateInitContainers(containers []core.Container, regularContainers []core.Container, volumes map[string]core.VolumeSource, podClaimNames sets.Set[string], gracePeriod *int64, fldPath *field.Path, opts PodValidationOptions, podRestartPolicy *core.RestartPolicy, hostUsers bool) field.ErrorList {
|
func validateInitContainers(containers []core.Container, os *core.PodOS, regularContainers []core.Container, volumes map[string]core.VolumeSource, podClaimNames sets.Set[string], gracePeriod *int64, fldPath *field.Path, opts PodValidationOptions, podRestartPolicy *core.RestartPolicy, hostUsers bool) field.ErrorList {
|
||||||
var allErrs field.ErrorList
|
var allErrs field.ErrorList
|
||||||
|
|
||||||
allNames := sets.Set[string]{}
|
allNames := sets.Set[string]{}
|
||||||
@ -3528,7 +3572,7 @@ func validateInitContainers(containers []core.Container, regularContainers []cor
|
|||||||
switch {
|
switch {
|
||||||
case restartAlways:
|
case restartAlways:
|
||||||
if ctr.Lifecycle != nil {
|
if ctr.Lifecycle != nil {
|
||||||
allErrs = append(allErrs, validateLifecycle(ctr.Lifecycle, gracePeriod, idxPath.Child("lifecycle"), opts)...)
|
allErrs = append(allErrs, validateLifecycle(ctr.Lifecycle, gracePeriod, idxPath.Child("lifecycle"), opts, os)...)
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, validateLivenessProbe(ctr.LivenessProbe, gracePeriod, idxPath.Child("livenessProbe"), opts)...)
|
allErrs = append(allErrs, validateLivenessProbe(ctr.LivenessProbe, gracePeriod, idxPath.Child("livenessProbe"), opts)...)
|
||||||
allErrs = append(allErrs, validateReadinessProbe(ctr.ReadinessProbe, gracePeriod, idxPath.Child("readinessProbe"), opts)...)
|
allErrs = append(allErrs, validateReadinessProbe(ctr.ReadinessProbe, gracePeriod, idxPath.Child("readinessProbe"), opts)...)
|
||||||
@ -3632,7 +3676,7 @@ func validateHostUsers(spec *core.PodSpec, fldPath *field.Path) field.ErrorList
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validateContainers is called by pod spec and template validation to validate the list of regular containers.
|
// validateContainers is called by pod spec and template validation to validate the list of regular containers.
|
||||||
func validateContainers(containers []core.Container, volumes map[string]core.VolumeSource, podClaimNames sets.Set[string], gracePeriod *int64, fldPath *field.Path, opts PodValidationOptions, podRestartPolicy *core.RestartPolicy, hostUsers bool) field.ErrorList {
|
func validateContainers(containers []core.Container, os *core.PodOS, volumes map[string]core.VolumeSource, podClaimNames sets.Set[string], gracePeriod *int64, fldPath *field.Path, opts PodValidationOptions, podRestartPolicy *core.RestartPolicy, hostUsers bool) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if len(containers) == 0 {
|
if len(containers) == 0 {
|
||||||
@ -3660,7 +3704,7 @@ func validateContainers(containers []core.Container, volumes map[string]core.Vol
|
|||||||
// Regular init container and ephemeral container validation will return
|
// Regular init container and ephemeral container validation will return
|
||||||
// field.Forbidden() for these paths.
|
// field.Forbidden() for these paths.
|
||||||
if ctr.Lifecycle != nil {
|
if ctr.Lifecycle != nil {
|
||||||
allErrs = append(allErrs, validateLifecycle(ctr.Lifecycle, gracePeriod, path.Child("lifecycle"), opts)...)
|
allErrs = append(allErrs, validateLifecycle(ctr.Lifecycle, gracePeriod, path.Child("lifecycle"), opts, os)...)
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, validateLivenessProbe(ctr.LivenessProbe, gracePeriod, path.Child("livenessProbe"), opts)...)
|
allErrs = append(allErrs, validateLivenessProbe(ctr.LivenessProbe, gracePeriod, path.Child("livenessProbe"), opts)...)
|
||||||
allErrs = append(allErrs, validateReadinessProbe(ctr.ReadinessProbe, gracePeriod, path.Child("readinessProbe"), opts)...)
|
allErrs = append(allErrs, validateReadinessProbe(ctr.ReadinessProbe, gracePeriod, path.Child("readinessProbe"), opts)...)
|
||||||
@ -4207,8 +4251,8 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi
|
|||||||
allErrs = append(allErrs, vErrs...)
|
allErrs = append(allErrs, vErrs...)
|
||||||
podClaimNames := gatherPodResourceClaimNames(spec.ResourceClaims)
|
podClaimNames := gatherPodResourceClaimNames(spec.ResourceClaims)
|
||||||
allErrs = append(allErrs, validatePodResourceClaims(podMeta, spec.ResourceClaims, fldPath.Child("resourceClaims"))...)
|
allErrs = append(allErrs, validatePodResourceClaims(podMeta, spec.ResourceClaims, fldPath.Child("resourceClaims"))...)
|
||||||
allErrs = append(allErrs, validateContainers(spec.Containers, vols, podClaimNames, gracePeriod, fldPath.Child("containers"), opts, &spec.RestartPolicy, hostUsers)...)
|
allErrs = append(allErrs, validateContainers(spec.Containers, spec.OS, vols, podClaimNames, gracePeriod, fldPath.Child("containers"), opts, &spec.RestartPolicy, hostUsers)...)
|
||||||
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, podClaimNames, gracePeriod, fldPath.Child("initContainers"), opts, &spec.RestartPolicy, hostUsers)...)
|
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.OS, spec.Containers, vols, podClaimNames, gracePeriod, fldPath.Child("initContainers"), opts, &spec.RestartPolicy, hostUsers)...)
|
||||||
allErrs = append(allErrs, validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, podClaimNames, fldPath.Child("ephemeralContainers"), opts, &spec.RestartPolicy, hostUsers)...)
|
allErrs = append(allErrs, validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, podClaimNames, fldPath.Child("ephemeralContainers"), opts, &spec.RestartPolicy, hostUsers)...)
|
||||||
|
|
||||||
if opts.PodLevelResourcesEnabled {
|
if opts.PodLevelResourcesEnabled {
|
||||||
|
@ -8372,6 +8372,7 @@ func TestValidateLinuxPodSecurityContext(t *testing.T) {
|
|||||||
|
|
||||||
func TestValidateContainers(t *testing.T) {
|
func TestValidateContainers(t *testing.T) {
|
||||||
volumeDevices := make(map[string]core.VolumeSource)
|
volumeDevices := make(map[string]core.VolumeSource)
|
||||||
|
podOS := &core.PodOS{Name: core.OSName(v1.Linux)}
|
||||||
capabilities.ResetForTest()
|
capabilities.ResetForTest()
|
||||||
capabilities.Initialize(capabilities.Capabilities{
|
capabilities.Initialize(capabilities.Capabilities{
|
||||||
AllowPrivileged: true,
|
AllowPrivileged: true,
|
||||||
@ -8568,11 +8569,19 @@ func TestValidateContainers(t *testing.T) {
|
|||||||
{ResourceName: "memory", RestartPolicy: "NotRequired"},
|
{ResourceName: "memory", RestartPolicy: "NotRequired"},
|
||||||
{ResourceName: "cpu", RestartPolicy: "RestartContainer"},
|
{ResourceName: "cpu", RestartPolicy: "RestartContainer"},
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
Name: "container-with-stopsignal-lifecycle",
|
||||||
|
Image: "image",
|
||||||
|
ImagePullPolicy: "IfNotPresent",
|
||||||
|
TerminationMessagePolicy: "File",
|
||||||
|
Lifecycle: &core.Lifecycle{
|
||||||
|
StopSignal: ptr.To(core.SIGTERM),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var PodRestartPolicy core.RestartPolicy = "Always"
|
var PodRestartPolicy core.RestartPolicy = "Always"
|
||||||
if errs := validateContainers(successCase, volumeDevices, nil, defaultGracePeriod, field.NewPath("field"), PodValidationOptions{}, &PodRestartPolicy, noUserNamespace); len(errs) != 0 {
|
if errs := validateContainers(successCase, podOS, volumeDevices, nil, defaultGracePeriod, field.NewPath("field"), PodValidationOptions{}, &PodRestartPolicy, noUserNamespace); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9187,7 +9196,7 @@ func TestValidateContainers(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range errorCases {
|
for _, tc := range errorCases {
|
||||||
t.Run(tc.title+"__@L"+tc.line, func(t *testing.T) {
|
t.Run(tc.title+"__@L"+tc.line, func(t *testing.T) {
|
||||||
errs := validateContainers(tc.containers, volumeDevices, nil, defaultGracePeriod, field.NewPath("containers"), PodValidationOptions{}, &PodRestartPolicy, noUserNamespace)
|
errs := validateContainers(tc.containers, podOS, volumeDevices, nil, defaultGracePeriod, field.NewPath("containers"), PodValidationOptions{}, &PodRestartPolicy, noUserNamespace)
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Fatal("expected error but received none")
|
t.Fatal("expected error but received none")
|
||||||
}
|
}
|
||||||
@ -9202,6 +9211,7 @@ func TestValidateContainers(t *testing.T) {
|
|||||||
|
|
||||||
func TestValidateInitContainers(t *testing.T) {
|
func TestValidateInitContainers(t *testing.T) {
|
||||||
volumeDevices := make(map[string]core.VolumeSource)
|
volumeDevices := make(map[string]core.VolumeSource)
|
||||||
|
podOS := &core.PodOS{Name: core.OSName(v1.Linux)}
|
||||||
capabilities.ResetForTest()
|
capabilities.ResetForTest()
|
||||||
capabilities.Initialize(capabilities.Capabilities{
|
capabilities.Initialize(capabilities.Capabilities{
|
||||||
AllowPrivileged: true,
|
AllowPrivileged: true,
|
||||||
@ -9285,7 +9295,7 @@ func TestValidateInitContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
var PodRestartPolicy core.RestartPolicy = "Never"
|
var PodRestartPolicy core.RestartPolicy = "Never"
|
||||||
if errs := validateInitContainers(successCase, containers, volumeDevices, nil, defaultGracePeriod, field.NewPath("field"), PodValidationOptions{AllowSidecarResizePolicy: true}, &PodRestartPolicy, noUserNamespace); len(errs) != 0 {
|
if errs := validateInitContainers(successCase, podOS, containers, volumeDevices, nil, defaultGracePeriod, field.NewPath("field"), PodValidationOptions{AllowSidecarResizePolicy: true}, &PodRestartPolicy, noUserNamespace); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9679,7 +9689,7 @@ func TestValidateInitContainers(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range errorCases {
|
for _, tc := range errorCases {
|
||||||
t.Run(tc.title+"__@L"+tc.line, func(t *testing.T) {
|
t.Run(tc.title+"__@L"+tc.line, func(t *testing.T) {
|
||||||
errs := validateInitContainers(tc.initContainers, containers, volumeDevices, nil, defaultGracePeriod, field.NewPath("initContainers"), PodValidationOptions{}, &PodRestartPolicy, noUserNamespace)
|
errs := validateInitContainers(tc.initContainers, podOS, containers, volumeDevices, nil, defaultGracePeriod, field.NewPath("initContainers"), PodValidationOptions{}, &PodRestartPolicy, noUserNamespace)
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Fatal("expected error but received none")
|
t.Fatal("expected error but received none")
|
||||||
}
|
}
|
||||||
@ -11051,6 +11061,22 @@ func TestValidatePod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
"Pod with valid StopSignal and valid OS": *podtest.MakePod("test-pod",
|
||||||
|
podtest.SetOS(core.Linux),
|
||||||
|
podtest.SetContainers(podtest.MakeContainer(
|
||||||
|
"test-container",
|
||||||
|
podtest.SetContainerImage("image"),
|
||||||
|
podtest.SetContainerLifecycle(core.Lifecycle{StopSignal: ptr.To(core.SIGTERM)}),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
"Pod with valid StopSignal and valid OS (Windows)": *podtest.MakePod("test-pod",
|
||||||
|
podtest.SetOS(core.Windows),
|
||||||
|
podtest.SetContainers(podtest.MakeContainer(
|
||||||
|
"test-container",
|
||||||
|
podtest.SetContainerImage("image"),
|
||||||
|
podtest.SetContainerLifecycle(core.Lifecycle{StopSignal: ptr.To(core.SIGTERM)}),
|
||||||
|
)),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range successCases {
|
for k, v := range successCases {
|
||||||
@ -12390,6 +12416,27 @@ func TestValidatePod(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
"Pod with a StopSignal without `spec.os.name`": {
|
||||||
|
expectedError: "spec.containers[0].lifecycle.stopSignal: Forbidden: may not be set for containers with empty `spec.os.name`",
|
||||||
|
spec: *podtest.MakePod("test-pod",
|
||||||
|
podtest.SetContainers(podtest.MakeContainer(
|
||||||
|
"test-container",
|
||||||
|
podtest.SetContainerImage("image"),
|
||||||
|
podtest.SetContainerLifecycle(core.Lifecycle{StopSignal: ptr.To(core.SIGTERM)}),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Pod with a StopSignal not supported by OS": {
|
||||||
|
expectedError: "spec.containers[0].lifecycle.stopSignal: Unsupported value: \"SIGHUP\": supported values: \"SIGKILL\", \"SIGTERM\"",
|
||||||
|
spec: *podtest.MakePod("test-pod",
|
||||||
|
podtest.SetOS(core.Windows),
|
||||||
|
podtest.SetContainers(podtest.MakeContainer(
|
||||||
|
"test-container",
|
||||||
|
podtest.SetContainerImage("image"),
|
||||||
|
podtest.SetContainerLifecycle(core.Lifecycle{StopSignal: ptr.To(core.SIGHUP)}),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
@ -26968,3 +27015,58 @@ func TestValidateNodeSwapStatus(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateStopSignal(t *testing.T) {
|
||||||
|
fldPath := field.NewPath("root")
|
||||||
|
sigkill := core.SIGKILL
|
||||||
|
sigterm := core.SIGTERM
|
||||||
|
sighup := core.SIGHUP
|
||||||
|
linux := core.PodOS{Name: core.Linux}
|
||||||
|
windows := core.PodOS{Name: core.Windows}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
stopsignal *core.Signal
|
||||||
|
os *core.PodOS
|
||||||
|
expectErr field.ErrorList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Empty spec.os.name",
|
||||||
|
stopsignal: &sigterm,
|
||||||
|
os: nil,
|
||||||
|
expectErr: field.ErrorList{field.Forbidden(fldPath, "may not be set for containers with empty `spec.os.name`")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid signal passed to windows pod",
|
||||||
|
stopsignal: &sighup,
|
||||||
|
os: &windows,
|
||||||
|
expectErr: field.ErrorList{field.NotSupported(fldPath, &sighup, sets.List(supportedStopSignalsWindows))},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid signal passed to windows pod",
|
||||||
|
stopsignal: &sigkill,
|
||||||
|
os: &windows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid signal passed to linux pod",
|
||||||
|
stopsignal: &sighup,
|
||||||
|
os: &linux,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
errs := validateStopSignal(tc.stopsignal, fldPath, tc.os)
|
||||||
|
|
||||||
|
if len(tc.expectErr) > 0 && len(errs) == 0 {
|
||||||
|
t.Errorf("Unexpected success")
|
||||||
|
} else if len(tc.expectErr) == 0 && len(errs) != 0 {
|
||||||
|
t.Errorf("Unexpected error(s): %v", errs)
|
||||||
|
} else if len(tc.expectErr) > 0 {
|
||||||
|
if tc.expectErr[0].Error() != errs[0].Error() {
|
||||||
|
t.Errorf("Unexpected error(s): %v", errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user