mirror of
https://github.com/optim-enterprises-bv/kubernetes.git
synced 2025-11-03 11:48:15 +00:00
validate resize request
We validate resize request by ensuring that pod QoS is unchanged and only cpu and memory resources and resize policies are mutated.
This commit is contained in:
@@ -5135,16 +5135,6 @@ var updatablePodSpecFields = []string{
|
||||
"`spec.activeDeadlineSeconds`",
|
||||
"`spec.tolerations` (only additions to existing tolerations)",
|
||||
"`spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)",
|
||||
"`spec.containers[*].resources` (for CPU/memory only)",
|
||||
}
|
||||
|
||||
// TODO(vinaykul,InPlacePodVerticalScaling): Drop this var once InPlacePodVerticalScaling goes GA and featuregate is gone.
|
||||
var updatablePodSpecFieldsNoResources = []string{
|
||||
"`spec.containers[*].image`",
|
||||
"`spec.initContainers[*].image`",
|
||||
"`spec.activeDeadlineSeconds`",
|
||||
"`spec.tolerations` (only additions to existing tolerations)",
|
||||
"`spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)",
|
||||
}
|
||||
|
||||
// ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
|
||||
@@ -5206,45 +5196,12 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel
|
||||
return allErrs
|
||||
}
|
||||
|
||||
if qos.GetPodQOS(oldPod) != qos.ComputePodQOS(newPod) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, newPod.Status.QOSClass, "Pod QoS is immutable"))
|
||||
}
|
||||
|
||||
// handle updateable fields by munging those fields prior to deep equal comparison.
|
||||
mungedPodSpec := *newPod.Spec.DeepCopy()
|
||||
// munge spec.containers[*].image
|
||||
var newContainers []core.Container
|
||||
for ix, container := range mungedPodSpec.Containers {
|
||||
container.Image = oldPod.Spec.Containers[ix].Image // +k8s:verify-mutation:reason=clone
|
||||
// When the feature-gate is turned off, any new requests attempting to update CPU or memory
|
||||
// resource values will result in validation failure.
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
// Resources are mutable for CPU & memory only
|
||||
// - user can now modify Resources to express new desired Resources
|
||||
mungeCpuMemResources := func(resourceList, oldResourceList core.ResourceList) core.ResourceList {
|
||||
if oldResourceList == nil {
|
||||
return nil
|
||||
}
|
||||
var mungedResourceList core.ResourceList
|
||||
if resourceList == nil {
|
||||
mungedResourceList = make(core.ResourceList)
|
||||
} else {
|
||||
mungedResourceList = resourceList.DeepCopy()
|
||||
}
|
||||
delete(mungedResourceList, core.ResourceCPU)
|
||||
delete(mungedResourceList, core.ResourceMemory)
|
||||
if cpu, found := oldResourceList[core.ResourceCPU]; found {
|
||||
mungedResourceList[core.ResourceCPU] = cpu
|
||||
}
|
||||
if mem, found := oldResourceList[core.ResourceMemory]; found {
|
||||
mungedResourceList[core.ResourceMemory] = mem
|
||||
}
|
||||
return mungedResourceList
|
||||
}
|
||||
lim := mungeCpuMemResources(container.Resources.Limits, oldPod.Spec.Containers[ix].Resources.Limits)
|
||||
req := mungeCpuMemResources(container.Resources.Requests, oldPod.Spec.Containers[ix].Resources.Requests)
|
||||
container.Resources = core.ResourceRequirements{Limits: lim, Requests: req}
|
||||
}
|
||||
newContainers = append(newContainers, container)
|
||||
}
|
||||
mungedPodSpec.Containers = newContainers
|
||||
@@ -5321,10 +5278,7 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel
|
||||
// This diff isn't perfect, but it's a helluva lot better an "I'm not going to tell you what the difference is".
|
||||
// TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
|
||||
specDiff := cmp.Diff(oldPod.Spec, mungedPodSpec)
|
||||
errs := field.Forbidden(specPath, fmt.Sprintf("pod updates may not change fields other than %s\n%v", strings.Join(updatablePodSpecFieldsNoResources, ","), specDiff))
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||
errs = field.Forbidden(specPath, fmt.Sprintf("pod updates may not change fields other than %s\n%v", strings.Join(updatablePodSpecFields, ","), specDiff))
|
||||
}
|
||||
errs := field.Forbidden(specPath, fmt.Sprintf("pod updates may not change fields other than %s\n%v", strings.Join(updatablePodSpecFields, ","), specDiff))
|
||||
allErrs = append(allErrs, errs)
|
||||
}
|
||||
return allErrs
|
||||
@@ -5531,6 +5485,65 @@ func ValidatePodEphemeralContainersUpdate(newPod, oldPod *core.Pod, opts PodVali
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidatePodResize(newPod, oldPod *core.Pod, opts PodValidationOptions) field.ErrorList {
|
||||
// Part 1: Validate newPod's spec and updates to metadata
|
||||
fldPath := field.NewPath("metadata")
|
||||
allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
|
||||
allErrs = append(allErrs, validatePodMetadataAndSpec(newPod, opts)...)
|
||||
allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"), opts)...)
|
||||
|
||||
// static pods cannot be resized.
|
||||
if _, ok := oldPod.Annotations[core.MirrorPodAnnotationKey]; ok {
|
||||
return field.ErrorList{field.Forbidden(field.NewPath(""), "static pods cannot be resized")}
|
||||
}
|
||||
|
||||
// Part 2: Validate that the changes between oldPod.Spec.Containers[].Resources and
|
||||
// newPod.Spec.Containers[].Resources are allowed.
|
||||
specPath := field.NewPath("spec")
|
||||
if qos.GetPodQOS(oldPod) != qos.ComputePodQOS(newPod) {
|
||||
allErrs = append(allErrs, field.Invalid(specPath, newPod.Status.QOSClass, "Pod QoS is immutable"))
|
||||
}
|
||||
|
||||
// Ensure that only CPU and memory resources are mutable.
|
||||
mungedPodSpec := *newPod.Spec.DeepCopy()
|
||||
var newContainers []core.Container
|
||||
for ix, container := range mungedPodSpec.Containers {
|
||||
mungeCPUMemResources := func(resourceList, oldResourceList core.ResourceList) core.ResourceList {
|
||||
if oldResourceList == nil {
|
||||
return nil
|
||||
}
|
||||
var mungedResourceList core.ResourceList
|
||||
if resourceList == nil {
|
||||
mungedResourceList = make(core.ResourceList)
|
||||
} else {
|
||||
mungedResourceList = resourceList.DeepCopy()
|
||||
}
|
||||
delete(mungedResourceList, core.ResourceCPU)
|
||||
delete(mungedResourceList, core.ResourceMemory)
|
||||
if cpu, found := oldResourceList[core.ResourceCPU]; found {
|
||||
mungedResourceList[core.ResourceCPU] = cpu
|
||||
}
|
||||
if mem, found := oldResourceList[core.ResourceMemory]; found {
|
||||
mungedResourceList[core.ResourceMemory] = mem
|
||||
}
|
||||
return mungedResourceList
|
||||
}
|
||||
lim := mungeCPUMemResources(container.Resources.Limits, oldPod.Spec.Containers[ix].Resources.Limits)
|
||||
req := mungeCPUMemResources(container.Resources.Requests, oldPod.Spec.Containers[ix].Resources.Requests)
|
||||
container.Resources = core.ResourceRequirements{Limits: lim, Requests: req}
|
||||
container.ResizePolicy = oldPod.Spec.Containers[ix].ResizePolicy // +k8s:verify-mutation:reason=clone
|
||||
newContainers = append(newContainers, container)
|
||||
}
|
||||
mungedPodSpec.Containers = newContainers
|
||||
if !apiequality.Semantic.DeepEqual(mungedPodSpec, oldPod.Spec) {
|
||||
// This likely means that the user has made changes to CPU and Memory resources.
|
||||
specDiff := cmp.Diff(oldPod.Spec, mungedPodSpec)
|
||||
errs := field.Forbidden(specPath, fmt.Sprintf("pod resize may not change fields other than cpu and memory\n%v", specDiff))
|
||||
allErrs = append(allErrs, errs)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePodBinding tests if required fields in the pod binding are legal.
|
||||
func ValidatePodBinding(binding *core.Binding) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
Reference in New Issue
Block a user