diff --git a/.secrets.baseline b/.secrets.baseline index 510ca5cad38..4059b5219fb 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -934,7 +934,7 @@ "filename": "infra/feast-operator/api/v1/featurestore_types.go", "hashed_secret": "44e17306b837162269a410204daaa5ecee4ec22c", "is_verified": false, - "line_number": 729 + "line_number": 734 } ], "infra/feast-operator/api/v1/zz_generated.deepcopy.go": [ @@ -943,21 +943,21 @@ "filename": "infra/feast-operator/api/v1/zz_generated.deepcopy.go", "hashed_secret": "f914fc9324de1bec1ad13dec94a8ea2ddb41fc87", "is_verified": false, - "line_number": 686 + "line_number": 693 }, { "type": "Secret Keyword", "filename": "infra/feast-operator/api/v1/zz_generated.deepcopy.go", "hashed_secret": "44e17306b837162269a410204daaa5ecee4ec22c", "is_verified": false, - "line_number": 1254 + "line_number": 1261 }, { "type": "Secret Keyword", "filename": "infra/feast-operator/api/v1/zz_generated.deepcopy.go", "hashed_secret": "c2028031c154bbe86fd69bef740855c74b927dcf", "is_verified": false, - "line_number": 1259 + "line_number": 1266 } ], "infra/feast-operator/api/v1alpha1/featurestore_types.go": [ @@ -1539,5 +1539,5 @@ } ] }, - "generated_at": "2026-03-18T13:51:43Z" + "generated_at": "2026-04-04T12:08:20Z" } diff --git a/infra/feast-operator/api/v1/featurestore_types.go b/infra/feast-operator/api/v1/featurestore_types.go index 9567a9de2b5..11a975b2c67 100644 --- a/infra/feast-operator/api/v1/featurestore_types.go +++ b/infra/feast-operator/api/v1/featurestore_types.go @@ -308,6 +308,11 @@ type FeatureStoreServices struct { UI *ServerConfigs `json:"ui,omitempty"` DeploymentStrategy *appsv1.DeploymentStrategy `json:"deploymentStrategy,omitempty"` SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + // PodAnnotations are annotations to be applied to the Deployment's PodTemplate metadata. + // This enables annotation-driven integrations like OpenTelemetry auto-instrumentation, + // Istio sidecar injection, Vault agent injection, etc. + // +optional + PodAnnotations map[string]string `json:"podAnnotations,omitempty"` // Disable the 'feast repo initialization' initContainer DisableInitContainers bool `json:"disableInitContainers,omitempty"` // Runs feast apply on pod start to populate the registry. Defaults to true. Ignored when DisableInitContainers is true. diff --git a/infra/feast-operator/api/v1/zz_generated.deepcopy.go b/infra/feast-operator/api/v1/zz_generated.deepcopy.go index 87043017c71..8a86c588f9c 100644 --- a/infra/feast-operator/api/v1/zz_generated.deepcopy.go +++ b/infra/feast-operator/api/v1/zz_generated.deepcopy.go @@ -369,6 +369,13 @@ func (in *FeatureStoreServices) DeepCopyInto(out *FeatureStoreServices) { *out = new(corev1.PodSecurityContext) (*in).DeepCopyInto(*out) } + if in.PodAnnotations != nil { + in, out := &in.PodAnnotations, &out.PodAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.RunFeastApplyOnInit != nil { in, out := &in.RunFeastApplyOnInit, &out.RunFeastApplyOnInit *out = new(bool) diff --git a/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml b/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml index 884b1291264..6bdfbd830b0 100644 --- a/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml +++ b/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml @@ -2508,6 +2508,12 @@ spec: type: object type: object type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations are annotations to be applied to the + Deployment's PodTemplate metadata. + type: object podDisruptionBudgets: description: PodDisruptionBudgets configures a PodDisruptionBudget for the FeatureStore deployment. @@ -8232,6 +8238,12 @@ spec: type: object type: object type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations are annotations to be applied + to the Deployment's PodTemplate metadata. + type: object podDisruptionBudgets: description: PodDisruptionBudgets configures a PodDisruptionBudget for the FeatureStore deployment. diff --git a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml index cdf50015e0f..5f8c64b8c5d 100644 --- a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +++ b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml @@ -2508,6 +2508,12 @@ spec: type: object type: object type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations are annotations to be applied to the + Deployment's PodTemplate metadata. + type: object podDisruptionBudgets: description: PodDisruptionBudgets configures a PodDisruptionBudget for the FeatureStore deployment. @@ -8235,6 +8241,12 @@ spec: type: object type: object type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations are annotations to be applied + to the Deployment's PodTemplate metadata. + type: object podDisruptionBudgets: description: PodDisruptionBudgets configures a PodDisruptionBudget for the FeatureStore deployment. diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index c1818a54744..6967a513bb0 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -2516,6 +2516,12 @@ spec: type: object type: object type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations are annotations to be applied to the + Deployment's PodTemplate metadata. + type: object podDisruptionBudgets: description: PodDisruptionBudgets configures a PodDisruptionBudget for the FeatureStore deployment. @@ -8243,6 +8249,12 @@ spec: type: object type: object type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations are annotations to be applied + to the Deployment's PodTemplate metadata. + type: object podDisruptionBudgets: description: PodDisruptionBudgets configures a PodDisruptionBudget for the FeatureStore deployment. diff --git a/infra/feast-operator/docs/api/markdown/ref.md b/infra/feast-operator/docs/api/markdown/ref.md index b7c7a1b8a71..f964d592adc 100644 --- a/infra/feast-operator/docs/api/markdown/ref.md +++ b/infra/feast-operator/docs/api/markdown/ref.md @@ -239,6 +239,9 @@ _Appears in:_ | `ui` _[ServerConfigs](#serverconfigs)_ | Creates a UI server container | | `deploymentStrategy` _[DeploymentStrategy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#deploymentstrategy-v1-apps)_ | | | `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#podsecuritycontext-v1-core)_ | | +| `podAnnotations` _object (keys:string, values:string)_ | PodAnnotations are annotations to be applied to the Deployment's PodTemplate metadata. +This enables annotation-driven integrations like OpenTelemetry auto-instrumentation, +Istio sidecar injection, Vault agent injection, etc. | | `disableInitContainers` _boolean_ | Disable the 'feast repo initialization' initContainer | | `runFeastApplyOnInit` _boolean_ | Runs feast apply on pod start to populate the registry. Defaults to true. Ignored when DisableInitContainers is true. | | `volumes` _[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#volume-v1-core) array_ | Volumes specifies the volumes to mount in the FeatureStore deployment. A corresponding `VolumeMount` should be added to whichever feast service(s) require access to said volume(s). | diff --git a/infra/feast-operator/internal/controller/services/services.go b/infra/feast-operator/internal/controller/services/services.go index dbd399c745c..d8ff12a9348 100644 --- a/infra/feast-operator/internal/controller/services/services.go +++ b/infra/feast-operator/internal/controller/services/services.go @@ -414,7 +414,8 @@ func (feast *FeastServices) setDeployment(deploy *appsv1.Deployment) error { Strategy: feast.getDeploymentStrategy(), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: deploy.GetLabels(), + Labels: deploy.GetLabels(), + Annotations: cr.Status.Applied.Services.PodAnnotations, }, Spec: corev1.PodSpec{ ServiceAccountName: feast.initFeastSA().Name, diff --git a/infra/feast-operator/internal/controller/services/services_test.go b/infra/feast-operator/internal/controller/services/services_test.go index 131cdb850df..4b7b4343216 100644 --- a/infra/feast-operator/internal/controller/services/services_test.go +++ b/infra/feast-operator/internal/controller/services/services_test.go @@ -202,6 +202,64 @@ var _ = Describe("Registry Service", func() { }) }) + Describe("PodAnnotations Configuration", func() { + It("should apply podAnnotations to deployment pod template", func() { + featureStore.Spec.Services.PodAnnotations = map[string]string{ + "instrumentation.opentelemetry.io/inject-python": "true", + "sidecar.istio.io/inject": "true", + } + Expect(k8sClient.Update(ctx, featureStore)).To(Succeed()) + Expect(feast.ApplyDefaults()).To(Succeed()) + applySpecToStatus(featureStore) + feast.refreshFeatureStore(ctx, typeNamespacedName) + + deployment := feast.initFeastDeploy() + Expect(deployment).NotTo(BeNil()) + Expect(feast.setDeployment(deployment)).To(Succeed()) + + Expect(deployment.Spec.Template.Annotations).To(Equal(map[string]string{ + "instrumentation.opentelemetry.io/inject-python": "true", + "sidecar.istio.io/inject": "true", + })) + }) + + It("should have no pod template annotations when podAnnotations is not set", func() { + Expect(feast.ApplyDefaults()).To(Succeed()) + applySpecToStatus(featureStore) + feast.refreshFeatureStore(ctx, typeNamespacedName) + + deployment := feast.initFeastDeploy() + Expect(deployment).NotTo(BeNil()) + Expect(feast.setDeployment(deployment)).To(Succeed()) + + Expect(deployment.Spec.Template.Annotations).To(BeNil()) + }) + + It("should remove pod template annotations when podAnnotations is removed", func() { + featureStore.Spec.Services.PodAnnotations = map[string]string{ + "instrumentation.opentelemetry.io/inject-python": "true", + } + Expect(k8sClient.Update(ctx, featureStore)).To(Succeed()) + Expect(feast.ApplyDefaults()).To(Succeed()) + applySpecToStatus(featureStore) + feast.refreshFeatureStore(ctx, typeNamespacedName) + + deployment := feast.initFeastDeploy() + Expect(deployment).NotTo(BeNil()) + Expect(feast.setDeployment(deployment)).To(Succeed()) + Expect(deployment.Spec.Template.Annotations).To(HaveKey("instrumentation.opentelemetry.io/inject-python")) + + featureStore.Spec.Services.PodAnnotations = nil + Expect(k8sClient.Update(ctx, featureStore)).To(Succeed()) + Expect(feast.ApplyDefaults()).To(Succeed()) + applySpecToStatus(featureStore) + feast.refreshFeatureStore(ctx, typeNamespacedName) + + Expect(feast.setDeployment(deployment)).To(Succeed()) + Expect(deployment.Spec.Template.Annotations).To(BeNil()) + }) + }) + Describe("NodeSelector Configuration", func() { It("should apply NodeSelector to pod spec when configured", func() { // Set NodeSelector for registry service