From 6ea6e76daf9571aa330e7f8a70e0894d82ccc62b Mon Sep 17 00:00:00 2001 From: Ania Borowiec Date: Thu, 21 May 2026 23:24:36 +0000 Subject: [PATCH 1/2] Add PreemptionPolicy field to PodGroupSpec --- api/openapi-spec/swagger.json | 8 ++ ...__scheduling.k8s.io__v1alpha2_openapi.json | 9 ++ pkg/apis/scheduling/fuzzer/fuzzer.go | 4 + pkg/apis/scheduling/types.go | 21 ++++ .../v1alpha2/zz_generated.conversion.go | 12 ++- .../v1alpha2/zz_generated.defaults.go | 5 + .../v1alpha2/zz_generated.validations.go | 77 ++++++++++++++ pkg/apis/scheduling/validation/validation.go | 16 ++- .../scheduling/validation/validation_test.go | 36 +++++++ pkg/apis/scheduling/zz_generated.deepcopy.go | 10 ++ pkg/generated/openapi/zz_generated.openapi.go | 17 ++++ pkg/registry/scheduling/podgroup/strategy.go | 15 +++ .../scheduling/podgroup/strategy_test.go | 49 ++++++++- pkg/registry/scheduling/workload/strategy.go | 15 +++ .../scheduling/workload/strategy_test.go | 77 +++++++++++++- .../preemption/podgrouppreemption_test.go | 96 +++++++++++++++++- pkg/scheduler/framework/preemption/types.go | 6 +- pkg/scheduler/testing/wrappers.go | 12 +++ .../api/scheduling/v1alpha2/generated.pb.go | 91 +++++++++++++++++ .../api/scheduling/v1alpha2/generated.proto | 28 +++++ .../k8s.io/api/scheduling/v1alpha2/types.go | 28 +++++ .../v1alpha2/types_swagger_doc_generated.go | 2 + .../v1alpha2/zz_generated.deepcopy.go | 15 ++- .../scheduling.k8s.io.v1alpha2.PodGroup.json | 3 +- .../scheduling.k8s.io.v1alpha2.PodGroup.pb | Bin 701 -> 724 bytes .../scheduling.k8s.io.v1alpha2.PodGroup.yaml | 1 + .../scheduling.k8s.io.v1alpha2.Workload.json | 3 +- .../scheduling.k8s.io.v1alpha2.Workload.pb | Bin 600 -> 623 bytes .../scheduling.k8s.io.v1alpha2.Workload.yaml | 1 + .../applyconfigurations/internal/internal.go | 7 ++ .../scheduling/v1alpha2/podgroupspec.go | 16 +++ .../scheduling/v1alpha2/podgrouptemplate.go | 15 +++ .../podgroup/declarative_validation_test.go | 49 +++++++-- .../zz_generated.validations.v1alpha2_test.go | 5 + .../workload/declarative_validation_test.go | 42 ++++++-- .../zz_generated.validations.v1alpha2_test.go | 3 + .../preemption/podgrouppreemption_test.go | 23 +++++ 37 files changed, 781 insertions(+), 36 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 0ac34e43f19..66077b4a39d 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -19980,6 +19980,10 @@ "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha2.PodGroupTemplateReference", "description": "PodGroupTemplateRef references an optional PodGroup template within other object (e.g. Workload) that was used to create the PodGroup. This field is immutable." }, + "preemptionPolicy": { + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is immutable. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", + "type": "string" + }, "priority": { "description": "Priority is the value of priority of this pod group. Various system components use this field to find the priority of the pod group. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. This field is immutable. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", "format": "int32", @@ -20059,6 +20063,10 @@ "description": "Name is a unique identifier for the PodGroupTemplate within the Workload. It must be a DNS label. This field is immutable.", "type": "string" }, + "preemptionPolicy": { + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", + "type": "string" + }, "priority": { "description": "Priority is the value of priority of pod groups created from this template. Various system components use this field to find the priority of the pod group. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", "format": "int32", diff --git a/api/openapi-spec/v3/apis__scheduling.k8s.io__v1alpha2_openapi.json b/api/openapi-spec/v3/apis__scheduling.k8s.io__v1alpha2_openapi.json index 1c38c983b21..6257b3e731e 100644 --- a/api/openapi-spec/v3/apis__scheduling.k8s.io__v1alpha2_openapi.json +++ b/api/openapi-spec/v3/apis__scheduling.k8s.io__v1alpha2_openapi.json @@ -211,6 +211,11 @@ ], "description": "PodGroupTemplateRef references an optional PodGroup template within other object (e.g. Workload) that was used to create the PodGroup. This field is immutable." }, + "preemptionPolicy": { + "default": "PreemptLowerPriority", + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is immutable. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", + "type": "string" + }, "priority": { "description": "Priority is the value of priority of this pod group. Various system components use this field to find the priority of the pod group. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. This field is immutable. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", "format": "int32", @@ -300,6 +305,10 @@ "description": "Name is a unique identifier for the PodGroupTemplate within the Workload. It must be a DNS label. This field is immutable.", "type": "string" }, + "preemptionPolicy": { + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", + "type": "string" + }, "priority": { "description": "Priority is the value of priority of pod groups created from this template. Various system components use this field to find the priority of the pod group. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", "format": "int32", diff --git a/pkg/apis/scheduling/fuzzer/fuzzer.go b/pkg/apis/scheduling/fuzzer/fuzzer.go index 8142ef2e48c..33d6a03f8ca 100644 --- a/pkg/apis/scheduling/fuzzer/fuzzer.go +++ b/pkg/apis/scheduling/fuzzer/fuzzer.go @@ -38,6 +38,10 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { if s.Spec.DisruptionMode == nil { s.Spec.DisruptionMode = new(scheduling.DisruptionModePod) } + if s.Spec.PreemptionPolicy == nil { + preemptLowerPriority := core.PreemptLowerPriority + s.Spec.PreemptionPolicy = &preemptLowerPriority + } }, } } diff --git a/pkg/apis/scheduling/types.go b/pkg/apis/scheduling/types.go index dd7950f702b..f07ddf87686 100644 --- a/pkg/apis/scheduling/types.go +++ b/pkg/apis/scheduling/types.go @@ -244,6 +244,16 @@ type PodGroupTemplate struct { // +featureGate=WorkloadAwarePreemption // +optional Priority *int32 + + // PreemptionPolicy is the Policy for preempting pods with lower priority. + // One of Never, PreemptLowerPriority. + // Defaults to PreemptLowerPriority if unset. + // This field is available only when the WorkloadAwarePreemption feature gate + // is enabled. + // + // +featureGate=WorkloadAwarePreemption + // +optional + PreemptionPolicy *core.PreemptionPolicy } // PodGroupSchedulingPolicy defines the scheduling configuration for a PodGroup. @@ -456,6 +466,17 @@ type PodGroupSpec struct { // +featureGate=WorkloadAwarePreemption // +optional Priority *int32 + + // PreemptionPolicy is the Policy for preempting pods with lower priority. + // One of Never, PreemptLowerPriority. + // Defaults to PreemptLowerPriority if unset. + // This field is immutable. + // This field is available only when the WorkloadAwarePreemption feature gate + // is enabled. + // + // +featureGate=WorkloadAwarePreemption + // +optional + PreemptionPolicy *core.PreemptionPolicy } // PodGroupStatus represents information about the status of a pod group. diff --git a/pkg/apis/scheduling/v1alpha2/zz_generated.conversion.go b/pkg/apis/scheduling/v1alpha2/zz_generated.conversion.go index 673ca5b222c..737b2a925a7 100644 --- a/pkg/apis/scheduling/v1alpha2/zz_generated.conversion.go +++ b/pkg/apis/scheduling/v1alpha2/zz_generated.conversion.go @@ -24,10 +24,12 @@ package v1alpha2 import ( unsafe "unsafe" + v1 "k8s.io/api/core/v1" schedulingv1alpha2 "k8s.io/api/scheduling/v1alpha2" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" + core "k8s.io/kubernetes/pkg/apis/core" scheduling "k8s.io/kubernetes/pkg/apis/scheduling" ) @@ -411,6 +413,7 @@ func autoConvert_v1alpha2_PodGroupSpec_To_scheduling_PodGroupSpec(in *scheduling out.DisruptionMode = (*scheduling.DisruptionMode)(unsafe.Pointer(in.DisruptionMode)) out.PriorityClassName = in.PriorityClassName out.Priority = (*int32)(unsafe.Pointer(in.Priority)) + out.PreemptionPolicy = (*core.PreemptionPolicy)(unsafe.Pointer(in.PreemptionPolicy)) return nil } @@ -429,6 +432,7 @@ func autoConvert_scheduling_PodGroupSpec_To_v1alpha2_PodGroupSpec(in *scheduling out.DisruptionMode = (*schedulingv1alpha2.DisruptionMode)(unsafe.Pointer(in.DisruptionMode)) out.PriorityClassName = in.PriorityClassName out.Priority = (*int32)(unsafe.Pointer(in.Priority)) + out.PreemptionPolicy = (*v1.PreemptionPolicy)(unsafe.Pointer(in.PreemptionPolicy)) return nil } @@ -438,7 +442,7 @@ func Convert_scheduling_PodGroupSpec_To_v1alpha2_PodGroupSpec(in *scheduling.Pod } func autoConvert_v1alpha2_PodGroupStatus_To_scheduling_PodGroupStatus(in *schedulingv1alpha2.PodGroupStatus, out *scheduling.PodGroupStatus, s conversion.Scope) error { - out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions)) + out.Conditions = *(*[]metav1.Condition)(unsafe.Pointer(&in.Conditions)) out.ResourceClaimStatuses = *(*[]scheduling.PodGroupResourceClaimStatus)(unsafe.Pointer(&in.ResourceClaimStatuses)) return nil } @@ -449,7 +453,7 @@ func Convert_v1alpha2_PodGroupStatus_To_scheduling_PodGroupStatus(in *scheduling } func autoConvert_scheduling_PodGroupStatus_To_v1alpha2_PodGroupStatus(in *scheduling.PodGroupStatus, out *schedulingv1alpha2.PodGroupStatus, s conversion.Scope) error { - out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions)) + out.Conditions = *(*[]metav1.Condition)(unsafe.Pointer(&in.Conditions)) out.ResourceClaimStatuses = *(*[]schedulingv1alpha2.PodGroupResourceClaimStatus)(unsafe.Pointer(&in.ResourceClaimStatuses)) return nil } @@ -469,6 +473,7 @@ func autoConvert_v1alpha2_PodGroupTemplate_To_scheduling_PodGroupTemplate(in *sc out.DisruptionMode = (*scheduling.DisruptionMode)(unsafe.Pointer(in.DisruptionMode)) out.PriorityClassName = in.PriorityClassName out.Priority = (*int32)(unsafe.Pointer(in.Priority)) + out.PreemptionPolicy = (*core.PreemptionPolicy)(unsafe.Pointer(in.PreemptionPolicy)) return nil } @@ -487,6 +492,7 @@ func autoConvert_scheduling_PodGroupTemplate_To_v1alpha2_PodGroupTemplate(in *sc out.DisruptionMode = (*schedulingv1alpha2.DisruptionMode)(unsafe.Pointer(in.DisruptionMode)) out.PriorityClassName = in.PriorityClassName out.Priority = (*int32)(unsafe.Pointer(in.Priority)) + out.PreemptionPolicy = (*v1.PreemptionPolicy)(unsafe.Pointer(in.PreemptionPolicy)) return nil } diff --git a/pkg/apis/scheduling/v1alpha2/zz_generated.defaults.go b/pkg/apis/scheduling/v1alpha2/zz_generated.defaults.go index cd1725ad90f..f3b56e46797 100644 --- a/pkg/apis/scheduling/v1alpha2/zz_generated.defaults.go +++ b/pkg/apis/scheduling/v1alpha2/zz_generated.defaults.go @@ -22,6 +22,7 @@ limitations under the License. package v1alpha2 import ( + v1 "k8s.io/api/core/v1" schedulingv1alpha2 "k8s.io/api/scheduling/v1alpha2" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -40,6 +41,10 @@ func SetObjectDefaults_PodGroup(in *schedulingv1alpha2.PodGroup) { var ptrVar1 schedulingv1alpha2.DisruptionMode = "Pod" in.Spec.DisruptionMode = &ptrVar1 } + if in.Spec.PreemptionPolicy == nil { + var ptrVar1 v1.PreemptionPolicy = "PreemptLowerPriority" + in.Spec.PreemptionPolicy = &ptrVar1 + } } func SetObjectDefaults_PodGroupList(in *schedulingv1alpha2.PodGroupList) { diff --git a/pkg/apis/scheduling/v1alpha2/zz_generated.validations.go b/pkg/apis/scheduling/v1alpha2/zz_generated.validations.go index 4cb9ab0b0c5..228a4920140 100644 --- a/pkg/apis/scheduling/v1alpha2/zz_generated.validations.go +++ b/pkg/apis/scheduling/v1alpha2/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + v1 "k8s.io/api/core/v1" schedulingv1alpha2 "k8s.io/api/scheduling/v1alpha2" equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" @@ -770,6 +771,47 @@ func Validate_PodGroupSpec( errs = append(errs, fn(fldPath.Child("priority"), obj.Priority, oldVal, oldObj != nil)...) } + { // field schedulingv1alpha2.PodGroupSpec.PreemptionPolicy + fn := func( + fldPath *field.Path, + obj, oldObj *v1.PreemptionPolicy, + oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update { + if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) { + return nil + } + } + // call field-attached validations + earlyReturn := false + if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "WorkloadAwarePreemption", false, validate.ForbiddenPointer).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "WorkloadAwarePreemption", false, validate.OptionalPointer).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "WorkloadAwarePreemption", true, // optional fields with default values are effectively required + validate.RequiredPointer).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "WorkloadAwarePreemption", true, validate.Immutable).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *schedulingv1alpha2.PodGroupSpec) *v1.PreemptionPolicy { + return oldObj.PreemptionPolicy + }) + errs = append(errs, fn(fldPath.Child("preemptionPolicy"), obj.PreemptionPolicy, oldVal, oldObj != nil)...) + } + return errs } @@ -1089,6 +1131,41 @@ func Validate_PodGroupTemplate( errs = append(errs, fn(fldPath.Child("priority"), obj.Priority, oldVal, oldObj != nil)...) } + { // field schedulingv1alpha2.PodGroupTemplate.PreemptionPolicy + fn := func( + fldPath *field.Path, + obj, oldObj *v1.PreemptionPolicy, + oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update { + if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) { + return nil + } + } + // call field-attached validations + earlyReturn := false + if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "WorkloadAwarePreemption", false, validate.ForbiddenPointer).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "WorkloadAwarePreemption", false, validate.OptionalPointer).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "WorkloadAwarePreemption", true, validate.OptionalPointer).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *schedulingv1alpha2.PodGroupTemplate) *v1.PreemptionPolicy { + return oldObj.PreemptionPolicy + }) + errs = append(errs, fn(fldPath.Child("preemptionPolicy"), obj.PreemptionPolicy, oldVal, oldObj != nil)...) + } + return errs } diff --git a/pkg/apis/scheduling/validation/validation.go b/pkg/apis/scheduling/validation/validation.go index 16807cefefd..8d4e0035d69 100644 --- a/pkg/apis/scheduling/validation/validation.go +++ b/pkg/apis/scheduling/validation/validation.go @@ -70,7 +70,11 @@ func ValidatePriorityClassUpdate(pc, oldPc *scheduling.PriorityClass) field.Erro // ValidatePodGroup tests if all fields in a PodGroup are set correctly. func ValidatePodGroup(podGroup *scheduling.PodGroup) field.ErrorList { - return apivalidation.ValidateObjectMeta(&podGroup.ObjectMeta, true, apivalidation.ValidatePodGroupName, field.NewPath("metadata")) + allErrs := apivalidation.ValidateObjectMeta(&podGroup.ObjectMeta, true, apivalidation.ValidatePodGroupName, field.NewPath("metadata")) + if podGroup.Spec.PreemptionPolicy != nil { + allErrs = append(allErrs, apivalidation.ValidatePreemptionPolicy(podGroup.Spec.PreemptionPolicy, field.NewPath("spec", "preemptionPolicy"))...) + } + return allErrs } // ValidatePodGroupUpdate tests if an update to PodGroup is valid. @@ -80,7 +84,15 @@ func ValidatePodGroupUpdate(podGroup, oldPodGroup *scheduling.PodGroup) field.Er // ValidateWorkload tests if all fields in a Workload are set correctly. func ValidateWorkload(workload *scheduling.Workload) field.ErrorList { - return apivalidation.ValidateObjectMeta(&workload.ObjectMeta, true, validateWorkloadName, field.NewPath("metadata")) + allErrs := apivalidation.ValidateObjectMeta(&workload.ObjectMeta, true, validateWorkloadName, field.NewPath("metadata")) + fldPath := field.NewPath("spec", "podGroupTemplates") + for i, template := range workload.Spec.PodGroupTemplates { + idxPath := fldPath.Index(i) + if template.PreemptionPolicy != nil { + allErrs = append(allErrs, apivalidation.ValidatePreemptionPolicy(template.PreemptionPolicy, idxPath.Child("preemptionPolicy"))...) + } + } + return allErrs } // ValidateWorkloadUpdate tests if an update to Workload is valid. diff --git a/pkg/apis/scheduling/validation/validation_test.go b/pkg/apis/scheduling/validation/validation_test.go index 9060b4cbd23..20cd9725d30 100644 --- a/pkg/apis/scheduling/validation/validation_test.go +++ b/pkg/apis/scheduling/validation/validation_test.go @@ -191,6 +191,15 @@ func TestValidateWorkload(t *testing.T) { "no scheduling constraints": mkWorkload(func(w *scheduling.Workload) { w.Spec.PodGroupTemplates[1].SchedulingConstraints = nil }), + "default preemption policy (nil)": mkWorkload(), // preemption policy is unset (nil) by default + "valid preemption policy (Never)": mkWorkload(func(w *scheduling.Workload) { + preemptNever := core.PreemptNever + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptNever + }), + "valid preemption policy (PreemptLowerPriority)": mkWorkload(func(w *scheduling.Workload) { + preemptLowerPriority := core.PreemptLowerPriority + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptLowerPriority + }), } for name, workload := range successCases { errs := ValidateWorkload(workload) @@ -251,6 +260,15 @@ func TestValidateWorkload(t *testing.T) { field.Invalid(field.NewPath("metadata", "namespace"), strings.Repeat("n", 64), "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')"), }, }, + "invalid preemption policy": { + workload: mkWorkload(func(w *scheduling.Workload) { + invalidPolicy := core.PreemptionPolicy("Invalid") + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &invalidPolicy + }), + expectedErrs: field.ErrorList{ + field.NotSupported(field.NewPath("spec", "podGroupTemplates").Index(0).Child("preemptionPolicy"), "Invalid", []string{"PreemptLowerPriority", "Never"}), + }, + }, } for name, tc := range failureCases { @@ -363,6 +381,15 @@ func TestValidatePodGroup(t *testing.T) { "no scheduling constraints": mkPodGroup(func(pg *scheduling.PodGroup) { pg.Spec.SchedulingConstraints = nil }), + "default preemption policy (nil)": mkPodGroup(), // preemption policy is unset (nil) by default + "valid preemption policy (Never)": mkPodGroup(func(pg *scheduling.PodGroup) { + preemptNever := core.PreemptNever + pg.Spec.PreemptionPolicy = &preemptNever + }), + "valid preemption policy (PreemptLowerPriority)": mkPodGroup(func(pg *scheduling.PodGroup) { + preemptLowerPriority := core.PreemptLowerPriority + pg.Spec.PreemptionPolicy = &preemptLowerPriority + }), } for name, podGroup := range successCases { errs := ValidatePodGroup(podGroup) @@ -423,6 +450,15 @@ func TestValidatePodGroup(t *testing.T) { field.Invalid(field.NewPath("metadata", "namespace"), strings.Repeat("n", 64), "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')"), }, }, + "invalid preemption policy": { + podGroup: mkPodGroup(func(pg *scheduling.PodGroup) { + invalidPolicy := core.PreemptionPolicy("Invalid") + pg.Spec.PreemptionPolicy = &invalidPolicy + }), + expectedErrs: field.ErrorList{ + field.NotSupported(field.NewPath("spec", "preemptionPolicy"), "Invalid", []string{"PreemptLowerPriority", "Never"}), + }, + }, } for name, tc := range failureCases { diff --git a/pkg/apis/scheduling/zz_generated.deepcopy.go b/pkg/apis/scheduling/zz_generated.deepcopy.go index 636a9590aec..857ea4a4c41 100644 --- a/pkg/apis/scheduling/zz_generated.deepcopy.go +++ b/pkg/apis/scheduling/zz_generated.deepcopy.go @@ -245,6 +245,11 @@ func (in *PodGroupSpec) DeepCopyInto(out *PodGroupSpec) { *out = new(int32) **out = **in } + if in.PreemptionPolicy != nil { + in, out := &in.PreemptionPolicy, &out.PreemptionPolicy + *out = new(core.PreemptionPolicy) + **out = **in + } return } @@ -314,6 +319,11 @@ func (in *PodGroupTemplate) DeepCopyInto(out *PodGroupTemplate) { *out = new(int32) **out = **in } + if in.PreemptionPolicy != nil { + in, out := &in.PreemptionPolicy, &out.PreemptionPolicy + *out = new(core.PreemptionPolicy) + **out = **in + } return } diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 6e05d1c82bf..409363e23ed 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -54259,6 +54259,15 @@ func schema_k8sio_api_scheduling_v1alpha2_PodGroupSpec(ref common.ReferenceCallb Format: "int32", }, }, + "preemptionPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is immutable. This field is available only when the WorkloadAwarePreemption feature gate is enabled.\n\n\nPossible enum values:\n - `\"Never\"` means that pod never preempts other pods with lower priority.\n - `\"PreemptLowerPriority\"` means that pod can preempt other pods with lower priority.", + Default: "PreemptLowerPriority", + Type: []string{"string"}, + Format: "", + Enum: []interface{}{"Never", "PreemptLowerPriority"}, + }, + }, }, Required: []string{"schedulingPolicy"}, }, @@ -54402,6 +54411,14 @@ func schema_k8sio_api_scheduling_v1alpha2_PodGroupTemplate(ref common.ReferenceC Format: "int32", }, }, + "preemptionPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is available only when the WorkloadAwarePreemption feature gate is enabled.\n\n\nPossible enum values:\n - `\"Never\"` means that pod never preempts other pods with lower priority.\n - `\"PreemptLowerPriority\"` means that pod can preempt other pods with lower priority.", + Type: []string{"string"}, + Format: "", + Enum: []interface{}{"Never", "PreemptLowerPriority"}, + }, + }, }, Required: []string{"name", "schedulingPolicy"}, }, diff --git a/pkg/registry/scheduling/podgroup/strategy.go b/pkg/registry/scheduling/podgroup/strategy.go index 2aef4b67cd0..46b3231fe3f 100644 --- a/pkg/registry/scheduling/podgroup/strategy.go +++ b/pkg/registry/scheduling/podgroup/strategy.go @@ -179,6 +179,7 @@ func dropDisabledPodGroupSpecFields(podGroupSpec, oldPodGroupSpec *scheduling.Po dropDisabledDisruptionModeField(podGroupSpec, oldPodGroupSpec) dropDisabledPriorityClassNameField(podGroupSpec, oldPodGroupSpec) dropDisabledPriorityField(podGroupSpec, oldPodGroupSpec) + dropDisabledPreemptionPolicyField(podGroupSpec, oldPodGroupSpec) } func dropDisabledPodGroupStatusFields(newPodGroup, oldPodGroup *scheduling.PodGroup) { @@ -244,6 +245,16 @@ func dropDisabledPriorityField(podGroupSpec, oldPodGroupSpec *scheduling.PodGrou podGroupSpec.Priority = nil } +// dropDisabledPreemptionPolicyField removes the PreemptionPolicy field unless it is +// already used in the old PodGroup spec. +func dropDisabledPreemptionPolicyField(podGroupSpec, oldPodGroupSpec *scheduling.PodGroupSpec) { + if utilfeature.DefaultFeatureGate.Enabled(features.WorkloadAwarePreemption) || preemptionPolicyInUse(oldPodGroupSpec) { + // No need to drop anything. + return + } + podGroupSpec.PreemptionPolicy = nil +} + func schedulingConstraintsInUse(podGroupSpec *scheduling.PodGroupSpec) bool { return podGroupSpec != nil && podGroupSpec.SchedulingConstraints != nil } @@ -263,3 +274,7 @@ func priorityClassNameInUse(podGroupSpec *scheduling.PodGroupSpec) bool { func priorityInUse(podGroupSpec *scheduling.PodGroupSpec) bool { return podGroupSpec != nil && podGroupSpec.Priority != nil } + +func preemptionPolicyInUse(podGroupSpec *scheduling.PodGroupSpec) bool { + return podGroupSpec != nil && podGroupSpec.PreemptionPolicy != nil +} diff --git a/pkg/registry/scheduling/podgroup/strategy_test.go b/pkg/registry/scheduling/podgroup/strategy_test.go index 576f9504b14..cf378e68a5c 100644 --- a/pkg/registry/scheduling/podgroup/strategy_test.go +++ b/pkg/registry/scheduling/podgroup/strategy_test.go @@ -27,6 +27,7 @@ import ( genericapirequest "k8s.io/apiserver/pkg/endpoints/request" utilfeature "k8s.io/apiserver/pkg/util/feature" featuregatetesting "k8s.io/component-base/featuregate/testing" + "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/scheduling" // Side-effect import: registers PodGroup with legacyscheme.Scheme so // the ConvertToVersion calls below resolve the type. @@ -66,12 +67,27 @@ func podGroupWithSchedulingConstraints(keys ...string) *scheduling.PodGroup { return pg } -func podGroupWithDisruptionMode(mode scheduling.DisruptionMode) *scheduling.PodGroup { +func basePodGroupWithWAP() *scheduling.PodGroup { pg := podGroup.DeepCopy() + disruptionModePod := scheduling.DisruptionModePod + preemptLowerPriority := core.PreemptLowerPriority + pg.Spec.DisruptionMode = &disruptionModePod + pg.Spec.PreemptionPolicy = &preemptLowerPriority + return pg +} + +func podGroupWithDisruptionMode(mode scheduling.DisruptionMode) *scheduling.PodGroup { + pg := basePodGroupWithWAP() pg.Spec.DisruptionMode = &mode return pg } +func podGroupWithPreemptionPolicy(policy core.PreemptionPolicy) *scheduling.PodGroup { + pg := basePodGroupWithWAP() + pg.Spec.PreemptionPolicy = &policy + return pg +} + var ( fieldImmutableError = "field is immutable" minCountError = "must be greater than or equal to 1" @@ -82,6 +98,7 @@ var ( subdomainNameError = "lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters" forbiddenError = "Forbidden" supportedModesError = `supported values: "Pod", "PodGroup"` + supportedPoliciesError = `supported values: "PreemptLowerPriority", "Never"` ) func TestStrategy(t *testing.T) { @@ -258,6 +275,25 @@ func TestStrategyCreate(t *testing.T) { enableWorkloadAwarePreemption: true, expectValidationError: maximumError, }, + "workload aware preemption disabled - drop preemptionPolicy": { + obj: podGroupWithPreemptionPolicy(core.PreemptNever), + expectObj: podGroup, + }, + "workload aware preemption enabled - preserve preemptionPolicy (Never)": { + obj: podGroupWithPreemptionPolicy(core.PreemptNever), + expectObj: podGroupWithPreemptionPolicy(core.PreemptNever), + enableWorkloadAwarePreemption: true, + }, + "workload aware preemption enabled - preserve preemptionPolicy (PreemptLowerPriority)": { + obj: podGroupWithPreemptionPolicy(core.PreemptLowerPriority), + expectObj: podGroupWithPreemptionPolicy(core.PreemptLowerPriority), + enableWorkloadAwarePreemption: true, + }, + "workload aware preemption enabled - invalid preemptionPolicy": { + obj: podGroupWithPreemptionPolicy(core.PreemptionPolicy("Invalid")), + enableWorkloadAwarePreemption: true, + expectValidationError: supportedPoliciesError, + }, } for name, tc := range testCases { @@ -448,6 +484,17 @@ func TestStrategyUpdate(t *testing.T) { enableWorkloadAwarePreemption: true, expectValidationError: fieldImmutableError, }, + "preemptionPolicy update, workload aware preemption disabled": { + oldObj: podGroupWithPreemptionPolicy(core.PreemptNever), + newObj: podGroupWithPreemptionPolicy(core.PreemptLowerPriority), + expectValidationError: forbiddenError, + }, + "preemptionPolicy update, workload aware preemption enabled": { + oldObj: podGroupWithPreemptionPolicy(core.PreemptNever), + newObj: podGroupWithPreemptionPolicy(core.PreemptLowerPriority), + enableWorkloadAwarePreemption: true, + expectValidationError: fieldImmutableError, + }, } for name, tc := range testCases { diff --git a/pkg/registry/scheduling/workload/strategy.go b/pkg/registry/scheduling/workload/strategy.go index 2bcdbd54af6..ac5f08ef5e2 100644 --- a/pkg/registry/scheduling/workload/strategy.go +++ b/pkg/registry/scheduling/workload/strategy.go @@ -130,6 +130,7 @@ func dropDisabledPodGroupTemplatesFields(templates, oldTemplates []scheduling.Po dropDisabledDisruptionModeField(template, oldTemplate) dropDisabledPriorityClassNameField(template, oldTemplate) dropDisabledPriorityField(template, oldTemplate) + dropDisabledPreemptionPolicyField(template, oldTemplate) } } @@ -181,6 +182,16 @@ func dropDisabledPriorityField(template, oldTemplate *scheduling.PodGroupTemplat template.Priority = nil } +// dropDisabledPreemptionPolicyField removes the PreemptionPolicy field from a template unless it is +// already used in the old template. +func dropDisabledPreemptionPolicyField(template, oldTemplate *scheduling.PodGroupTemplate) { + if utilfeature.DefaultFeatureGate.Enabled(features.WorkloadAwarePreemption) || preemptionPolicyInUse(oldTemplate) { + // No need to drop anything. + return + } + template.PreemptionPolicy = nil +} + func schedulingConstraintsInUse(pgt *scheduling.PodGroupTemplate) bool { return pgt != nil && pgt.SchedulingConstraints != nil } @@ -200,3 +211,7 @@ func priorityClassNameInUse(pgt *scheduling.PodGroupTemplate) bool { func priorityInUse(pgt *scheduling.PodGroupTemplate) bool { return pgt != nil && pgt.Priority != nil } + +func preemptionPolicyInUse(pgt *scheduling.PodGroupTemplate) bool { + return pgt != nil && pgt.PreemptionPolicy != nil +} diff --git a/pkg/registry/scheduling/workload/strategy_test.go b/pkg/registry/scheduling/workload/strategy_test.go index 81f5804993f..b37f436b3ac 100644 --- a/pkg/registry/scheduling/workload/strategy_test.go +++ b/pkg/registry/scheduling/workload/strategy_test.go @@ -27,6 +27,7 @@ import ( genericapirequest "k8s.io/apiserver/pkg/endpoints/request" utilfeature "k8s.io/apiserver/pkg/util/feature" featuregatetesting "k8s.io/component-base/featuregate/testing" + "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/scheduling" // Side-effect import: registers Workload with legacyscheme.Scheme so // the ConvertToVersion calls below resolve the type. @@ -57,13 +58,16 @@ var ( podDisruptionMode = scheduling.DisruptionModePod podGroupDisruptionMode = scheduling.DisruptionModePodGroup invalidDisruptionMode = scheduling.DisruptionMode("Invalid") + preemptNever = core.PreemptNever + preemptLowerPriority = core.PreemptLowerPriority fieldImmutableError = "field is immutable" minCountError = "must be greater than or equal to 1" tooManyItemsError = "must have at most 1 item" requiredError = "Required value" subdomainNameError = "lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters" - supportedModesError = `supported values: "Pod", "PodGroup"` + supportedModesError = `supported values: "Pod", "PodGroup"` + supportedPoliciesError = `supported values: "PreemptLowerPriority", "Never"` ) func TestWorkloadStrategy(t *testing.T) { @@ -233,6 +237,50 @@ func TestStrategyCreate(t *testing.T) { enableWorkloadAwarePreemption: true, expectValidationError: subdomainNameError, }, + "workload aware preemption disabled - drop preemptionPolicy": { + obj: func() *scheduling.Workload { + w := workload.DeepCopy() + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptNever + return w + }(), + expectObj: workload, + }, + "workload aware preemption enabled - preserve preemptionPolicy (Never)": { + obj: func() *scheduling.Workload { + w := workload.DeepCopy() + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptNever + return w + }(), + enableWorkloadAwarePreemption: true, + expectObj: func() *scheduling.Workload { + w := workload.DeepCopy() + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptNever + return w + }(), + }, + "workload aware preemption enabled - preserve preemptionPolicy (PreemptLowerPriority)": { + obj: func() *scheduling.Workload { + w := workload.DeepCopy() + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptLowerPriority + return w + }(), + enableWorkloadAwarePreemption: true, + expectObj: func() *scheduling.Workload { + w := workload.DeepCopy() + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptLowerPriority + return w + }(), + }, + "workload aware preemption enabled - invalid preemptionPolicy": { + obj: func() *scheduling.Workload { + w := workload.DeepCopy() + invalidPolicy := core.PreemptionPolicy("Invalid") + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &invalidPolicy + return w + }(), + enableWorkloadAwarePreemption: true, + expectValidationError: supportedPoliciesError, + }, } for name, tc := range testCases { @@ -530,6 +578,33 @@ func TestStrategyUpdate(t *testing.T) { enableWorkloadAwarePreemption: true, expectValidationError: fieldImmutableError, }, + "preemptionPolicy update, workload aware preemption disabled": { + oldObj: func() *scheduling.Workload { + w := workload.DeepCopy() + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptNever + return w + }(), + newObj: func() *scheduling.Workload { + w := workload.DeepCopy() + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptLowerPriority + return w + }(), + expectValidationError: fieldImmutableError, + }, + "preemptionPolicy update, workload aware preemption enabled": { + oldObj: func() *scheduling.Workload { + w := workload.DeepCopy() + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptNever + return w + }(), + newObj: func() *scheduling.Workload { + w := workload.DeepCopy() + w.Spec.PodGroupTemplates[0].PreemptionPolicy = &preemptLowerPriority + return w + }(), + enableWorkloadAwarePreemption: true, + expectValidationError: fieldImmutableError, + }, } for name, tc := range testCases { diff --git a/pkg/scheduler/framework/preemption/podgrouppreemption_test.go b/pkg/scheduler/framework/preemption/podgrouppreemption_test.go index 788355efb9a..0ed2187c0d2 100644 --- a/pkg/scheduler/framework/preemption/podgrouppreemption_test.go +++ b/pkg/scheduler/framework/preemption/podgrouppreemption_test.go @@ -205,7 +205,7 @@ func TestPodGroupEvaluator_SelectVictimsOnDomain(t *testing.T) { expectedStatus: fwk.NewStatus(fwk.Success), }, { - name: "PodGroup with PreemptNever does not perform preemption", + name: "PodGroup with PreemptNever in a single pod is ignored and performs preemption", nodeNames: []string{"node1", "node2", "node3"}, initPods: []*v1.Pod{ st.MakePod().Name("p1").UID("v1").Node("node1").Priority(lowPriority).Obj(), @@ -216,6 +216,28 @@ func TestPodGroupEvaluator_SelectVictimsOnDomain(t *testing.T) { st.MakePodGroup().Name("preemptor-pg").Priority(highPriority).Obj(), []*v1.Pod{ st.MakePod().Name("p-1").UID("p-1").Priority(highPriority).PreemptionPolicy(v1.PreemptNever).Obj(), + }, + ), + blockingRules: []blockingRule{ + {nodeName: "node1", capacity: 1, blockingVictims: sets.New("p1")}, + {nodeName: "node2", capacity: 1, blockingVictims: sets.New("p2")}, + {nodeName: "node3", capacity: 1, blockingVictims: sets.New("p3")}, + }, + expectedPods: []string{"p1"}, + expectedStatus: fwk.NewStatus(fwk.Success), + }, + { + name: "PodGroup with PreemptNever does not perform preemption", + nodeNames: []string{"node1", "node2", "node3"}, + initPods: []*v1.Pod{ + st.MakePod().Name("p1").UID("v1").Node("node1").Priority(lowPriority).Obj(), + st.MakePod().Name("p2").UID("v2").Node("node2").Priority(lowPriority).PodGroupName("pg1").Obj(), + st.MakePod().Name("p3").UID("v3").Node("node3").Priority(lowPriority).PodGroupName("pg2").Obj(), + }, + preemptor: newPodGroupPreemptor( + st.MakePodGroup().Name("preemptor-pg").Priority(highPriority).PreemptionPolicy(v1.PreemptNever).Obj(), + []*v1.Pod{ + st.MakePod().Name("p-1").UID("p-1").Priority(highPriority).Obj(), st.MakePod().Name("p-2").UID("p-2").Priority(highPriority).Obj(), }, ), @@ -225,7 +247,47 @@ func TestPodGroupEvaluator_SelectVictimsOnDomain(t *testing.T) { {nodeName: "node3", capacity: 1, blockingVictims: sets.New("p3")}, }, expectedPods: []string{}, - expectedStatus: fwk.NewStatus(fwk.Unschedulable), + expectedStatus: fwk.NewStatus(fwk.Unschedulable, "not eligible due to preemptionPolicy=Never."), + }, + { + name: "PodGroup with default preemption policy performs preemption", + nodeNames: []string{"node1", "node2", "node3"}, + initPods: []*v1.Pod{ + st.MakePod().Name("p1").UID("v1").Node("node1").Priority(lowPriority).Obj(), + st.MakePod().Name("p2").UID("v2").Node("node2").Priority(lowPriority).PodGroupName("pg1").Obj(), + st.MakePod().Name("p3").UID("v3").Node("node3").Priority(lowPriority).PodGroupName("pg2").Obj(), + }, + preemptor: newPodGroupPreemptor( + st.MakePodGroup().Name("preemptor-pg").Priority(highPriority).Obj(), // PreemptionPolicy is nil (default) + []*v1.Pod{st.MakePod().Name("p-1").UID("p-1").Priority(highPriority).Obj()}, + ), + blockingRules: []blockingRule{ + {nodeName: "node1", capacity: 1, blockingVictims: sets.New("p1")}, + {nodeName: "node2", capacity: 1, blockingVictims: sets.New("p2")}, + {nodeName: "node3", capacity: 1, blockingVictims: sets.New("p3")}, + }, + expectedPods: []string{"p1"}, + expectedStatus: fwk.NewStatus(fwk.Success), + }, + { + name: "PodGroup with PreemptLowerPriority performs preemption", + nodeNames: []string{"node1", "node2", "node3"}, + initPods: []*v1.Pod{ + st.MakePod().Name("p1").UID("v1").Node("node1").Priority(lowPriority).Obj(), + st.MakePod().Name("p2").UID("v2").Node("node2").Priority(lowPriority).PodGroupName("pg1").Obj(), + st.MakePod().Name("p3").UID("v3").Node("node3").Priority(lowPriority).PodGroupName("pg2").Obj(), + }, + preemptor: newPodGroupPreemptor( + st.MakePodGroup().Name("preemptor-pg").Priority(highPriority).PreemptionPolicy(v1.PreemptLowerPriority).Obj(), + []*v1.Pod{st.MakePod().Name("p-1").UID("p-1").Priority(highPriority).Obj()}, + ), + blockingRules: []blockingRule{ + {nodeName: "node1", capacity: 1, blockingVictims: sets.New("p1")}, + {nodeName: "node2", capacity: 1, blockingVictims: sets.New("p2")}, + {nodeName: "node3", capacity: 1, blockingVictims: sets.New("p3")}, + }, + expectedPods: []string{"p1"}, + expectedStatus: fwk.NewStatus(fwk.Success), }, { name: "Preemptor group is not eligible if any member has nominated node with terminating pods", @@ -432,7 +494,7 @@ func TestPodGroupEvaluator_SelectVictimsOnDomain(t *testing.T) { expectedStatus: fwk.NewStatus(fwk.Success), }, { - name: "PodGroup with PreemptNever does not perform preemption", + name: "PodGroup with PreemptNever in a single pod is ignored and performs preemption with PodGroup victims", nodeNames: []string{"node1", "node2", "node3"}, initPods: []*v1.Pod{ st.MakePod().Name("p1").UID("v1").Node("node1").Priority(lowPriority).Obj(), @@ -447,6 +509,32 @@ func TestPodGroupEvaluator_SelectVictimsOnDomain(t *testing.T) { st.MakePodGroup().Name("preemptor-pg").Priority(highPriority).Obj(), []*v1.Pod{ st.MakePod().Name("p-1").UID("p-1").Priority(highPriority).PreemptionPolicy(v1.PreemptNever).Obj(), + }, + ), + blockingRules: []blockingRule{ + {nodeName: "node1", capacity: 1, blockingVictims: sets.New("p1")}, + {nodeName: "node2", capacity: 1, blockingVictims: sets.New("p2")}, + {nodeName: "node3", capacity: 1, blockingVictims: sets.New("p3")}, + }, + expectedPods: []string{"p1"}, + expectedStatus: fwk.NewStatus(fwk.Success), + }, + { + name: "PodGroup with PreemptNever does not perform preemption", + nodeNames: []string{"node1", "node2", "node3"}, + initPods: []*v1.Pod{ + st.MakePod().Name("p1").UID("v1").Node("node1").Priority(lowPriority).Obj(), + st.MakePod().Name("p2").UID("v2").Node("node2").Priority(lowPriority).PodGroupName("pg1").Obj(), + st.MakePod().Name("p3").UID("v3").Node("node3").Priority(lowPriority).PodGroupName("pg2").Obj(), + }, + initPodGroups: []*schedulingapi.PodGroup{ + st.MakePodGroup().Name("pg1").UID("pg1").DisruptionMode(schedulingapi.DisruptionModePod).Priority(lowPriority).Obj(), + st.MakePodGroup().Name("pg2").UID("pg2").DisruptionMode(schedulingapi.DisruptionModePod).Priority(lowPriority).Obj(), + }, + preemptor: newPodGroupPreemptor( + st.MakePodGroup().Name("preemptor-pg").Priority(highPriority).PreemptionPolicy(v1.PreemptNever).Obj(), + []*v1.Pod{ + st.MakePod().Name("p-1").UID("p-1").Priority(highPriority).Obj(), st.MakePod().Name("p-2").UID("p-2").Priority(highPriority).Obj(), }, ), @@ -456,7 +544,7 @@ func TestPodGroupEvaluator_SelectVictimsOnDomain(t *testing.T) { {nodeName: "node3", capacity: 1, blockingVictims: sets.New("p3")}, }, expectedPods: []string{}, - expectedStatus: fwk.NewStatus(fwk.Unschedulable), + expectedStatus: fwk.NewStatus(fwk.Unschedulable, "not eligible due to preemptionPolicy=Never."), }, { name: "PDB: Prefer lower priority pod for preemption, when preemption without pdb violation is not possible", diff --git a/pkg/scheduler/framework/preemption/types.go b/pkg/scheduler/framework/preemption/types.go index 27116765cb7..f663281ccc3 100644 --- a/pkg/scheduler/framework/preemption/types.go +++ b/pkg/scheduler/framework/preemption/types.go @@ -42,10 +42,8 @@ type podGroupPreemptor struct { func newPodGroupPreemptor(pg *schedulingapi.PodGroup, pods []*v1.Pod) *podGroupPreemptor { preemptionPolicy := v1.PreemptLowerPriority - for _, pod := range pods { - if p := pod.Spec.PreemptionPolicy; p != nil && *p == v1.PreemptNever { - preemptionPolicy = *p - } + if pg != nil && pg.Spec.PreemptionPolicy != nil { + preemptionPolicy = *pg.Spec.PreemptionPolicy } return &podGroupPreemptor{ priority: util.PodGroupPriority(pg), diff --git a/pkg/scheduler/testing/wrappers.go b/pkg/scheduler/testing/wrappers.go index f8dcaee4e26..25a52991986 100644 --- a/pkg/scheduler/testing/wrappers.go +++ b/pkg/scheduler/testing/wrappers.go @@ -1666,6 +1666,12 @@ func (wrapper *PodGroupWrapper) Priority(priority int32) *PodGroupWrapper { return wrapper } +// PreemptionPolicy sets the preemption policy of the inner PodGroup. +func (wrapper *PodGroupWrapper) PreemptionPolicy(policy v1.PreemptionPolicy) *PodGroupWrapper { + wrapper.PodGroup.Spec.PreemptionPolicy = &policy + return wrapper +} + // WorkloadWrapper wraps a Workload inside. type WorkloadWrapper struct{ schedulingapi.Workload } @@ -1730,3 +1736,9 @@ func (wrapper *PodGroupTemplateWrapper) BasicPolicy() *PodGroupTemplateWrapper { wrapper.SchedulingPolicy.Basic = &schedulingapi.BasicSchedulingPolicy{} return wrapper } + +// PreemptionPolicy sets the preemption policy of the inner PodGroupTemplate. +func (wrapper *PodGroupTemplateWrapper) PreemptionPolicy(policy v1.PreemptionPolicy) *PodGroupTemplateWrapper { + wrapper.PodGroupTemplate.PreemptionPolicy = &policy + return wrapper +} diff --git a/staging/src/k8s.io/api/scheduling/v1alpha2/generated.pb.go b/staging/src/k8s.io/api/scheduling/v1alpha2/generated.pb.go index 12402df3d27..c88f34d4275 100644 --- a/staging/src/k8s.io/api/scheduling/v1alpha2/generated.pb.go +++ b/staging/src/k8s.io/api/scheduling/v1alpha2/generated.pb.go @@ -24,6 +24,7 @@ import ( io "io" + k8s_io_api_core_v1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" math_bits "math/bits" @@ -397,6 +398,13 @@ func (m *PodGroupSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.PreemptionPolicy != nil { + i -= len(*m.PreemptionPolicy) + copy(dAtA[i:], *m.PreemptionPolicy) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.PreemptionPolicy))) + i-- + dAtA[i] = 0x42 + } if m.Priority != nil { i = encodeVarintGenerated(dAtA, i, uint64(*m.Priority)) i-- @@ -536,6 +544,13 @@ func (m *PodGroupTemplate) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.PreemptionPolicy != nil { + i -= len(*m.PreemptionPolicy) + copy(dAtA[i:], *m.PreemptionPolicy) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.PreemptionPolicy))) + i-- + dAtA[i] = 0x42 + } if m.Priority != nil { i = encodeVarintGenerated(dAtA, i, uint64(*m.Priority)) i-- @@ -1029,6 +1044,10 @@ func (m *PodGroupSpec) Size() (n int) { if m.Priority != nil { n += 1 + sovGenerated(uint64(*m.Priority)) } + if m.PreemptionPolicy != nil { + l = len(*m.PreemptionPolicy) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -1082,6 +1101,10 @@ func (m *PodGroupTemplate) Size() (n int) { if m.Priority != nil { n += 1 + sovGenerated(uint64(*m.Priority)) } + if m.PreemptionPolicy != nil { + l = len(*m.PreemptionPolicy) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -1305,6 +1328,7 @@ func (this *PodGroupSpec) String() string { `DisruptionMode:` + valueToStringGenerated(this.DisruptionMode) + `,`, `PriorityClassName:` + fmt.Sprintf("%v", this.PriorityClassName) + `,`, `Priority:` + valueToStringGenerated(this.Priority) + `,`, + `PreemptionPolicy:` + valueToStringGenerated(this.PreemptionPolicy) + `,`, `}`, }, "") return s @@ -1347,6 +1371,7 @@ func (this *PodGroupTemplate) String() string { `DisruptionMode:` + valueToStringGenerated(this.DisruptionMode) + `,`, `PriorityClassName:` + fmt.Sprintf("%v", this.PriorityClassName) + `,`, `Priority:` + valueToStringGenerated(this.Priority) + `,`, + `PreemptionPolicy:` + valueToStringGenerated(this.PreemptionPolicy) + `,`, `}`, }, "") return s @@ -2552,6 +2577,39 @@ func (m *PodGroupSpec) Unmarshal(dAtA []byte) error { } } m.Priority = &v + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PreemptionPolicy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := k8s_io_api_core_v1.PreemptionPolicy(dAtA[iNdEx:postIndex]) + m.PreemptionPolicy = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -2940,6 +2998,39 @@ func (m *PodGroupTemplate) Unmarshal(dAtA []byte) error { } } m.Priority = &v + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PreemptionPolicy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := k8s_io_api_core_v1.PreemptionPolicy(dAtA[iNdEx:postIndex]) + m.PreemptionPolicy = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/staging/src/k8s.io/api/scheduling/v1alpha2/generated.proto b/staging/src/k8s.io/api/scheduling/v1alpha2/generated.proto index e03bcf4a5e9..7205c2ca515 100644 --- a/staging/src/k8s.io/api/scheduling/v1alpha2/generated.proto +++ b/staging/src/k8s.io/api/scheduling/v1alpha2/generated.proto @@ -21,6 +21,7 @@ syntax = "proto2"; package k8s.io.api.scheduling.v1alpha2; +import "k8s.io/api/core/v1/generated.proto"; import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; import "k8s.io/apimachinery/pkg/runtime/generated.proto"; import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto"; @@ -289,6 +290,21 @@ message PodGroupSpec { // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:immutable // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:maximum=1000000000 # HighestUserDefinablePriority optional int32 priority = 7; + + // PreemptionPolicy is the Policy for preempting pods with lower priority. + // One of Never, PreemptLowerPriority. + // Defaults to PreemptLowerPriority if unset. + // This field is immutable. + // This field is available only when the WorkloadAwarePreemption feature gate + // is enabled. + // + // +featureGate=WorkloadAwarePreemption + // +optional + // +k8s:ifDisabled("WorkloadAwarePreemption")=+k8s:forbidden + // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:optional + // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:immutable + // +default="PreemptLowerPriority" + optional string preemptionPolicy = 8; } // PodGroupStatus represents information about the status of a pod group. @@ -417,6 +433,18 @@ message PodGroupTemplate { // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:optional // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:maximum=1000000000 # HighestUserDefinablePriority optional int32 priority = 7; + + // PreemptionPolicy is the Policy for preempting pods with lower priority. + // One of Never, PreemptLowerPriority. + // Defaults to PreemptLowerPriority if unset. + // This field is available only when the WorkloadAwarePreemption feature gate + // is enabled. + // + // +featureGate=WorkloadAwarePreemption + // +optional + // +k8s:ifDisabled("WorkloadAwarePreemption")=+k8s:forbidden + // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:optional + optional string preemptionPolicy = 8; } // PodGroupTemplateReference references a PodGroup template defined in some object (e.g. Workload). diff --git a/staging/src/k8s.io/api/scheduling/v1alpha2/types.go b/staging/src/k8s.io/api/scheduling/v1alpha2/types.go index 0bd5b94a392..e00e7b548da 100644 --- a/staging/src/k8s.io/api/scheduling/v1alpha2/types.go +++ b/staging/src/k8s.io/api/scheduling/v1alpha2/types.go @@ -17,6 +17,7 @@ limitations under the License. package v1alpha2 import ( + apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -201,6 +202,18 @@ type PodGroupTemplate struct { // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:optional // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:maximum=1000000000 # HighestUserDefinablePriority Priority *int32 `json:"priority,omitempty" protobuf:"varint,7,opt,name=priority"` + + // PreemptionPolicy is the Policy for preempting pods with lower priority. + // One of Never, PreemptLowerPriority. + // Defaults to PreemptLowerPriority if unset. + // This field is available only when the WorkloadAwarePreemption feature gate + // is enabled. + // + // +featureGate=WorkloadAwarePreemption + // +optional + // +k8s:ifDisabled("WorkloadAwarePreemption")=+k8s:forbidden + // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:optional + PreemptionPolicy *apiv1.PreemptionPolicy `json:"preemptionPolicy,omitempty" protobuf:"bytes,8,opt,name=preemptionPolicy"` } // PodGroupSchedulingPolicy defines the scheduling configuration for a PodGroup. @@ -455,6 +468,21 @@ type PodGroupSpec struct { // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:immutable // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:maximum=1000000000 # HighestUserDefinablePriority Priority *int32 `json:"priority,omitempty" protobuf:"varint,7,opt,name=priority"` + + // PreemptionPolicy is the Policy for preempting pods with lower priority. + // One of Never, PreemptLowerPriority. + // Defaults to PreemptLowerPriority if unset. + // This field is immutable. + // This field is available only when the WorkloadAwarePreemption feature gate + // is enabled. + // + // +featureGate=WorkloadAwarePreemption + // +optional + // +k8s:ifDisabled("WorkloadAwarePreemption")=+k8s:forbidden + // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:optional + // +k8s:ifEnabled("WorkloadAwarePreemption")=+k8s:immutable + // +default="PreemptLowerPriority" + PreemptionPolicy *apiv1.PreemptionPolicy `json:"preemptionPolicy,omitempty" protobuf:"bytes,8,opt,name=preemptionPolicy"` } // PodGroupStatus represents information about the status of a pod group. diff --git a/staging/src/k8s.io/api/scheduling/v1alpha2/types_swagger_doc_generated.go b/staging/src/k8s.io/api/scheduling/v1alpha2/types_swagger_doc_generated.go index 2dc06b1c6f7..8eca1bf4873 100644 --- a/staging/src/k8s.io/api/scheduling/v1alpha2/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/scheduling/v1alpha2/types_swagger_doc_generated.go @@ -114,6 +114,7 @@ var map_PodGroupSpec = map[string]string{ "disruptionMode": "DisruptionMode defines the mode in which a given PodGroup can be disrupted. Controllers are expected to fill this field by copying it from a PodGroupTemplate. One of Pod, PodGroup. Defaults to Pod if unset. This field is immutable. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", "priorityClassName": "PriorityClassName defines the priority that should be considered when scheduling this pod group. Controllers are expected to fill this field by copying it from a PodGroupTemplate. Otherwise, it is validated and resolved similarly to the PriorityClassName on PodGroupTemplate (i.e. if no priority class is specified, admission control can set this to the global default priority class if it exists. Otherwise, the pod group's priority will be zero). This field is immutable. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", "priority": "Priority is the value of priority of this pod group. Various system components use this field to find the priority of the pod group. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. This field is immutable. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", + "preemptionPolicy": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is immutable. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", } func (PodGroupSpec) SwaggerDoc() map[string]string { @@ -139,6 +140,7 @@ var map_PodGroupTemplate = map[string]string{ "disruptionMode": "DisruptionMode defines the mode in which a given PodGroup can be disrupted. One of Pod, PodGroup. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", "priorityClassName": "PriorityClassName indicates the priority that should be considered when scheduling a pod group created from this template. If no priority class is specified, admission control can set this to the global default priority class if it exists. Otherwise, pod groups created from this template will have the priority set to zero. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", "priority": "Priority is the value of priority of pod groups created from this template. Various system components use this field to find the priority of the pod group. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", + "preemptionPolicy": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is available only when the WorkloadAwarePreemption feature gate is enabled.", } func (PodGroupTemplate) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/scheduling/v1alpha2/zz_generated.deepcopy.go b/staging/src/k8s.io/api/scheduling/v1alpha2/zz_generated.deepcopy.go index 901bb0a3ad9..35481655b88 100644 --- a/staging/src/k8s.io/api/scheduling/v1alpha2/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/scheduling/v1alpha2/zz_generated.deepcopy.go @@ -22,7 +22,8 @@ limitations under the License. package v1alpha2 import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -244,6 +245,11 @@ func (in *PodGroupSpec) DeepCopyInto(out *PodGroupSpec) { *out = new(int32) **out = **in } + if in.PreemptionPolicy != nil { + in, out := &in.PreemptionPolicy, &out.PreemptionPolicy + *out = new(v1.PreemptionPolicy) + **out = **in + } return } @@ -262,7 +268,7 @@ func (in *PodGroupStatus) DeepCopyInto(out *PodGroupStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -313,6 +319,11 @@ func (in *PodGroupTemplate) DeepCopyInto(out *PodGroupTemplate) { *out = new(int32) **out = **in } + if in.PreemptionPolicy != nil { + in, out := &in.PreemptionPolicy, &out.PreemptionPolicy + *out = new(v1.PreemptionPolicy) + **out = **in + } return } diff --git a/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.PodGroup.json b/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.PodGroup.json index bab4a230a3a..3122d079d4a 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.PodGroup.json +++ b/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.PodGroup.json @@ -72,7 +72,8 @@ ], "disruptionMode": "disruptionModeValue", "priorityClassName": "priorityClassNameValue", - "priority": 7 + "priority": 7, + "preemptionPolicy": "preemptionPolicyValue" }, "status": { "conditions": [ diff --git a/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.PodGroup.pb b/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.PodGroup.pb index 4813dba502602bd65d7852b15d8ce4672f0c620f..246d677db8f294604e2861df8a82faa28574195a 100644 GIT binary patch delta 46 zcmdnXdWCg@F5|+DdZ~$lmh@>-3T-Q diff --git a/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.PodGroup.yaml b/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.PodGroup.yaml index 169b493f20b..420046fc049 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.PodGroup.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.PodGroup.yaml @@ -38,6 +38,7 @@ spec: workload: podGroupTemplateName: podGroupTemplateNameValue workloadName: workloadNameValue + preemptionPolicy: preemptionPolicyValue priority: 7 priorityClassName: priorityClassNameValue resourceClaims: diff --git a/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.Workload.json b/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.Workload.json index 672365d0ad8..f27d0528919 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.Workload.json +++ b/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.Workload.json @@ -74,7 +74,8 @@ ], "disruptionMode": "disruptionModeValue", "priorityClassName": "priorityClassNameValue", - "priority": 7 + "priority": 7, + "preemptionPolicy": "preemptionPolicyValue" } ] } diff --git a/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.Workload.pb b/staging/src/k8s.io/api/testdata/HEAD/scheduling.k8s.io.v1alpha2.Workload.pb index d26486caaa9c5cca78172cec15f716e4842f75cc..f18514d8e9f2ec92c1314c417c3f081ffdfdf0b8 100644 GIT binary patch delta 52 zcmcb?@}6aaF5{k!dZ~ Date: Fri, 22 May 2026 09:44:00 +0000 Subject: [PATCH 2/2] gofmt --- pkg/apis/core/validation/validation_test.go | 24 ++-- .../apis/config/v1beta1/defaults_test.go | 128 +++++++++--------- .../scheduling/workload/strategy_test.go | 10 +- .../framework/plugins/noderesources/fit.go | 12 +- 4 files changed, 87 insertions(+), 87 deletions(-) diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index ab0bf5ab66d..e678f12fe44 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -22698,20 +22698,20 @@ func TestValidateSecret(t *testing.T) { secret core.Secret valid bool }{ - "valid": {validSecret(), true}, - "empty name": {emptyName, false}, - "invalid name": {invalidName, false}, - "empty namespace": {emptyNs, false}, - "invalid namespace": {invalidNs, false}, - "over max size": {overMaxSize, false}, - "invalid key": {invalidKey, false}, - "valid service-account-token secret": {validServiceAccountTokenSecret(), true}, - "empty service-account-token annotation": {emptyTokenAnnotation, false}, + "valid": {validSecret(), true}, + "empty name": {emptyName, false}, + "invalid name": {invalidName, false}, + "empty namespace": {emptyNs, false}, + "invalid namespace": {invalidNs, false}, + "over max size": {overMaxSize, false}, + "invalid key": {invalidKey, false}, + "valid service-account-token secret": {validServiceAccountTokenSecret(), true}, + "empty service-account-token annotation": {emptyTokenAnnotation, false}, "missing service-account-token annotation": {missingTokenAnnotation, false}, "missing service-account-token annotations": {missingTokenAnnotations, false}, - "leading dot key": {leadingDotKey, true}, - "dot key": {dotKey, false}, - "double dot key": {doubleDotKey, false}, + "leading dot key": {leadingDotKey, true}, + "dot key": {dotKey, false}, + "double dot key": {doubleDotKey, false}, } for name, tc := range tests { diff --git a/pkg/kubelet/apis/config/v1beta1/defaults_test.go b/pkg/kubelet/apis/config/v1beta1/defaults_test.go index abc6926f7b5..914086bb8fb 100644 --- a/pkg/kubelet/apis/config/v1beta1/defaults_test.go +++ b/pkg/kubelet/apis/config/v1beta1/defaults_test.go @@ -296,71 +296,71 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) { CacheUnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second}, }, }, - RegistryPullQPS: ptr.To[int32](0), - RegistryBurst: 10, - EventRecordQPS: ptr.To[int32](0), - EventBurst: 100, - EnableDebuggingHandlers: ptr.To(false), - HealthzPort: ptr.To[int32](0), - HealthzBindAddress: "127.0.0.1", - OOMScoreAdj: ptr.To[int32](0), - ClusterDNS: []string{}, - StreamingConnectionIdleTimeout: metav1.Duration{Duration: 4 * time.Hour}, - NodeStatusUpdateFrequency: metav1.Duration{Duration: 10 * time.Second}, - NodeStatusReportFrequency: metav1.Duration{Duration: 5 * time.Minute}, - NodeLeaseDurationSeconds: 40, - ContainerRuntimeEndpoint: "unix:///run/containerd/containerd.sock", - ImageMinimumGCAge: metav1.Duration{Duration: 2 * time.Minute}, - ImageGCHighThresholdPercent: ptr.To[int32](0), - ImageGCLowThresholdPercent: ptr.To[int32](0), - ImagePullCredentialsVerificationPolicy: "NeverVerifyPreloadedImages", - VolumeStatsAggPeriod: metav1.Duration{Duration: time.Minute}, - CgroupsPerQOS: ptr.To(false), - CgroupDriver: "cgroupfs", - CPUManagerPolicy: "none", - CPUManagerPolicyOptions: map[string]string{}, - CPUManagerReconcilePeriod: metav1.Duration{Duration: 10 * time.Second}, - MemoryManagerPolicy: v1beta1.NoneMemoryManagerPolicy, - TopologyManagerPolicy: v1beta1.NoneTopologyManagerPolicy, - TopologyManagerScope: v1beta1.ContainerTopologyManagerScope, - QOSReserved: map[string]string{}, - RuntimeRequestTimeout: metav1.Duration{Duration: 2 * time.Minute}, - HairpinMode: v1beta1.PromiscuousBridge, - MaxPods: 110, - PodPidsLimit: ptr.To[int64](0), - ResolverConfig: ptr.To(""), - CPUCFSQuota: ptr.To(false), - CPUCFSQuotaPeriod: &zeroDuration, - NodeStatusMaxImages: ptr.To[int32](0), - MaxOpenFiles: 1000000, - ContentType: "application/vnd.kubernetes.protobuf", - KubeAPIQPS: ptr.To[int32](0), - KubeAPIBurst: 100, - SerializeImagePulls: ptr.To(false), - MaxParallelImagePulls: nil, - EvictionHard: map[string]string{}, - EvictionSoft: map[string]string{}, - EvictionSoftGracePeriod: map[string]string{}, - EvictionPressureTransitionPeriod: metav1.Duration{Duration: 5 * time.Minute}, - EvictionMinimumReclaim: map[string]string{}, - MergeDefaultEvictionSettings: ptr.To(false), - EnableControllerAttachDetach: ptr.To(false), - MakeIPTablesUtilChains: ptr.To(false), - IPTablesMasqueradeBit: ptr.To[int32](0), - IPTablesDropBit: ptr.To[int32](0), - FeatureGates: map[string]bool{}, - FailSwapOn: ptr.To(false), - MemorySwap: v1beta1.MemorySwapConfiguration{SwapBehavior: ""}, - ContainerLogMaxSize: "10Mi", - ContainerLogMaxFiles: ptr.To[int32](0), - ContainerLogMaxWorkers: ptr.To[int32](1), - ContainerLogMonitorInterval: &metav1.Duration{Duration: 10 * time.Second}, + RegistryPullQPS: ptr.To[int32](0), + RegistryBurst: 10, + EventRecordQPS: ptr.To[int32](0), + EventBurst: 100, + EnableDebuggingHandlers: ptr.To(false), + HealthzPort: ptr.To[int32](0), + HealthzBindAddress: "127.0.0.1", + OOMScoreAdj: ptr.To[int32](0), + ClusterDNS: []string{}, + StreamingConnectionIdleTimeout: metav1.Duration{Duration: 4 * time.Hour}, + NodeStatusUpdateFrequency: metav1.Duration{Duration: 10 * time.Second}, + NodeStatusReportFrequency: metav1.Duration{Duration: 5 * time.Minute}, + NodeLeaseDurationSeconds: 40, + ContainerRuntimeEndpoint: "unix:///run/containerd/containerd.sock", + ImageMinimumGCAge: metav1.Duration{Duration: 2 * time.Minute}, + ImageGCHighThresholdPercent: ptr.To[int32](0), + ImageGCLowThresholdPercent: ptr.To[int32](0), + ImagePullCredentialsVerificationPolicy: "NeverVerifyPreloadedImages", + VolumeStatsAggPeriod: metav1.Duration{Duration: time.Minute}, + CgroupsPerQOS: ptr.To(false), + CgroupDriver: "cgroupfs", + CPUManagerPolicy: "none", + CPUManagerPolicyOptions: map[string]string{}, + CPUManagerReconcilePeriod: metav1.Duration{Duration: 10 * time.Second}, + MemoryManagerPolicy: v1beta1.NoneMemoryManagerPolicy, + TopologyManagerPolicy: v1beta1.NoneTopologyManagerPolicy, + TopologyManagerScope: v1beta1.ContainerTopologyManagerScope, + QOSReserved: map[string]string{}, + RuntimeRequestTimeout: metav1.Duration{Duration: 2 * time.Minute}, + HairpinMode: v1beta1.PromiscuousBridge, + MaxPods: 110, + PodPidsLimit: ptr.To[int64](0), + ResolverConfig: ptr.To(""), + CPUCFSQuota: ptr.To(false), + CPUCFSQuotaPeriod: &zeroDuration, + NodeStatusMaxImages: ptr.To[int32](0), + MaxOpenFiles: 1000000, + ContentType: "application/vnd.kubernetes.protobuf", + KubeAPIQPS: ptr.To[int32](0), + KubeAPIBurst: 100, + SerializeImagePulls: ptr.To(false), + MaxParallelImagePulls: nil, + EvictionHard: map[string]string{}, + EvictionSoft: map[string]string{}, + EvictionSoftGracePeriod: map[string]string{}, + EvictionPressureTransitionPeriod: metav1.Duration{Duration: 5 * time.Minute}, + EvictionMinimumReclaim: map[string]string{}, + MergeDefaultEvictionSettings: ptr.To(false), + EnableControllerAttachDetach: ptr.To(false), + MakeIPTablesUtilChains: ptr.To(false), + IPTablesMasqueradeBit: ptr.To[int32](0), + IPTablesDropBit: ptr.To[int32](0), + FeatureGates: map[string]bool{}, + FailSwapOn: ptr.To(false), + MemorySwap: v1beta1.MemorySwapConfiguration{SwapBehavior: ""}, + ContainerLogMaxSize: "10Mi", + ContainerLogMaxFiles: ptr.To[int32](0), + ContainerLogMaxWorkers: ptr.To[int32](1), + ContainerLogMonitorInterval: &metav1.Duration{Duration: 10 * time.Second}, ConfigMapAndSecretChangeDetectionStrategy: v1beta1.WatchChangeDetectionStrategy, - SystemReserved: map[string]string{}, - KubeReserved: map[string]string{}, - EnforceNodeAllocatable: []string{}, - AllowedUnsafeSysctls: []string{}, - VolumePluginDir: DefaultVolumePluginDir, + SystemReserved: map[string]string{}, + KubeReserved: map[string]string{}, + EnforceNodeAllocatable: []string{}, + AllowedUnsafeSysctls: []string{}, + VolumePluginDir: DefaultVolumePluginDir, Logging: logsapi.LoggingConfiguration{ Format: "text", FlushFrequency: logsapi.TimeOrMetaDuration{Duration: metav1.Duration{Duration: 5 * time.Second}, SerializeAsString: true}, diff --git a/pkg/registry/scheduling/workload/strategy_test.go b/pkg/registry/scheduling/workload/strategy_test.go index b37f436b3ac..082dd805341 100644 --- a/pkg/registry/scheduling/workload/strategy_test.go +++ b/pkg/registry/scheduling/workload/strategy_test.go @@ -61,11 +61,11 @@ var ( preemptNever = core.PreemptNever preemptLowerPriority = core.PreemptLowerPriority - fieldImmutableError = "field is immutable" - minCountError = "must be greater than or equal to 1" - tooManyItemsError = "must have at most 1 item" - requiredError = "Required value" - subdomainNameError = "lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters" + fieldImmutableError = "field is immutable" + minCountError = "must be greater than or equal to 1" + tooManyItemsError = "must have at most 1 item" + requiredError = "Required value" + subdomainNameError = "lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters" supportedModesError = `supported values: "Pod", "PodGroup"` supportedPoliciesError = `supported values: "PreemptLowerPriority", "Never"` ) diff --git a/pkg/scheduler/framework/plugins/noderesources/fit.go b/pkg/scheduler/framework/plugins/noderesources/fit.go index ce437246e5e..94a50dbec00 100644 --- a/pkg/scheduler/framework/plugins/noderesources/fit.go +++ b/pkg/scheduler/framework/plugins/noderesources/fit.go @@ -232,12 +232,12 @@ func NewFit(_ context.Context, plArgs runtime.Object, h fwk.Handle, fts feature. } pl := &Fit{ - ignoredResources: sets.New(args.IgnoredResources...), - ignoredResourceGroups: sets.New(args.IgnoredResourceGroups...), - enableInPlacePodVerticalScaling: fts.EnableInPlacePodVerticalScaling, - handle: h, - enablePodLevelResources: fts.EnablePodLevelResources, - enableDRAExtendedResource: fts.EnableDRAExtendedResource, + ignoredResources: sets.New(args.IgnoredResources...), + ignoredResourceGroups: sets.New(args.IgnoredResourceGroups...), + enableInPlacePodVerticalScaling: fts.EnableInPlacePodVerticalScaling, + handle: h, + enablePodLevelResources: fts.EnablePodLevelResources, + enableDRAExtendedResource: fts.EnableDRAExtendedResource, enableInPlacePodLevelResourcesVerticalScaling: fts.EnableInPlacePodLevelResourcesVerticalScaling, resourceAllocationScorer: scorer, }