mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 10:51:29 +00:00
Graduate SuspendJob to GA
This commit is contained in:
parent
d12787bc2c
commit
b2d2ec9e76
@ -208,9 +208,6 @@ type JobSpec struct {
|
|||||||
// Suspending a Job will reset the StartTime field of the Job, effectively
|
// Suspending a Job will reset the StartTime field of the Job, effectively
|
||||||
// resetting the ActiveDeadlineSeconds timer too. Defaults to false.
|
// resetting the ActiveDeadlineSeconds timer too. Defaults to false.
|
||||||
//
|
//
|
||||||
// This field is beta-level, gated by SuspendJob feature flag (enabled by
|
|
||||||
// default).
|
|
||||||
//
|
|
||||||
// +optional
|
// +optional
|
||||||
Suspend *bool
|
Suspend *bool
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ func SetDefaults_Job(obj *batchv1.Job) {
|
|||||||
mode := batchv1.NonIndexedCompletion
|
mode := batchv1.NonIndexedCompletion
|
||||||
obj.Spec.CompletionMode = &mode
|
obj.Spec.CompletionMode = &mode
|
||||||
}
|
}
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.SuspendJob) && obj.Spec.Suspend == nil {
|
if obj.Spec.Suspend == nil {
|
||||||
obj.Spec.Suspend = utilpointer.BoolPtr(false)
|
obj.Spec.Suspend = utilpointer.BoolPtr(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,6 @@ func TestSetDefaultJob(t *testing.T) {
|
|||||||
defaultLabels := map[string]string{"default": "default"}
|
defaultLabels := map[string]string{"default": "default"}
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
indexedJobEnabled bool
|
indexedJobEnabled bool
|
||||||
suspendJobEnabled bool
|
|
||||||
original *batchv1.Job
|
original *batchv1.Job
|
||||||
expected *batchv1.Job
|
expected *batchv1.Job
|
||||||
expectLabels bool
|
expectLabels bool
|
||||||
@ -58,6 +57,7 @@ func TestSetDefaultJob(t *testing.T) {
|
|||||||
Completions: pointer.Int32Ptr(1),
|
Completions: pointer.Int32Ptr(1),
|
||||||
Parallelism: pointer.Int32Ptr(1),
|
Parallelism: pointer.Int32Ptr(1),
|
||||||
BackoffLimit: pointer.Int32Ptr(6),
|
BackoffLimit: pointer.Int32Ptr(6),
|
||||||
|
Suspend: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectLabels: true,
|
expectLabels: true,
|
||||||
@ -77,12 +77,12 @@ func TestSetDefaultJob(t *testing.T) {
|
|||||||
Parallelism: pointer.Int32Ptr(1),
|
Parallelism: pointer.Int32Ptr(1),
|
||||||
BackoffLimit: pointer.Int32Ptr(6),
|
BackoffLimit: pointer.Int32Ptr(6),
|
||||||
CompletionMode: completionModePtr(batchv1.NonIndexedCompletion),
|
CompletionMode: completionModePtr(batchv1.NonIndexedCompletion),
|
||||||
|
Suspend: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectLabels: true,
|
expectLabels: true,
|
||||||
},
|
},
|
||||||
"All unspecified, suspend job enabled -> sets all to default values": {
|
"All unspecified, suspend job enabled -> sets all to default values": {
|
||||||
suspendJobEnabled: true,
|
|
||||||
original: &batchv1.Job{
|
original: &batchv1.Job{
|
||||||
Spec: batchv1.JobSpec{
|
Spec: batchv1.JobSpec{
|
||||||
Template: v1.PodTemplateSpec{
|
Template: v1.PodTemplateSpec{
|
||||||
@ -101,7 +101,6 @@ func TestSetDefaultJob(t *testing.T) {
|
|||||||
expectLabels: true,
|
expectLabels: true,
|
||||||
},
|
},
|
||||||
"suspend set, everything else is defaulted": {
|
"suspend set, everything else is defaulted": {
|
||||||
suspendJobEnabled: true,
|
|
||||||
original: &batchv1.Job{
|
original: &batchv1.Job{
|
||||||
Spec: batchv1.JobSpec{
|
Spec: batchv1.JobSpec{
|
||||||
Suspend: pointer.BoolPtr(true),
|
Suspend: pointer.BoolPtr(true),
|
||||||
@ -136,6 +135,7 @@ func TestSetDefaultJob(t *testing.T) {
|
|||||||
Completions: pointer.Int32Ptr(1),
|
Completions: pointer.Int32Ptr(1),
|
||||||
Parallelism: pointer.Int32Ptr(1),
|
Parallelism: pointer.Int32Ptr(1),
|
||||||
BackoffLimit: pointer.Int32Ptr(6),
|
BackoffLimit: pointer.Int32Ptr(6),
|
||||||
|
Suspend: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -152,6 +152,7 @@ func TestSetDefaultJob(t *testing.T) {
|
|||||||
Spec: batchv1.JobSpec{
|
Spec: batchv1.JobSpec{
|
||||||
Parallelism: pointer.Int32Ptr(0),
|
Parallelism: pointer.Int32Ptr(0),
|
||||||
BackoffLimit: pointer.Int32Ptr(6),
|
BackoffLimit: pointer.Int32Ptr(6),
|
||||||
|
Suspend: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectLabels: true,
|
expectLabels: true,
|
||||||
@ -169,6 +170,7 @@ func TestSetDefaultJob(t *testing.T) {
|
|||||||
Spec: batchv1.JobSpec{
|
Spec: batchv1.JobSpec{
|
||||||
Parallelism: pointer.Int32Ptr(2),
|
Parallelism: pointer.Int32Ptr(2),
|
||||||
BackoffLimit: pointer.Int32Ptr(6),
|
BackoffLimit: pointer.Int32Ptr(6),
|
||||||
|
Suspend: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectLabels: true,
|
expectLabels: true,
|
||||||
@ -187,6 +189,7 @@ func TestSetDefaultJob(t *testing.T) {
|
|||||||
Completions: pointer.Int32Ptr(2),
|
Completions: pointer.Int32Ptr(2),
|
||||||
Parallelism: pointer.Int32Ptr(1),
|
Parallelism: pointer.Int32Ptr(1),
|
||||||
BackoffLimit: pointer.Int32Ptr(6),
|
BackoffLimit: pointer.Int32Ptr(6),
|
||||||
|
Suspend: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectLabels: true,
|
expectLabels: true,
|
||||||
@ -205,6 +208,7 @@ func TestSetDefaultJob(t *testing.T) {
|
|||||||
Completions: pointer.Int32Ptr(1),
|
Completions: pointer.Int32Ptr(1),
|
||||||
Parallelism: pointer.Int32Ptr(1),
|
Parallelism: pointer.Int32Ptr(1),
|
||||||
BackoffLimit: pointer.Int32Ptr(5),
|
BackoffLimit: pointer.Int32Ptr(5),
|
||||||
|
Suspend: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectLabels: true,
|
expectLabels: true,
|
||||||
@ -265,7 +269,6 @@ func TestSetDefaultJob(t *testing.T) {
|
|||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.IndexedJob, test.indexedJobEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.IndexedJob, test.indexedJobEnabled)()
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.SuspendJob, test.suspendJobEnabled)()
|
|
||||||
|
|
||||||
original := test.original
|
original := test.original
|
||||||
expected := test.expected
|
expected := test.expected
|
||||||
|
@ -767,7 +767,7 @@ func (jm *Controller) syncJob(ctx context.Context, key string) (forget bool, rEr
|
|||||||
}
|
}
|
||||||
if complete {
|
if complete {
|
||||||
finishedCondition = newCondition(batch.JobComplete, v1.ConditionTrue, "", "")
|
finishedCondition = newCondition(batch.JobComplete, v1.ConditionTrue, "", "")
|
||||||
} else if feature.DefaultFeatureGate.Enabled(features.SuspendJob) && manageJobCalled {
|
} else if manageJobCalled {
|
||||||
// Update the conditions / emit events only if manageJob was called in
|
// Update the conditions / emit events only if manageJob was called in
|
||||||
// this syncJob. Otherwise wait for the right syncJob call to make
|
// this syncJob. Otherwise wait for the right syncJob call to make
|
||||||
// updates.
|
// updates.
|
||||||
@ -1257,7 +1257,7 @@ func getStatus(job *batch.Job, pods []*v1.Pod, uncounted *uncountedTerminatedPod
|
|||||||
// jobSuspended returns whether a Job is suspended while taking the feature
|
// jobSuspended returns whether a Job is suspended while taking the feature
|
||||||
// gate into account.
|
// gate into account.
|
||||||
func jobSuspended(job *batch.Job) bool {
|
func jobSuspended(job *batch.Job) bool {
|
||||||
return feature.DefaultFeatureGate.Enabled(features.SuspendJob) && job.Spec.Suspend != nil && *job.Spec.Suspend
|
return job.Spec.Suspend != nil && *job.Spec.Suspend
|
||||||
}
|
}
|
||||||
|
|
||||||
// manageJob is the core method responsible for managing the number of running
|
// manageJob is the core method responsible for managing the number of running
|
||||||
|
@ -225,7 +225,6 @@ func TestControllerSyncJob(t *testing.T) {
|
|||||||
|
|
||||||
// features
|
// features
|
||||||
indexedJobEnabled bool
|
indexedJobEnabled bool
|
||||||
suspendJobEnabled bool
|
|
||||||
jobReadyPodsEnabled bool
|
jobReadyPodsEnabled bool
|
||||||
}{
|
}{
|
||||||
"job start": {
|
"job start": {
|
||||||
@ -659,7 +658,6 @@ func TestControllerSyncJob(t *testing.T) {
|
|||||||
"suspending a job with satisfied expectations": {
|
"suspending a job with satisfied expectations": {
|
||||||
// Suspended Job should delete active pods when expectations are
|
// Suspended Job should delete active pods when expectations are
|
||||||
// satisfied.
|
// satisfied.
|
||||||
suspendJobEnabled: true,
|
|
||||||
suspend: true,
|
suspend: true,
|
||||||
parallelism: 2,
|
parallelism: 2,
|
||||||
activePods: 2, // parallelism == active, expectations satisfied
|
activePods: 2, // parallelism == active, expectations satisfied
|
||||||
@ -679,7 +677,6 @@ func TestControllerSyncJob(t *testing.T) {
|
|||||||
// Job in the syncJob call because the controller will wait for
|
// Job in the syncJob call because the controller will wait for
|
||||||
// expectations to be satisfied first. The next syncJob call (not tested
|
// expectations to be satisfied first. The next syncJob call (not tested
|
||||||
// here) will be the same as the previous test.
|
// here) will be the same as the previous test.
|
||||||
suspendJobEnabled: true,
|
|
||||||
suspend: true,
|
suspend: true,
|
||||||
parallelism: 2,
|
parallelism: 2,
|
||||||
activePods: 3, // active > parallelism, expectations unsatisfied
|
activePods: 3, // active > parallelism, expectations unsatisfied
|
||||||
@ -692,7 +689,6 @@ func TestControllerSyncJob(t *testing.T) {
|
|||||||
expectedActive: 3,
|
expectedActive: 3,
|
||||||
},
|
},
|
||||||
"resuming a suspended job": {
|
"resuming a suspended job": {
|
||||||
suspendJobEnabled: true,
|
|
||||||
wasSuspended: true,
|
wasSuspended: true,
|
||||||
suspend: false,
|
suspend: false,
|
||||||
parallelism: 2,
|
parallelism: 2,
|
||||||
@ -711,7 +707,6 @@ func TestControllerSyncJob(t *testing.T) {
|
|||||||
// cases above), but since this job is being deleted, we don't expect
|
// cases above), but since this job is being deleted, we don't expect
|
||||||
// anything changed here from before the job was suspended. The
|
// anything changed here from before the job was suspended. The
|
||||||
// JobSuspended condition is also missing.
|
// JobSuspended condition is also missing.
|
||||||
suspendJobEnabled: true,
|
|
||||||
suspend: true,
|
suspend: true,
|
||||||
deleting: true,
|
deleting: true,
|
||||||
parallelism: 2,
|
parallelism: 2,
|
||||||
@ -733,7 +728,6 @@ func TestControllerSyncJob(t *testing.T) {
|
|||||||
t.Skip("Can't track status if finalizers can't be removed")
|
t.Skip("Can't track status if finalizers can't be removed")
|
||||||
}
|
}
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.IndexedJob, tc.indexedJobEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.IndexedJob, tc.indexedJobEnabled)()
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.SuspendJob, tc.suspendJobEnabled)()
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobReadyPods, tc.jobReadyPodsEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobReadyPods, tc.jobReadyPodsEnabled)()
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobTrackingWithFinalizers, wFinalizers)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobTrackingWithFinalizers, wFinalizers)()
|
||||||
|
|
||||||
@ -1696,9 +1690,6 @@ func TestSyncJobPastDeadline(t *testing.T) {
|
|||||||
expectedFailed int32
|
expectedFailed int32
|
||||||
expectedCondition batch.JobConditionType
|
expectedCondition batch.JobConditionType
|
||||||
expectedConditionReason string
|
expectedConditionReason string
|
||||||
|
|
||||||
// features
|
|
||||||
suspendJobEnabled bool
|
|
||||||
}{
|
}{
|
||||||
"activeDeadlineSeconds less than single pod execution": {
|
"activeDeadlineSeconds less than single pod execution": {
|
||||||
parallelism: 1,
|
parallelism: 1,
|
||||||
@ -1750,7 +1741,6 @@ func TestSyncJobPastDeadline(t *testing.T) {
|
|||||||
expectedConditionReason: "BackoffLimitExceeded",
|
expectedConditionReason: "BackoffLimitExceeded",
|
||||||
},
|
},
|
||||||
"activeDeadlineSeconds is not triggered when Job is suspended": {
|
"activeDeadlineSeconds is not triggered when Job is suspended": {
|
||||||
suspendJobEnabled: true,
|
|
||||||
suspend: true,
|
suspend: true,
|
||||||
parallelism: 1,
|
parallelism: 1,
|
||||||
completions: 2,
|
completions: 2,
|
||||||
@ -1765,8 +1755,6 @@ func TestSyncJobPastDeadline(t *testing.T) {
|
|||||||
|
|
||||||
for name, tc := range testCases {
|
for name, tc := range testCases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.SuspendJob, tc.suspendJobEnabled)()
|
|
||||||
|
|
||||||
// job manager setup
|
// job manager setup
|
||||||
clientSet := clientset.NewForConfigOrDie(&restclient.Config{Host: "", ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
clientSet := clientset.NewForConfigOrDie(&restclient.Config{Host: "", ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||||
manager, sharedInformerFactory := newControllerFromClient(clientSet, controller.NoResyncPeriodFunc)
|
manager, sharedInformerFactory := newControllerFromClient(clientSet, controller.NoResyncPeriodFunc)
|
||||||
|
@ -653,6 +653,7 @@ const (
|
|||||||
// owner: @adtac
|
// owner: @adtac
|
||||||
// alpha: v1.21
|
// alpha: v1.21
|
||||||
// beta: v1.22
|
// beta: v1.22
|
||||||
|
// GA: v1.24
|
||||||
//
|
//
|
||||||
// Allows jobs to be created in the suspended state.
|
// Allows jobs to be created in the suspended state.
|
||||||
SuspendJob featuregate.Feature = "SuspendJob"
|
SuspendJob featuregate.Feature = "SuspendJob"
|
||||||
@ -910,7 +911,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
IngressClassNamespacedParams: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.24
|
IngressClassNamespacedParams: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.24
|
||||||
ServiceInternalTrafficPolicy: {Default: true, PreRelease: featuregate.Beta},
|
ServiceInternalTrafficPolicy: {Default: true, PreRelease: featuregate.Beta},
|
||||||
LogarithmicScaleDown: {Default: true, PreRelease: featuregate.Beta},
|
LogarithmicScaleDown: {Default: true, PreRelease: featuregate.Beta},
|
||||||
SuspendJob: {Default: true, PreRelease: featuregate.Beta},
|
SuspendJob: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.25
|
||||||
KubeletPodResourcesGetAllocatable: {Default: true, PreRelease: featuregate.Beta},
|
KubeletPodResourcesGetAllocatable: {Default: true, PreRelease: featuregate.Beta},
|
||||||
CSIVolumeHealth: {Default: false, PreRelease: featuregate.Alpha},
|
CSIVolumeHealth: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
WindowsHostProcessContainers: {Default: true, PreRelease: featuregate.Beta},
|
WindowsHostProcessContainers: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
@ -97,10 +97,6 @@ func (jobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
|||||||
job.Spec.CompletionMode = nil
|
job.Spec.CompletionMode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.SuspendJob) {
|
|
||||||
job.Spec.Suspend = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers) {
|
if utilfeature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers) {
|
||||||
// Until this feature graduates to GA and soaks in clusters, we use an
|
// Until this feature graduates to GA and soaks in clusters, we use an
|
||||||
// annotation to mark whether jobs are tracked with it.
|
// annotation to mark whether jobs are tracked with it.
|
||||||
@ -141,15 +137,6 @@ func (jobStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object
|
|||||||
newJob.Spec.CompletionMode = nil
|
newJob.Spec.CompletionMode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.SuspendJob) {
|
|
||||||
// There are 3 possible values (nil, true, false) for each flag, so 9
|
|
||||||
// combinations. We want to disallow everything except true->false and
|
|
||||||
// true->nil when the feature gate is disabled. Or, basically allow this
|
|
||||||
// only when oldJob is true.
|
|
||||||
if oldJob.Spec.Suspend == nil || !*oldJob.Spec.Suspend {
|
|
||||||
newJob.Spec.Suspend = oldJob.Spec.Suspend
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers) && !hasJobTrackingAnnotation(oldJob) {
|
if !utilfeature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers) && !hasJobTrackingAnnotation(oldJob) {
|
||||||
dropJobTrackingAnnotation(newJob)
|
dropJobTrackingAnnotation(newJob)
|
||||||
}
|
}
|
||||||
|
@ -43,16 +43,12 @@ var ignoreErrValueDetail = cmpopts.IgnoreFields(field.Error{}, "BadValue", "Deta
|
|||||||
func TestJobStrategy(t *testing.T) {
|
func TestJobStrategy(t *testing.T) {
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
indexedJobEnabled bool
|
indexedJobEnabled bool
|
||||||
suspendJobEnabled bool
|
|
||||||
trackingWithFinalizersEnabled bool
|
trackingWithFinalizersEnabled bool
|
||||||
}{
|
}{
|
||||||
"features disabled": {},
|
"features disabled": {},
|
||||||
"indexed job enabled": {
|
"indexed job enabled": {
|
||||||
indexedJobEnabled: true,
|
indexedJobEnabled: true,
|
||||||
},
|
},
|
||||||
"suspend job enabled": {
|
|
||||||
suspendJobEnabled: true,
|
|
||||||
},
|
|
||||||
"new job tracking enabled": {
|
"new job tracking enabled": {
|
||||||
trackingWithFinalizersEnabled: true,
|
trackingWithFinalizersEnabled: true,
|
||||||
},
|
},
|
||||||
@ -60,7 +56,6 @@ func TestJobStrategy(t *testing.T) {
|
|||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IndexedJob, tc.indexedJobEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IndexedJob, tc.indexedJobEnabled)()
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SuspendJob, tc.suspendJobEnabled)()
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobTrackingWithFinalizers, tc.trackingWithFinalizersEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobTrackingWithFinalizers, tc.trackingWithFinalizersEnabled)()
|
||||||
testJobStrategy(t)
|
testJobStrategy(t)
|
||||||
})
|
})
|
||||||
@ -69,7 +64,6 @@ func TestJobStrategy(t *testing.T) {
|
|||||||
|
|
||||||
func testJobStrategy(t *testing.T) {
|
func testJobStrategy(t *testing.T) {
|
||||||
indexedJobEnabled := utilfeature.DefaultFeatureGate.Enabled(features.IndexedJob)
|
indexedJobEnabled := utilfeature.DefaultFeatureGate.Enabled(features.IndexedJob)
|
||||||
suspendJobEnabled := utilfeature.DefaultFeatureGate.Enabled(features.SuspendJob)
|
|
||||||
trackingWithFinalizersEnabled := utilfeature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers)
|
trackingWithFinalizersEnabled := utilfeature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers)
|
||||||
ctx := genericapirequest.NewDefaultContext()
|
ctx := genericapirequest.NewDefaultContext()
|
||||||
if !Strategy.NamespaceScoped() {
|
if !Strategy.NamespaceScoped() {
|
||||||
@ -130,9 +124,6 @@ func testJobStrategy(t *testing.T) {
|
|||||||
if indexedJobEnabled != (job.Spec.CompletionMode != nil) {
|
if indexedJobEnabled != (job.Spec.CompletionMode != nil) {
|
||||||
t.Errorf("Job should allow setting .spec.completionMode only when %v feature is enabled", features.IndexedJob)
|
t.Errorf("Job should allow setting .spec.completionMode only when %v feature is enabled", features.IndexedJob)
|
||||||
}
|
}
|
||||||
if !suspendJobEnabled && (job.Spec.Suspend != nil) {
|
|
||||||
t.Errorf("Job should allow setting .spec.suspend only when %v feature is enabled", features.SuspendJob)
|
|
||||||
}
|
|
||||||
wantAnnotations := map[string]string{"foo": "bar"}
|
wantAnnotations := map[string]string{"foo": "bar"}
|
||||||
if trackingWithFinalizersEnabled {
|
if trackingWithFinalizersEnabled {
|
||||||
wantAnnotations[batchv1.JobTrackingFinalizer] = ""
|
wantAnnotations[batchv1.JobTrackingFinalizer] = ""
|
||||||
@ -210,14 +201,8 @@ func testJobStrategy(t *testing.T) {
|
|||||||
// disabled. We don't care about other combinations.
|
// disabled. We don't care about other combinations.
|
||||||
job.Spec.Suspend, updatedJob.Spec.Suspend = pointer.BoolPtr(false), pointer.BoolPtr(true)
|
job.Spec.Suspend, updatedJob.Spec.Suspend = pointer.BoolPtr(false), pointer.BoolPtr(true)
|
||||||
Strategy.PrepareForUpdate(ctx, updatedJob, job)
|
Strategy.PrepareForUpdate(ctx, updatedJob, job)
|
||||||
if !suspendJobEnabled && *updatedJob.Spec.Suspend {
|
|
||||||
t.Errorf("[SuspendJob=%v] .spec.suspend should not be updated from false to true", suspendJobEnabled)
|
|
||||||
}
|
|
||||||
job.Spec.Suspend, updatedJob.Spec.Suspend = nil, pointer.BoolPtr(true)
|
job.Spec.Suspend, updatedJob.Spec.Suspend = nil, pointer.BoolPtr(true)
|
||||||
Strategy.PrepareForUpdate(ctx, updatedJob, job)
|
Strategy.PrepareForUpdate(ctx, updatedJob, job)
|
||||||
if !suspendJobEnabled && updatedJob.Spec.Suspend != nil {
|
|
||||||
t.Errorf("[SuspendJob=%v] .spec.suspend should not be updated from nil to non-nil", suspendJobEnabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we correctly implement the interface.
|
// Make sure we correctly implement the interface.
|
||||||
// Otherwise a typo could silently change the default.
|
// Otherwise a typo could silently change the default.
|
||||||
|
@ -190,9 +190,6 @@ type JobSpec struct {
|
|||||||
// Suspending a Job will reset the StartTime field of the Job, effectively
|
// Suspending a Job will reset the StartTime field of the Job, effectively
|
||||||
// resetting the ActiveDeadlineSeconds timer too. Defaults to false.
|
// resetting the ActiveDeadlineSeconds timer too. Defaults to false.
|
||||||
//
|
//
|
||||||
// This field is beta-level, gated by SuspendJob feature flag (enabled by
|
|
||||||
// default).
|
|
||||||
//
|
|
||||||
// +optional
|
// +optional
|
||||||
Suspend *bool `json:"suspend,omitempty" protobuf:"varint,10,opt,name=suspend"`
|
Suspend *bool `json:"suspend,omitempty" protobuf:"varint,10,opt,name=suspend"`
|
||||||
}
|
}
|
||||||
|
@ -607,32 +607,18 @@ func TestSuspendJob(t *testing.T) {
|
|||||||
// Exhaustively test all combinations other than trivial true->true and
|
// Exhaustively test all combinations other than trivial true->true and
|
||||||
// false->false cases.
|
// false->false cases.
|
||||||
{
|
{
|
||||||
featureGate: true,
|
|
||||||
create: step{flag: false, wantActive: 2},
|
create: step{flag: false, wantActive: 2},
|
||||||
update: step{flag: true, wantActive: 0, wantStatus: v1.ConditionTrue, wantReason: "Suspended"},
|
update: step{flag: true, wantActive: 0, wantStatus: v1.ConditionTrue, wantReason: "Suspended"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
featureGate: true,
|
|
||||||
create: step{flag: true, wantActive: 0, wantStatus: v1.ConditionTrue, wantReason: "Suspended"},
|
create: step{flag: true, wantActive: 0, wantStatus: v1.ConditionTrue, wantReason: "Suspended"},
|
||||||
update: step{flag: false, wantActive: 2, wantStatus: v1.ConditionFalse, wantReason: "Resumed"},
|
update: step{flag: false, wantActive: 2, wantStatus: v1.ConditionFalse, wantReason: "Resumed"},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
featureGate: false,
|
|
||||||
create: step{flag: false, wantActive: 2},
|
|
||||||
update: step{flag: true, wantActive: 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
featureGate: false,
|
|
||||||
create: step{flag: true, wantActive: 2},
|
|
||||||
update: step{flag: false, wantActive: 2},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
name := fmt.Sprintf("feature=%v,create=%v,update=%v", tc.featureGate, tc.create.flag, tc.update.flag)
|
name := fmt.Sprintf("feature=%v,create=%v,update=%v", tc.featureGate, tc.create.flag, tc.update.flag)
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.SuspendJob, tc.featureGate)()
|
|
||||||
|
|
||||||
closeFn, restConfig, clientSet, ns := setup(t, "suspend")
|
closeFn, restConfig, clientSet, ns := setup(t, "suspend")
|
||||||
defer closeFn()
|
defer closeFn()
|
||||||
ctx, cancel := startJobController(restConfig, clientSet)
|
ctx, cancel := startJobController(restConfig, clientSet)
|
||||||
@ -683,8 +669,6 @@ func TestSuspendJob(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSuspendJobControllerRestart(t *testing.T) {
|
func TestSuspendJobControllerRestart(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.SuspendJob, true)()
|
|
||||||
|
|
||||||
closeFn, restConfig, clientSet, ns := setup(t, "suspend")
|
closeFn, restConfig, clientSet, ns := setup(t, "suspend")
|
||||||
defer closeFn()
|
defer closeFn()
|
||||||
ctx, cancel := startJobController(restConfig, clientSet)
|
ctx, cancel := startJobController(restConfig, clientSet)
|
||||||
@ -705,18 +689,6 @@ func TestSuspendJobControllerRestart(t *testing.T) {
|
|||||||
validateJobPodsStatus(ctx, t, clientSet, job, podsByStatus{
|
validateJobPodsStatus(ctx, t, clientSet, job, podsByStatus{
|
||||||
Active: 0,
|
Active: 0,
|
||||||
}, true)
|
}, true)
|
||||||
|
|
||||||
// Disable feature gate and restart controller to test that pods get created.
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.SuspendJob, false)()
|
|
||||||
cancel()
|
|
||||||
ctx, cancel = startJobController(restConfig, clientSet)
|
|
||||||
job, err = clientSet.BatchV1().Jobs(ns.Name).Get(ctx, job.Name, metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to get Job: %v", err)
|
|
||||||
}
|
|
||||||
validateJobPodsStatus(ctx, t, clientSet, job, podsByStatus{
|
|
||||||
Active: 2,
|
|
||||||
}, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeSelectorUpdate(t *testing.T) {
|
func TestNodeSelectorUpdate(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user