From e89fc47098be6aea4c357f12f02ec32400a7c41a Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Mon, 12 Feb 2024 08:37:26 +0200 Subject: [PATCH 1/4] Add managed-by label support. --- api/jobset/v1alpha2/jobset_types.go | 7 ++ api/jobset/v1alpha2/jobset_webhook.go | 17 +++- api/jobset/v1alpha2/jobset_webhook_test.go | 86 ++++++++++++++++ pkg/controllers/jobset_controller.go | 5 + pkg/util/testing/wrappers.go | 6 ++ .../controller/jobset_controller_test.go | 98 +++++++++++++++++++ .../webhook/jobset_webhook_test.go | 19 ++++ 7 files changed, 234 insertions(+), 4 deletions(-) diff --git a/api/jobset/v1alpha2/jobset_types.go b/api/jobset/v1alpha2/jobset_types.go index 9b43f2e86..7bd97be3f 100644 --- a/api/jobset/v1alpha2/jobset_types.go +++ b/api/jobset/v1alpha2/jobset_types.go @@ -39,6 +39,13 @@ const ( NodeSelectorStrategyKey string = "alpha.jobset.sigs.k8s.io/node-selector" NamespacedJobKey string = "alpha.jobset.sigs.k8s.io/namespaced-job" NoScheduleTaintKey string = "alpha.jobset.sigs.k8s.io/no-schedule" + + // LabelManagedBy is used to indicate the controller or entity that manages an JobSet + LabelManagedBy = "jobset.sigs.k8s.io/managed-by" + + // JobSetManager is used as the value for LabelManagedBy to identify the jobset controller manager + // as the manager of a specific JobSet. + JobSetManager = "jobset" ) type JobSetConditionType string diff --git a/api/jobset/v1alpha2/jobset_webhook.go b/api/jobset/v1alpha2/jobset_webhook.go index 7f09a7bf2..d8295854c 100644 --- a/api/jobset/v1alpha2/jobset_webhook.go +++ b/api/jobset/v1alpha2/jobset_webhook.go @@ -73,6 +73,13 @@ func (js *JobSet) Default() { js.Spec.ReplicatedJobs[i].Template.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyOnFailure } } + + if _, found := js.Labels[LabelManagedBy]; !found { + if js.Labels == nil { + js.Labels = make(map[string]string, 1) + } + js.Labels[LabelManagedBy] = JobSetManager + } } //+kubebuilder:webhook:path=/validate-jobset-x-k8s-io-v1alpha2-jobset,mutating=false,failurePolicy=fail,sideEffects=None,groups=jobset.x-k8s.io,resources=jobsets,verbs=create;update,versions=v1alpha2,name=vjobset.kb.io,admissionReviewVersions=v1 @@ -136,14 +143,16 @@ func (js *JobSet) ValidateCreate() (admission.Warnings, error) { // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (js *JobSet) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { mungedSpec := js.Spec.DeepCopy() - oldSpec := old.(*JobSet).Spec - if ptr.Deref(oldSpec.Suspend, false) { + oldJS := old.(*JobSet) + if ptr.Deref(oldJS.Spec.Suspend, false) { for index := range js.Spec.ReplicatedJobs { - mungedSpec.ReplicatedJobs[index].Template.Spec.Template.Spec.NodeSelector = oldSpec.ReplicatedJobs[index].Template.Spec.Template.Spec.NodeSelector + mungedSpec.ReplicatedJobs[index].Template.Spec.Template.Spec.NodeSelector = oldJS.Spec.ReplicatedJobs[index].Template.Spec.Template.Spec.NodeSelector } } // Note that SucccessPolicy and failurePolicy are made immutable via CEL. - return nil, apivalidation.ValidateImmutableField(mungedSpec.ReplicatedJobs, oldSpec.ReplicatedJobs, field.NewPath("spec").Child("replicatedJobs")).ToAggregate() + errs := apivalidation.ValidateImmutableField(mungedSpec.ReplicatedJobs, oldJS.Spec.ReplicatedJobs, field.NewPath("spec").Child("replicatedJobs")) + errs = append(errs, apivalidation.ValidateImmutableField(js.Labels[LabelManagedBy], oldJS.Labels[LabelManagedBy], field.NewPath("metadata").Child("labels").Key(LabelManagedBy))...) + return nil, errs.ToAggregate() } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type diff --git a/api/jobset/v1alpha2/jobset_webhook_test.go b/api/jobset/v1alpha2/jobset_webhook_test.go index fa4e2f4c3..8a84efa5e 100644 --- a/api/jobset/v1alpha2/jobset_webhook_test.go +++ b/api/jobset/v1alpha2/jobset_webhook_test.go @@ -39,6 +39,9 @@ func TestJobSetDefaulting(t *testing.T) { { name: "job completion mode is unset", js: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -55,6 +58,9 @@ func TestJobSetDefaulting(t *testing.T) { }, }, want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -75,6 +81,9 @@ func TestJobSetDefaulting(t *testing.T) { { name: "job completion mode is set to non-indexed", js: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, Network: &Network{EnableDNSHostnames: ptr.To(true)}, @@ -91,6 +100,9 @@ func TestJobSetDefaulting(t *testing.T) { }, }, want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -111,6 +123,9 @@ func TestJobSetDefaulting(t *testing.T) { { name: "enableDNSHostnames is unset", js: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -127,6 +142,9 @@ func TestJobSetDefaulting(t *testing.T) { }, }, want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -147,6 +165,9 @@ func TestJobSetDefaulting(t *testing.T) { { name: "enableDNSHostnames is false", js: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -164,6 +185,9 @@ func TestJobSetDefaulting(t *testing.T) { }, }, want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -184,6 +208,9 @@ func TestJobSetDefaulting(t *testing.T) { { name: "pod restart policy unset", js: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -203,6 +230,9 @@ func TestJobSetDefaulting(t *testing.T) { }, }, want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -227,6 +257,9 @@ func TestJobSetDefaulting(t *testing.T) { { name: "pod restart policy set", js: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -248,6 +281,9 @@ func TestJobSetDefaulting(t *testing.T) { }, }, want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, StartupPolicy: defaultStartupPolicy, @@ -272,6 +308,9 @@ func TestJobSetDefaulting(t *testing.T) { { name: "success policy unset", js: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ StartupPolicy: defaultStartupPolicy, Network: &Network{EnableDNSHostnames: ptr.To(true)}, @@ -292,6 +331,9 @@ func TestJobSetDefaulting(t *testing.T) { }, }, want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ StartupPolicy: defaultStartupPolicy, SuccessPolicy: defaultSuccessPolicy, @@ -316,6 +358,9 @@ func TestJobSetDefaulting(t *testing.T) { { name: "success policy operator set, replicatedJobNames unset", js: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: &SuccessPolicy{ Operator: OperatorAny, @@ -339,6 +384,9 @@ func TestJobSetDefaulting(t *testing.T) { }, }, want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: &SuccessPolicy{ Operator: OperatorAny, @@ -415,6 +463,44 @@ func TestJobSetDefaulting(t *testing.T) { }, }, }, + { + name: "managed-by label is unset", + js: &JobSet{ + Spec: JobSetSpec{ + SuccessPolicy: defaultSuccessPolicy, + Network: &Network{EnableDNSHostnames: ptr.To(true)}, + ReplicatedJobs: []ReplicatedJob{ + { + Template: batchv1.JobTemplateSpec{ + Spec: batchv1.JobSpec{ + Template: TestPodTemplate, + CompletionMode: completionModePtr(batchv1.IndexedCompletion), + }, + }, + }, + }, + }, + }, + want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, + Spec: JobSetSpec{ + SuccessPolicy: defaultSuccessPolicy, + Network: &Network{EnableDNSHostnames: ptr.To(true)}, + ReplicatedJobs: []ReplicatedJob{ + { + Template: batchv1.JobTemplateSpec{ + Spec: batchv1.JobSpec{ + Template: TestPodTemplate, + CompletionMode: completionModePtr(batchv1.IndexedCompletion), + }, + }, + }, + }, + }, + }, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/pkg/controllers/jobset_controller.go b/pkg/controllers/jobset_controller.go index 9c9e055c3..668c2d63c 100644 --- a/pkg/controllers/jobset_controller.go +++ b/pkg/controllers/jobset_controller.go @@ -90,6 +90,11 @@ func (r *JobSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, client.IgnoreNotFound(err) } + if manager, found := js.Labels[jobset.LabelManagedBy]; found && manager != jobset.JobSetManager { + // the JobSet is not managed by this controller + return ctrl.Result{}, nil + } + log := ctrl.LoggerFrom(ctx).WithValues("jobset", klog.KObj(&js)) ctx = ctrl.LoggerInto(ctx, log) log.V(2).Info("Reconciling JobSet") diff --git a/pkg/util/testing/wrappers.go b/pkg/util/testing/wrappers.go index f3ca69ef3..b762816bc 100644 --- a/pkg/util/testing/wrappers.go +++ b/pkg/util/testing/wrappers.go @@ -78,6 +78,12 @@ func (j *JobSetWrapper) SetAnnotations(annotations map[string]string) *JobSetWra return j } +// SetLabels sets the value of the jobSet.metadata.labels. +func (j *JobSetWrapper) SetLabels(labels map[string]string) *JobSetWrapper { + j.Labels = labels + return j +} + // GenerateName sets the JobSet name. func (j *JobSetWrapper) SetGenerateName(namePrefix string) *JobSetWrapper { // Name and GenerateName are mutually exclusive, so we must unset the Name field. diff --git a/test/integration/controller/jobset_controller_test.go b/test/integration/controller/jobset_controller_test.go index 75a3ab9e8..7f7bd936e 100644 --- a/test/integration/controller/jobset_controller_test.go +++ b/test/integration/controller/jobset_controller_test.go @@ -22,6 +22,7 @@ import ( "strconv" "time" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" batchv1 "k8s.io/api/batch/v1" @@ -1125,6 +1126,103 @@ var _ = ginkgo.Describe("JobSet controller", func() { }, }), ) // end of DescribeTable + + ginkgo.When("A JobSet is managed by another controller", ginkgo.Ordered, func() { + var ( + ctx context.Context + ns *corev1.Namespace + js *jobset.JobSet + ) + ginkgo.BeforeAll(func() { + ctx = context.Background() + // Create test namespace for each entry. + ns = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "jobset-ns-", + }, + } + gomega.Expect(k8sClient.Create(ctx, ns)).To(gomega.Succeed()) + + js = testJobSet(ns).SetGenerateName("name-prefix").SetLabels(map[string]string{ + jobset.LabelManagedBy: "other-controller", + }).Obj() + + ginkgo.By(fmt.Sprintf("creating jobSet %s/%s", js.Name, js.Namespace)) + gomega.Eventually(func() error { + return k8sClient.Create(ctx, js) + }, timeout, interval).Should(gomega.Succeed()) + }) + + ginkgo.AfterAll(func() { + gomega.Expect(testutil.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed()) + }) + + ginkgo.It("Should not create any jobs for it, while suspended", func() { + var jobList batchv1.JobList + gomega.Consistently(func(g gomega.Gomega) { + g.Expect(k8sClient.List(ctx, &jobList, client.InNamespace(js.Namespace))).To(gomega.Succeed()) + g.Expect(len(jobList.Items)).To(gomega.BeZero()) + }, timeout, interval).Should(gomega.Succeed()) + }) + + ginkgo.It("Should not create any jobs for it, when unsuspended", func() { + var jobList batchv1.JobList + ginkgo.By("Unsuspending the jobset", func() { + updatedJs := &jobset.JobSet{} + + gomega.Eventually(func(g gomega.Gomega) { + g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(js), updatedJs)).To(gomega.Succeed()) + updatedJs.Spec.Suspend = ptr.To(false) + g.Expect(k8sClient.Update(ctx, updatedJs)).To(gomega.Succeed()) + + }).Should(gomega.Succeed()) + }) + + gomega.Consistently(func(g gomega.Gomega) { + g.Expect(k8sClient.List(ctx, &jobList, client.InNamespace(js.Namespace))).To(gomega.Succeed()) + g.Expect(len(jobList.Items)).To(gomega.BeZero()) + }, timeout, interval).Should(gomega.Succeed()) + }) + + ginkgo.It("updates to its status are preserved", func() { + updatedJs := &jobset.JobSet{} + wantStatus := jobset.JobSetStatus{ + Conditions: []metav1.Condition{ + { + Type: string(jobset.JobSetFailed), + Status: metav1.ConditionFalse, + Reason: "ByTest", + LastTransitionTime: metav1.Now(), + }, + }, + Restarts: 1, + ReplicatedJobsStatus: []jobset.ReplicatedJobStatus{ + { + Name: "replicated-job-a", + Ready: 2, + Succeeded: 3, + Failed: 4, + Active: 5, + Suspended: 6, + }, + }, + } + + ginkgo.By("Updateing the jobset status", func() { + gomega.Eventually(func(g gomega.Gomega) { + g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(js), updatedJs)).To(gomega.Succeed()) + updatedJs.Status = wantStatus + g.Expect(k8sClient.Status().Update(ctx, updatedJs)).To(gomega.Succeed()) + + }).Should(gomega.Succeed()) + }) + + gomega.Consistently(func(g gomega.Gomega) { + g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(js), updatedJs)).To(gomega.Succeed()) + g.Expect(updatedJs.Status).To(gomega.BeComparableTo(wantStatus, cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime"))) + }, timeout, interval).Should(gomega.Succeed()) + }) + }) }) // end of Describe func makeAllJobsReady(jl *batchv1.JobList) { diff --git a/test/integration/webhook/jobset_webhook_test.go b/test/integration/webhook/jobset_webhook_test.go index b7839ad70..b5fd98db5 100644 --- a/test/integration/webhook/jobset_webhook_test.go +++ b/test/integration/webhook/jobset_webhook_test.go @@ -335,5 +335,24 @@ var _ = ginkgo.Describe("jobset webhook defaulting", func() { }, updateShouldFail: true, }), + ginkgo.Entry("validate jobSet immutable for managed-by label", &testCase{ + makeJobSet: func(ns *corev1.Namespace) *testing.JobSetWrapper { + return testing.MakeJobSet("js-hostnames-non-indexed", ns.Name). + Suspend(true). + EnableDNSHostnames(true). + ReplicatedJob(testing.MakeReplicatedJob("rjob"). + Job(testing.MakeJobTemplate("job", ns.Name). + PodSpec(testing.TestPodSpec). + CompletionMode(batchv1.IndexedCompletion).Obj()). + Obj()) + }, + defaultsApplied: func(js *jobset.JobSet) bool { + return js.Labels[jobset.LabelManagedBy] == jobset.JobSetManager + }, + updateJobSet: func(js *jobset.JobSet) { + js.Labels[jobset.LabelManagedBy] = "new-manager" + }, + updateShouldFail: true, + }), ) // end of DescribeTable }) // end of Describe From 7d93e3396fea044cdfcbff220f76c1339b6a3f23 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Mon, 12 Feb 2024 17:40:19 +0200 Subject: [PATCH 2/4] Review Reamarks --- api/jobset/v1alpha2/jobset_types.go | 2 +- pkg/controllers/jobset_controller.go | 7 ++++--- test/integration/controller/jobset_controller_test.go | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/api/jobset/v1alpha2/jobset_types.go b/api/jobset/v1alpha2/jobset_types.go index 7bd97be3f..6c8fb64a6 100644 --- a/api/jobset/v1alpha2/jobset_types.go +++ b/api/jobset/v1alpha2/jobset_types.go @@ -41,7 +41,7 @@ const ( NoScheduleTaintKey string = "alpha.jobset.sigs.k8s.io/no-schedule" // LabelManagedBy is used to indicate the controller or entity that manages an JobSet - LabelManagedBy = "jobset.sigs.k8s.io/managed-by" + LabelManagedBy = "alpha.jobset.sigs.k8s.io/managed-by" // JobSetManager is used as the value for LabelManagedBy to identify the jobset controller manager // as the manager of a specific JobSet. diff --git a/pkg/controllers/jobset_controller.go b/pkg/controllers/jobset_controller.go index 668c2d63c..6b80126d1 100644 --- a/pkg/controllers/jobset_controller.go +++ b/pkg/controllers/jobset_controller.go @@ -90,13 +90,14 @@ func (r *JobSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, client.IgnoreNotFound(err) } + log := ctrl.LoggerFrom(ctx).WithValues("jobset", klog.KObj(&js)) + ctx = ctrl.LoggerInto(ctx, log) + if manager, found := js.Labels[jobset.LabelManagedBy]; found && manager != jobset.JobSetManager { - // the JobSet is not managed by this controller + log.V(5).Info("Skipping JobSet managed by a different controller", "managed-by", manager) return ctrl.Result{}, nil } - log := ctrl.LoggerFrom(ctx).WithValues("jobset", klog.KObj(&js)) - ctx = ctrl.LoggerInto(ctx, log) log.V(2).Info("Reconciling JobSet") // Get Jobs owned by JobSet. diff --git a/test/integration/controller/jobset_controller_test.go b/test/integration/controller/jobset_controller_test.go index 7f7bd936e..242846678 100644 --- a/test/integration/controller/jobset_controller_test.go +++ b/test/integration/controller/jobset_controller_test.go @@ -1167,7 +1167,7 @@ var _ = ginkgo.Describe("JobSet controller", func() { ginkgo.It("Should not create any jobs for it, when unsuspended", func() { var jobList batchv1.JobList - ginkgo.By("Unsuspending the jobset", func() { + ginkgo.By("Unsuspending the JobSet", func() { updatedJs := &jobset.JobSet{} gomega.Eventually(func(g gomega.Gomega) { @@ -1184,7 +1184,7 @@ var _ = ginkgo.Describe("JobSet controller", func() { }, timeout, interval).Should(gomega.Succeed()) }) - ginkgo.It("updates to its status are preserved", func() { + ginkgo.It("Updates to its status are preserved", func() { updatedJs := &jobset.JobSet{} wantStatus := jobset.JobSetStatus{ Conditions: []metav1.Condition{ @@ -1208,7 +1208,7 @@ var _ = ginkgo.Describe("JobSet controller", func() { }, } - ginkgo.By("Updateing the jobset status", func() { + ginkgo.By("Updating the JobSet status", func() { gomega.Eventually(func(g gomega.Gomega) { g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(js), updatedJs)).To(gomega.Succeed()) updatedJs.Status = wantStatus From 37ccd54356ad7ed66f3d72a4dfb1391c2be20be5 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Tue, 13 Feb 2024 07:57:04 +0200 Subject: [PATCH 3/4] Review Remarks --- api/jobset/v1alpha2/jobset_webhook_test.go | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/api/jobset/v1alpha2/jobset_webhook_test.go b/api/jobset/v1alpha2/jobset_webhook_test.go index 8a84efa5e..b5f53ebb2 100644 --- a/api/jobset/v1alpha2/jobset_webhook_test.go +++ b/api/jobset/v1alpha2/jobset_webhook_test.go @@ -501,6 +501,47 @@ func TestJobSetDefaulting(t *testing.T) { }, }, }, + { + name: "when provided, managed-by label is preserved", + js: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: "other-controller"}, + }, + Spec: JobSetSpec{ + SuccessPolicy: defaultSuccessPolicy, + Network: &Network{EnableDNSHostnames: ptr.To(true)}, + ReplicatedJobs: []ReplicatedJob{ + { + Template: batchv1.JobTemplateSpec{ + Spec: batchv1.JobSpec{ + Template: TestPodTemplate, + CompletionMode: completionModePtr(batchv1.IndexedCompletion), + }, + }, + }, + }, + }, + }, + want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: "other-controller"}, + }, + Spec: JobSetSpec{ + SuccessPolicy: defaultSuccessPolicy, + Network: &Network{EnableDNSHostnames: ptr.To(true)}, + ReplicatedJobs: []ReplicatedJob{ + { + Template: batchv1.JobTemplateSpec{ + Spec: batchv1.JobSpec{ + Template: TestPodTemplate, + CompletionMode: completionModePtr(batchv1.IndexedCompletion), + }, + }, + }, + }, + }, + }, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { From a0556f31b8882f3a616b605b1f0a9069094d8f12 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Tue, 13 Feb 2024 08:41:23 +0200 Subject: [PATCH 4/4] Fix UT after rebase --- api/jobset/v1alpha2/jobset_webhook_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api/jobset/v1alpha2/jobset_webhook_test.go b/api/jobset/v1alpha2/jobset_webhook_test.go index b5f53ebb2..c0c6a8364 100644 --- a/api/jobset/v1alpha2/jobset_webhook_test.go +++ b/api/jobset/v1alpha2/jobset_webhook_test.go @@ -438,6 +438,9 @@ func TestJobSetDefaulting(t *testing.T) { }, }, want: &JobSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{LabelManagedBy: JobSetManager}, + }, Spec: JobSetSpec{ SuccessPolicy: &SuccessPolicy{ Operator: OperatorAny, @@ -487,6 +490,7 @@ func TestJobSetDefaulting(t *testing.T) { }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, + StartupPolicy: defaultStartupPolicy, Network: &Network{EnableDNSHostnames: ptr.To(true)}, ReplicatedJobs: []ReplicatedJob{ { @@ -528,6 +532,7 @@ func TestJobSetDefaulting(t *testing.T) { }, Spec: JobSetSpec{ SuccessPolicy: defaultSuccessPolicy, + StartupPolicy: defaultStartupPolicy, Network: &Network{EnableDNSHostnames: ptr.To(true)}, ReplicatedJobs: []ReplicatedJob{ {