From cde5760cc94cccd4cbeed918acca09d1b106d7e5 Mon Sep 17 00:00:00 2001 From: Tommy Hughes IV Date: Tue, 19 Nov 2024 10:20:10 -0600 Subject: [PATCH] feat: Operator will create k8s serviceaccount for each feast service (#4767) create a serviceaccount for each feast service Signed-off-by: Tommy Hughes --- infra/feast-operator/config/rbac/role.yaml | 1 + infra/feast-operator/dist/install.yaml | 1 + .../controller/featurestore_controller.go | 3 +- .../featurestore_controller_test.go | 12 +++++ .../internal/controller/services/services.go | 48 ++++++++++++++++--- 5 files changed, 57 insertions(+), 8 deletions(-) diff --git a/infra/feast-operator/config/rbac/role.yaml b/infra/feast-operator/config/rbac/role.yaml index 4b19baa663..6ca2085990 100644 --- a/infra/feast-operator/config/rbac/role.yaml +++ b/infra/feast-operator/config/rbac/role.yaml @@ -20,6 +20,7 @@ rules: resources: - configmaps - persistentvolumeclaims + - serviceaccounts - services verbs: - create diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index 7f208f548f..f23dc8b208 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -2071,6 +2071,7 @@ rules: resources: - configmaps - persistentvolumeclaims + - serviceaccounts - services verbs: - create diff --git a/infra/feast-operator/internal/controller/featurestore_controller.go b/infra/feast-operator/internal/controller/featurestore_controller.go index fc74fcf412..7c78b79d59 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller.go +++ b/infra/feast-operator/internal/controller/featurestore_controller.go @@ -53,7 +53,7 @@ type FeatureStoreReconciler struct { //+kubebuilder:rbac:groups=feast.dev,resources=featurestores/status,verbs=get;update;patch //+kubebuilder:rbac:groups=feast.dev,resources=featurestores/finalizers,verbs=update //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;create;update;watch;delete -//+kubebuilder:rbac:groups=core,resources=services;configmaps;persistentvolumeclaims,verbs=get;list;create;update;watch;delete +//+kubebuilder:rbac:groups=core,resources=services;configmaps;persistentvolumeclaims;serviceaccounts,verbs=get;list;create;update;watch;delete // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -143,6 +143,7 @@ func (r *FeatureStoreReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Owns(&corev1.PersistentVolumeClaim{}). + Owns(&corev1.ServiceAccount{}). Watches(&feastdevv1alpha1.FeatureStore{}, handler.EnqueueRequestsFromMapFunc(r.mapFeastRefsToFeastRequests)). Complete(r) } diff --git a/infra/feast-operator/internal/controller/featurestore_controller_test.go b/infra/feast-operator/internal/controller/featurestore_controller_test.go index 14cac19600..1e9bbc2f52 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_test.go @@ -173,6 +173,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).NotTo(HaveOccurred()) Expect(deploy.Spec.Replicas).To(Equal(&services.DefaultReplicas)) Expect(controllerutil.HasControllerReference(deploy)).To(BeTrue()) + Expect(deploy.Spec.Template.Spec.ServiceAccountName).To(Equal(deploy.Name)) Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) svc := &corev1.Service{} @@ -216,6 +217,7 @@ var _ = Describe("FeatureStore Controller", func() { }, deploy) Expect(err).NotTo(HaveOccurred()) + Expect(deploy.Spec.Template.Spec.ServiceAccountName).To(Equal(deploy.Name)) Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(1)) env := getFeatureStoreYamlEnvVar(deploy.Spec.Template.Spec.Containers[0].Env) @@ -517,6 +519,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).NotTo(HaveOccurred()) Expect(deploy.Spec.Replicas).To(Equal(&services.DefaultReplicas)) Expect(controllerutil.HasControllerReference(deploy)).To(BeTrue()) + Expect(deploy.Spec.Template.Spec.ServiceAccountName).To(Equal(deploy.Name)) Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) svc := &corev1.Service{} @@ -555,6 +558,11 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).NotTo(HaveOccurred()) Expect(deployList.Items).To(HaveLen(3)) + saList := corev1.ServiceAccountList{} + err = k8sClient.List(ctx, &saList, listOpts) + Expect(err).NotTo(HaveOccurred()) + Expect(saList.Items).To(HaveLen(3)) + svcList := corev1.ServiceList{} err = k8sClient.List(ctx, &svcList, listOpts) Expect(err).NotTo(HaveOccurred()) @@ -580,6 +588,7 @@ var _ = Describe("FeatureStore Controller", func() { }, deploy) Expect(err).NotTo(HaveOccurred()) + Expect(deploy.Spec.Template.Spec.ServiceAccountName).To(Equal(deploy.Name)) Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(1)) env := getFeatureStoreYamlEnvVar(deploy.Spec.Template.Spec.Containers[0].Env) @@ -613,6 +622,7 @@ var _ = Describe("FeatureStore Controller", func() { }, deploy) Expect(err).NotTo(HaveOccurred()) + Expect(deploy.Spec.Template.Spec.ServiceAccountName).To(Equal(deploy.Name)) Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(1)) env = getFeatureStoreYamlEnvVar(deploy.Spec.Template.Spec.Containers[0].Env) @@ -650,6 +660,7 @@ var _ = Describe("FeatureStore Controller", func() { }, deploy) Expect(err).NotTo(HaveOccurred()) + Expect(deploy.Spec.Template.Spec.ServiceAccountName).To(Equal(deploy.Name)) Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(3)) Expect(deploy.Spec.Template.Spec.Containers[0].ImagePullPolicy).To(Equal(corev1.PullAlways)) @@ -799,6 +810,7 @@ var _ = Describe("FeatureStore Controller", func() { }, deploy) Expect(err).NotTo(HaveOccurred()) + Expect(deploy.Spec.Template.Spec.ServiceAccountName).To(Equal(deploy.Name)) Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(3)) 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()) diff --git a/infra/feast-operator/internal/controller/services/services.go b/infra/feast-operator/internal/controller/services/services.go index 665556b57b..3a079b5f49 100644 --- a/infra/feast-operator/internal/controller/services/services.go +++ b/infra/feast-operator/internal/controller/services/services.go @@ -88,12 +88,6 @@ func (feast *FeastServices) Deploy() error { } func (feast *FeastServices) deployFeastServiceByType(feastType FeastServiceType) error { - if err := feast.createService(feastType); err != nil { - return feast.setFeastServiceCondition(err, feastType) - } - if err := feast.createDeployment(feastType); err != nil { - return feast.setFeastServiceCondition(err, feastType) - } if pvcCreate, shouldCreate := shouldCreatePvc(feast.FeatureStore, feastType); shouldCreate { if err := feast.createPVC(pvcCreate, feastType); err != nil { return feast.setFeastServiceCondition(err, feastType) @@ -101,14 +95,26 @@ func (feast *FeastServices) deployFeastServiceByType(feastType FeastServiceType) } else { _ = feast.deleteOwnedFeastObj(feast.initPVC(feastType)) } + if err := feast.createServiceAccount(feastType); err != nil { + return feast.setFeastServiceCondition(err, feastType) + } + if err := feast.createDeployment(feastType); err != nil { + return feast.setFeastServiceCondition(err, feastType) + } + if err := feast.createService(feastType); err != nil { + return feast.setFeastServiceCondition(err, feastType) + } return feast.setFeastServiceCondition(nil, feastType) } func (feast *FeastServices) removeFeastServiceByType(feastType FeastServiceType) error { + if err := feast.deleteOwnedFeastObj(feast.initFeastSvc(feastType)); err != nil { + return err + } if err := feast.deleteOwnedFeastObj(feast.initFeastDeploy(feastType)); err != nil { return err } - if err := feast.deleteOwnedFeastObj(feast.initFeastSvc(feastType)); err != nil { + if err := feast.deleteOwnedFeastObj(feast.initFeastSA(feastType)); err != nil { return err } if err := feast.deleteOwnedFeastObj(feast.initPVC(feastType)); err != nil { @@ -131,6 +137,19 @@ func (feast *FeastServices) createService(feastType FeastServiceType) error { return nil } +func (feast *FeastServices) createServiceAccount(feastType FeastServiceType) error { + logger := log.FromContext(feast.Context) + sa := feast.initFeastSA(feastType) + if op, err := controllerutil.CreateOrUpdate(feast.Context, feast.Client, sa, controllerutil.MutateFn(func() error { + return feast.setServiceAccount(sa, feastType) + })); err != nil { + return err + } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { + logger.Info("Successfully reconciled", "ServiceAccount", sa.Name, "operation", op) + } + return nil +} + func (feast *FeastServices) createDeployment(feastType FeastServiceType) error { logger := log.FromContext(feast.Context) deploy := feast.initFeastDeploy(feastType) @@ -173,6 +192,7 @@ func (feast *FeastServices) setDeployment(deploy *appsv1.Deployment, feastType F deploySettings := FeastServiceConstants[feastType] serviceConfigs := feast.getServiceConfigs(feastType) defaultServiceConfigs := serviceConfigs.DefaultConfigs + sa := feast.initFeastSA(feastType) // standard configs are applied here probeHandler := corev1.ProbeHandler{ @@ -188,6 +208,7 @@ func (feast *FeastServices) setDeployment(deploy *appsv1.Deployment, feastType F Labels: deploy.GetLabels(), }, Spec: corev1.PodSpec{ + ServiceAccountName: sa.Name, Containers: []corev1.Container{ { Name: string(feastType), @@ -252,6 +273,11 @@ func (feast *FeastServices) setService(svc *corev1.Service, feastType FeastServi return controllerutil.SetControllerReference(feast.FeatureStore, svc, feast.Scheme) } +func (feast *FeastServices) setServiceAccount(sa *corev1.ServiceAccount, feastType FeastServiceType) error { + sa.Labels = feast.getLabels(feastType) + return controllerutil.SetControllerReference(feast.FeatureStore, sa, feast.Scheme) +} + func (feast *FeastServices) createNewPVC(pvcCreate *feastdevv1alpha1.PvcCreate, feastType FeastServiceType) (*corev1.PersistentVolumeClaim, error) { pvc := feast.initPVC(feastType) @@ -426,6 +452,14 @@ func (feast *FeastServices) initFeastSvc(feastType FeastServiceType) *corev1.Ser return svc } +func (feast *FeastServices) initFeastSA(feastType FeastServiceType) *corev1.ServiceAccount { + sa := &corev1.ServiceAccount{ + ObjectMeta: feast.GetObjectMeta(feastType), + } + sa.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("ServiceAccount")) + return sa +} + func (feast *FeastServices) initPVC(feastType FeastServiceType) *corev1.PersistentVolumeClaim { pvc := &corev1.PersistentVolumeClaim{ ObjectMeta: feast.GetObjectMeta(feastType),