From c06067dad941318eda167d3ad3c21d8c6a3897e3 Mon Sep 17 00:00:00 2001 From: lrangine <19699092+lokeshrangineni@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:59:17 -0500 Subject: [PATCH 1/2] Adding EnvFrom support for the OptionalConfigs type to the feast go operator Signed-off-by: lrangine <19699092+lokeshrangineni@users.noreply.github.com> --- .../api/v1alpha1/featurestore_types.go | 1 + .../api/v1alpha1/zz_generated.deepcopy.go | 11 + .../crd/bases/feast.dev_featurestores.yaml | 246 ++++++++++++++++++ infra/feast-operator/dist/install.yaml | 246 ++++++++++++++++++ .../featurestore_controller_db_store_test.go | 43 ++- .../featurestore_controller_ephemeral_test.go | 58 ++++- ...restore_controller_kubernetes_auth_test.go | 58 ++++- ...eaturestore_controller_objectstore_test.go | 46 +++- .../featurestore_controller_oidc_auth_test.go | 56 +++- .../featurestore_controller_pvc_test.go | 57 +++- .../featurestore_controller_test.go | 78 +++++- .../internal/controller/services/services.go | 3 + 12 files changed, 889 insertions(+), 14 deletions(-) diff --git a/infra/feast-operator/api/v1alpha1/featurestore_types.go b/infra/feast-operator/api/v1alpha1/featurestore_types.go index f8d0c0f7da..6eeef15d07 100644 --- a/infra/feast-operator/api/v1alpha1/featurestore_types.go +++ b/infra/feast-operator/api/v1alpha1/featurestore_types.go @@ -296,6 +296,7 @@ type DefaultConfigs struct { // OptionalConfigs k8s container settings that are optional type OptionalConfigs struct { Env *[]corev1.EnvVar `json:"env,omitempty"` + EnvFrom *[]corev1.EnvFromSource `json:"envFrom,omitempty"` ImagePullPolicy *corev1.PullPolicy `json:"imagePullPolicy,omitempty"` Resources *corev1.ResourceRequirements `json:"resources,omitempty"` } diff --git a/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go b/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go index f1e0503088..72e6fc7200 100644 --- a/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -474,6 +474,17 @@ func (in *OptionalConfigs) DeepCopyInto(out *OptionalConfigs) { } } } + if in.EnvFrom != nil { + in, out := &in.EnvFrom, &out.EnvFrom + *out = new([]v1.EnvFromSource) + if **in != nil { + in, out := *in, *out + *out = make([]v1.EnvFromSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } if in.ImagePullPolicy != nil { in, out := &in.ImagePullPolicy, &out.ImagePullPolicy *out = new(v1.PullPolicy) 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 bd0f5aa61d..270cf4d353 100644 --- a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +++ b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml @@ -221,6 +221,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: @@ -589,6 +630,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: @@ -976,6 +1058,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of + a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: @@ -1485,6 +1608,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of + a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: @@ -1858,6 +2022,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of + a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: @@ -2253,6 +2458,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source + of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap + must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend + to each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret + must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index ef2b1f2272..af0761a5a6 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -229,6 +229,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: @@ -597,6 +638,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: @@ -984,6 +1066,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of + a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: @@ -1493,6 +1616,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of + a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: @@ -1866,6 +2030,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of + a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret must + be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: @@ -2261,6 +2466,47 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source + of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap + must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend + to each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret + must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: type: string imagePullPolicy: diff --git a/infra/feast-operator/internal/controller/featurestore_controller_db_store_test.go b/infra/feast-operator/internal/controller/featurestore_controller_db_store_test.go index 0bde0dfd7b..b12a670beb 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_db_store_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_db_store_test.go @@ -202,10 +202,31 @@ var _ = Describe("FeatureStore Controller - db storage services", func() { Expect(k8sClient.Create(ctx, secret)).To(Succeed()) } + By("creating the config map and secret for envFrom") + envFromConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + Data: map[string]string{"example-key": "example-value"}, + } + err = k8sClient.Create(context.TODO(), envFromConfigMap) + Expect(err).ToNot(HaveOccurred()) + + envFromSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + StringData: map[string]string{"secret-key": "secret-value"}, + } + err = k8sClient.Create(context.TODO(), envFromSecret) + Expect(err).ToNot(HaveOccurred()) + By("creating the custom resource for the Kind FeatureStore") err = k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { - resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{}) + resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{}, withEnvFrom()) resource.Spec.Services.OfflineStore.Persistence = &feastdevv1alpha1.OfflineStorePersistence{ DBPersistence: &feastdevv1alpha1.OfflineStoreDBStorePersistence{ Type: string(offlineType), @@ -256,6 +277,26 @@ var _ = Describe("FeatureStore Controller - db storage services", func() { err = k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) + By("Deleting the configmap and secret for envFrom") + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), configMap) + Expect(err).ToNot(HaveOccurred()) + + // Delete Secret + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), secret) + Expect(err).ToNot(HaveOccurred()) + By("Cleanup the secrets") Expect(k8sClient.Delete(ctx, onlineSecret)).To(Succeed()) Expect(k8sClient.Delete(ctx, offlineSecret)).To(Succeed()) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go b/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go index 70ac81a056..80908aec45 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go @@ -62,11 +62,32 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { registryPath := "/data/registry.db" BeforeEach(func() { + By("creating the config map and secret for envFrom") + envFromConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + Data: map[string]string{"example-key": "example-value"}, + } + err := k8sClient.Create(context.TODO(), envFromConfigMap) + Expect(err).ToNot(HaveOccurred()) + + envFromSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + StringData: map[string]string{"secret-key": "secret-value"}, + } + err = k8sClient.Create(context.TODO(), envFromSecret) + Expect(err).ToNot(HaveOccurred()) + By("creating the custom resource for the Kind FeatureStore") - err := k8sClient.Get(ctx, typeNamespacedName, featurestore) + err = k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, - {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}) + {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}, withEnvFrom()) resource.Spec.Services.OfflineStore.Persistence = &feastdevv1alpha1.OfflineStorePersistence{ FilePersistence: &feastdevv1alpha1.OfflineStoreFilePersistence{ Type: offlineType, @@ -97,6 +118,27 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + + // Delete ConfigMap + By("Deleting the configmap and secret for envFrom") + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), configMap) + Expect(err).ToNot(HaveOccurred()) + + // Delete Secret + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), secret) + Expect(err).ToNot(HaveOccurred()) }) It("should successfully reconcile the resource", func() { @@ -141,6 +183,18 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.Path).To(Equal(onlineStorePath)) Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}})) + Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(&[]corev1.EnvFromSource{ + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, + }, + }, + })) Expect(resource.Status.Applied.Services.OnlineStore.ImagePullPolicy).To(Equal(&pullPolicy)) Expect(resource.Status.Applied.Services.OnlineStore.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Image).To(Equal(&image)) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_kubernetes_auth_test.go b/infra/feast-operator/internal/controller/featurestore_controller_kubernetes_auth_test.go index 57a73eb0eb..7370537e59 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_kubernetes_auth_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_kubernetes_auth_test.go @@ -59,10 +59,31 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { roles := []string{"reader", "writer"} BeforeEach(func() { + By("creating the config map and secret for envFrom") + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + Data: map[string]string{"example-key": "example-value"}, + } + err := k8sClient.Create(context.TODO(), configMap) + Expect(err).ToNot(HaveOccurred()) + + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + StringData: map[string]string{"secret-key": "secret-value"}, + } + err = k8sClient.Create(context.TODO(), secret) + Expect(err).ToNot(HaveOccurred()) + By("creating the custom resource for the Kind FeatureStore") - err := k8sClient.Get(ctx, typeNamespacedName, featurestore) + err = k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { - resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{}) + resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{}, withEnvFrom()) resource.Spec.AuthzConfig = &feastdevv1alpha1.AuthzConfig{KubernetesAuthz: &feastdevv1alpha1.KubernetesAuthz{ Roles: roles, }} @@ -75,6 +96,27 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) + // Delete ConfigMap + By("Deleting the configmap and secret for envFrom") + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), configMap) + Expect(err).ToNot(HaveOccurred()) + + // Delete Secret + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), secret) + Expect(err).ToNot(HaveOccurred()) + By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) }) @@ -126,6 +168,18 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.Path).To(Equal(services.EphemeralPath + "/" + services.DefaultOnlineStorePath)) Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{})) + Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(&[]corev1.EnvFromSource{ + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, + }, + }, + })) Expect(resource.Status.Applied.Services.OnlineStore.ImagePullPolicy).To(Equal(&pullPolicy)) Expect(resource.Status.Applied.Services.OnlineStore.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Image).To(Equal(&image)) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go b/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go index aff36f338e..5234c5aee1 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go @@ -64,11 +64,32 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { } BeforeEach(func() { + By("creating the config map and secret for envFrom") + envFromConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + Data: map[string]string{"example-key": "example-value"}, + } + err := k8sClient.Create(context.TODO(), envFromConfigMap) + Expect(err).ToNot(HaveOccurred()) + + envFromSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + StringData: map[string]string{"secret-key": "secret-value"}, + } + err = k8sClient.Create(context.TODO(), envFromSecret) + Expect(err).ToNot(HaveOccurred()) + By("creating the custom resource for the Kind FeatureStore") - err := k8sClient.Get(ctx, typeNamespacedName, featurestore) + err = k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, - {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}) + {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}, withEnvFrom()) resource.Spec.Services.OnlineStore = nil resource.Spec.Services.OfflineStore = nil resource.Spec.Services.Registry = &feastdevv1alpha1.Registry{ @@ -81,7 +102,6 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { }, }, } - Expect(k8sClient.Create(ctx, resource)).To(Succeed()) } }) @@ -90,6 +110,26 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) + By("Deleting the configmap and secret for envFrom") + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), configMap) + Expect(err).ToNot(HaveOccurred()) + + // Delete Secret + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), secret) + Expect(err).ToNot(HaveOccurred()) + By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) }) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_oidc_auth_test.go b/infra/feast-operator/internal/controller/featurestore_controller_oidc_auth_test.go index 08c92a88a9..dd7028529e 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_oidc_auth_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_oidc_auth_test.go @@ -70,10 +70,31 @@ var _ = Describe("FeatureStore Controller-OIDC authorization", func() { Expect(k8sClient.Create(ctx, oidcSecret)).To(Succeed()) } + By("creating the config map and secret for envFrom") + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + Data: map[string]string{"example-key": "example-value"}, + } + err = k8sClient.Create(context.TODO(), configMap) + Expect(err).ToNot(HaveOccurred()) + + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + StringData: map[string]string{"secret-key": "secret-value"}, + } + err = k8sClient.Create(context.TODO(), secret) + Expect(err).ToNot(HaveOccurred()) + By("creating the custom resource for the Kind FeatureStore") err = k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { - resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{}) + resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{}, withEnvFrom()) resource.Spec.AuthzConfig = &feastdevv1alpha1.AuthzConfig{OidcAuthz: &feastdevv1alpha1.OidcAuthz{ SecretRef: corev1.LocalObjectReference{ Name: oidcSecretName, @@ -82,6 +103,7 @@ var _ = Describe("FeatureStore Controller-OIDC authorization", func() { Expect(k8sClient.Create(ctx, resource)).To(Succeed()) } + }) AfterEach(func() { resource := &feastdevv1alpha1.FeatureStore{} @@ -97,6 +119,26 @@ var _ = Describe("FeatureStore Controller-OIDC authorization", func() { By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + + By("Deleting the configmap and secret for envFrom") + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), configMap) + Expect(err).ToNot(HaveOccurred()) + + // Delete Secret + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), secret) + Expect(err).ToNot(HaveOccurred()) }) It("should successfully reconcile the resource", func() { @@ -148,6 +190,18 @@ var _ = Describe("FeatureStore Controller-OIDC authorization", func() { Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.Path).To(Equal(services.EphemeralPath + "/" + services.DefaultOnlineStorePath)) Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{})) + Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(&[]corev1.EnvFromSource{ + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, + }, + }, + })) Expect(resource.Status.Applied.Services.OnlineStore.ImagePullPolicy).To(Equal(&pullPolicy)) Expect(resource.Status.Applied.Services.OnlineStore.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Image).To(Equal(&image)) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go b/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go index 887d9070ef..cb25d5a35d 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go @@ -75,11 +75,32 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { registryMountedPath := path.Join(registryMountPath, registryPath) BeforeEach(func() { + By("creating the config map and secret for envFrom") + envFromConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + Data: map[string]string{"example-key": "example-value"}, + } + err := k8sClient.Create(context.TODO(), envFromConfigMap) + Expect(err).ToNot(HaveOccurred()) + + envFromSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + StringData: map[string]string{"secret-key": "secret-value"}, + } + err = k8sClient.Create(context.TODO(), envFromSecret) + Expect(err).ToNot(HaveOccurred()) + By("creating the custom resource for the Kind FeatureStore") - err := k8sClient.Get(ctx, typeNamespacedName, featurestore) + err = k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, - {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}) + {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}, withEnvFrom()) resource.Spec.Services.OfflineStore.Persistence = &feastdevv1alpha1.OfflineStorePersistence{ FilePersistence: &feastdevv1alpha1.OfflineStoreFilePersistence{ Type: offlineType, @@ -125,6 +146,26 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + + By("Deleting the configmap and secret for envFrom") + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), configMap) + Expect(err).ToNot(HaveOccurred()) + + // Delete Secret + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), secret) + Expect(err).ToNot(HaveOccurred()) }) It("should successfully reconcile the resource", func() { @@ -191,6 +232,18 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.PvcConfig.Create.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.PvcConfig.Create.Resources).To(Equal(expectedResources)) Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}})) + Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(&[]corev1.EnvFromSource{ + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, + }, + }, + })) Expect(resource.Status.Applied.Services.OnlineStore.ImagePullPolicy).To(Equal(&pullPolicy)) Expect(resource.Status.Applied.Services.OnlineStore.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Image).To(Equal(&image)) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_test.go b/infra/feast-operator/internal/controller/featurestore_controller_test.go index 71b5d400f8..a022fe7287 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_test.go @@ -402,11 +402,32 @@ var _ = Describe("FeatureStore Controller", func() { featurestore := &feastdevv1alpha1.FeatureStore{} BeforeEach(func() { + By("creating the config map and secret for envFrom") + envFromConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + Data: map[string]string{"example-key": "example-value"}, + } + err := k8sClient.Create(context.TODO(), envFromConfigMap) + Expect(err).ToNot(HaveOccurred()) + + envFromSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + StringData: map[string]string{"secret-key": "secret-value"}, + } + err = k8sClient.Create(context.TODO(), envFromSecret) + Expect(err).ToNot(HaveOccurred()) + By("creating the custom resource for the Kind FeatureStore") - err := k8sClient.Get(ctx, typeNamespacedName, featurestore) + err = k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, - {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}) + {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}, withEnvFrom()) Expect(k8sClient.Create(ctx, resource)).To(Succeed()) } }) @@ -417,6 +438,27 @@ var _ = Describe("FeatureStore Controller", func() { By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + + // Delete ConfigMap + By("Deleting the configmap and secret for envFrom") + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), configMap) + Expect(err).ToNot(HaveOccurred()) + + // Delete Secret + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), secret) + Expect(err).ToNot(HaveOccurred()) }) It("should successfully reconcile the resource", func() { @@ -461,6 +503,18 @@ var _ = Describe("FeatureStore Controller", func() { Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.Path).To(Equal(services.EphemeralPath + "/" + services.DefaultOnlineStorePath)) Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}})) + Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(&[]corev1.EnvFromSource{ + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, + }, + }, + })) Expect(resource.Status.Applied.Services.OnlineStore.ImagePullPolicy).To(Equal(&pullPolicy)) Expect(resource.Status.Applied.Services.OnlineStore.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Image).To(Equal(&image)) @@ -1207,7 +1261,24 @@ var _ = Describe("FeatureStore Controller", func() { }) }) -func createFeatureStoreResource(resourceName string, image string, pullPolicy corev1.PullPolicy, envVars *[]corev1.EnvVar) *feastdevv1alpha1.FeatureStore { +func withEnvFrom() *[]corev1.EnvFromSource { + + return &[]corev1.EnvFromSource{ + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, + }, + }, + } + +} + +func createFeatureStoreResource(resourceName string, image string, pullPolicy corev1.PullPolicy, envVars *[]corev1.EnvVar, envFromVar *[]corev1.EnvFromSource) *feastdevv1alpha1.FeatureStore { return &feastdevv1alpha1.FeatureStore{ ObjectMeta: metav1.ObjectMeta{ Name: resourceName, @@ -1224,6 +1295,7 @@ func createFeatureStoreResource(resourceName string, image string, pullPolicy co }, OptionalConfigs: feastdevv1alpha1.OptionalConfigs{ Env: envVars, + EnvFrom: envFromVar, ImagePullPolicy: &pullPolicy, Resources: &corev1.ResourceRequirements{}, }, diff --git a/infra/feast-operator/internal/controller/services/services.go b/infra/feast-operator/internal/controller/services/services.go index 32cf91d09b..16f3e66390 100644 --- a/infra/feast-operator/internal/controller/services/services.go +++ b/infra/feast-operator/internal/controller/services/services.go @@ -708,6 +708,9 @@ func applyOptionalContainerConfigs(container *corev1.Container, optionalConfigs if optionalConfigs.Env != nil { container.Env = envOverride(container.Env, *optionalConfigs.Env) } + if optionalConfigs.EnvFrom != nil { + container.EnvFrom = *optionalConfigs.EnvFrom + } if optionalConfigs.ImagePullPolicy != nil { container.ImagePullPolicy = *optionalConfigs.ImagePullPolicy } From f3623f1f5c4f3fda1dc90eebdf16a21dbb910403 Mon Sep 17 00:00:00 2001 From: lrangine <19699092+lokeshrangineni@users.noreply.github.com> Date: Fri, 10 Jan 2025 19:16:28 -0500 Subject: [PATCH 2/2] * Refactored the code to avoid the redundent code. moved common code to util.go * Incorporated code review comments. Added assertion for environment variables at the container level. Signed-off-by: lrangine <19699092+lokeshrangineni@users.noreply.github.com> --- .../featurestore_controller_db_store_test.go | 43 +---- .../featurestore_controller_ephemeral_test.go | 66 ++------ ...restore_controller_kubernetes_auth_test.go | 67 ++------ ...eaturestore_controller_objectstore_test.go | 43 +---- .../featurestore_controller_oidc_auth_test.go | 57 +------ .../featurestore_controller_pvc_test.go | 60 +------ .../featurestore_controller_test.go | 105 +------------ ...featurestore_controller_test_utils_test.go | 147 ++++++++++++++++++ 8 files changed, 200 insertions(+), 388 deletions(-) create mode 100644 infra/feast-operator/internal/controller/featurestore_controller_test_utils_test.go diff --git a/infra/feast-operator/internal/controller/featurestore_controller_db_store_test.go b/infra/feast-operator/internal/controller/featurestore_controller_db_store_test.go index b12a670beb..48013c453c 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_db_store_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_db_store_test.go @@ -202,26 +202,7 @@ var _ = Describe("FeatureStore Controller - db storage services", func() { Expect(k8sClient.Create(ctx, secret)).To(Succeed()) } - By("creating the config map and secret for envFrom") - envFromConfigMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - Data: map[string]string{"example-key": "example-value"}, - } - err = k8sClient.Create(context.TODO(), envFromConfigMap) - Expect(err).ToNot(HaveOccurred()) - - envFromSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - StringData: map[string]string{"secret-key": "secret-value"}, - } - err = k8sClient.Create(context.TODO(), envFromSecret) - Expect(err).ToNot(HaveOccurred()) + createEnvFromSecretAndConfigMap() By("creating the custom resource for the Kind FeatureStore") err = k8sClient.Get(ctx, typeNamespacedName, featurestore) @@ -277,25 +258,7 @@ var _ = Describe("FeatureStore Controller - db storage services", func() { err = k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - By("Deleting the configmap and secret for envFrom") - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), configMap) - Expect(err).ToNot(HaveOccurred()) - - // Delete Secret - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), secret) - Expect(err).ToNot(HaveOccurred()) + deleteEnvFromSecretAndConfigMap() By("Cleanup the secrets") Expect(k8sClient.Delete(ctx, onlineSecret)).To(Succeed()) @@ -639,6 +602,7 @@ var _ = Describe("FeatureStore Controller - db storage services", func() { offlineContainer := services.GetOfflineContainer(deploy.Spec.Template.Spec.Containers) Expect(offlineContainer.Env).To(HaveLen(1)) + assertEnvFrom(*offlineContainer) env = getFeatureStoreYamlEnvVar(offlineContainer.Env) Expect(env).NotTo(BeNil()) @@ -656,6 +620,7 @@ var _ = Describe("FeatureStore Controller - db storage services", func() { onlineContainer := services.GetOnlineContainer(deploy.Spec.Template.Spec.Containers) Expect(onlineContainer.VolumeMounts).To(HaveLen(1)) Expect(onlineContainer.Env).To(HaveLen(1)) + assertEnvFrom(*onlineContainer) Expect(onlineContainer.ImagePullPolicy).To(Equal(corev1.PullAlways)) env = getFeatureStoreYamlEnvVar(onlineContainer.Env) Expect(env).NotTo(BeNil()) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go b/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go index 80908aec45..dbf21a9d91 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go @@ -62,29 +62,9 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { registryPath := "/data/registry.db" BeforeEach(func() { - By("creating the config map and secret for envFrom") - envFromConfigMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - Data: map[string]string{"example-key": "example-value"}, - } - err := k8sClient.Create(context.TODO(), envFromConfigMap) - Expect(err).ToNot(HaveOccurred()) - - envFromSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - StringData: map[string]string{"secret-key": "secret-value"}, - } - err = k8sClient.Create(context.TODO(), envFromSecret) - Expect(err).ToNot(HaveOccurred()) - + createEnvFromSecretAndConfigMap() By("creating the custom resource for the Kind FeatureStore") - err = k8sClient.Get(ctx, typeNamespacedName, featurestore) + err := k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}, withEnvFrom()) @@ -119,26 +99,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) - // Delete ConfigMap - By("Deleting the configmap and secret for envFrom") - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), configMap) - Expect(err).ToNot(HaveOccurred()) - - // Delete Secret - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), secret) - Expect(err).ToNot(HaveOccurred()) + deleteEnvFromSecretAndConfigMap() }) It("should successfully reconcile the resource", func() { @@ -183,18 +144,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.Path).To(Equal(onlineStorePath)) Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}})) - Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(&[]corev1.EnvFromSource{ - { - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, - }, - }, - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, - }, - }, - })) + Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(withEnvFrom())) Expect(resource.Status.Applied.Services.OnlineStore.ImagePullPolicy).To(Equal(&pullPolicy)) Expect(resource.Status.Applied.Services.OnlineStore.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Image).To(Equal(&image)) @@ -363,9 +313,13 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { offlineContainer := services.GetOfflineContainer(deploy.Spec.Template.Spec.Containers) Expect(offlineContainer.Env).To(HaveLen(1)) + assertEnvFrom(*offlineContainer) env = getFeatureStoreYamlEnvVar(offlineContainer.Env) Expect(env).NotTo(BeNil()) + //check envFrom for offlineContainer + assertEnvFrom(*offlineContainer) + fsYamlStr, err = feast.GetServiceFeatureStoreYamlBase64() Expect(err).NotTo(HaveOccurred()) Expect(fsYamlStr).To(Equal(env.Value)) @@ -488,6 +442,10 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { env = getFeatureStoreYamlEnvVar(onlineContainer.Env) Expect(env).NotTo(BeNil()) + //check envFrom + // Validate `envFrom` for ConfigMap and Secret + assertEnvFrom(*onlineContainer) + fsYamlStr, err = feast.GetServiceFeatureStoreYamlBase64() Expect(err).NotTo(HaveOccurred()) Expect(fsYamlStr).To(Equal(env.Value)) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_kubernetes_auth_test.go b/infra/feast-operator/internal/controller/featurestore_controller_kubernetes_auth_test.go index 7370537e59..c4c40caedc 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_kubernetes_auth_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_kubernetes_auth_test.go @@ -59,29 +59,10 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { roles := []string{"reader", "writer"} BeforeEach(func() { - By("creating the config map and secret for envFrom") - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - Data: map[string]string{"example-key": "example-value"}, - } - err := k8sClient.Create(context.TODO(), configMap) - Expect(err).ToNot(HaveOccurred()) - - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - StringData: map[string]string{"secret-key": "secret-value"}, - } - err = k8sClient.Create(context.TODO(), secret) - Expect(err).ToNot(HaveOccurred()) + createEnvFromSecretAndConfigMap() By("creating the custom resource for the Kind FeatureStore") - err = k8sClient.Get(ctx, typeNamespacedName, featurestore) + err := k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{}, withEnvFrom()) resource.Spec.AuthzConfig = &feastdevv1alpha1.AuthzConfig{KubernetesAuthz: &feastdevv1alpha1.KubernetesAuthz{ @@ -96,26 +77,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - // Delete ConfigMap - By("Deleting the configmap and secret for envFrom") - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), configMap) - Expect(err).ToNot(HaveOccurred()) - - // Delete Secret - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), secret) - Expect(err).ToNot(HaveOccurred()) + deleteEnvFromSecretAndConfigMap() By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) @@ -168,18 +130,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.Path).To(Equal(services.EphemeralPath + "/" + services.DefaultOnlineStorePath)) Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{})) - Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(&[]corev1.EnvFromSource{ - { - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, - }, - }, - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, - }, - }, - })) + Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(withEnvFrom())) Expect(resource.Status.Applied.Services.OnlineStore.ImagePullPolicy).To(Equal(&pullPolicy)) Expect(resource.Status.Applied.Services.OnlineStore.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Image).To(Equal(&image)) @@ -470,9 +421,12 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Expect(repoConfig).To(Equal(&testConfig)) // check offline - env = getFeatureStoreYamlEnvVar(services.GetOfflineContainer(deploy.Spec.Template.Spec.Containers).Env) + offlineContainer := services.GetOfflineContainer(deploy.Spec.Template.Spec.Containers) + env = getFeatureStoreYamlEnvVar(offlineContainer.Env) Expect(env).NotTo(BeNil()) + assertEnvFrom(*offlineContainer) + // check offline config fsYamlStr, err = feast.GetServiceFeatureStoreYamlBase64() Expect(err).NotTo(HaveOccurred()) @@ -486,9 +440,12 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Expect(repoConfig).To(Equal(&testConfig)) // check online - env = getFeatureStoreYamlEnvVar(services.GetOnlineContainer(deploy.Spec.Template.Spec.Containers).Env) + onlineContainer := services.GetOnlineContainer(deploy.Spec.Template.Spec.Containers) + env = getFeatureStoreYamlEnvVar(onlineContainer.Env) Expect(env).NotTo(BeNil()) + assertEnvFrom(*onlineContainer) + // check online config fsYamlStr, err = feast.GetServiceFeatureStoreYamlBase64() Expect(err).NotTo(HaveOccurred()) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go b/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go index 5234c5aee1..6b287673c4 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go @@ -64,29 +64,10 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { } BeforeEach(func() { - By("creating the config map and secret for envFrom") - envFromConfigMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - Data: map[string]string{"example-key": "example-value"}, - } - err := k8sClient.Create(context.TODO(), envFromConfigMap) - Expect(err).ToNot(HaveOccurred()) - - envFromSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - StringData: map[string]string{"secret-key": "secret-value"}, - } - err = k8sClient.Create(context.TODO(), envFromSecret) - Expect(err).ToNot(HaveOccurred()) + createEnvFromSecretAndConfigMap() By("creating the custom resource for the Kind FeatureStore") - err = k8sClient.Get(ctx, typeNamespacedName, featurestore) + err := k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}, withEnvFrom()) @@ -110,25 +91,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - By("Deleting the configmap and secret for envFrom") - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), configMap) - Expect(err).ToNot(HaveOccurred()) - - // Delete Secret - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), secret) - Expect(err).ToNot(HaveOccurred()) + deleteEnvFromSecretAndConfigMap() By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_oidc_auth_test.go b/infra/feast-operator/internal/controller/featurestore_controller_oidc_auth_test.go index dd7028529e..913ab2695e 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_oidc_auth_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_oidc_auth_test.go @@ -70,26 +70,7 @@ var _ = Describe("FeatureStore Controller-OIDC authorization", func() { Expect(k8sClient.Create(ctx, oidcSecret)).To(Succeed()) } - By("creating the config map and secret for envFrom") - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - Data: map[string]string{"example-key": "example-value"}, - } - err = k8sClient.Create(context.TODO(), configMap) - Expect(err).ToNot(HaveOccurred()) - - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - StringData: map[string]string{"secret-key": "secret-value"}, - } - err = k8sClient.Create(context.TODO(), secret) - Expect(err).ToNot(HaveOccurred()) + createEnvFromSecretAndConfigMap() By("creating the custom resource for the Kind FeatureStore") err = k8sClient.Get(ctx, typeNamespacedName, featurestore) @@ -120,25 +101,7 @@ var _ = Describe("FeatureStore Controller-OIDC authorization", func() { By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) - By("Deleting the configmap and secret for envFrom") - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), configMap) - Expect(err).ToNot(HaveOccurred()) - - // Delete Secret - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), secret) - Expect(err).ToNot(HaveOccurred()) + deleteEnvFromSecretAndConfigMap() }) It("should successfully reconcile the resource", func() { @@ -190,18 +153,7 @@ var _ = Describe("FeatureStore Controller-OIDC authorization", func() { Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.Path).To(Equal(services.EphemeralPath + "/" + services.DefaultOnlineStorePath)) Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{})) - Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(&[]corev1.EnvFromSource{ - { - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, - }, - }, - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, - }, - }, - })) + Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(withEnvFrom())) Expect(resource.Status.Applied.Services.OnlineStore.ImagePullPolicy).To(Equal(&pullPolicy)) Expect(resource.Status.Applied.Services.OnlineStore.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Image).To(Equal(&image)) @@ -276,6 +228,9 @@ var _ = Describe("FeatureStore Controller-OIDC authorization", func() { Expect(services.GetOnlineContainer(deploy.Spec.Template.Spec.Containers).VolumeMounts).To(HaveLen(1)) Expect(services.GetRegistryContainer(deploy.Spec.Template.Spec.Containers).VolumeMounts).To(HaveLen(1)) + assertEnvFrom(*services.GetOnlineContainer(deploy.Spec.Template.Spec.Containers)) + assertEnvFrom(*services.GetOfflineContainer(deploy.Spec.Template.Spec.Containers)) + // check Feast Role feastRole := &rbacv1.Role{} err = k8sClient.Get(ctx, types.NamespacedName{ diff --git a/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go b/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go index cb25d5a35d..fa40a34d95 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go @@ -75,29 +75,10 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { registryMountedPath := path.Join(registryMountPath, registryPath) BeforeEach(func() { - By("creating the config map and secret for envFrom") - envFromConfigMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - Data: map[string]string{"example-key": "example-value"}, - } - err := k8sClient.Create(context.TODO(), envFromConfigMap) - Expect(err).ToNot(HaveOccurred()) - - envFromSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - StringData: map[string]string{"secret-key": "secret-value"}, - } - err = k8sClient.Create(context.TODO(), envFromSecret) - Expect(err).ToNot(HaveOccurred()) + createEnvFromSecretAndConfigMap() By("creating the custom resource for the Kind FeatureStore") - err = k8sClient.Get(ctx, typeNamespacedName, featurestore) + err := k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}, withEnvFrom()) @@ -147,25 +128,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { By("Cleanup the specific resource instance FeatureStore") Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) - By("Deleting the configmap and secret for envFrom") - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), configMap) - Expect(err).ToNot(HaveOccurred()) - - // Delete Secret - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), secret) - Expect(err).ToNot(HaveOccurred()) + deleteEnvFromSecretAndConfigMap() }) It("should successfully reconcile the resource", func() { @@ -232,18 +195,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.PvcConfig.Create.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.PvcConfig.Create.Resources).To(Equal(expectedResources)) Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}})) - Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(&[]corev1.EnvFromSource{ - { - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, - }, - }, - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, - }, - }, - })) + Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(withEnvFrom())) Expect(resource.Status.Applied.Services.OnlineStore.ImagePullPolicy).To(Equal(&pullPolicy)) Expect(resource.Status.Applied.Services.OnlineStore.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Image).To(Equal(&image)) @@ -336,6 +288,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { offlinePvcName := feast.GetFeastServiceName(services.OfflineFeastType) Expect(offlineVolMount.Name).To(Equal(offlinePvcName)) + assertEnvFrom(*offlineContainer) + // check offline pvc pvc := &corev1.PersistentVolumeClaim{} err = k8sClient.Get(ctx, types.NamespacedName{ @@ -360,6 +314,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(onlineVolMount.MountPath).To(Equal(onlineStoreMountPath)) Expect(onlineVolMount.Name).To(Equal(onlinePvcName)) + assertEnvFrom(*onlineContainer) + // check online pvc pvc = &corev1.PersistentVolumeClaim{} err = k8sClient.Get(ctx, types.NamespacedName{ diff --git a/infra/feast-operator/internal/controller/featurestore_controller_test.go b/infra/feast-operator/internal/controller/featurestore_controller_test.go index a022fe7287..b4d5befe4e 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_test.go @@ -402,29 +402,10 @@ var _ = Describe("FeatureStore Controller", func() { featurestore := &feastdevv1alpha1.FeatureStore{} BeforeEach(func() { - By("creating the config map and secret for envFrom") - envFromConfigMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - Data: map[string]string{"example-key": "example-value"}, - } - err := k8sClient.Create(context.TODO(), envFromConfigMap) - Expect(err).ToNot(HaveOccurred()) - - envFromSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - StringData: map[string]string{"secret-key": "secret-value"}, - } - err = k8sClient.Create(context.TODO(), envFromSecret) - Expect(err).ToNot(HaveOccurred()) + createEnvFromSecretAndConfigMap() By("creating the custom resource for the Kind FeatureStore") - err = k8sClient.Get(ctx, typeNamespacedName, featurestore) + err := k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}, withEnvFrom()) @@ -440,25 +421,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) // Delete ConfigMap - By("Deleting the configmap and secret for envFrom") - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-configmap", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), configMap) - Expect(err).ToNot(HaveOccurred()) - - // Delete Secret - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-secret", - Namespace: "default", - }, - } - err = k8sClient.Delete(context.TODO(), secret) - Expect(err).ToNot(HaveOccurred()) + deleteEnvFromSecretAndConfigMap() }) It("should successfully reconcile the resource", func() { @@ -503,18 +466,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.Path).To(Equal(services.EphemeralPath + "/" + services.DefaultOnlineStorePath)) Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{{Name: testEnvVarName, Value: testEnvVarValue}, {Name: "fieldRefName", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}})) - Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(&[]corev1.EnvFromSource{ - { - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, - }, - }, - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, - }, - }, - })) + Expect(resource.Status.Applied.Services.OnlineStore.EnvFrom).To(Equal(withEnvFrom())) Expect(resource.Status.Applied.Services.OnlineStore.ImagePullPolicy).To(Equal(&pullPolicy)) Expect(resource.Status.Applied.Services.OnlineStore.Resources).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore.Image).To(Equal(&image)) @@ -681,6 +633,8 @@ var _ = Describe("FeatureStore Controller", func() { env = getFeatureStoreYamlEnvVar(offlineContainer.Env) Expect(env).NotTo(BeNil()) + assertEnvFrom(*offlineContainer) + fsYamlStr, err = feast.GetServiceFeatureStoreYamlBase64() Expect(err).NotTo(HaveOccurred()) Expect(fsYamlStr).To(Equal(env.Value)) @@ -699,6 +653,8 @@ var _ = Describe("FeatureStore Controller", func() { env = getFeatureStoreYamlEnvVar(onlineContainer.Env) Expect(env).NotTo(BeNil()) + assertEnvFrom(*onlineContainer) + fsYamlStr, err = feast.GetServiceFeatureStoreYamlBase64() Expect(err).NotTo(HaveOccurred()) Expect(fsYamlStr).To(Equal(env.Value)) @@ -1261,51 +1217,6 @@ var _ = Describe("FeatureStore Controller", func() { }) }) -func withEnvFrom() *[]corev1.EnvFromSource { - - return &[]corev1.EnvFromSource{ - { - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, - }, - }, - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, - }, - }, - } - -} - -func createFeatureStoreResource(resourceName string, image string, pullPolicy corev1.PullPolicy, envVars *[]corev1.EnvVar, envFromVar *[]corev1.EnvFromSource) *feastdevv1alpha1.FeatureStore { - return &feastdevv1alpha1.FeatureStore{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourceName, - Namespace: "default", - }, - Spec: feastdevv1alpha1.FeatureStoreSpec{ - FeastProject: feastProject, - Services: &feastdevv1alpha1.FeatureStoreServices{ - OfflineStore: &feastdevv1alpha1.OfflineStore{}, - OnlineStore: &feastdevv1alpha1.OnlineStore{ - ServiceConfigs: feastdevv1alpha1.ServiceConfigs{ - DefaultConfigs: feastdevv1alpha1.DefaultConfigs{ - Image: &image, - }, - OptionalConfigs: feastdevv1alpha1.OptionalConfigs{ - Env: envVars, - EnvFrom: envFromVar, - ImagePullPolicy: &pullPolicy, - Resources: &corev1.ResourceRequirements{}, - }, - }, - }, - }, - }, - } -} - func getFeatureStoreYamlEnvVar(envs []corev1.EnvVar) *corev1.EnvVar { for _, e := range envs { if e.Name == services.TmpFeatureStoreYamlEnvVar { diff --git a/infra/feast-operator/internal/controller/featurestore_controller_test_utils_test.go b/infra/feast-operator/internal/controller/featurestore_controller_test_utils_test.go new file mode 100644 index 0000000000..57fa87ce73 --- /dev/null +++ b/infra/feast-operator/internal/controller/featurestore_controller_test_utils_test.go @@ -0,0 +1,147 @@ +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + + feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" +) + +func assertEnvFrom(container corev1.Container) { + envFrom := container.EnvFrom + Expect(envFrom).NotTo(BeNil()) + checkEnvFromCounter := 0 + + for _, source := range envFrom { + if source.ConfigMapRef != nil && source.ConfigMapRef.Name == "example-configmap" { + checkEnvFromCounter += 1 + // Simulate retrieval of ConfigMap data and validate + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: source.ConfigMapRef.Name, + Namespace: "default", + }, configMap) + Expect(err).NotTo(HaveOccurred()) + // Validate a specific key-value pair from the ConfigMap + Expect(configMap.Data["example-key"]).To(Equal("example-value")) + } + + if source.SecretRef != nil && source.SecretRef.Name == "example-secret" { + checkEnvFromCounter += 1 + // Simulate retrieval of Secret data and validate + secret := &corev1.Secret{} + err := k8sClient.Get(context.TODO(), types.NamespacedName{ + Name: source.SecretRef.Name, + Namespace: "default", + }, secret) + Expect(err).NotTo(HaveOccurred()) + // Validate a specific key-value pair from the Secret + Expect(string(secret.Data["secret-key"])).To(Equal("secret-value")) + } + } + Expect(checkEnvFromCounter).To(Equal(2)) +} + +func createEnvFromSecretAndConfigMap() { + By("creating the config map and secret for envFrom") + envFromConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + Data: map[string]string{"example-key": "example-value"}, + } + err := k8sClient.Create(context.TODO(), envFromConfigMap) + Expect(err).ToNot(HaveOccurred()) + + envFromSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + StringData: map[string]string{"secret-key": "secret-value"}, + } + err = k8sClient.Create(context.TODO(), envFromSecret) + Expect(err).ToNot(HaveOccurred()) +} + +func deleteEnvFromSecretAndConfigMap() { + // Delete ConfigMap + By("Deleting the configmap and secret for envFrom") + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-configmap", + Namespace: "default", + }, + } + err := k8sClient.Delete(context.TODO(), configMap) + Expect(err).ToNot(HaveOccurred()) + + // Delete Secret + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-secret", + Namespace: "default", + }, + } + err = k8sClient.Delete(context.TODO(), secret) + Expect(err).ToNot(HaveOccurred()) +} + +func createFeatureStoreResource(resourceName string, image string, pullPolicy corev1.PullPolicy, envVars *[]corev1.EnvVar, envFromVar *[]corev1.EnvFromSource) *feastdevv1alpha1.FeatureStore { + return &feastdevv1alpha1.FeatureStore{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + Spec: feastdevv1alpha1.FeatureStoreSpec{ + FeastProject: feastProject, + Services: &feastdevv1alpha1.FeatureStoreServices{ + OfflineStore: &feastdevv1alpha1.OfflineStore{ + ServiceConfigs: feastdevv1alpha1.ServiceConfigs{ + OptionalConfigs: feastdevv1alpha1.OptionalConfigs{ + EnvFrom: envFromVar, + }, + }, + }, + OnlineStore: &feastdevv1alpha1.OnlineStore{ + ServiceConfigs: feastdevv1alpha1.ServiceConfigs{ + DefaultConfigs: feastdevv1alpha1.DefaultConfigs{ + Image: &image, + }, + OptionalConfigs: feastdevv1alpha1.OptionalConfigs{ + Env: envVars, + EnvFrom: envFromVar, + ImagePullPolicy: &pullPolicy, + Resources: &corev1.ResourceRequirements{}, + }, + }, + }, + }, + }, + } +} + +func withEnvFrom() *[]corev1.EnvFromSource { + + return &[]corev1.EnvFromSource{ + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-configmap"}, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "example-secret"}, + }, + }, + } + +}