diff --git a/pkg/registry/batch/job/strategy.go b/pkg/registry/batch/job/strategy.go index d0b9f4f3d8a..a24f88fc7a6 100644 --- a/pkg/registry/batch/job/strategy.go +++ b/pkg/registry/batch/job/strategy.go @@ -192,7 +192,7 @@ func validationOptionsForJob(newJob, oldJob *batch.Job) batchvalidation.JobValid notStarted := oldJob.Status.StartTime == nil opts.AllowMutableSchedulingDirectives = suspended && notStarted if utilfeature.DefaultFeatureGate.Enabled(features.MutablePodResourcesForSuspendedJobs) { - opts.AllowMutablePodResources = batchvalidation.IsConditionTrue(oldJob.Status.Conditions, batch.JobSuspended) && oldJob.Status.Active == 0 + opts.AllowMutablePodResources = suspended && batchvalidation.IsConditionTrue(oldJob.Status.Conditions, batch.JobSuspended) && oldJob.Status.Active == 0 } if utilfeature.DefaultFeatureGate.Enabled(features.MutableSchedulingDirectivesForSuspendedJobs) { opts.AllowMutableSchedulingDirectives = suspended && batchvalidation.IsConditionTrue(oldJob.Status.Conditions, batch.JobSuspended) && oldJob.Status.Active == 0 diff --git a/pkg/registry/batch/job/strategy_test.go b/pkg/registry/batch/job/strategy_test.go index 58da8cd5a57..deafa30ef5c 100644 --- a/pkg/registry/batch/job/strategy_test.go +++ b/pkg/registry/batch/job/strategy_test.go @@ -1447,6 +1447,40 @@ func TestJobStrategy_ValidateUpdate_MutablePodResources(t *testing.T) { {Type: field.ErrorTypeInvalid, Field: "spec.template.spec"}, }, }, + "feature gate enabled - job resuming (suspend=false) but JobSuspended condition still true - resource updates rejected": { + enableFeatureGate: true, + job: &batch.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myjob", + Namespace: metav1.NamespaceDefault, + ResourceVersion: "0", + }, + Spec: batch.JobSpec{ + Selector: validSelector, + Template: validPodTemplateSpec, + ManualSelector: ptr.To(true), + Parallelism: ptr.To[int32](1), + Suspend: ptr.To(false), + }, + Status: batch.JobStatus{ + Active: 0, + Conditions: []batch.JobCondition{ + { + Type: batch.JobSuspended, + Status: api.ConditionStatus(metav1.ConditionTrue), + }, + }, + }, + }, + update: func(job *batch.Job) { + job.Spec.Template.Spec.Containers[0].Resources.Requests = api.ResourceList{ + api.ResourceCPU: resource.MustParse("200m"), + } + }, + wantErrs: field.ErrorList{ + {Type: field.ErrorTypeInvalid, Field: "spec.template"}, + }, + }, } for name, tc := range cases { @@ -4251,6 +4285,40 @@ func TestJobStrategy_ValidateUpdate_MutableSchedulingDirectives(t *testing.T) { {Type: field.ErrorTypeInvalid, Field: "spec.template"}, }, }, + "feature gate enabled - job resuming (suspend=false) but JobSuspended condition still true - scheduling directives update rejected": { + enableFeatureGate: true, + job: &batch.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myjob", + Namespace: metav1.NamespaceDefault, + ResourceVersion: "0", + }, + Spec: batch.JobSpec{ + Selector: validSelector, + Template: validPodTemplateSpec, + ManualSelector: ptr.To(true), + Parallelism: ptr.To[int32](1), + Suspend: ptr.To(false), + }, + Status: batch.JobStatus{ + Active: 0, + Conditions: []batch.JobCondition{ + { + Type: batch.JobSuspended, + Status: api.ConditionStatus(metav1.ConditionTrue), + }, + }, + }, + }, + update: func(job *batch.Job) { + job.Spec.Template.Spec.NodeSelector = map[string]string{ + "key": "value", + } + }, + wantErrs: field.ErrorList{ + {Type: field.ErrorTypeInvalid, Field: "spec.template"}, + }, + }, } for name, tc := range cases {