Skip to content

Commit be6e6c2

Browse files
feat: Add annotations to cronjob CRDs (#5701)
* feat: Add annotations to cronjob CRDs Signed-off-by: Frederic Kayser <[email protected]> * Implement handling of annotations Signed-off-by: Frederic Kayser <[email protected]> * Add auto-generated files Signed-off-by: Frederic Kayser <[email protected]> * Implement changes from review Signed-off-by: Frederic Kayser <[email protected]> * Implement changes from review Signed-off-by: Frederic Kayser <[email protected]> --------- Signed-off-by: Frederic Kayser <[email protected]> Co-authored-by: Francisco Arceo <[email protected]>
1 parent f855ad2 commit be6e6c2

File tree

7 files changed

+138
-0
lines changed

7 files changed

+138
-0
lines changed

infra/feast-operator/api/v1alpha1/featurestore_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ type FeastInitOptions struct {
111111

112112
// FeastCronJob defines a CronJob to execute against a Feature Store deployment.
113113
type FeastCronJob struct {
114+
// Annotations to be added to the CronJob metadata.
115+
Annotations map[string]string `json:"annotations,omitempty"`
116+
114117
// Specification of the desired behavior of a job.
115118
JobSpec *JobSpec `json:"jobSpec,omitempty"`
116119
ContainerConfigs *CronJobContainerConfigs `json:"containerConfigs,omitempty"`

infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ spec:
8787
description: FeastCronJob defines a CronJob to execute against a Feature
8888
Store deployment.
8989
properties:
90+
annotations:
91+
additionalProperties:
92+
type: string
93+
description: Annotations to be added to the CronJob metadata.
94+
type: object
9095
concurrencyPolicy:
9196
description: Specifies how to treat concurrent executions of a
9297
Job.
@@ -4063,6 +4068,11 @@ spec:
40634068
description: FeastCronJob defines a CronJob to execute against
40644069
a Feature Store deployment.
40654070
properties:
4071+
annotations:
4072+
additionalProperties:
4073+
type: string
4074+
description: Annotations to be added to the CronJob metadata.
4075+
type: object
40664076
concurrencyPolicy:
40674077
description: Specifies how to treat concurrent executions
40684078
of a Job.

infra/feast-operator/dist/install.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ spec:
9595
description: FeastCronJob defines a CronJob to execute against a Feature
9696
Store deployment.
9797
properties:
98+
annotations:
99+
additionalProperties:
100+
type: string
101+
description: Annotations to be added to the CronJob metadata.
102+
type: object
98103
concurrencyPolicy:
99104
description: Specifies how to treat concurrent executions of a
100105
Job.
@@ -4071,6 +4076,11 @@ spec:
40714076
description: FeastCronJob defines a CronJob to execute against
40724077
a Feature Store deployment.
40734078
properties:
4079+
annotations:
4080+
additionalProperties:
4081+
type: string
4082+
description: Annotations to be added to the CronJob metadata.
4083+
type: object
40744084
concurrencyPolicy:
40754085
description: Specifies how to treat concurrent executions
40764086
of a Job.

infra/feast-operator/docs/api/markdown/ref.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ _Appears in:_
9898

9999
| Field | Description |
100100
| --- | --- |
101+
| `annotations` _object (keys:string, values:string)_ | Annotations to be added to the CronJob metadata. |
101102
| `jobSpec` _[JobSpec](#jobspec)_ | Specification of the desired behavior of a job. |
102103
| `containerConfigs` _[CronJobContainerConfigs](#cronjobcontainerconfigs)_ | |
103104
| `schedule` _string_ | The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. |

infra/feast-operator/internal/controller/services/cronjob.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ func (feast *FeastServices) initCronJob() *batchv1.CronJob {
5454
func (feast *FeastServices) setCronJob(cronJob *batchv1.CronJob) error {
5555
appliedCronJob := feast.Handler.FeatureStore.Status.Applied.CronJob
5656
cronJob.Labels = feast.getFeastTypeLabels(CronJobFeastType)
57+
if appliedCronJob.Annotations != nil {
58+
cronJob.Annotations = make(map[string]string, len(appliedCronJob.Annotations))
59+
for k, v := range appliedCronJob.Annotations {
60+
cronJob.Annotations[k] = v
61+
}
62+
}
5763
cronJob.Spec = batchv1.CronJobSpec{
5864
Schedule: appliedCronJob.Schedule,
5965
JobTemplate: batchv1.JobTemplateSpec{

infra/feast-operator/test/api/featurestore_types_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,35 @@ func registryWithGRPCFalse(featureStore *feastdevv1alpha1.FeatureStore) *feastde
438438
return fsCopy
439439
}
440440

441+
func cronJobWithAnnotations(featureStore *feastdevv1alpha1.FeatureStore) *feastdevv1alpha1.FeatureStore {
442+
fsCopy := featureStore.DeepCopy()
443+
fsCopy.Spec.CronJob = &feastdevv1alpha1.FeastCronJob{
444+
Annotations: map[string]string{
445+
"test-annotation": "test-value",
446+
"another-annotation": "another-value",
447+
},
448+
Schedule: "0 0 * * *",
449+
}
450+
return fsCopy
451+
}
452+
453+
func cronJobWithEmptyAnnotations(featureStore *feastdevv1alpha1.FeatureStore) *feastdevv1alpha1.FeatureStore {
454+
fsCopy := featureStore.DeepCopy()
455+
fsCopy.Spec.CronJob = &feastdevv1alpha1.FeastCronJob{
456+
Annotations: map[string]string{},
457+
Schedule: "0 0 * * *",
458+
}
459+
return fsCopy
460+
}
461+
462+
func cronJobWithoutAnnotations(featureStore *feastdevv1alpha1.FeatureStore) *feastdevv1alpha1.FeatureStore {
463+
fsCopy := featureStore.DeepCopy()
464+
fsCopy.Spec.CronJob = &feastdevv1alpha1.FeastCronJob{
465+
Schedule: "0 0 * * *",
466+
}
467+
return fsCopy
468+
}
469+
441470
func quotedSlice(stringSlice []string) string {
442471
quotedSlice := make([]string, len(stringSlice))
443472

@@ -645,4 +674,76 @@ var _ = Describe("FeatureStore API", func() {
645674
})
646675
})
647676
})
677+
678+
Context("When creating a CronJob", func() {
679+
ctx := context.Background()
680+
681+
BeforeEach(func() {
682+
By("verifying the custom resource FeatureStore is not there")
683+
resource := &feastdevv1alpha1.FeatureStore{}
684+
err := k8sClient.Get(ctx, typeNamespacedName, resource)
685+
Expect(err != nil && errors.IsNotFound(err)).To(BeTrue())
686+
})
687+
AfterEach(func() {
688+
By("Cleaning up the test resource")
689+
resource := &feastdevv1alpha1.FeatureStore{}
690+
err := k8sClient.Get(ctx, typeNamespacedName, resource)
691+
if err == nil {
692+
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
693+
}
694+
err = k8sClient.Get(ctx, typeNamespacedName, resource)
695+
Expect(err != nil && errors.IsNotFound(err)).To(BeTrue())
696+
})
697+
698+
Context("with annotations", func() {
699+
It("should succeed when annotations are provided", func() {
700+
featurestore := createFeatureStore()
701+
resource := cronJobWithAnnotations(featurestore)
702+
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
703+
})
704+
705+
It("should succeed when annotations are empty", func() {
706+
featurestore := createFeatureStore()
707+
resource := cronJobWithEmptyAnnotations(featurestore)
708+
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
709+
})
710+
711+
It("should succeed when annotations are not specified", func() {
712+
featurestore := createFeatureStore()
713+
resource := cronJobWithoutAnnotations(featurestore)
714+
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
715+
})
716+
717+
It("should apply the annotations correctly in the status", func() {
718+
featurestore := createFeatureStore()
719+
resource := cronJobWithAnnotations(featurestore)
720+
services.ApplyDefaultsToStatus(resource)
721+
722+
Expect(resource.Status.Applied.CronJob).NotTo(BeNil())
723+
Expect(resource.Status.Applied.CronJob.Annotations).NotTo(BeNil())
724+
Expect(resource.Status.Applied.CronJob.Annotations).To(HaveLen(2))
725+
Expect(resource.Status.Applied.CronJob.Annotations["test-annotation"]).To(Equal("test-value"))
726+
Expect(resource.Status.Applied.CronJob.Annotations["another-annotation"]).To(Equal("another-value"))
727+
})
728+
729+
It("should keep empty annotations in the status", func() {
730+
featurestore := createFeatureStore()
731+
resource := cronJobWithEmptyAnnotations(featurestore)
732+
services.ApplyDefaultsToStatus(resource)
733+
734+
Expect(resource.Status.Applied.CronJob).NotTo(BeNil())
735+
Expect(resource.Status.Applied.CronJob.Annotations).NotTo(BeNil())
736+
Expect(resource.Status.Applied.CronJob.Annotations).To(BeEmpty())
737+
})
738+
739+
It("should have nil annotations in status when not specified", func() {
740+
featurestore := createFeatureStore()
741+
resource := cronJobWithoutAnnotations(featurestore)
742+
services.ApplyDefaultsToStatus(resource)
743+
744+
Expect(resource.Status.Applied.CronJob).NotTo(BeNil())
745+
Expect(resource.Status.Applied.CronJob.Annotations).To(BeNil())
746+
})
747+
})
748+
})
648749
})

0 commit comments

Comments
 (0)