diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index 3cb76ab2624..2e8cce54ae3 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -5382,6 +5382,9 @@ func ValidatePodStatusUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions } } + // Pod QoS is immutable + allErrs = append(allErrs, ValidateImmutableField(newPod.Status.QOSClass, oldPod.Status.QOSClass, fldPath.Child("qosClass"))...) + // If pod should not restart, make sure the status update does not transition // any terminated containers to a non-terminated state. allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.ContainerStatuses, oldPod.Status.ContainerStatuses, fldPath.Child("containerStatuses"), oldPod.Spec.RestartPolicy)...) diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index d1649032573..d93c1a44878 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -14454,6 +14454,32 @@ func TestValidatePodStatusUpdate(t *testing.T) { ), "", "restartable init container can restart if RestartPolicyAlways", + }, { + *podtest.MakePod("foo", + podtest.SetStatus(core.PodStatus{ + QOSClass: core.PodQOSBurstable, + }), + ), + *podtest.MakePod("foo", + podtest.SetStatus(core.PodStatus{ + QOSClass: core.PodQOSGuaranteed, + }), + ), + "tatus.qosClass: Invalid value: \"Burstable\": field is immutable", + "qosClass can not be changed", + }, { + *podtest.MakePod("foo", + podtest.SetStatus(core.PodStatus{ + QOSClass: core.PodQOSBurstable, + }), + ), + *podtest.MakePod("foo", + podtest.SetStatus(core.PodStatus{ + QOSClass: core.PodQOSBurstable, + }), + ), + "", + "qosClass no change", }, } diff --git a/pkg/registry/core/pod/strategy.go b/pkg/registry/core/pod/strategy.go index 3478fcc69b2..541fc43a220 100644 --- a/pkg/registry/core/pod/strategy.go +++ b/pkg/registry/core/pod/strategy.go @@ -226,6 +226,11 @@ func (podStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime. // don't allow the pods/status endpoint to touch owner references since old kubelets corrupt them in a way // that breaks garbage collection newPod.OwnerReferences = oldPod.OwnerReferences + // the Pod QoS is immutable and populated at creation time by the kube-apiserver. + // we need to backfill it for backward compatibility because the old kubelet dropped this field when the pod was rejected. + if newPod.Status.QOSClass == "" { + newPod.Status.QOSClass = oldPod.Status.QOSClass + } } func (podStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {