@@ -19,6 +19,7 @@ package controller
1919import (
2020 "context"
2121 "encoding/base64"
22+ "reflect"
2223
2324 . "github.com/onsi/ginkgo/v2"
2425 . "github.com/onsi/gomega"
@@ -382,7 +383,9 @@ var _ = Describe("FeatureStore Controller", func() {
382383 Context ("When reconciling a resource with all services enabled" , func () {
383384 const resourceName = "services"
384385 image := "test:latest"
385- var pullPolicy corev1.PullPolicy = corev1 .PullAlways
386+ var pullPolicy = corev1 .PullAlways
387+ var testEnvVarName = "testEnvVarName"
388+ var testEnvVarValue = "testEnvVarValue"
386389
387390 ctx := context .Background ()
388391
@@ -396,29 +399,8 @@ var _ = Describe("FeatureStore Controller", func() {
396399 By ("creating the custom resource for the Kind FeatureStore" )
397400 err := k8sClient .Get (ctx , typeNamespacedName , featurestore )
398401 if err != nil && errors .IsNotFound (err ) {
399- resource := & feastdevv1alpha1.FeatureStore {
400- ObjectMeta : metav1.ObjectMeta {
401- Name : resourceName ,
402- Namespace : "default" ,
403- },
404- Spec : feastdevv1alpha1.FeatureStoreSpec {
405- FeastProject : feastProject ,
406- Services : & feastdevv1alpha1.FeatureStoreServices {
407- OfflineStore : & feastdevv1alpha1.OfflineStore {},
408- OnlineStore : & feastdevv1alpha1.OnlineStore {
409- ServiceConfigs : feastdevv1alpha1.ServiceConfigs {
410- DefaultConfigs : feastdevv1alpha1.DefaultConfigs {
411- Image : & image ,
412- },
413- OptionalConfigs : feastdevv1alpha1.OptionalConfigs {
414- ImagePullPolicy : & pullPolicy ,
415- Resources : & corev1.ResourceRequirements {},
416- },
417- },
418- },
419- },
420- },
421- }
402+ resource := createFeatureStoreResource (resourceName , image , pullPolicy , & []corev1.EnvVar {{Name : testEnvVarName , Value : testEnvVarValue },
403+ {Name : "fieldRefName" , ValueFrom : & corev1.EnvVarSource {FieldRef : & corev1.ObjectFieldSelector {APIVersion : "v1" , FieldPath : "metadata.namespace" }}}})
422404 Expect (k8sClient .Create (ctx , resource )).To (Succeed ())
423405 }
424406 })
@@ -463,6 +445,7 @@ var _ = Describe("FeatureStore Controller", func() {
463445 Expect (resource .Status .Applied .Services .OfflineStore .Resources ).To (BeNil ())
464446 Expect (resource .Status .Applied .Services .OfflineStore .Image ).To (Equal (& services .DefaultImage ))
465447 Expect (resource .Status .Applied .Services .OnlineStore ).NotTo (BeNil ())
448+ 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" }}}}))
466449 Expect (resource .Status .Applied .Services .OnlineStore .ImagePullPolicy ).To (Equal (& pullPolicy ))
467450 Expect (resource .Status .Applied .Services .OnlineStore .Resources ).NotTo (BeNil ())
468451 Expect (resource .Status .Applied .Services .OnlineStore .Image ).To (Equal (& image ))
@@ -658,7 +641,7 @@ var _ = Describe("FeatureStore Controller", func() {
658641 deploy )
659642 Expect (err ).NotTo (HaveOccurred ())
660643 Expect (deploy .Spec .Template .Spec .Containers ).To (HaveLen (1 ))
661- Expect (deploy .Spec .Template .Spec .Containers [0 ].Env ).To (HaveLen (1 ))
644+ Expect (deploy .Spec .Template .Spec .Containers [0 ].Env ).To (HaveLen (3 ))
662645 Expect (deploy .Spec .Template .Spec .Containers [0 ].ImagePullPolicy ).To (Equal (corev1 .PullAlways ))
663646 env = getFeatureStoreYamlEnvVar (deploy .Spec .Template .Spec .Containers [0 ].Env )
664647 Expect (env ).NotTo (BeNil ())
@@ -689,6 +672,7 @@ var _ = Describe("FeatureStore Controller", func() {
689672 Registry : regRemote ,
690673 }
691674 Expect (repoConfigOnline ).To (Equal (onlineConfig ))
675+ Expect (deploy .Spec .Template .Spec .Containers [0 ].Env ).To (HaveLen (3 ))
692676
693677 // check client config
694678 cm := & corev1.ConfigMap {}
@@ -751,6 +735,89 @@ var _ = Describe("FeatureStore Controller", func() {
751735 Expect (repoConfig ).To (Equal (testConfig ))
752736 })
753737
738+ It ("should properly set container env variables" , func () {
739+ By ("Reconciling the created resource" )
740+ controllerReconciler := & FeatureStoreReconciler {
741+ Client : k8sClient ,
742+ Scheme : k8sClient .Scheme (),
743+ }
744+
745+ _ , err := controllerReconciler .Reconcile (ctx , reconcile.Request {
746+ NamespacedName : typeNamespacedName ,
747+ })
748+ Expect (err ).NotTo (HaveOccurred ())
749+
750+ resource := & feastdevv1alpha1.FeatureStore {}
751+ err = k8sClient .Get (ctx , typeNamespacedName , resource )
752+ Expect (err ).NotTo (HaveOccurred ())
753+
754+ req , err := labels .NewRequirement (services .NameLabelKey , selection .Equals , []string {resource .Name })
755+ Expect (err ).NotTo (HaveOccurred ())
756+ labelSelector := labels .NewSelector ().Add (* req )
757+ listOpts := & client.ListOptions {Namespace : resource .Namespace , LabelSelector : labelSelector }
758+ deployList := appsv1.DeploymentList {}
759+ err = k8sClient .List (ctx , & deployList , listOpts )
760+ Expect (err ).NotTo (HaveOccurred ())
761+ Expect (deployList .Items ).To (HaveLen (3 ))
762+
763+ svcList := corev1.ServiceList {}
764+ err = k8sClient .List (ctx , & svcList , listOpts )
765+ Expect (err ).NotTo (HaveOccurred ())
766+ Expect (svcList .Items ).To (HaveLen (3 ))
767+
768+ cmList := corev1.ConfigMapList {}
769+ err = k8sClient .List (ctx , & cmList , listOpts )
770+ Expect (err ).NotTo (HaveOccurred ())
771+ Expect (cmList .Items ).To (HaveLen (1 ))
772+
773+ feast := services.FeastServices {
774+ Client : controllerReconciler .Client ,
775+ Context : ctx ,
776+ Scheme : controllerReconciler .Scheme ,
777+ FeatureStore : resource ,
778+ }
779+
780+ fsYamlStr := ""
781+ fsYamlStr , err = feast .GetServiceFeatureStoreYamlBase64 (services .OnlineFeastType )
782+ Expect (err ).NotTo (HaveOccurred ())
783+
784+ // check online config
785+ deploy := & appsv1.Deployment {}
786+ err = k8sClient .Get (ctx , types.NamespacedName {
787+ Name : feast .GetFeastServiceName (services .OnlineFeastType ),
788+ Namespace : resource .Namespace ,
789+ },
790+ deploy )
791+ Expect (err ).NotTo (HaveOccurred ())
792+ Expect (deploy .Spec .Template .Spec .Containers ).To (HaveLen (1 ))
793+ Expect (deploy .Spec .Template .Spec .Containers [0 ].Env ).To (HaveLen (3 ))
794+ Expect (areEnvVarArraysEqual (deploy .Spec .Template .Spec .Containers [0 ].Env , []corev1.EnvVar {{Name : testEnvVarName , Value : testEnvVarValue }, {Name : services .FeatureStoreYamlEnvVar , Value : fsYamlStr }, {Name : "fieldRefName" , ValueFrom : & corev1.EnvVarSource {FieldRef : & corev1.ObjectFieldSelector {APIVersion : "v1" , FieldPath : "metadata.namespace" }}}})).To (BeTrue ())
795+ Expect (deploy .Spec .Template .Spec .Containers [0 ].ImagePullPolicy ).To (Equal (corev1 .PullAlways ))
796+
797+ // change feast project and reconcile
798+ resourceNew := resource .DeepCopy ()
799+ resourceNew .Spec .Services .OnlineStore .Env = & []corev1.EnvVar {{Name : testEnvVarName , Value : testEnvVarValue + "1" }, {Name : services .FeatureStoreYamlEnvVar , Value : fsYamlStr }, {Name : "fieldRefName" , ValueFrom : & corev1.EnvVarSource {FieldRef : & corev1.ObjectFieldSelector {FieldPath : "metadata.name" }}}}
800+ err = k8sClient .Update (ctx , resourceNew )
801+ Expect (err ).NotTo (HaveOccurred ())
802+ _ , err = controllerReconciler .Reconcile (ctx , reconcile.Request {
803+ NamespacedName : typeNamespacedName ,
804+ })
805+ Expect (err ).NotTo (HaveOccurred ())
806+
807+ err = k8sClient .Get (ctx , typeNamespacedName , resource )
808+ Expect (err ).NotTo (HaveOccurred ())
809+ Expect (areEnvVarArraysEqual (* resource .Status .Applied .Services .OnlineStore .Env , []corev1.EnvVar {{Name : testEnvVarName , Value : testEnvVarValue + "1" }, {Name : services .FeatureStoreYamlEnvVar , Value : fsYamlStr }, {Name : "fieldRefName" , ValueFrom : & corev1.EnvVarSource {FieldRef : & corev1.ObjectFieldSelector {FieldPath : "metadata.name" }}}})).To (BeTrue ())
810+ err = k8sClient .Get (ctx , types.NamespacedName {
811+ Name : feast .GetFeastServiceName (services .OnlineFeastType ),
812+ Namespace : resource .Namespace ,
813+ },
814+ deploy )
815+ Expect (err ).NotTo (HaveOccurred ())
816+
817+ Expect (deploy .Spec .Template .Spec .Containers [0 ].Env ).To (HaveLen (3 ))
818+ Expect (areEnvVarArraysEqual (deploy .Spec .Template .Spec .Containers [0 ].Env , []corev1.EnvVar {{Name : testEnvVarName , Value : testEnvVarValue + "1" }, {Name : services .FeatureStoreYamlEnvVar , Value : fsYamlStr }, {Name : "fieldRefName" , ValueFrom : & corev1.EnvVarSource {FieldRef : & corev1.ObjectFieldSelector {APIVersion : "v1" , FieldPath : "metadata.name" }}}})).To (BeTrue ())
819+ })
820+
754821 It ("Should delete k8s objects owned by the FeatureStore CR" , func () {
755822 By ("changing which feast services are configured in the CR" )
756823 controllerReconciler := & FeatureStoreReconciler {
@@ -1122,6 +1189,33 @@ var _ = Describe("FeatureStore Controller", func() {
11221189 })
11231190})
11241191
1192+ func createFeatureStoreResource (resourceName string , image string , pullPolicy corev1.PullPolicy , envVars * []corev1.EnvVar ) * feastdevv1alpha1.FeatureStore {
1193+ return & feastdevv1alpha1.FeatureStore {
1194+ ObjectMeta : metav1.ObjectMeta {
1195+ Name : resourceName ,
1196+ Namespace : "default" ,
1197+ },
1198+ Spec : feastdevv1alpha1.FeatureStoreSpec {
1199+ FeastProject : feastProject ,
1200+ Services : & feastdevv1alpha1.FeatureStoreServices {
1201+ OfflineStore : & feastdevv1alpha1.OfflineStore {},
1202+ OnlineStore : & feastdevv1alpha1.OnlineStore {
1203+ ServiceConfigs : feastdevv1alpha1.ServiceConfigs {
1204+ DefaultConfigs : feastdevv1alpha1.DefaultConfigs {
1205+ Image : & image ,
1206+ },
1207+ OptionalConfigs : feastdevv1alpha1.OptionalConfigs {
1208+ Env : envVars ,
1209+ ImagePullPolicy : & pullPolicy ,
1210+ Resources : & corev1.ResourceRequirements {},
1211+ },
1212+ },
1213+ },
1214+ },
1215+ },
1216+ }
1217+ }
1218+
11251219func getFeatureStoreYamlEnvVar (envs []corev1.EnvVar ) * corev1.EnvVar {
11261220 for _ , e := range envs {
11271221 if e .Name == services .FeatureStoreYamlEnvVar {
@@ -1130,3 +1224,25 @@ func getFeatureStoreYamlEnvVar(envs []corev1.EnvVar) *corev1.EnvVar {
11301224 }
11311225 return nil
11321226}
1227+
1228+ func areEnvVarArraysEqual (arr1 []corev1.EnvVar , arr2 []corev1.EnvVar ) bool {
1229+ if len (arr1 ) != len (arr2 ) {
1230+ return false
1231+ }
1232+
1233+ // Create a map to count occurrences of EnvVars in the first array.
1234+ envMap := make (map [string ]corev1.EnvVar )
1235+
1236+ for _ , env := range arr1 {
1237+ envMap [env .Name ] = env
1238+ }
1239+
1240+ // Check the second array against the map.
1241+ for _ , env := range arr2 {
1242+ if _ , exists := envMap [env .Name ]; ! exists || ! reflect .DeepEqual (envMap [env .Name ], env ) {
1243+ return false
1244+ }
1245+ }
1246+
1247+ return true
1248+ }
0 commit comments