Graduate JobTrackingWithFinalizers to stable

Change-Id: Ifc749a85b1270c0155ac511b91d4681d53236820
This commit is contained in:
Aldo Culquicondor
2022-11-01 11:16:34 -04:00
parent c8a3657bde
commit 4948918155
15 changed files with 306 additions and 564 deletions

View File

@@ -93,13 +93,10 @@ func (jobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
job.Generation = 1
if utilfeature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers) {
// Until this feature graduates to GA and soaks in clusters, we use an
// annotation to mark whether jobs are tracked with it.
addJobTrackingAnnotation(job)
} else {
dropJobTrackingAnnotation(job)
}
// While legacy tracking is supported, we use an annotation to mark whether
// jobs are tracked with finalizers.
addJobTrackingAnnotation(job)
if !utilfeature.DefaultFeatureGate.Enabled(features.JobPodFailurePolicy) {
job.Spec.PodFailurePolicy = nil
}
@@ -122,20 +119,12 @@ func hasJobTrackingAnnotation(job *batch.Job) bool {
return ok
}
func dropJobTrackingAnnotation(job *batch.Job) {
delete(job.Annotations, batchv1.JobTrackingFinalizer)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (jobStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newJob := obj.(*batch.Job)
oldJob := old.(*batch.Job)
newJob.Status = oldJob.Status
if !utilfeature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers) && !hasJobTrackingAnnotation(oldJob) {
dropJobTrackingAnnotation(newJob)
}
if !utilfeature.DefaultFeatureGate.Enabled(features.JobPodFailurePolicy) && oldJob.Spec.PodFailurePolicy == nil {
newJob.Spec.PodFailurePolicy = nil
}
@@ -147,6 +136,13 @@ func (jobStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object
if !apiequality.Semantic.DeepEqual(newJob.Spec, oldJob.Spec) {
newJob.Generation = oldJob.Generation + 1
}
// While legacy tracking is supported, we use an annotation to mark whether
// jobs are tracked with finalizers. This annotation cannot be removed by
// users.
if hasJobTrackingAnnotation(oldJob) {
addJobTrackingAnnotation(newJob)
}
}
// Validate validates a new job.
@@ -170,7 +166,7 @@ func validationOptionsForJob(newJob, oldJob *batch.Job) validation.JobValidation
}
opts := validation.JobValidationOptions{
PodValidationOptions: pod.GetValidationOptionsFromPodTemplate(newPodTemplate, oldPodTemplate),
AllowTrackingAnnotation: utilfeature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers),
AllowTrackingAnnotation: true,
}
if oldJob != nil {
// Because we don't support the tracking with finalizers for already

View File

@@ -184,6 +184,30 @@ func TestJobStrategy_PrepareForUpdate(t *testing.T) {
},
},
},
"add tracking annotation back": {
job: batch.Job{
ObjectMeta: getValidObjectMetaWithAnnotations(0, map[string]string{batchv1.JobTrackingFinalizer: ""}),
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
PodFailurePolicy: podFailurePolicy,
},
},
updatedJob: batch.Job{
ObjectMeta: getValidObjectMeta(0),
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
},
},
wantJob: batch.Job{
ObjectMeta: getValidObjectMetaWithAnnotations(1, map[string]string{batchv1.JobTrackingFinalizer: ""}),
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
},
},
},
}
for name, tc := range cases {
@@ -279,24 +303,6 @@ func TestJobStrategy_PrepareForCreate(t *testing.T) {
// TODO(#111514): refactor by spliting into dedicated test functions
func TestJobStrategy(t *testing.T) {
cases := map[string]struct {
trackingWithFinalizersEnabled bool
}{
"features disabled": {},
"new job tracking enabled": {
trackingWithFinalizersEnabled: true,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobTrackingWithFinalizers, tc.trackingWithFinalizersEnabled)()
testJobStrategy(t)
})
}
}
func testJobStrategy(t *testing.T) {
trackingWithFinalizersEnabled := utilfeature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers)
ctx := genericapirequest.NewDefaultContext()
if !Strategy.NamespaceScoped() {
t.Errorf("Job must be namespace scoped")
@@ -356,9 +362,9 @@ func testJobStrategy(t *testing.T) {
if job.Spec.CompletionMode == nil {
t.Errorf("Job should allow setting .spec.completionMode")
}
wantAnnotations := map[string]string{"foo": "bar"}
if trackingWithFinalizersEnabled {
wantAnnotations[batchv1.JobTrackingFinalizer] = ""
wantAnnotations := map[string]string{
"foo": "bar",
batchv1.JobTrackingFinalizer: "",
}
if diff := cmp.Diff(wantAnnotations, job.Annotations); diff != "" {
t.Errorf("Job has annotations (-want,+got):\n%s", diff)
@@ -405,9 +411,8 @@ func testJobStrategy(t *testing.T) {
if updatedJob.Generation != 2 {
t.Errorf("expected Generation=2, got %d", updatedJob.Generation)
}
wantAnnotations = make(map[string]string)
if trackingWithFinalizersEnabled {
wantAnnotations[batchv1.JobTrackingFinalizer] = ""
wantAnnotations = map[string]string{
batchv1.JobTrackingFinalizer: "",
}
if diff := cmp.Diff(wantAnnotations, updatedJob.Annotations); diff != "" {
t.Errorf("Job has annotations (-want,+got):\n%s", diff)
@@ -418,17 +423,6 @@ func testJobStrategy(t *testing.T) {
t.Errorf("Expected a validation error")
}
// Ensure going from legacy tracking Job to tracking with finalizers is
// disallowed.
job = job.DeepCopy()
job.Annotations = nil
updatedJob = job.DeepCopy()
updatedJob.Annotations = map[string]string{batch.JobTrackingFinalizer: ""}
errs = Strategy.ValidateUpdate(ctx, updatedJob, job)
if len(errs) != 1 {
t.Errorf("Expected update validation error")
}
// Test updating suspend false->true and nil-> true when the feature gate is
// disabled. We don't care about other combinations.
job.Spec.Suspend, updatedJob.Spec.Suspend = pointer.BoolPtr(false), pointer.BoolPtr(true)
@@ -475,7 +469,6 @@ func TestJobStrategyValidateUpdate(t *testing.T) {
job *batch.Job
update func(*batch.Job)
wantErrs field.ErrorList
trackingWithFinalizersEnabled bool
mutableSchedulingDirectivesEnabled bool
}{
"update parallelism": {
@@ -518,7 +511,7 @@ func TestJobStrategyValidateUpdate(t *testing.T) {
{Type: field.ErrorTypeInvalid, Field: "spec.completions"},
},
},
"adding tracking annotation disallowed, gate disabled": {
"adding tracking annotation disallowed": {
job: &batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "myjob",
@@ -540,30 +533,7 @@ func TestJobStrategyValidateUpdate(t *testing.T) {
{Type: field.ErrorTypeForbidden, Field: "metadata.annotations[batch.kubernetes.io/job-tracking]"},
},
},
"adding tracking annotation disallowed, gate enabled": {
job: &batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "myjob",
Namespace: metav1.NamespaceDefault,
ResourceVersion: "0",
Annotations: map[string]string{"foo": "bar"},
},
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
ManualSelector: pointer.BoolPtr(true),
Parallelism: pointer.Int32Ptr(1),
},
},
update: func(job *batch.Job) {
job.Annotations[batch.JobTrackingFinalizer] = ""
},
wantErrs: field.ErrorList{
{Type: field.ErrorTypeForbidden, Field: "metadata.annotations[batch.kubernetes.io/job-tracking]"},
},
trackingWithFinalizersEnabled: true,
},
"preserving tracking annotation, feature disabled": {
"preserving tracking annotation": {
job: &batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "myjob",
@@ -683,7 +653,6 @@ func TestJobStrategyValidateUpdate(t *testing.T) {
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobTrackingWithFinalizers, tc.trackingWithFinalizersEnabled)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobMutableNodeSchedulingDirectives, tc.mutableSchedulingDirectivesEnabled)()
newJob := tc.job.DeepCopy()
tc.update(newJob)