From fa6be076124f1038860470e723ac5e2937b44895 Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Fri, 22 Nov 2024 22:18:01 +0100 Subject: [PATCH 01/11] Initial commit Signed-off-by: Daniele Martinoli --- .../api/v1alpha1/featurestore_types.go | 10 + .../api/v1alpha1/zz_generated.deepcopy.go | 45 ++ .../crd/bases/feast.dev_featurestores.yaml | 24 + ...a1_featurestore_ephemeral_persistence.yaml | 3 + ...v1alpha1_featurestore_kubernetes_auth.yaml | 28 + ..._featurestore_objectstore_persistence.yaml | 3 + ...v1alpha1_featurestore_pvc_persistence.yaml | 3 + infra/feast-operator/dist/install.yaml | 24 + .../internal/controller/auth/auth.go | 220 +++++++ .../internal/controller/auth/auth_types.go | 30 + .../controller/featurestore_controller.go | 1 + .../featurestore_controller_ephemeral_test.go | 10 +- ...restore_controller_kubernetes_auth_test.go | 566 ++++++++++++++++++ ...eaturestore_controller_objectstore_test.go | 2 + .../featurestore_controller_pvc_test.go | 10 +- .../featurestore_controller_test.go | 19 +- .../internal/controller/handler/handler.go | 28 + .../controller/handler/handler_types.go | 16 + .../controller/services/repo_config.go | 12 + .../controller/services/services_types.go | 18 +- 20 files changed, 1060 insertions(+), 12 deletions(-) create mode 100644 infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml create mode 100644 infra/feast-operator/internal/controller/auth/auth.go create mode 100644 infra/feast-operator/internal/controller/auth/auth_types.go create mode 100644 infra/feast-operator/internal/controller/featurestore_controller_kubernetes_auth_test.go create mode 100644 infra/feast-operator/internal/controller/handler/handler.go create mode 100644 infra/feast-operator/internal/controller/handler/handler_types.go diff --git a/infra/feast-operator/api/v1alpha1/featurestore_types.go b/infra/feast-operator/api/v1alpha1/featurestore_types.go index 50bf682213f..7957db0249d 100644 --- a/infra/feast-operator/api/v1alpha1/featurestore_types.go +++ b/infra/feast-operator/api/v1alpha1/featurestore_types.go @@ -59,6 +59,7 @@ type FeatureStoreSpec struct { // FeastProject is the Feast project id. This can be any alphanumeric string with underscores, but it cannot start with an underscore. Required. FeastProject string `json:"feastProject"` Services *FeatureStoreServices `json:"services,omitempty"` + AuthConfig *AuthConfig `json:"auth,omitempty"` } // FeatureStoreServices defines the desired feast service deployments. ephemeral registry is deployed by default. @@ -263,6 +264,15 @@ type OptionalConfigs struct { Resources *corev1.ResourceRequirements `json:"resources,omitempty"` } +// AuthConfig defines the authorization settings for the deployed Feast services. +type AuthConfig struct { + KubernetesAuth *KubernetesAuth `json:"kubernetes,omitempty"` +} + +type KubernetesAuth struct { + Roles []string `json:"roles,omitempty"` +} + // FeatureStoreStatus defines the observed state of FeatureStore type FeatureStoreStatus struct { // Shows the currently applied feast configuration, including any pertinent defaults diff --git a/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go b/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go index 196b2147005..b399128f646 100644 --- a/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -26,6 +26,26 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuthConfig) DeepCopyInto(out *AuthConfig) { + *out = *in + if in.KubernetesAuth != nil { + in, out := &in.KubernetesAuth, &out.KubernetesAuth + *out = new(KubernetesAuth) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthConfig. +func (in *AuthConfig) DeepCopy() *AuthConfig { + if in == nil { + return nil + } + out := new(AuthConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DefaultConfigs) DeepCopyInto(out *DefaultConfigs) { *out = *in @@ -158,6 +178,11 @@ func (in *FeatureStoreSpec) DeepCopyInto(out *FeatureStoreSpec) { *out = new(FeatureStoreServices) (*in).DeepCopyInto(*out) } + if in.AuthConfig != nil { + in, out := &in.AuthConfig, &out.AuthConfig + *out = new(AuthConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureStoreSpec. @@ -194,6 +219,26 @@ func (in *FeatureStoreStatus) DeepCopy() *FeatureStoreStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesAuth) DeepCopyInto(out *KubernetesAuth) { + *out = *in + if in.Roles != nil { + in, out := &in.Roles, &out.Roles + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesAuth. +func (in *KubernetesAuth) DeepCopy() *KubernetesAuth { + if in == nil { + return nil + } + out := new(KubernetesAuth) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LocalRegistryConfig) DeepCopyInto(out *LocalRegistryConfig) { *out = *in 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 f1c7fca8f51..1318962d222 100644 --- a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +++ b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml @@ -48,6 +48,18 @@ spec: spec: description: FeatureStoreSpec defines the desired state of FeatureStore properties: + auth: + description: AuthConfig defines the authorization settings for the + deployed Feast services. + properties: + kubernetes: + properties: + roles: + items: + type: string + type: array + type: object + type: object feastProject: description: FeastProject is the Feast project id. This can be any alphanumeric string with underscores, but it cannot start with an @@ -1039,6 +1051,18 @@ spec: description: Shows the currently applied feast configuration, including any pertinent defaults properties: + auth: + description: AuthConfig defines the authorization settings for + the deployed Feast services. + properties: + kubernetes: + properties: + roles: + items: + type: string + type: array + type: object + type: object feastProject: description: FeastProject is the Feast project id. This can be any alphanumeric string with underscores, but it cannot start diff --git a/infra/feast-operator/config/samples/v1alpha1_featurestore_ephemeral_persistence.yaml b/infra/feast-operator/config/samples/v1alpha1_featurestore_ephemeral_persistence.yaml index 512fed9d4c0..8449ec1bf51 100644 --- a/infra/feast-operator/config/samples/v1alpha1_featurestore_ephemeral_persistence.yaml +++ b/infra/feast-operator/config/samples/v1alpha1_featurestore_ephemeral_persistence.yaml @@ -6,15 +6,18 @@ spec: feastProject: my_project services: onlineStore: + image: quay.io/dmartino/feature-server:0.2 persistence: file: path: /data/online_store.db offlineStore: + image: quay.io/dmartino/feature-server:0.2 persistence: file: type: dask registry: local: + image: quay.io/dmartino/feature-server:0.2 persistence: file: path: /data/registry.db diff --git a/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml b/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml new file mode 100644 index 00000000000..add9c4431fb --- /dev/null +++ b/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml @@ -0,0 +1,28 @@ +apiVersion: feast.dev/v1alpha1 +kind: FeatureStore +metadata: + name: sample-kubernetes-auth +spec: + feastProject: my_project + services: + onlineStore: + image: quay.io/dmartino/feature-server:0.3 + persistence: + file: + path: /data/online_store.db + offlineStore: + image: quay.io/dmartino/feature-server:0.3 + persistence: + file: + type: dask + registry: + local: + image: quay.io/dmartino/feature-server:0.3 + persistence: + file: + path: /data/registry.db + auth: + kubernetes: + roles: + - reader + - writer diff --git a/infra/feast-operator/config/samples/v1alpha1_featurestore_objectstore_persistence.yaml b/infra/feast-operator/config/samples/v1alpha1_featurestore_objectstore_persistence.yaml index 45f12a67a18..e038242da4c 100644 --- a/infra/feast-operator/config/samples/v1alpha1_featurestore_objectstore_persistence.yaml +++ b/infra/feast-operator/config/samples/v1alpha1_featurestore_objectstore_persistence.yaml @@ -6,15 +6,18 @@ spec: feastProject: my_project services: onlineStore: + image: quay.io/dmartino/feature-server:0.2 persistence: file: path: /data/online_store.db offlineStore: + image: quay.io/dmartino/feature-server:0.2 persistence: file: type: dask registry: local: + image: quay.io/dmartino/feature-server:0.2 persistence: file: path: s3://bucket/registry.db diff --git a/infra/feast-operator/config/samples/v1alpha1_featurestore_pvc_persistence.yaml b/infra/feast-operator/config/samples/v1alpha1_featurestore_pvc_persistence.yaml index b7c7412c0f0..fe2a5071cda 100644 --- a/infra/feast-operator/config/samples/v1alpha1_featurestore_pvc_persistence.yaml +++ b/infra/feast-operator/config/samples/v1alpha1_featurestore_pvc_persistence.yaml @@ -6,6 +6,7 @@ spec: feastProject: my_project services: onlineStore: + image: quay.io/dmartino/feature-server:0.2 persistence: file: path: online_store.db @@ -14,6 +15,7 @@ spec: name: online-pvc mountPath: /data/online offlineStore: + image: quay.io/dmartino/feature-server:0.2 persistence: file: type: duckdb @@ -26,6 +28,7 @@ spec: mountPath: /data/offline registry: local: + image: quay.io/dmartino/feature-server:0.2 persistence: file: path: registry.db diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index 83181f53b09..8abf3002d96 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -56,6 +56,18 @@ spec: spec: description: FeatureStoreSpec defines the desired state of FeatureStore properties: + auth: + description: AuthConfig defines the authorization settings for the + deployed Feast services. + properties: + kubernetes: + properties: + roles: + items: + type: string + type: array + type: object + type: object feastProject: description: FeastProject is the Feast project id. This can be any alphanumeric string with underscores, but it cannot start with an @@ -1047,6 +1059,18 @@ spec: description: Shows the currently applied feast configuration, including any pertinent defaults properties: + auth: + description: AuthConfig defines the authorization settings for + the deployed Feast services. + properties: + kubernetes: + properties: + roles: + items: + type: string + type: array + type: object + type: object feastProject: description: FeastProject is the Feast project id. This can be any alphanumeric string with underscores, but it cannot start diff --git a/infra/feast-operator/internal/controller/auth/auth.go b/infra/feast-operator/internal/controller/auth/auth.go new file mode 100644 index 00000000000..3be0349f97f --- /dev/null +++ b/infra/feast-operator/internal/controller/auth/auth.go @@ -0,0 +1,220 @@ +package auth + +import ( + "context" + "slices" + + feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" + rbacv1 "k8s.io/api/rbac/v1" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// Deploy the feast services +func (auth *FeastAuth) Deploy() error { + authConfig := auth.Handler.FeatureStore.Status.Applied.AuthConfig + if authConfig != nil { + if authConfig.KubernetesAuth != nil { + if err := auth.deployKubernetesAuth(authConfig.KubernetesAuth); err != nil { + return err + } + } else { + auth.removeOrphanedRoles() + _ = auth.Handler.DeleteOwnedFeastObj(auth.initFeastRole()) + _ = auth.Handler.DeleteOwnedFeastObj(auth.initFeastRoleBinding()) + } + } + return nil +} + +func (auth *FeastAuth) deployKubernetesAuth(kubernetesAuth *feastdevv1alpha1.KubernetesAuth) error { + auth.removeOrphanedRoles() + + if err := auth.createFeastRole(); err != nil { + return auth.setFeastKubernetesAuthCondition(err) + } + if err := auth.createFeastRoleBinding(); err != nil { + return auth.setFeastKubernetesAuthCondition(err) + } + + for _, roleName := range kubernetesAuth.Roles { + if err := auth.createAuthRole(roleName); err != nil { + return auth.setFeastKubernetesAuthCondition(err) + } + } + return auth.setFeastKubernetesAuthCondition(nil) +} + +func (auth *FeastAuth) removeOrphanedRoles() { + roleList := &rbacv1.RoleList{} + err := auth.Handler.Client.List(context.TODO(), roleList, &client.ListOptions{ + Namespace: auth.Handler.FeatureStore.Namespace, + LabelSelector: labels.SelectorFromSet(auth.getLabels()), + }) + if err != nil { + return + } + + desiredRoles := []string{} + if auth.Handler.FeatureStore.Status.Applied.AuthConfig.KubernetesAuth != nil { + desiredRoles = auth.Handler.FeatureStore.Status.Applied.AuthConfig.KubernetesAuth.Roles + } + for _, role := range roleList.Items { + roleName := role.Name + if roleName != auth.getFeastRoleName() && !slices.Contains(desiredRoles, roleName) { + _ = auth.Handler.DeleteOwnedFeastObj(auth.initAuthRole(roleName)) + } + } +} + +func (auth *FeastAuth) createFeastRole() error { + logger := log.FromContext(auth.Handler.Context) + role := auth.initFeastRole() + if op, err := controllerutil.CreateOrUpdate(auth.Handler.Context, auth.Handler.Client, role, controllerutil.MutateFn(func() error { + return auth.setFeastRole(role) + })); err != nil { + return err + } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { + logger.Info("Successfully reconciled", "Role", role.Name, "operation", op) + } + + return nil +} + +func (auth *FeastAuth) initFeastRole() *rbacv1.Role { + role := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{Name: auth.getFeastRoleName(), Namespace: auth.Handler.FeatureStore.Namespace}, + } + role.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("Role")) + return role +} + +func (auth *FeastAuth) setFeastRole(role *rbacv1.Role) error { + role.Labels = auth.getLabels() + role.Rules = []rbacv1.PolicyRule{ + { + APIGroups: []string{rbacv1.GroupName}, + Resources: []string{"roles", "rolebindings"}, + Verbs: []string{"get", "list", "watch"}, + }, + } + + return controllerutil.SetControllerReference(auth.Handler.FeatureStore, role, auth.Handler.Scheme) +} + +func (auth *FeastAuth) createFeastRoleBinding() error { + logger := log.FromContext(auth.Handler.Context) + roleBinding := auth.initFeastRoleBinding() + if op, err := controllerutil.CreateOrUpdate(auth.Handler.Context, auth.Handler.Client, roleBinding, controllerutil.MutateFn(func() error { + return auth.setFeastRoleBinding(roleBinding) + })); err != nil { + return err + } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { + logger.Info("Successfully reconciled", "RoleBinding", roleBinding.Name, "operation", op) + } + + return nil +} + +func (auth *FeastAuth) initFeastRoleBinding() *rbacv1.RoleBinding { + roleBinding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{Name: auth.getFeastRoleName(), Namespace: auth.Handler.FeatureStore.Namespace}, + } + roleBinding.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("RoleBinding")) + return roleBinding +} + +func (auth *FeastAuth) setFeastRoleBinding(roleBinding *rbacv1.RoleBinding) error { + roleBinding.Labels = auth.getLabels() + roleBinding.Subjects = []rbacv1.Subject{} + if auth.Handler.FeatureStore.Status.Applied.Services.OfflineStore != nil { + roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{ + Kind: rbacv1.ServiceAccountKind, + Name: services.GetFeastServiceName(auth.Handler.FeatureStore, services.OfflineFeastType), + Namespace: auth.Handler.FeatureStore.Namespace, + }) + } + if auth.Handler.FeatureStore.Status.Applied.Services.OnlineStore != nil { + roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{ + Kind: rbacv1.ServiceAccountKind, + Name: services.GetFeastServiceName(auth.Handler.FeatureStore, services.OnlineFeastType), + Namespace: auth.Handler.FeatureStore.Namespace, + }) + } + if auth.Handler.FeatureStore.Status.Applied.Services.Registry != nil { + roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{ + Kind: rbacv1.ServiceAccountKind, + Name: services.GetFeastServiceName(auth.Handler.FeatureStore, services.RegistryFeastType), + Namespace: auth.Handler.FeatureStore.Namespace, + }) + } + roleBinding.RoleRef = rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "Role", + Name: auth.getFeastRoleName(), + } + + return controllerutil.SetControllerReference(auth.Handler.FeatureStore, roleBinding, auth.Handler.Scheme) +} + +func (auth *FeastAuth) createAuthRole(roleName string) error { + logger := log.FromContext(auth.Handler.Context) + role := auth.initAuthRole(roleName) + if op, err := controllerutil.CreateOrUpdate(auth.Handler.Context, auth.Handler.Client, role, controllerutil.MutateFn(func() error { + return auth.setAuthRole(role) + })); err != nil { + return err + } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { + logger.Info("Successfully reconciled", "Role", role.Name, "operation", op) + } + + return nil +} + +func (auth *FeastAuth) initAuthRole(roleName string) *rbacv1.Role { + role := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{Name: roleName, Namespace: auth.Handler.FeatureStore.Namespace}, + } + role.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("Role")) + return role +} + +func (auth *FeastAuth) setAuthRole(role *rbacv1.Role) error { + role.Labels = auth.getLabels() + role.Rules = []rbacv1.PolicyRule{} + + return controllerutil.SetControllerReference(auth.Handler.FeatureStore, role, auth.Handler.Scheme) +} + +func (auth *FeastAuth) getLabels() map[string]string { + return map[string]string{ + nameLabelKey: auth.Handler.FeatureStore.Name, + } +} + +func (auth *FeastAuth) setFeastKubernetesAuthCondition(err error) error { + if err != nil { + logger := log.FromContext(auth.Handler.Context) + cond := feastKubernetesAuthConditions[metav1.ConditionFalse] + cond.Message = "Error: " + err.Error() + apimeta.SetStatusCondition(&auth.Handler.FeatureStore.Status.Conditions, cond) + logger.Error(err, "Error deploying the Kubernetes authorization") + return err + } else { + apimeta.SetStatusCondition(&auth.Handler.FeatureStore.Status.Conditions, feastKubernetesAuthConditions[metav1.ConditionTrue]) + } + return nil +} + +func (auth *FeastAuth) getFeastRoleName() string { + return GetFeastRoleName(auth.Handler.FeatureStore) +} + +func GetFeastRoleName(featureStore *feastdevv1alpha1.FeatureStore) string { + return services.GetFeastName(featureStore) +} diff --git a/infra/feast-operator/internal/controller/auth/auth_types.go b/infra/feast-operator/internal/controller/auth/auth_types.go new file mode 100644 index 00000000000..c51025abeba --- /dev/null +++ b/infra/feast-operator/internal/controller/auth/auth_types.go @@ -0,0 +1,30 @@ +package auth + +import ( + feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// FeastAuth is an interface for configuring feast authorization +type FeastAuth struct { + Handler handler.FeastHandler +} + +var ( + nameLabelKey = feastdevv1alpha1.GroupVersion.Group + "/name" + + feastKubernetesAuthConditions = map[metav1.ConditionStatus]metav1.Condition{ + metav1.ConditionTrue: { + Type: feastdevv1alpha1.KubernetesAuthReadyType, + Status: metav1.ConditionTrue, + Reason: feastdevv1alpha1.ReadyReason, + Message: feastdevv1alpha1.KubernetesAuthReadyMessage, + }, + metav1.ConditionFalse: { + Type: feastdevv1alpha1.KubernetesAuthReadyType, + Status: metav1.ConditionFalse, + Reason: feastdevv1alpha1.KubernetesAuthFailedReason, + }, + } +) diff --git a/infra/feast-operator/internal/controller/featurestore_controller.go b/infra/feast-operator/internal/controller/featurestore_controller.go index 278ea4a78fd..fb881e7b733 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller.go +++ b/infra/feast-operator/internal/controller/featurestore_controller.go @@ -54,6 +54,7 @@ type FeatureStoreReconciler struct { //+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;serviceaccounts,verbs=get;list;create;update;watch;delete +//+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,verbs=get;list;create;update;watch;delete //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list // Reconcile is part of the main kubernetes reconciliation loop which aims to 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 913f022022d..12f278b9cb2 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go @@ -285,6 +285,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { RegistryType: services.RegistryFileConfigType, Path: registryPath, }, + AuthConfig: noAuthConfig(), } Expect(repoConfig).To(Equal(testConfig)) @@ -321,7 +322,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { OfflineStore: services.OfflineStoreConfig{ Type: services.OfflineFilePersistenceDuckDbConfigType, }, - Registry: regRemote, + Registry: regRemote, + AuthConfig: noAuthConfig(), } Expect(repoConfigOffline).To(Equal(offlineConfig)) @@ -362,7 +364,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Path: onlineStorePath, Type: services.OnlineSqliteConfigType, }, - Registry: regRemote, + Registry: regRemote, + AuthConfig: noAuthConfig(), } Expect(repoConfigOnline).To(Equal(onlineConfig)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(3)) @@ -388,7 +391,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Path: fmt.Sprintf("http://feast-%s-online.default.svc.cluster.local:80", resourceName), Type: services.OnlineRemoteConfigType, }, - Registry: regRemote, + Registry: regRemote, + AuthConfig: noAuthConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) 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 new file mode 100644 index 00000000000..8d2edf7a083 --- /dev/null +++ b/infra/feast-operator/internal/controller/featurestore_controller_kubernetes_auth_test.go @@ -0,0 +1,566 @@ +/* +Copyright 2024 Feast Community. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "encoding/base64" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "gopkg.in/yaml.v3" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/feast-dev/feast/infra/feast-operator/api/feastversion" + feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/auth" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" +) + +var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { + Context("When deploying a resource with all ephemeral services and Kubernetes authorization", func() { + const resourceName = "kubernetes-auth" + var pullPolicy = corev1.PullAlways + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", + } + featurestore := &feastdevv1alpha1.FeatureStore{} + roles := []string{"reader", "writer"} + + BeforeEach(func() { + 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.Spec.AuthConfig = &feastdevv1alpha1.AuthConfig{KubernetesAuth: &feastdevv1alpha1.KubernetesAuth{ + Roles: roles, + }} + + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + AfterEach(func() { + resource := &feastdevv1alpha1.FeatureStore{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance FeatureStore") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &FeatureStoreReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + + resource := &feastdevv1alpha1.FeatureStore{} + err = k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + feast := services.FeastServices{ + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, + } + Expect(resource.Status).NotTo(BeNil()) + Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) + Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) + Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) + expectedAuthConfig := &feastdevv1alpha1.AuthConfig{ + KubernetesAuth: &feastdevv1alpha1.KubernetesAuth{ + Roles: roles, + }, + } + Expect(resource.Status.Applied.AuthConfig).To(Equal(expectedAuthConfig)) + Expect(resource.Status.Applied.Services).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.OfflineStore).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.OfflineStore.Persistence).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.OfflineStore.Persistence.FilePersistence).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.OfflineStore.Persistence.FilePersistence.Type).To(Equal(string(services.OfflineDaskConfigType))) + Expect(resource.Status.Applied.Services.OfflineStore.ImagePullPolicy).To(BeNil()) + Expect(resource.Status.Applied.Services.OfflineStore.Resources).To(BeNil()) + Expect(resource.Status.Applied.Services.OfflineStore.Image).To(Equal(&services.DefaultImage)) + Expect(resource.Status.Applied.Services.OnlineStore).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.OnlineStore.Persistence).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.Path).To(Equal(services.DefaultOnlineStoreEphemeralPath)) + Expect(resource.Status.Applied.Services.OnlineStore.Env).To(Equal(&[]corev1.EnvVar{})) + 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)) + Expect(resource.Status.Applied.Services.Registry).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.Registry.Local).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.Registry.Local.Persistence).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.Registry.Local.Persistence.FilePersistence).NotTo(BeNil()) + Expect(resource.Status.Applied.Services.Registry.Local.Persistence.FilePersistence.Path).To(Equal(services.DefaultRegistryEphemeralPath)) + Expect(resource.Status.Applied.Services.Registry.Local.ImagePullPolicy).To(BeNil()) + Expect(resource.Status.Applied.Services.Registry.Local.Resources).To(BeNil()) + Expect(resource.Status.Applied.Services.Registry.Local.Image).To(Equal(&services.DefaultImage)) + + Expect(resource.Status.ServiceHostnames.OfflineStore).To(Equal(feast.GetFeastServiceName(services.OfflineFeastType) + "." + resource.Namespace + domain)) + Expect(resource.Status.ServiceHostnames.OnlineStore).To(Equal(feast.GetFeastServiceName(services.OnlineFeastType) + "." + resource.Namespace + domain)) + Expect(resource.Status.ServiceHostnames.Registry).To(Equal(feast.GetFeastServiceName(services.RegistryFeastType) + "." + resource.Namespace + domain)) + + Expect(resource.Status.Conditions).NotTo(BeEmpty()) + cond := apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.ReadyType) + Expect(cond).ToNot(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionTrue)) + Expect(cond.Reason).To(Equal(feastdevv1alpha1.ReadyReason)) + Expect(cond.Type).To(Equal(feastdevv1alpha1.ReadyType)) + Expect(cond.Message).To(Equal(feastdevv1alpha1.ReadyMessage)) + + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + Expect(cond).ToNot(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionTrue)) + Expect(cond.Reason).To(Equal(feastdevv1alpha1.ReadyReason)) + Expect(cond.Type).To(Equal(feastdevv1alpha1.KubernetesAuthReadyType)) + Expect(cond.Message).To(Equal(feastdevv1alpha1.KubernetesAuthReadyMessage)) + + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) + Expect(cond).ToNot(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionTrue)) + Expect(cond.Reason).To(Equal(feastdevv1alpha1.ReadyReason)) + Expect(cond.Type).To(Equal(feastdevv1alpha1.RegistryReadyType)) + Expect(cond.Message).To(Equal(feastdevv1alpha1.RegistryReadyMessage)) + + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.ClientReadyType) + Expect(cond).ToNot(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionTrue)) + Expect(cond.Reason).To(Equal(feastdevv1alpha1.ReadyReason)) + Expect(cond.Type).To(Equal(feastdevv1alpha1.ClientReadyType)) + Expect(cond.Message).To(Equal(feastdevv1alpha1.ClientReadyMessage)) + + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.OfflineStoreReadyType) + Expect(cond).ToNot(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionTrue)) + Expect(cond.Reason).To(Equal(feastdevv1alpha1.ReadyReason)) + Expect(cond.Type).To(Equal(feastdevv1alpha1.OfflineStoreReadyType)) + Expect(cond.Message).To(Equal(feastdevv1alpha1.OfflineStoreReadyMessage)) + + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.OnlineStoreReadyType) + Expect(cond).ToNot(BeNil()) + Expect(cond.Status).To(Equal(metav1.ConditionTrue)) + Expect(cond.Reason).To(Equal(feastdevv1alpha1.ReadyReason)) + Expect(cond.Type).To(Equal(feastdevv1alpha1.OnlineStoreReadyType)) + Expect(cond.Message).To(Equal(feastdevv1alpha1.OnlineStoreReadyMessage)) + + Expect(resource.Status.Phase).To(Equal(feastdevv1alpha1.ReadyPhase)) + + // check offline deployment + deploy := &appsv1.Deployment{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: feast.GetFeastServiceName(services.OfflineFeastType), + Namespace: resource.Namespace, + }, + deploy) + Expect(err).NotTo(HaveOccurred()) + Expect(deploy.Spec.Replicas).To(Equal(&services.DefaultReplicas)) + Expect(controllerutil.HasControllerReference(deploy)).To(BeTrue()) + Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) + Expect(deploy.Spec.Template.Spec.Volumes).To(HaveLen(0)) + Expect(deploy.Spec.Template.Spec.Containers[0].VolumeMounts).To(HaveLen(0)) + + // check online deployment + deploy = &appsv1.Deployment{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: feast.GetFeastServiceName(services.OnlineFeastType), + Namespace: resource.Namespace, + }, + deploy) + Expect(err).NotTo(HaveOccurred()) + Expect(deploy.Spec.Replicas).To(Equal(&services.DefaultReplicas)) + Expect(controllerutil.HasControllerReference(deploy)).To(BeTrue()) + Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) + Expect(deploy.Spec.Template.Spec.Volumes).To(HaveLen(0)) + Expect(deploy.Spec.Template.Spec.Containers[0].VolumeMounts).To(HaveLen(0)) + + // check registry deployment + deploy = &appsv1.Deployment{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: feast.GetFeastServiceName(services.RegistryFeastType), + Namespace: resource.Namespace, + }, + deploy) + Expect(err).NotTo(HaveOccurred()) + Expect(deploy.Spec.Replicas).To(Equal(&services.DefaultReplicas)) + Expect(controllerutil.HasControllerReference(deploy)).To(BeTrue()) + Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1)) + Expect(deploy.Spec.Template.Spec.Volumes).To(HaveLen(0)) + Expect(deploy.Spec.Template.Spec.Containers[0].VolumeMounts).To(HaveLen(0)) + + // check configured Roles + for _, roleName := range roles { + role := &rbacv1.Role{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: roleName, + Namespace: resource.Namespace, + }, + role) + Expect(err).NotTo(HaveOccurred()) + Expect(role.Rules).To(BeEmpty()) + } + + // check Feast Role + feastRole := &rbacv1.Role{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: auth.GetFeastRoleName(resource), + Namespace: resource.Namespace, + }, + feastRole) + Expect(err).NotTo(HaveOccurred()) + Expect(feastRole.Rules).ToNot(BeEmpty()) + Expect(feastRole.Rules).To(HaveLen(1)) + Expect(feastRole.Rules[0].APIGroups).To(HaveLen(1)) + Expect(feastRole.Rules[0].APIGroups[0]).To(Equal(rbacv1.GroupName)) + Expect(feastRole.Rules[0].Resources).To(HaveLen(2)) + Expect(feastRole.Rules[0].Resources).To(ContainElement("roles")) + Expect(feastRole.Rules[0].Resources).To(ContainElement("rolebindings")) + Expect(feastRole.Rules[0].Verbs).To(HaveLen(3)) + Expect(feastRole.Rules[0].Verbs).To(ContainElement("get")) + Expect(feastRole.Rules[0].Verbs).To(ContainElement("list")) + Expect(feastRole.Rules[0].Verbs).To(ContainElement("watch")) + + // check RoleBinding + roleBinding := &rbacv1.RoleBinding{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: auth.GetFeastRoleName(resource), + Namespace: resource.Namespace, + }, + roleBinding) + Expect(err).NotTo(HaveOccurred()) + + // check ServiceAccounts + expectedRoleRef := rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "Role", + Name: feastRole.Name, + } + for _, serviceType := range []services.FeastServiceType{services.RegistryFeastType, services.OnlineFeastType, services.OfflineFeastType} { + sa := &corev1.ServiceAccount{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: feast.GetFeastServiceName(serviceType), + Namespace: resource.Namespace, + }, + sa) + Expect(err).NotTo(HaveOccurred()) + + expectedSubject := rbacv1.Subject{ + Kind: rbacv1.ServiceAccountKind, + Name: sa.Name, + Namespace: sa.Namespace, + } + Expect(roleBinding.Subjects).To(ContainElement(expectedSubject)) + Expect(roleBinding.RoleRef).To(Equal(expectedRoleRef)) + } + + By("Updating the user roled and reconciling") + resourceNew := resource.DeepCopy() + rolesNew := roles[1:] + resourceNew.Spec.AuthConfig.KubernetesAuth.Roles = rolesNew + err = k8sClient.Update(ctx, resourceNew) + Expect(err).NotTo(HaveOccurred()) + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + + resource = &feastdevv1alpha1.FeatureStore{} + err = k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + feast.Handler.FeatureStore = resource + + // check new Roles + for _, roleName := range rolesNew { + role := &rbacv1.Role{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: roleName, + Namespace: resource.Namespace, + }, + role) + Expect(err).NotTo(HaveOccurred()) + Expect(role.Rules).To(BeEmpty()) + } + + // check deleted Role + role := &rbacv1.Role{} + deletedRole := roles[0] + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: deletedRole, + Namespace: resource.Namespace, + }, + role) + Expect(err).To(HaveOccurred()) + Expect(errors.IsNotFound(err)).To(BeTrue()) + + By("Clearing the kubernetes authorizatino and reconciling") + resourceNew = resource.DeepCopy() + resourceNew.Spec.AuthConfig = &feastdevv1alpha1.AuthConfig{} + err = k8sClient.Update(ctx, resourceNew) + Expect(err).NotTo(HaveOccurred()) + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + + resource = &feastdevv1alpha1.FeatureStore{} + err = k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + feast.Handler.FeatureStore = resource + + // check no Roles + for _, roleName := range roles { + role := &rbacv1.Role{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: roleName, + Namespace: resource.Namespace, + }, + role) + Expect(err).To(HaveOccurred()) + Expect(errors.IsNotFound(err)).To(BeTrue()) + } + // check no RoleBinding + roleBinding = &rbacv1.RoleBinding{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: auth.GetFeastRoleName(resource), + Namespace: resource.Namespace, + }, + roleBinding) + Expect(err).To(HaveOccurred()) + Expect(errors.IsNotFound(err)).To(BeTrue()) + }) + + It("should properly encode a feature_store.yaml config", func() { + By("Reconciling the created resource") + controllerReconciler := &FeatureStoreReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + + resource := &feastdevv1alpha1.FeatureStore{} + err = k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + req, err := labels.NewRequirement(services.NameLabelKey, selection.Equals, []string{resource.Name}) + Expect(err).NotTo(HaveOccurred()) + labelSelector := labels.NewSelector().Add(*req) + listOpts := &client.ListOptions{Namespace: resource.Namespace, LabelSelector: labelSelector} + deployList := appsv1.DeploymentList{} + err = k8sClient.List(ctx, &deployList, listOpts) + Expect(err).NotTo(HaveOccurred()) + Expect(deployList.Items).To(HaveLen(3)) + + svcList := corev1.ServiceList{} + err = k8sClient.List(ctx, &svcList, listOpts) + Expect(err).NotTo(HaveOccurred()) + Expect(svcList.Items).To(HaveLen(3)) + + cmList := corev1.ConfigMapList{} + err = k8sClient.List(ctx, &cmList, listOpts) + Expect(err).NotTo(HaveOccurred()) + Expect(cmList.Items).To(HaveLen(1)) + + feast := services.FeastServices{ + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, + } + + // check registry deployment + deploy := &appsv1.Deployment{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: feast.GetFeastServiceName(services.RegistryFeastType), + Namespace: resource.Namespace, + }, + deploy) + Expect(err).NotTo(HaveOccurred()) + env := getFeatureStoreYamlEnvVar(deploy.Spec.Template.Spec.Containers[0].Env) + Expect(env).NotTo(BeNil()) + + // check registry config + fsYamlStr, err := feast.GetServiceFeatureStoreYamlBase64(services.RegistryFeastType) + Expect(err).NotTo(HaveOccurred()) + Expect(fsYamlStr).To(Equal(env.Value)) + + envByte, err := base64.StdEncoding.DecodeString(env.Value) + Expect(err).NotTo(HaveOccurred()) + repoConfig := &services.RepoConfig{} + err = yaml.Unmarshal(envByte, repoConfig) + Expect(err).NotTo(HaveOccurred()) + testConfig := &services.RepoConfig{ + Project: feastProject, + Provider: services.LocalProviderType, + EntityKeySerializationVersion: feastdevv1alpha1.SerializationVersion, + Registry: services.RegistryConfig{ + RegistryType: services.RegistryFileConfigType, + Path: services.DefaultRegistryEphemeralPath, + S3AdditionalKwargs: nil, + }, + AuthConfig: services.AuthConfig{ + Type: services.KubernetesAuthType, + }, + } + Expect(repoConfig).To(Equal(testConfig)) + + // check offline deployment + deploy = &appsv1.Deployment{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: feast.GetFeastServiceName(services.OfflineFeastType), + Namespace: resource.Namespace, + }, + deploy) + Expect(err).NotTo(HaveOccurred()) + env = getFeatureStoreYamlEnvVar(deploy.Spec.Template.Spec.Containers[0].Env) + Expect(env).NotTo(BeNil()) + + // check offline config + fsYamlStr, err = feast.GetServiceFeatureStoreYamlBase64(services.OfflineFeastType) + Expect(err).NotTo(HaveOccurred()) + Expect(fsYamlStr).To(Equal(env.Value)) + + envByte, err = base64.StdEncoding.DecodeString(env.Value) + Expect(err).NotTo(HaveOccurred()) + repoConfig = &services.RepoConfig{} + err = yaml.Unmarshal(envByte, repoConfig) + Expect(err).NotTo(HaveOccurred()) + regRemote := services.RegistryConfig{ + RegistryType: services.RegistryRemoteConfigType, + Path: fmt.Sprintf("feast-%s-registry.default.svc.cluster.local:80", resourceName), + } + testConfig = &services.RepoConfig{ + Project: feastProject, + Provider: services.LocalProviderType, + EntityKeySerializationVersion: feastdevv1alpha1.SerializationVersion, + OfflineStore: services.OfflineStoreConfig{ + Type: services.OfflineDaskConfigType, + }, + Registry: regRemote, + AuthConfig: services.AuthConfig{ + Type: services.KubernetesAuthType, + }, + } + Expect(repoConfig).To(Equal(testConfig)) + + // check online deployment + deploy = &appsv1.Deployment{} + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: feast.GetFeastServiceName(services.OnlineFeastType), + Namespace: resource.Namespace, + }, + deploy) + Expect(err).NotTo(HaveOccurred()) + env = getFeatureStoreYamlEnvVar(deploy.Spec.Template.Spec.Containers[0].Env) + Expect(env).NotTo(BeNil()) + + // check online config + fsYamlStr, err = feast.GetServiceFeatureStoreYamlBase64(services.OnlineFeastType) + Expect(err).NotTo(HaveOccurred()) + Expect(fsYamlStr).To(Equal(env.Value)) + + envByte, err = base64.StdEncoding.DecodeString(env.Value) + Expect(err).NotTo(HaveOccurred()) + repoConfig = &services.RepoConfig{} + err = yaml.Unmarshal(envByte, repoConfig) + Expect(err).NotTo(HaveOccurred()) + offlineRemote := services.OfflineStoreConfig{ + Host: fmt.Sprintf("feast-%s-offline.default.svc.cluster.local", resourceName), + Type: services.OfflineRemoteConfigType, + Port: services.HttpPort, + } + testConfig = &services.RepoConfig{ + Project: feastProject, + Provider: services.LocalProviderType, + EntityKeySerializationVersion: feastdevv1alpha1.SerializationVersion, + OfflineStore: offlineRemote, + OnlineStore: services.OnlineStoreConfig{ + Path: services.DefaultOnlineStoreEphemeralPath, + Type: services.OnlineSqliteConfigType, + }, + Registry: regRemote, + AuthConfig: services.AuthConfig{ + Type: services.KubernetesAuthType, + }, + } + Expect(repoConfig).To(Equal(testConfig)) + + // check client config + cm := &corev1.ConfigMap{} + name := feast.GetFeastServiceName(services.ClientFeastType) + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: name, + Namespace: resource.Namespace, + }, + cm) + Expect(err).NotTo(HaveOccurred()) + repoConfigClient := &services.RepoConfig{} + err = yaml.Unmarshal([]byte(cm.Data[services.FeatureStoreYamlCmKey]), repoConfigClient) + Expect(err).NotTo(HaveOccurred()) + clientConfig := &services.RepoConfig{ + Project: feastProject, + Provider: services.LocalProviderType, + EntityKeySerializationVersion: feastdevv1alpha1.SerializationVersion, + OfflineStore: offlineRemote, + OnlineStore: services.OnlineStoreConfig{ + Path: fmt.Sprintf("http://feast-%s-online.default.svc.cluster.local:80", resourceName), + Type: services.OnlineRemoteConfigType, + }, + Registry: services.RegistryConfig{ + RegistryType: services.RegistryRemoteConfigType, + Path: fmt.Sprintf("feast-%s-registry.default.svc.cluster.local:80", resourceName), + }, + AuthConfig: services.AuthConfig{ + Type: services.KubernetesAuthType, + }, + } + Expect(repoConfigClient).To(Equal(clientConfig)) + }) + }) +}) 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 cedcba6124b..a7301a713b8 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go @@ -312,6 +312,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Path: registryPath, S3AdditionalKwargs: &s3AdditionalKwargs, }, + AuthConfig: noAuthConfig(), } Expect(repoConfig).To(Equal(testConfig)) @@ -355,6 +356,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { RegistryType: services.RegistryRemoteConfigType, Path: fmt.Sprintf("feast-%s-registry.default.svc.cluster.local:80", resourceName), }, + AuthConfig: noAuthConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) 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 f124db55a6c..4b8dd843e6e 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go @@ -455,6 +455,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { RegistryType: services.RegistryFileConfigType, Path: registryMountedPath, }, + AuthConfig: noAuthConfig(), } Expect(repoConfig).To(Equal(testConfig)) @@ -492,7 +493,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { OfflineStore: services.OfflineStoreConfig{ Type: services.OfflineFilePersistenceDuckDbConfigType, }, - Registry: regRemote, + Registry: regRemote, + AuthConfig: noAuthConfig(), } Expect(repoConfigOffline).To(Equal(offlineConfig)) @@ -533,7 +535,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Path: onlineStoreMountedPath, Type: services.OnlineSqliteConfigType, }, - Registry: regRemote, + Registry: regRemote, + AuthConfig: noAuthConfig(), } Expect(repoConfigOnline).To(Equal(onlineConfig)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(3)) @@ -559,7 +562,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Path: fmt.Sprintf("http://feast-%s-online.default.svc.cluster.local:80", resourceName), Type: services.OnlineRemoteConfigType, }, - Registry: regRemote, + Registry: regRemote, + AuthConfig: noAuthConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_test.go b/infra/feast-operator/internal/controller/featurestore_controller_test.go index 00a6e71c71c..0b27b058f73 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_test.go @@ -240,6 +240,7 @@ var _ = Describe("FeatureStore Controller", func() { RegistryType: services.RegistryFileConfigType, Path: services.DefaultRegistryEphemeralPath, }, + AuthConfig: noAuthConfig(), } Expect(repoConfig).To(Equal(testConfig)) @@ -263,6 +264,7 @@ var _ = Describe("FeatureStore Controller", func() { RegistryType: services.RegistryRemoteConfigType, Path: "feast-test-resource-registry.default.svc.cluster.local:80", }, + AuthConfig: noAuthConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) @@ -611,6 +613,7 @@ var _ = Describe("FeatureStore Controller", func() { RegistryType: services.RegistryFileConfigType, Path: services.DefaultRegistryEphemeralPath, }, + AuthConfig: noAuthConfig(), } Expect(repoConfig).To(Equal(testConfig)) @@ -648,7 +651,8 @@ var _ = Describe("FeatureStore Controller", func() { OfflineStore: services.OfflineStoreConfig{ Type: services.OfflineFilePersistenceDaskConfigType, }, - Registry: regRemote, + Registry: regRemote, + AuthConfig: noAuthConfig(), } Expect(repoConfigOffline).To(Equal(offlineConfig)) @@ -690,7 +694,8 @@ var _ = Describe("FeatureStore Controller", func() { Path: services.DefaultOnlineStoreEphemeralPath, Type: services.OnlineSqliteConfigType, }, - Registry: regRemote, + Registry: regRemote, + AuthConfig: noAuthConfig(), } Expect(repoConfigOnline).To(Equal(onlineConfig)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(3)) @@ -716,7 +721,8 @@ var _ = Describe("FeatureStore Controller", func() { Path: "http://feast-services-online.default.svc.cluster.local:80", Type: services.OnlineRemoteConfigType, }, - Registry: regRemote, + Registry: regRemote, + AuthConfig: noAuthConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) @@ -1030,6 +1036,7 @@ var _ = Describe("FeatureStore Controller", func() { RegistryType: services.RegistryRemoteConfigType, Path: "feast-" + referencedRegistry.Name + "-registry.default.svc.cluster.local:80", }, + AuthConfig: noAuthConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) @@ -1247,6 +1254,12 @@ func getFeatureStoreYamlEnvVar(envs []corev1.EnvVar) *corev1.EnvVar { return nil } +func noAuthConfig() services.AuthConfig { + return services.AuthConfig{ + Type: services.NoAuthAuthType, + } +} + func areEnvVarArraysEqual(arr1 []corev1.EnvVar, arr2 []corev1.EnvVar) bool { if len(arr1) != len(arr2) { return false diff --git a/infra/feast-operator/internal/controller/handler/handler.go b/infra/feast-operator/internal/controller/handler/handler.go new file mode 100644 index 00000000000..73bacffea47 --- /dev/null +++ b/infra/feast-operator/internal/controller/handler/handler.go @@ -0,0 +1,28 @@ +package handler + +import ( + apierrors "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// delete an object if the FeatureStore is set as the object's controller/owner +func (handler *FeastHandler) DeleteOwnedFeastObj(obj client.Object) error { + name := obj.GetName() + kind := obj.GetObjectKind().GroupVersionKind().Kind + if err := handler.Client.Get(handler.Context, client.ObjectKeyFromObject(obj), obj); err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return err + } + for _, ref := range obj.GetOwnerReferences() { + if *ref.Controller && ref.UID == handler.FeatureStore.UID { + if err := handler.Client.Delete(handler.Context, obj); err != nil { + return err + } + log.FromContext(handler.Context).Info("Successfully deleted", kind, name) + } + } + return nil +} diff --git a/infra/feast-operator/internal/controller/handler/handler_types.go b/infra/feast-operator/internal/controller/handler/handler_types.go new file mode 100644 index 00000000000..1369f9d6f0e --- /dev/null +++ b/infra/feast-operator/internal/controller/handler/handler_types.go @@ -0,0 +1,16 @@ +package handler + +import ( + "context" + + feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type FeastHandler struct { + client.Client + Context context.Context + Scheme *runtime.Scheme + FeatureStore *feastdevv1alpha1.FeatureStore +} diff --git a/infra/feast-operator/internal/controller/services/repo_config.go b/infra/feast-operator/internal/controller/services/repo_config.go index 899a9157d9b..ef20dbf6eeb 100644 --- a/infra/feast-operator/internal/controller/services/repo_config.go +++ b/infra/feast-operator/internal/controller/services/repo_config.go @@ -232,6 +232,18 @@ func getClientRepoConfig(featureStore *feastdevv1alpha1.FeatureStore) RepoConfig Path: status.ServiceHostnames.Registry, } } + + if status.Applied.AuthConfig == nil { + clientRepoConfig.AuthConfig = AuthConfig{ + Type: NoAuthAuthType, + } + } else { + if status.Applied.AuthConfig.KubernetesAuth != nil { + clientRepoConfig.AuthConfig = AuthConfig{ + Type: KubernetesAuthType, + } + } + } return clientRepoConfig } diff --git a/infra/feast-operator/internal/controller/services/services_types.go b/infra/feast-operator/internal/controller/services/services_types.go index 1251a2ff9e3..65bc92aabe2 100644 --- a/infra/feast-operator/internal/controller/services/services_types.go +++ b/infra/feast-operator/internal/controller/services/services_types.go @@ -62,6 +62,9 @@ const ( RegistryDBPersistenceSQLConfigType RegistryConfigType = "sql" LocalProviderType FeastProviderType = "local" + + NoAuthAuthType AuthType = "no_auth" + KubernetesAuthType AuthType = "kubernetes" ) var ( @@ -72,15 +75,15 @@ var ( FeastServiceConstants = map[FeastServiceType]deploymentSettings{ OfflineFeastType: { - Command: []string{"feast", "serve_offline", "-h", "0.0.0.0"}, + Command: []string{"feast", "--log-level", "DEBUG", "serve_offline", "-h", "0.0.0.0"}, TargetPort: 8815, }, OnlineFeastType: { - Command: []string{"feast", "serve", "-h", "0.0.0.0"}, + Command: []string{"feast", "--log-level", "DEBUG", "serve", "-h", "0.0.0.0"}, TargetPort: 6566, }, RegistryFeastType: { - Command: []string{"feast", "serve_registry"}, + Command: []string{"feast", "--log-level", "DEBUG", "serve_registry"}, TargetPort: 6570, }, } @@ -141,6 +144,9 @@ var ( } ) +// AuthType defines the authorization type +type AuthType string + // FeastServiceType is the type of feast service type FeastServiceType string @@ -172,6 +178,7 @@ type RepoConfig struct { OfflineStore OfflineStoreConfig `yaml:"offline_store,omitempty"` OnlineStore OnlineStoreConfig `yaml:"online_store,omitempty"` Registry RegistryConfig `yaml:"registry,omitempty"` + AuthConfig AuthConfig `yaml:"auth,omitempty"` EntityKeySerializationVersion int `yaml:"entity_key_serialization_version,omitempty"` } @@ -198,6 +205,11 @@ type RegistryConfig struct { DBParameters map[string]interface{} `yaml:",inline,omitempty"` } +// AuthConfig is the RBAC authorization configuration. +type AuthConfig struct { + Type AuthType `yaml:"type,omitempty"` +} + type deploymentSettings struct { Command []string TargetPort int32 From bcd7fe4dd088a3a5da087475a764bea184992bd7 Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Fri, 22 Nov 2024 22:21:54 +0100 Subject: [PATCH 02/11] refactoring types with FeastHandler Signed-off-by: Daniele Martinoli --- .../api/v1alpha1/featurestore_types.go | 35 ++--- infra/feast-operator/config/rbac/role.yaml | 11 ++ infra/feast-operator/dist/install.yaml | 11 ++ .../controller/featurestore_controller.go | 49 +++++-- .../featurestore_controller_ephemeral_test.go | 27 ++-- ...eaturestore_controller_objectstore_test.go | 29 ++-- .../featurestore_controller_pvc_test.go | 31 +++-- .../featurestore_controller_test.go | 96 ++++++++----- .../controller/handler/handler_types.go | 4 + .../internal/controller/services/client.go | 8 +- .../controller/services/repo_config.go | 6 +- .../controller/services/repo_config_test.go | 69 ++++++++++ .../internal/controller/services/services.go | 130 ++++++++---------- .../controller/services/services_types.go | 11 +- .../internal/controller/services/util.go | 8 ++ .../test/api/featurestore_types_test.go | 6 + 16 files changed, 353 insertions(+), 178 deletions(-) diff --git a/infra/feast-operator/api/v1alpha1/featurestore_types.go b/infra/feast-operator/api/v1alpha1/featurestore_types.go index 7957db0249d..dea0b409a3c 100644 --- a/infra/feast-operator/api/v1alpha1/featurestore_types.go +++ b/infra/feast-operator/api/v1alpha1/featurestore_types.go @@ -28,26 +28,29 @@ const ( FailedPhase = "Failed" // Feast condition types: - ClientReadyType = "Client" - OfflineStoreReadyType = "OfflineStore" - OnlineStoreReadyType = "OnlineStore" - RegistryReadyType = "Registry" - ReadyType = "FeatureStore" + ClientReadyType = "Client" + OfflineStoreReadyType = "OfflineStore" + OnlineStoreReadyType = "OnlineStore" + RegistryReadyType = "Registry" + ReadyType = "FeatureStore" + KubernetesAuthReadyType = "KubernetesAuth" // Feast condition reasons: - ReadyReason = "Ready" - FailedReason = "FeatureStoreFailed" - OfflineStoreFailedReason = "OfflineStoreDeploymentFailed" - OnlineStoreFailedReason = "OnlineStoreDeploymentFailed" - RegistryFailedReason = "RegistryDeploymentFailed" - ClientFailedReason = "ClientDeploymentFailed" + ReadyReason = "Ready" + FailedReason = "FeatureStoreFailed" + OfflineStoreFailedReason = "OfflineStoreDeploymentFailed" + OnlineStoreFailedReason = "OnlineStoreDeploymentFailed" + RegistryFailedReason = "RegistryDeploymentFailed" + ClientFailedReason = "ClientDeploymentFailed" + KubernetesAuthFailedReason = "KubernetesAuthorizationDeploymentFailed" // Feast condition messages: - ReadyMessage = "FeatureStore installation complete" - OfflineStoreReadyMessage = "Offline Store installation complete" - OnlineStoreReadyMessage = "Online Store installation complete" - RegistryReadyMessage = "Registry installation complete" - ClientReadyMessage = "Client installation complete" + ReadyMessage = "FeatureStore installation complete" + OfflineStoreReadyMessage = "Offline Store installation complete" + OnlineStoreReadyMessage = "Online Store installation complete" + RegistryReadyMessage = "Registry installation complete" + ClientReadyMessage = "Client installation complete" + KubernetesAuthReadyMessage = "Kubernetes authorization installation complete" // entity_key_serialization_version SerializationVersion = 3 diff --git a/infra/feast-operator/config/rbac/role.yaml b/infra/feast-operator/config/rbac/role.yaml index a4b0acfc1cf..5e9ed7f7393 100644 --- a/infra/feast-operator/config/rbac/role.yaml +++ b/infra/feast-operator/config/rbac/role.yaml @@ -62,3 +62,14 @@ rules: - get - patch - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + verbs: + - create + - delete + - get + - list + - update + - watch diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index 8abf3002d96..78cfd10d135 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -2330,6 +2330,17 @@ rules: - get - patch - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + verbs: + - create + - delete + - get + - list + - update + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/infra/feast-operator/internal/controller/featurestore_controller.go b/infra/feast-operator/internal/controller/featurestore_controller.go index fb881e7b733..01d0d7436dc 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller.go +++ b/infra/feast-operator/internal/controller/featurestore_controller.go @@ -23,6 +23,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -30,11 +31,13 @@ import ( "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" + handler "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/auth" + feasthandler "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" ) @@ -108,13 +111,15 @@ func (r *FeatureStoreReconciler) deployFeast(ctx context.Context, cr *feastdevv1 Message: feastdevv1alpha1.ReadyMessage, } - feast := services.FeastServices{ - Client: r.Client, - Context: ctx, - FeatureStore: cr, - Scheme: r.Scheme, + auth := auth.FeastAuth{ + Handler: feasthandler.FeastHandler{ + Client: r.Client, + Context: ctx, + FeatureStore: cr, + Scheme: r.Scheme, + }, } - if err = feast.Deploy(); err != nil { + if err = auth.Deploy(); err != nil { condition = metav1.Condition{ Type: feastdevv1alpha1.ReadyType, Status: metav1.ConditionFalse, @@ -122,6 +127,23 @@ func (r *FeatureStoreReconciler) deployFeast(ctx context.Context, cr *feastdevv1 Message: "Error: " + err.Error(), } result = ctrl.Result{Requeue: true, RequeueAfter: RequeueDelayError} + } else { + feast := services.FeastServices{ + Handler: feasthandler.FeastHandler{ + Client: r.Client, + Context: ctx, + FeatureStore: cr, + Scheme: r.Scheme, + }} + if err = feast.Deploy(); err != nil { + condition = metav1.Condition{ + Type: feastdevv1alpha1.ReadyType, + Status: metav1.ConditionFalse, + Reason: feastdevv1alpha1.FailedReason, + Message: "Error: " + err.Error(), + } + result = ctrl.Result{Requeue: true, RequeueAfter: RequeueDelayError} + } } logger.Info(condition.Message) @@ -146,6 +168,8 @@ func (r *FeatureStoreReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.Service{}). Owns(&corev1.PersistentVolumeClaim{}). Owns(&corev1.ServiceAccount{}). + Owns(&rbacv1.RoleBinding{}). + Owns(&rbacv1.Role{}). Watches(&feastdevv1alpha1.FeatureStore{}, handler.EnqueueRequestsFromMapFunc(r.mapFeastRefsToFeastRequests)). Complete(r) } @@ -170,11 +194,12 @@ func (r *FeatureStoreReconciler) mapFeastRefsToFeastRequests(ctx context.Context // this if statement is extra protection against any potential infinite reconcile loops if feastRefNsName != objNsName { feast := services.FeastServices{ - Client: r.Client, - Context: ctx, - FeatureStore: &obj, - Scheme: r.Scheme, - } + Handler: feasthandler.FeastHandler{ + Client: r.Client, + Context: ctx, + FeatureStore: &obj, + Scheme: r.Scheme, + }} if feast.IsRemoteRefRegistry() { remoteRef := obj.Status.Applied.Services.Registry.Remote.FeastRef remoteRefNsName := types.NamespacedName{Name: remoteRef.Name, Namespace: remoteRef.Namespace} 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 12f278b9cb2..bec48a2f8c5 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go @@ -39,6 +39,7 @@ import ( "github.com/feast-dev/feast/infra/feast-operator/api/feastversion" feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" ) @@ -115,15 +116,18 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(err).NotTo(HaveOccurred()) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } Expect(resource.Status).NotTo(BeNil()) Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) + Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Persistence).NotTo(BeNil()) @@ -161,6 +165,9 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(cond.Type).To(Equal(feastdevv1alpha1.ReadyType)) Expect(cond.Message).To(Equal(feastdevv1alpha1.ReadyMessage)) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + Expect(cond).To(BeNil()) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) Expect(cond).ToNot(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionTrue)) @@ -249,10 +256,12 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(cmList.Items).To(HaveLen(1)) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } // check registry config @@ -412,7 +421,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { resource = &feastdevv1alpha1.FeatureStore{} err = k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - feast.FeatureStore = resource + feast.Handler.FeatureStore = resource // check registry config deploy = &appsv1.Deployment{} 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 a7301a713b8..3cf38d20591 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go @@ -38,6 +38,7 @@ import ( "github.com/feast-dev/feast/infra/feast-operator/api/feastversion" feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" ) @@ -110,15 +111,18 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(err).NotTo(HaveOccurred()) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } Expect(resource.Status).NotTo(BeNil()) Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) + Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).To(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore).To(BeNil()) @@ -146,6 +150,9 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(cond.Type).To(Equal(feastdevv1alpha1.ReadyType)) Expect(cond.Message).To(Equal(feastdevv1alpha1.ReadyMessage)) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + Expect(cond).To(BeNil()) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) Expect(cond).ToNot(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionTrue)) @@ -220,7 +227,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { resource = &feastdevv1alpha1.FeatureStore{} err = k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - feast.FeatureStore = resource + feast.Handler.FeatureStore = resource Expect(resource.Status.Applied.Services.Registry.Local.Persistence.FilePersistence.S3AdditionalKwargs).NotTo(BeNil()) Expect(resource.Status.Applied.Services.Registry.Local.Persistence.FilePersistence.S3AdditionalKwargs).NotTo(Equal(&s3AdditionalKwargs)) Expect(resource.Status.Applied.Services.Registry.Local.Persistence.FilePersistence.S3AdditionalKwargs).To(Equal(&newS3AdditionalKwargs)) @@ -274,10 +281,12 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(cmList.Items).To(HaveLen(1)) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } // check registry deployment @@ -373,7 +382,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { resource = &feastdevv1alpha1.FeatureStore{} err = k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - feast.FeatureStore = resource + feast.Handler.FeatureStore = resource // check registry config deploy = &appsv1.Deployment{} 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 4b8dd843e6e..7cfefde02d0 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go @@ -42,6 +42,7 @@ import ( "github.com/feast-dev/feast/infra/feast-operator/api/feastversion" feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" ) @@ -141,15 +142,18 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(err).NotTo(HaveOccurred()) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } Expect(resource.Status).NotTo(BeNil()) Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) + Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Persistence).NotTo(BeNil()) @@ -217,6 +221,9 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(cond.Type).To(Equal(feastdevv1alpha1.ReadyType)) Expect(cond.Message).To(Equal(feastdevv1alpha1.ReadyMessage)) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + Expect(cond).To(BeNil()) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) Expect(cond).ToNot(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionTrue)) @@ -353,7 +360,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { resource = &feastdevv1alpha1.FeatureStore{} err = k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - feast.FeatureStore = resource + feast.Handler.FeatureStore = resource Expect(resource.Status.Applied.Services.OnlineStore.Persistence.FilePersistence.PvcConfig).To(BeNil()) // check online deployment @@ -368,7 +375,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(deploy.Spec.Template.Spec.Containers[0].VolumeMounts).To(HaveLen(0)) // check online pvc is deleted - log.FromContext(feast.Context).Info("Checking deletion of", "PersistentVolumeClaim", deploy.Name) + log.FromContext(feast.Handler.Context).Info("Checking deletion of", "PersistentVolumeClaim", deploy.Name) pvc = &corev1.PersistentVolumeClaim{} err = k8sClient.Get(ctx, types.NamespacedName{ Name: deploy.Name, @@ -418,10 +425,12 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(cmList.Items).To(HaveLen(1)) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } // check registry deployment @@ -587,7 +596,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { resource = &feastdevv1alpha1.FeatureStore{} err = k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - feast.FeatureStore = resource + feast.Handler.FeatureStore = resource // check registry config deploy = &appsv1.Deployment{} diff --git a/infra/feast-operator/internal/controller/featurestore_controller_test.go b/infra/feast-operator/internal/controller/featurestore_controller_test.go index 0b27b058f73..b59458cac25 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_test.go @@ -39,6 +39,7 @@ import ( "github.com/feast-dev/feast/infra/feast-operator/api/feastversion" feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" ) @@ -118,10 +119,12 @@ var _ = Describe("FeatureStore Controller", func() { Expect(cmList.Items).To(HaveLen(1)) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } Expect(resource.Status).NotTo(BeNil()) Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) @@ -130,6 +133,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(resource.Status.ServiceHostnames.OnlineStore).To(BeEmpty()) Expect(resource.Status.ServiceHostnames.Registry).To(Equal(feast.GetFeastServiceName(services.RegistryFeastType) + "." + resource.Namespace + ".svc.cluster.local:80")) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) + Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).To(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore).To(BeNil()) @@ -204,10 +208,12 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).NotTo(HaveOccurred()) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } deploy := &appsv1.Deployment{} @@ -321,10 +327,12 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).NotTo(HaveOccurred()) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } deploy := &appsv1.Deployment{} @@ -369,6 +377,9 @@ var _ = Describe("FeatureStore Controller", func() { Expect(cond.Reason).To(Equal(feastdevv1alpha1.FailedReason)) Expect(cond.Message).To(Equal("Error: Object " + resource.Namespace + "/" + name + " is already owned by another Service controller " + name)) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + Expect(cond).To(BeNil()) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) Expect(cond).ToNot(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionFalse)) @@ -436,15 +447,18 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).NotTo(HaveOccurred()) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } Expect(resource.Status).NotTo(BeNil()) Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) + Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Persistence).NotTo(BeNil()) @@ -482,6 +496,9 @@ var _ = Describe("FeatureStore Controller", func() { Expect(cond.Type).To(Equal(feastdevv1alpha1.ReadyType)) Expect(cond.Message).To(Equal(feastdevv1alpha1.ReadyMessage)) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + Expect(cond).To(BeNil()) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) Expect(cond).ToNot(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionTrue)) @@ -576,10 +593,12 @@ var _ = Describe("FeatureStore Controller", func() { Expect(cmList.Items).To(HaveLen(1)) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } // check registry config @@ -798,10 +817,12 @@ var _ = Describe("FeatureStore Controller", func() { Expect(cmList.Items).To(HaveLen(1)) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } fsYamlStr := "" @@ -963,6 +984,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).To(HaveOccurred()) err = k8sClient.Get(ctx, nsName, resource) Expect(err).NotTo(HaveOccurred()) + Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType)).To(BeNil()) Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType)).To(BeNil()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.ReadyType)).To(BeFalse()) cond := apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.ReadyType) @@ -978,6 +1000,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).To(HaveOccurred()) err = k8sClient.Get(ctx, nsName, resource) Expect(err).NotTo(HaveOccurred()) + Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType)).To(BeNil()) Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType)).To(BeNil()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.ReadyType)).To(BeFalse()) cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.ReadyType) @@ -995,6 +1018,7 @@ var _ = Describe("FeatureStore Controller", func() { err = k8sClient.Get(ctx, nsName, resource) Expect(err).NotTo(HaveOccurred()) + Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType)).To(BeNil()) Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType)).To(BeNil()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.ReadyType)).To(BeTrue()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.OnlineStoreReadyType)).To(BeTrue()) @@ -1003,10 +1027,12 @@ var _ = Describe("FeatureStore Controller", func() { Expect(resource.Status.ServiceHostnames.Registry).ToNot(BeEmpty()) Expect(resource.Status.ServiceHostnames.Registry).To(Equal(referencedRegistry.Status.ServiceHostnames.Registry)) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } // check client config @@ -1061,6 +1087,7 @@ var _ = Describe("FeatureStore Controller", func() { err = k8sClient.Get(ctx, nsName, resource) Expect(err).NotTo(HaveOccurred()) Expect(resource.Status.ServiceHostnames.Registry).To(BeEmpty()) + Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType)).To(BeNil()) Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType)).To(BeNil()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.ReadyType)).To(BeFalse()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.OnlineStoreReadyType)).To(BeTrue()) @@ -1088,10 +1115,12 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).NotTo(HaveOccurred()) feast := services.FeastServices{ - Client: controllerReconciler.Client, - Context: ctx, - Scheme: controllerReconciler.Scheme, - FeatureStore: resource, + Handler: handler.FeastHandler{ + Client: controllerReconciler.Client, + Context: ctx, + Scheme: controllerReconciler.Scheme, + FeatureStore: resource, + }, } deploy := &appsv1.Deployment{} @@ -1136,6 +1165,9 @@ var _ = Describe("FeatureStore Controller", func() { Expect(cond.Reason).To(Equal(feastdevv1alpha1.FailedReason)) Expect(cond.Message).To(Equal("Error: Object " + resource.Namespace + "/" + name + " is already owned by another Service controller " + name)) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + Expect(cond).To(BeNil()) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) Expect(cond).ToNot(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionTrue)) diff --git a/infra/feast-operator/internal/controller/handler/handler_types.go b/infra/feast-operator/internal/controller/handler/handler_types.go index 1369f9d6f0e..5a26776f569 100644 --- a/infra/feast-operator/internal/controller/handler/handler_types.go +++ b/infra/feast-operator/internal/controller/handler/handler_types.go @@ -8,6 +8,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) +const ( + FeastPrefix = "feast-" +) + type FeastHandler struct { client.Client Context context.Context diff --git a/infra/feast-operator/internal/controller/services/client.go b/infra/feast-operator/internal/controller/services/client.go index 1befd2df194..e8faa59f21e 100644 --- a/infra/feast-operator/internal/controller/services/client.go +++ b/infra/feast-operator/internal/controller/services/client.go @@ -30,12 +30,12 @@ func (feast *FeastServices) deployClient() error { } func (feast *FeastServices) createClientConfigMap() error { - logger := log.FromContext(feast.Context) + logger := log.FromContext(feast.Handler.Context) cm := &corev1.ConfigMap{ ObjectMeta: feast.GetObjectMeta(ClientFeastType), } cm.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("ConfigMap")) - if op, err := controllerutil.CreateOrUpdate(feast.Context, feast.Client, cm, controllerutil.MutateFn(func() error { + if op, err := controllerutil.CreateOrUpdate(feast.Handler.Context, feast.Handler.Client, cm, controllerutil.MutateFn(func() error { return feast.setClientConfigMap(cm) })); err != nil { return err @@ -52,6 +52,6 @@ func (feast *FeastServices) setClientConfigMap(cm *corev1.ConfigMap) error { return err } cm.Data = map[string]string{FeatureStoreYamlCmKey: string(clientYaml)} - feast.FeatureStore.Status.ClientConfigMap = cm.Name - return controllerutil.SetControllerReference(feast.FeatureStore, cm, feast.Scheme) + feast.Handler.FeatureStore.Status.ClientConfigMap = cm.Name + return controllerutil.SetControllerReference(feast.Handler.FeatureStore, cm, feast.Handler.Scheme) } diff --git a/infra/feast-operator/internal/controller/services/repo_config.go b/infra/feast-operator/internal/controller/services/repo_config.go index ef20dbf6eeb..23bbaed7f93 100644 --- a/infra/feast-operator/internal/controller/services/repo_config.go +++ b/infra/feast-operator/internal/controller/services/repo_config.go @@ -45,7 +45,7 @@ func (feast *FeastServices) getServiceFeatureStoreYaml(feastType FeastServiceTyp } func (feast *FeastServices) getServiceRepoConfig(feastType FeastServiceType) (RepoConfig, error) { - return getServiceRepoConfig(feastType, feast.FeatureStore, feast.extractConfigFromSecret) + return getServiceRepoConfig(feastType, feast.Handler.FeatureStore, feast.extractConfigFromSecret) } func getServiceRepoConfig(feastType FeastServiceType, featureStore *feastdevv1alpha1.FeatureStore, secretExtractionFunc func(secretRef string, secretKeyName string) (map[string]interface{}, error)) (RepoConfig, error) { @@ -203,7 +203,7 @@ func setRepoConfigOffline(services *feastdevv1alpha1.FeatureStoreServices, secre } func (feast *FeastServices) getClientFeatureStoreYaml() ([]byte, error) { - return yaml.Marshal(getClientRepoConfig(feast.FeatureStore)) + return yaml.Marshal(getClientRepoConfig(feast.Handler.FeatureStore)) } func getClientRepoConfig(featureStore *feastdevv1alpha1.FeatureStore) RepoConfig { @@ -233,7 +233,7 @@ func getClientRepoConfig(featureStore *feastdevv1alpha1.FeatureStore) RepoConfig } } - if status.Applied.AuthConfig == nil { + if status.Applied.AuthConfig.KubernetesAuth == nil { clientRepoConfig.AuthConfig = AuthConfig{ Type: NoAuthAuthType, } diff --git a/infra/feast-operator/internal/controller/services/repo_config_test.go b/infra/feast-operator/internal/controller/services/repo_config_test.go index eaeeb4ea09f..45075702a13 100644 --- a/infra/feast-operator/internal/controller/services/repo_config_test.go +++ b/infra/feast-operator/internal/controller/services/repo_config_test.go @@ -39,18 +39,21 @@ var _ = Describe("Repo Config", func() { var repoConfig RepoConfig repoConfig, err := getServiceRepoConfig(OfflineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) expectedRegistryConfig := RegistryConfig{ @@ -75,18 +78,21 @@ var _ = Describe("Repo Config", func() { ApplyDefaultsToStatus(featureStore) repoConfig, err = getServiceRepoConfig(OfflineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) expectedRegistryConfig = RegistryConfig{ @@ -110,18 +116,21 @@ var _ = Describe("Repo Config", func() { ApplyDefaultsToStatus(featureStore) repoConfig, err = getServiceRepoConfig(OfflineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) @@ -156,6 +165,7 @@ var _ = Describe("Repo Config", func() { ApplyDefaultsToStatus(featureStore) repoConfig, err = getServiceRepoConfig(OfflineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) expectedOfflineConfig := OfflineStoreConfig{ Type: "duckdb", } @@ -165,6 +175,7 @@ var _ = Describe("Repo Config", func() { repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) expectedOnlineConfig := OnlineStoreConfig{ Type: "sqlite", @@ -175,6 +186,7 @@ var _ = Describe("Repo Config", func() { repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) expectedRegistryConfig = RegistryConfig{ @@ -183,6 +195,63 @@ var _ = Describe("Repo Config", func() { } Expect(repoConfig.Registry).To(Equal(expectedRegistryConfig)) + By("Having kubernetes auth") + featureStore = minimalFeatureStore() + featureStore.Spec.AuthConfig = &feastdevv1alpha1.AuthConfig{ + KubernetesAuth: &feastdevv1alpha1.KubernetesAuth{}, + } + featureStore.Spec.Services = &feastdevv1alpha1.FeatureStoreServices{ + OfflineStore: &feastdevv1alpha1.OfflineStore{ + Persistence: &feastdevv1alpha1.OfflineStorePersistence{ + FilePersistence: &feastdevv1alpha1.OfflineStoreFilePersistence{}, + }, + }, + OnlineStore: &feastdevv1alpha1.OnlineStore{ + Persistence: &feastdevv1alpha1.OnlineStorePersistence{ + FilePersistence: &feastdevv1alpha1.OnlineStoreFilePersistence{}, + }, + }, + Registry: &feastdevv1alpha1.Registry{ + Local: &feastdevv1alpha1.LocalRegistryConfig{ + Persistence: &feastdevv1alpha1.RegistryPersistence{ + FilePersistence: &feastdevv1alpha1.RegistryFilePersistence{}, + }, + }, + }, + } + ApplyDefaultsToStatus(featureStore) + repoConfig, err = getServiceRepoConfig(OfflineFeastType, featureStore) + Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(KubernetesAuthType)) + expectedOfflineConfig = OfflineStoreConfig{ + Type: "dask", + } + Expect(repoConfig.OfflineStore).To(Equal(expectedOfflineConfig)) + Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) + Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) + + repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore) + Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(KubernetesAuthType)) + Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) + expectedOnlineConfig = OnlineStoreConfig{ + Type: "sqlite", + Path: DefaultOnlineStoreEphemeralPath, + } + Expect(repoConfig.OnlineStore).To(Equal(expectedOnlineConfig)) + Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) + + repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore) + Expect(err).NotTo(HaveOccurred()) + Expect(repoConfig.AuthConfig.Type).To(Equal(KubernetesAuthType)) + Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) + Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) + expectedRegistryConfig = RegistryConfig{ + RegistryType: "file", + Path: DefaultRegistryEphemeralPath, + } + Expect(repoConfig.Registry).To(Equal(expectedRegistryConfig)) + By("Having the all the db services") featureStore = minimalFeatureStore() featureStore.Spec.Services = &feastdevv1alpha1.FeatureStoreServices{ diff --git a/infra/feast-operator/internal/controller/services/services.go b/infra/feast-operator/internal/controller/services/services.go index 4667c8158ad..a627239a647 100644 --- a/infra/feast-operator/internal/controller/services/services.go +++ b/infra/feast-operator/internal/controller/services/services.go @@ -22,6 +22,7 @@ import ( "strings" feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -40,7 +41,7 @@ func (feast *FeastServices) Deploy() error { return err } - services := feast.FeatureStore.Status.Applied.Services + services := feast.Handler.FeatureStore.Status.Applied.Services if services != nil { if services.OfflineStore != nil { offlinePersistence := services.OfflineStore.Persistence @@ -171,12 +172,12 @@ func (feast *FeastServices) validateOfflineStorePersistence(offlinePersistence * } func (feast *FeastServices) deployFeastServiceByType(feastType FeastServiceType) error { - if pvcCreate, shouldCreate := shouldCreatePvc(feast.FeatureStore, feastType); shouldCreate { + if pvcCreate, shouldCreate := shouldCreatePvc(feast.Handler.FeatureStore, feastType); shouldCreate { if err := feast.createPVC(pvcCreate, feastType); err != nil { return feast.setFeastServiceCondition(err, feastType) } } else { - _ = feast.deleteOwnedFeastObj(feast.initPVC(feastType)) + _ = feast.Handler.DeleteOwnedFeastObj(feast.initPVC(feastType)) } if err := feast.createServiceAccount(feastType); err != nil { return feast.setFeastServiceCondition(err, feastType) @@ -191,26 +192,26 @@ func (feast *FeastServices) deployFeastServiceByType(feastType FeastServiceType) } func (feast *FeastServices) removeFeastServiceByType(feastType FeastServiceType) error { - if err := feast.deleteOwnedFeastObj(feast.initFeastSvc(feastType)); err != nil { + if err := feast.Handler.DeleteOwnedFeastObj(feast.initFeastSvc(feastType)); err != nil { return err } - if err := feast.deleteOwnedFeastObj(feast.initFeastDeploy(feastType)); err != nil { + if err := feast.Handler.DeleteOwnedFeastObj(feast.initFeastDeploy(feastType)); err != nil { return err } - if err := feast.deleteOwnedFeastObj(feast.initFeastSA(feastType)); err != nil { + if err := feast.Handler.DeleteOwnedFeastObj(feast.initFeastSA(feastType)); err != nil { return err } - if err := feast.deleteOwnedFeastObj(feast.initPVC(feastType)); err != nil { + if err := feast.Handler.DeleteOwnedFeastObj(feast.initPVC(feastType)); err != nil { return err } - apimeta.RemoveStatusCondition(&feast.FeatureStore.Status.Conditions, FeastServiceConditions[feastType][metav1.ConditionTrue].Type) + apimeta.RemoveStatusCondition(&feast.Handler.FeatureStore.Status.Conditions, FeastServiceConditions[feastType][metav1.ConditionTrue].Type) return nil } func (feast *FeastServices) createService(feastType FeastServiceType) error { - logger := log.FromContext(feast.Context) + logger := log.FromContext(feast.Handler.Context) svc := feast.initFeastSvc(feastType) - if op, err := controllerutil.CreateOrUpdate(feast.Context, feast.Client, svc, controllerutil.MutateFn(func() error { + if op, err := controllerutil.CreateOrUpdate(feast.Handler.Context, feast.Handler.Client, svc, controllerutil.MutateFn(func() error { return feast.setService(svc, feastType) })); err != nil { return err @@ -221,9 +222,9 @@ func (feast *FeastServices) createService(feastType FeastServiceType) error { } func (feast *FeastServices) createServiceAccount(feastType FeastServiceType) error { - logger := log.FromContext(feast.Context) + logger := log.FromContext(feast.Handler.Context) sa := feast.initFeastSA(feastType) - if op, err := controllerutil.CreateOrUpdate(feast.Context, feast.Client, sa, controllerutil.MutateFn(func() error { + if op, err := controllerutil.CreateOrUpdate(feast.Handler.Context, feast.Handler.Client, sa, controllerutil.MutateFn(func() error { return feast.setServiceAccount(sa, feastType) })); err != nil { return err @@ -234,9 +235,9 @@ func (feast *FeastServices) createServiceAccount(feastType FeastServiceType) err } func (feast *FeastServices) createDeployment(feastType FeastServiceType) error { - logger := log.FromContext(feast.Context) + logger := log.FromContext(feast.Handler.Context) deploy := feast.initFeastDeploy(feastType) - if op, err := controllerutil.CreateOrUpdate(feast.Context, feast.Client, deploy, controllerutil.MutateFn(func() error { + if op, err := controllerutil.CreateOrUpdate(feast.Handler.Context, feast.Handler.Client, deploy, controllerutil.MutateFn(func() error { return feast.setDeployment(deploy, feastType) })); err != nil { return err @@ -248,15 +249,15 @@ func (feast *FeastServices) createDeployment(feastType FeastServiceType) error { } func (feast *FeastServices) createPVC(pvcCreate *feastdevv1alpha1.PvcCreate, feastType FeastServiceType) error { - logger := log.FromContext(feast.Context) + logger := log.FromContext(feast.Handler.Context) pvc, err := feast.createNewPVC(pvcCreate, feastType) if err != nil { return err } - err = feast.Client.Get(feast.Context, client.ObjectKeyFromObject(pvc), pvc) + err = feast.Handler.Client.Get(feast.Handler.Context, client.ObjectKeyFromObject(pvc), pvc) if err != nil && apierrors.IsNotFound(err) { - err = feast.Client.Create(feast.Context, pvc) + err = feast.Handler.Client.Create(feast.Handler.Context, pvc) if err != nil { return err } @@ -329,11 +330,11 @@ func (feast *FeastServices) setDeployment(deploy *appsv1.Deployment, feastType F // configs are applied here container := &deploy.Spec.Template.Spec.Containers[0] applyOptionalContainerConfigs(container, serviceConfigs.OptionalConfigs) - if pvcConfig, hasPvcConfig := hasPvcConfig(feast.FeatureStore, feastType); hasPvcConfig { + if pvcConfig, hasPvcConfig := hasPvcConfig(feast.Handler.FeatureStore, feastType); hasPvcConfig { mountPvcConfig(&deploy.Spec.Template.Spec, pvcConfig, deploy.Name) } - return controllerutil.SetControllerReference(feast.FeatureStore, deploy, feast.Scheme) + return controllerutil.SetControllerReference(feast.Handler.FeatureStore, deploy, feast.Handler.Scheme) } func (feast *FeastServices) setService(svc *corev1.Service, feastType FeastServiceType) error { @@ -353,12 +354,12 @@ func (feast *FeastServices) setService(svc *corev1.Service, feastType FeastServi }, } - return controllerutil.SetControllerReference(feast.FeatureStore, svc, feast.Scheme) + return controllerutil.SetControllerReference(feast.Handler.FeatureStore, svc, feast.Handler.Scheme) } func (feast *FeastServices) setServiceAccount(sa *corev1.ServiceAccount, feastType FeastServiceType) error { sa.Labels = feast.getLabels(feastType) - return controllerutil.SetControllerReference(feast.FeatureStore, sa, feast.Scheme) + return controllerutil.SetControllerReference(feast.Handler.FeatureStore, sa, feast.Handler.Scheme) } func (feast *FeastServices) createNewPVC(pvcCreate *feastdevv1alpha1.PvcCreate, feastType FeastServiceType) (*corev1.PersistentVolumeClaim, error) { @@ -371,11 +372,11 @@ func (feast *FeastServices) createNewPVC(pvcCreate *feastdevv1alpha1.PvcCreate, if pvcCreate.StorageClassName != nil { pvc.Spec.StorageClassName = pvcCreate.StorageClassName } - return pvc, controllerutil.SetControllerReference(feast.FeatureStore, pvc, feast.Scheme) + return pvc, controllerutil.SetControllerReference(feast.Handler.FeatureStore, pvc, feast.Handler.Scheme) } func (feast *FeastServices) getServiceConfigs(feastType FeastServiceType) feastdevv1alpha1.ServiceConfigs { - appliedSpec := feast.FeatureStore.Status.Applied + appliedSpec := feast.Handler.FeatureStore.Status.Applied switch feastType { case OfflineFeastType: if appliedSpec.Services.OfflineStore != nil { @@ -397,41 +398,45 @@ func (feast *FeastServices) getServiceConfigs(feastType FeastServiceType) feastd // GetObjectMeta returns the feast k8s object metadata func (feast *FeastServices) GetObjectMeta(feastType FeastServiceType) metav1.ObjectMeta { - return metav1.ObjectMeta{Name: feast.GetFeastServiceName(feastType), Namespace: feast.FeatureStore.Namespace} + return metav1.ObjectMeta{Name: feast.GetFeastServiceName(feastType), Namespace: feast.Handler.FeatureStore.Namespace} } -// GetFeastServiceName returns the feast service object name based on service type func (feast *FeastServices) GetFeastServiceName(feastType FeastServiceType) string { - return feast.getFeastName() + "-" + string(feastType) + return GetFeastServiceName(feast.Handler.FeatureStore, feastType) +} + +// GetFeastServiceName returns the feast service object name based on service type +func GetFeastServiceName(featureStore *feastdevv1alpha1.FeatureStore, feastType FeastServiceType) string { + return GetFeastName(featureStore) + "-" + string(feastType) } -func (feast *FeastServices) getFeastName() string { - return FeastPrefix + feast.FeatureStore.Name +func GetFeastName(featureStore *feastdevv1alpha1.FeatureStore) string { + return handler.FeastPrefix + featureStore.Name } func (feast *FeastServices) getLabels(feastType FeastServiceType) map[string]string { return map[string]string{ - NameLabelKey: feast.FeatureStore.Name, + NameLabelKey: feast.Handler.FeatureStore.Name, ServiceTypeLabelKey: string(feastType), } } func (feast *FeastServices) setServiceHostnames() error { - feast.FeatureStore.Status.ServiceHostnames = feastdevv1alpha1.ServiceHostnames{} - services := feast.FeatureStore.Status.Applied.Services + feast.Handler.FeatureStore.Status.ServiceHostnames = feastdevv1alpha1.ServiceHostnames{} + services := feast.Handler.FeatureStore.Status.Applied.Services if services != nil { domain := svcDomain + ":" + strconv.Itoa(HttpPort) if services.OfflineStore != nil { objMeta := feast.GetObjectMeta(OfflineFeastType) - feast.FeatureStore.Status.ServiceHostnames.OfflineStore = objMeta.Name + "." + objMeta.Namespace + domain + feast.Handler.FeatureStore.Status.ServiceHostnames.OfflineStore = objMeta.Name + "." + objMeta.Namespace + domain } if services.OnlineStore != nil { objMeta := feast.GetObjectMeta(OnlineFeastType) - feast.FeatureStore.Status.ServiceHostnames.OnlineStore = objMeta.Name + "." + objMeta.Namespace + domain + feast.Handler.FeatureStore.Status.ServiceHostnames.OnlineStore = objMeta.Name + "." + objMeta.Namespace + domain } if feast.isLocalRegistry() { objMeta := feast.GetObjectMeta(RegistryFeastType) - feast.FeatureStore.Status.ServiceHostnames.Registry = objMeta.Name + "." + objMeta.Namespace + domain + feast.Handler.FeatureStore.Status.ServiceHostnames.Registry = objMeta.Name + "." + objMeta.Namespace + domain } else if feast.isRemoteRegistry() { return feast.setRemoteRegistryURL() } @@ -442,36 +447,36 @@ func (feast *FeastServices) setServiceHostnames() error { func (feast *FeastServices) setFeastServiceCondition(err error, feastType FeastServiceType) error { conditionMap := FeastServiceConditions[feastType] if err != nil { - logger := log.FromContext(feast.Context) + logger := log.FromContext(feast.Handler.Context) cond := conditionMap[metav1.ConditionFalse] cond.Message = "Error: " + err.Error() - apimeta.SetStatusCondition(&feast.FeatureStore.Status.Conditions, cond) + apimeta.SetStatusCondition(&feast.Handler.FeatureStore.Status.Conditions, cond) logger.Error(err, "Error deploying the FeatureStore "+string(ClientFeastType)+" service") return err } else { - apimeta.SetStatusCondition(&feast.FeatureStore.Status.Conditions, conditionMap[metav1.ConditionTrue]) + apimeta.SetStatusCondition(&feast.Handler.FeatureStore.Status.Conditions, conditionMap[metav1.ConditionTrue]) } return nil } func (feast *FeastServices) setRemoteRegistryURL() error { if feast.isRemoteHostnameRegistry() { - feast.FeatureStore.Status.ServiceHostnames.Registry = *feast.FeatureStore.Status.Applied.Services.Registry.Remote.Hostname + feast.Handler.FeatureStore.Status.ServiceHostnames.Registry = *feast.Handler.FeatureStore.Status.Applied.Services.Registry.Remote.Hostname } else if feast.IsRemoteRefRegistry() { - feastRemoteRef := feast.FeatureStore.Status.Applied.Services.Registry.Remote.FeastRef + feastRemoteRef := feast.Handler.FeatureStore.Status.Applied.Services.Registry.Remote.FeastRef // default to FeatureStore namespace if not set if len(feastRemoteRef.Namespace) == 0 { - feastRemoteRef.Namespace = feast.FeatureStore.Namespace + feastRemoteRef.Namespace = feast.Handler.FeatureStore.Namespace } nsName := types.NamespacedName{Name: feastRemoteRef.Name, Namespace: feastRemoteRef.Namespace} - crNsName := client.ObjectKeyFromObject(feast.FeatureStore) + crNsName := client.ObjectKeyFromObject(feast.Handler.FeatureStore) if nsName == crNsName { return errors.New("FeatureStore '" + crNsName.Name + "' can't reference itself in `spec.services.registry.remote.feastRef`") } remoteFeastObj := &feastdevv1alpha1.FeatureStore{} - if err := feast.Client.Get(feast.Context, nsName, remoteFeastObj); err != nil { + if err := feast.Handler.Client.Get(feast.Handler.Context, nsName, remoteFeastObj); err != nil { if apierrors.IsNotFound(err) { return errors.New("Referenced FeatureStore '" + feastRemoteRef.Name + "' was not found") } @@ -479,14 +484,16 @@ func (feast *FeastServices) setRemoteRegistryURL() error { } remoteFeast := FeastServices{ - Client: feast.Client, - Context: feast.Context, - FeatureStore: remoteFeastObj, - Scheme: feast.Scheme, + Handler: handler.FeastHandler{ + Client: feast.Handler.Client, + Context: feast.Handler.Context, + FeatureStore: remoteFeastObj, + Scheme: feast.Handler.Scheme, + }, } // referenced/remote registry must use the local install option and be in a 'Ready' state. if remoteFeast.isLocalRegistry() && apimeta.IsStatusConditionTrue(remoteFeastObj.Status.Conditions, feastdevv1alpha1.RegistryReadyType) { - feast.FeatureStore.Status.ServiceHostnames.Registry = remoteFeastObj.Status.ServiceHostnames.Registry + feast.Handler.FeatureStore.Status.ServiceHostnames.Registry = remoteFeastObj.Status.ServiceHostnames.Registry } else { return errors.New("Remote feast registry of referenced FeatureStore '" + feastRemoteRef.Name + "' is not ready") } @@ -495,17 +502,17 @@ func (feast *FeastServices) setRemoteRegistryURL() error { } func (feast *FeastServices) isLocalRegistry() bool { - return isLocalRegistry(feast.FeatureStore) + return isLocalRegistry(feast.Handler.FeatureStore) } func (feast *FeastServices) isRemoteRegistry() bool { - appliedServices := feast.FeatureStore.Status.Applied.Services + appliedServices := feast.Handler.FeatureStore.Status.Applied.Services return appliedServices != nil && appliedServices.Registry != nil && appliedServices.Registry.Remote != nil } func (feast *FeastServices) IsRemoteRefRegistry() bool { if feast.isRemoteRegistry() { - remote := feast.FeatureStore.Status.Applied.Services.Registry.Remote + remote := feast.Handler.FeatureStore.Status.Applied.Services.Registry.Remote return remote != nil && remote.FeastRef != nil } return false @@ -513,7 +520,7 @@ func (feast *FeastServices) IsRemoteRefRegistry() bool { func (feast *FeastServices) isRemoteHostnameRegistry() bool { if feast.isRemoteRegistry() { - remote := feast.FeatureStore.Status.Applied.Services.Registry.Remote + remote := feast.Handler.FeatureStore.Status.Applied.Services.Registry.Remote return remote != nil && remote.Hostname != nil } return false @@ -551,27 +558,6 @@ func (feast *FeastServices) initPVC(feastType FeastServiceType) *corev1.Persiste return pvc } -// delete an object if the FeatureStore is set as the object's controller/owner -func (feast *FeastServices) deleteOwnedFeastObj(obj client.Object) error { - name := obj.GetName() - kind := obj.GetObjectKind().GroupVersionKind().Kind - if err := feast.Client.Get(feast.Context, client.ObjectKeyFromObject(obj), obj); err != nil { - if apierrors.IsNotFound(err) { - return nil - } - return err - } - for _, ref := range obj.GetOwnerReferences() { - if *ref.Controller && ref.UID == feast.FeatureStore.UID { - if err := feast.Client.Delete(feast.Context, obj); err != nil { - return err - } - log.FromContext(feast.Context).Info("Successfully deleted", kind, name) - } - } - return nil -} - func applyOptionalContainerConfigs(container *corev1.Container, optionalConfigs feastdevv1alpha1.OptionalConfigs) { if optionalConfigs.Env != nil { container.Env = mergeEnvVarsArrays(container.Env, optionalConfigs.Env) diff --git a/infra/feast-operator/internal/controller/services/services_types.go b/infra/feast-operator/internal/controller/services/services_types.go index 65bc92aabe2..5b7625fa554 100644 --- a/infra/feast-operator/internal/controller/services/services_types.go +++ b/infra/feast-operator/internal/controller/services/services_types.go @@ -17,17 +17,13 @@ limitations under the License. package services import ( - "context" - "github.com/feast-dev/feast/infra/feast-operator/api/feastversion" feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + handler "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" ) const ( - FeastPrefix = "feast-" FeatureStoreYamlEnvVar = "FEATURE_STORE_YAML_BASE64" FeatureStoreYamlCmKey = "feature_store.yaml" DefaultRegistryEphemeralPath = "/tmp/registry.db" @@ -164,10 +160,7 @@ type FeastProviderType string // FeastServices is an interface for configuring and deploying feast services type FeastServices struct { - client.Client - Context context.Context - Scheme *runtime.Scheme - FeatureStore *feastdevv1alpha1.FeatureStore + Handler handler.FeastHandler } // RepoConfig is the Repo config. Typically loaded from feature_store.yaml. diff --git a/infra/feast-operator/internal/controller/services/util.go b/infra/feast-operator/internal/controller/services/util.go index 5e2daee6738..7f9142b2242 100644 --- a/infra/feast-operator/internal/controller/services/util.go +++ b/infra/feast-operator/internal/controller/services/util.go @@ -52,6 +52,14 @@ func shouldCreatePvc(featureStore *feastdevv1alpha1.FeatureStore, feastType Feas func ApplyDefaultsToStatus(cr *feastdevv1alpha1.FeatureStore) { cr.Status.FeastVersion = feastversion.FeastVersion applied := cr.Spec.DeepCopy() + + if applied.AuthConfig == nil { + applied.AuthConfig = &feastdevv1alpha1.AuthConfig{} + } + if applied.AuthConfig.KubernetesAuth != nil && applied.AuthConfig.KubernetesAuth.Roles == nil { + applied.AuthConfig.KubernetesAuth.Roles = []string{} + } + if applied.Services == nil { applied.Services = &feastdevv1alpha1.FeatureStoreServices{} } diff --git a/infra/feast-operator/test/api/featurestore_types_test.go b/infra/feast-operator/test/api/featurestore_types_test.go index 16af55b03be..bb3b315912e 100644 --- a/infra/feast-operator/test/api/featurestore_types_test.go +++ b/infra/feast-operator/test/api/featurestore_types_test.go @@ -377,5 +377,11 @@ var _ = Describe("FeatureStore API", func() { storage = resource.Status.Applied.Services.Registry.Local.Persistence.FilePersistence.PvcConfig.Create.Resources.Requests.Storage().String() Expect(storage).To(Equal("500Mi")) }) + It("should set the default AuthConfig", func() { + resource := featurestore + services.ApplyDefaultsToStatus(resource) + Expect(resource.Status.Applied.AuthConfig).ToNot(BeNil()) + Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) + }) }) }) From 17e6b43de069b39bae54d00b3fb7e68953e105ae Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Fri, 22 Nov 2024 22:29:06 +0100 Subject: [PATCH 03/11] no private image Signed-off-by: Daniele Martinoli --- .../samples/v1alpha1_featurestore_ephemeral_persistence.yaml | 3 --- .../config/samples/v1alpha1_featurestore_kubernetes_auth.yaml | 3 --- .../samples/v1alpha1_featurestore_objectstore_persistence.yaml | 3 --- .../config/samples/v1alpha1_featurestore_pvc_persistence.yaml | 3 --- 4 files changed, 12 deletions(-) diff --git a/infra/feast-operator/config/samples/v1alpha1_featurestore_ephemeral_persistence.yaml b/infra/feast-operator/config/samples/v1alpha1_featurestore_ephemeral_persistence.yaml index 8449ec1bf51..512fed9d4c0 100644 --- a/infra/feast-operator/config/samples/v1alpha1_featurestore_ephemeral_persistence.yaml +++ b/infra/feast-operator/config/samples/v1alpha1_featurestore_ephemeral_persistence.yaml @@ -6,18 +6,15 @@ spec: feastProject: my_project services: onlineStore: - image: quay.io/dmartino/feature-server:0.2 persistence: file: path: /data/online_store.db offlineStore: - image: quay.io/dmartino/feature-server:0.2 persistence: file: type: dask registry: local: - image: quay.io/dmartino/feature-server:0.2 persistence: file: path: /data/registry.db diff --git a/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml b/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml index add9c4431fb..17b40e51eea 100644 --- a/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml +++ b/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml @@ -6,18 +6,15 @@ spec: feastProject: my_project services: onlineStore: - image: quay.io/dmartino/feature-server:0.3 persistence: file: path: /data/online_store.db offlineStore: - image: quay.io/dmartino/feature-server:0.3 persistence: file: type: dask registry: local: - image: quay.io/dmartino/feature-server:0.3 persistence: file: path: /data/registry.db diff --git a/infra/feast-operator/config/samples/v1alpha1_featurestore_objectstore_persistence.yaml b/infra/feast-operator/config/samples/v1alpha1_featurestore_objectstore_persistence.yaml index e038242da4c..45f12a67a18 100644 --- a/infra/feast-operator/config/samples/v1alpha1_featurestore_objectstore_persistence.yaml +++ b/infra/feast-operator/config/samples/v1alpha1_featurestore_objectstore_persistence.yaml @@ -6,18 +6,15 @@ spec: feastProject: my_project services: onlineStore: - image: quay.io/dmartino/feature-server:0.2 persistence: file: path: /data/online_store.db offlineStore: - image: quay.io/dmartino/feature-server:0.2 persistence: file: type: dask registry: local: - image: quay.io/dmartino/feature-server:0.2 persistence: file: path: s3://bucket/registry.db diff --git a/infra/feast-operator/config/samples/v1alpha1_featurestore_pvc_persistence.yaml b/infra/feast-operator/config/samples/v1alpha1_featurestore_pvc_persistence.yaml index fe2a5071cda..b7c7412c0f0 100644 --- a/infra/feast-operator/config/samples/v1alpha1_featurestore_pvc_persistence.yaml +++ b/infra/feast-operator/config/samples/v1alpha1_featurestore_pvc_persistence.yaml @@ -6,7 +6,6 @@ spec: feastProject: my_project services: onlineStore: - image: quay.io/dmartino/feature-server:0.2 persistence: file: path: online_store.db @@ -15,7 +14,6 @@ spec: name: online-pvc mountPath: /data/online offlineStore: - image: quay.io/dmartino/feature-server:0.2 persistence: file: type: duckdb @@ -28,7 +26,6 @@ spec: mountPath: /data/offline registry: local: - image: quay.io/dmartino/feature-server:0.2 persistence: file: path: registry.db From 0817c9c4a02cad6b593bf99d76c9e29d8494b5f5 Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Mon, 25 Nov 2024 09:24:29 +0100 Subject: [PATCH 04/11] removed log-level Signed-off-by: Daniele Martinoli --- .../internal/controller/services/services_types.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/infra/feast-operator/internal/controller/services/services_types.go b/infra/feast-operator/internal/controller/services/services_types.go index 5b7625fa554..a6c91dff03a 100644 --- a/infra/feast-operator/internal/controller/services/services_types.go +++ b/infra/feast-operator/internal/controller/services/services_types.go @@ -71,15 +71,15 @@ var ( FeastServiceConstants = map[FeastServiceType]deploymentSettings{ OfflineFeastType: { - Command: []string{"feast", "--log-level", "DEBUG", "serve_offline", "-h", "0.0.0.0"}, + Command: []string{"feast", "serve_offline", "-h", "0.0.0.0"}, TargetPort: 8815, }, OnlineFeastType: { - Command: []string{"feast", "--log-level", "DEBUG", "serve", "-h", "0.0.0.0"}, + Command: []string{"feast", "serve", "-h", "0.0.0.0"}, TargetPort: 6566, }, RegistryFeastType: { - Command: []string{"feast", "--log-level", "DEBUG", "serve_registry"}, + Command: []string{"feast", "serve_registry"}, TargetPort: 6570, }, } From 62861c636c90d2b03ff0cf6994381b86597fab6f Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Mon, 25 Nov 2024 09:32:22 +0100 Subject: [PATCH 05/11] no empty list for default Role Signed-off-by: Daniele Martinoli --- infra/feast-operator/internal/controller/services/util.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/infra/feast-operator/internal/controller/services/util.go b/infra/feast-operator/internal/controller/services/util.go index 7f9142b2242..a0ad628ee64 100644 --- a/infra/feast-operator/internal/controller/services/util.go +++ b/infra/feast-operator/internal/controller/services/util.go @@ -56,9 +56,6 @@ func ApplyDefaultsToStatus(cr *feastdevv1alpha1.FeatureStore) { if applied.AuthConfig == nil { applied.AuthConfig = &feastdevv1alpha1.AuthConfig{} } - if applied.AuthConfig.KubernetesAuth != nil && applied.AuthConfig.KubernetesAuth.Roles == nil { - applied.AuthConfig.KubernetesAuth.Roles = []string{} - } if applied.Services == nil { applied.Services = &feastdevv1alpha1.FeatureStoreServices{} From a4c9378fedb7f68152f79f143b004a119db089af Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Mon, 25 Nov 2024 09:41:06 +0100 Subject: [PATCH 06/11] removed nameLabelKey, using serices.NameLabelKey Signed-off-by: Daniele Martinoli --- infra/feast-operator/internal/controller/auth/auth.go | 4 ++-- infra/feast-operator/internal/controller/auth/auth_types.go | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/infra/feast-operator/internal/controller/auth/auth.go b/infra/feast-operator/internal/controller/auth/auth.go index 3be0349f97f..bdc64c06f13 100644 --- a/infra/feast-operator/internal/controller/auth/auth.go +++ b/infra/feast-operator/internal/controller/auth/auth.go @@ -15,7 +15,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) -// Deploy the feast services +// Deploy the feast authorization func (auth *FeastAuth) Deploy() error { authConfig := auth.Handler.FeatureStore.Status.Applied.AuthConfig if authConfig != nil { @@ -193,7 +193,7 @@ func (auth *FeastAuth) setAuthRole(role *rbacv1.Role) error { func (auth *FeastAuth) getLabels() map[string]string { return map[string]string{ - nameLabelKey: auth.Handler.FeatureStore.Name, + services.NameLabelKey: auth.Handler.FeatureStore.Name, } } diff --git a/infra/feast-operator/internal/controller/auth/auth_types.go b/infra/feast-operator/internal/controller/auth/auth_types.go index c51025abeba..d3ee4086821 100644 --- a/infra/feast-operator/internal/controller/auth/auth_types.go +++ b/infra/feast-operator/internal/controller/auth/auth_types.go @@ -12,8 +12,6 @@ type FeastAuth struct { } var ( - nameLabelKey = feastdevv1alpha1.GroupVersion.Group + "/name" - feastKubernetesAuthConditions = map[metav1.ConditionStatus]metav1.Condition{ metav1.ConditionTrue: { Type: feastdevv1alpha1.KubernetesAuthReadyType, From c9a583c1cd1c894beb4eac822a6795cf35b28d9b Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Mon, 25 Nov 2024 16:27:58 +0100 Subject: [PATCH 07/11] improved CRD comments and using IsLocalRegistry Signed-off-by: Daniele Martinoli --- .../api/v1alpha1/featurestore_types.go | 8 ++++++++ .../crd/bases/feast.dev_featurestores.yaml | 20 +++++++++++++++++++ infra/feast-operator/dist/install.yaml | 18 +++++++++++++++++ .../internal/controller/auth/auth.go | 2 +- .../controller/services/repo_config.go | 2 +- .../internal/controller/services/services.go | 2 +- .../internal/controller/services/util.go | 4 ++-- 7 files changed, 51 insertions(+), 5 deletions(-) diff --git a/infra/feast-operator/api/v1alpha1/featurestore_types.go b/infra/feast-operator/api/v1alpha1/featurestore_types.go index dea0b409a3c..247a346dedf 100644 --- a/infra/feast-operator/api/v1alpha1/featurestore_types.go +++ b/infra/feast-operator/api/v1alpha1/featurestore_types.go @@ -272,7 +272,15 @@ type AuthConfig struct { KubernetesAuth *KubernetesAuth `json:"kubernetes,omitempty"` } +// KubernetesAuth provides a way to define the authorization settings using Kubernetes RBAC resources. +// https://kubernetes.io/docs/reference/access-authn-authz/rbac/ type KubernetesAuth struct { + // The Kubernetes RBAC roles to be deployed in the same namespace of the FeatureStore. + // Roles are managed by the operator and created with an empty list of rules. + // See the Feast permission model at https://docs.feast.dev/getting-started/concepts/permission + // The feature store admin is not obligated to manage roles using the Feast operator, roles can be managed independently. + // This configuration option is only providing a way to automate this procedure. + // Important note: the operator cannot ensure that these roles will match the ones used in the configured Feast permissions. Roles []string `json:"roles,omitempty"` } 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 1318962d222..9f6debcad44 100644 --- a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +++ b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml @@ -53,8 +53,18 @@ spec: deployed Feast services. properties: kubernetes: + description: |- + KubernetesAuth provides a way to define the authorization settings using Kubernetes RBAC resources. + https://kubernetes.io/docs/reference/access-authn-authz/rbac/ properties: roles: + description: |- + The Kubernetes RBAC roles to be deployed in the same namespace of the FeatureStore. + Roles are managed by the operator and created with an empty list of rules. + See the Feast permission model at https://docs.feast.dev/getting-started/concepts/permission + The feature store admin is not obligated to manage roles using the Feast operator, roles can be managed independently. + This configuration option is only providing a way to automate this procedure. + Important note: the operator cannot ensure that these roles will match the ones used in the configured Feast permissions. items: type: string type: array @@ -1056,8 +1066,18 @@ spec: the deployed Feast services. properties: kubernetes: + description: |- + KubernetesAuth provides a way to define the authorization settings using Kubernetes RBAC resources. + https://kubernetes.io/docs/reference/access-authn-authz/rbac/ properties: roles: + description: |- + The Kubernetes RBAC roles to be deployed in the same namespace of the FeatureStore. + Roles are managed by the operator and created with an empty list of rules. + See the Feast permission model at https://docs.feast.dev/getting-started/concepts/permission + The feature store admin is not obligated to manage roles using the Feast operator, roles can be managed independently. + This configuration option is only providing a way to automate this procedure. + Important note: the operator cannot ensure that these roles will match the ones used in the configured Feast permissions. items: type: string type: array diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index 78cfd10d135..4a6d75ae1ff 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -61,8 +61,17 @@ spec: deployed Feast services. properties: kubernetes: + description: |- + KubernetesAuth defines the authorization settings using Kubernetes RBAC. + https://kubernetes.io/docs/reference/access-authn-authz/rbac/ properties: roles: + description: |- + The Kubernetes RBAC roles to be deployed in the same namespace of the FeatureStore. + See the Feast permission model https://docs.feast.dev/getting-started/concepts/permission + Please note that the feature store admin is not obligated to manage roles using the Feast operator. + Roles can be managed independently. This configuration is only providing a way to automate this step. + Note that the operator cannot ensure that these roles will match the ones used in the configured Feast permissions. items: type: string type: array @@ -1064,8 +1073,17 @@ spec: the deployed Feast services. properties: kubernetes: + description: |- + KubernetesAuth defines the authorization settings using Kubernetes RBAC. + https://kubernetes.io/docs/reference/access-authn-authz/rbac/ properties: roles: + description: |- + The Kubernetes RBAC roles to be deployed in the same namespace of the FeatureStore. + See the Feast permission model https://docs.feast.dev/getting-started/concepts/permission + Please note that the feature store admin is not obligated to manage roles using the Feast operator. + Roles can be managed independently. This configuration is only providing a way to automate this step. + Note that the operator cannot ensure that these roles will match the ones used in the configured Feast permissions. items: type: string type: array diff --git a/infra/feast-operator/internal/controller/auth/auth.go b/infra/feast-operator/internal/controller/auth/auth.go index bdc64c06f13..9db7d7a9c95 100644 --- a/infra/feast-operator/internal/controller/auth/auth.go +++ b/infra/feast-operator/internal/controller/auth/auth.go @@ -146,7 +146,7 @@ func (auth *FeastAuth) setFeastRoleBinding(roleBinding *rbacv1.RoleBinding) erro Namespace: auth.Handler.FeatureStore.Namespace, }) } - if auth.Handler.FeatureStore.Status.Applied.Services.Registry != nil { + if services.IsLocalRegistry(auth.Handler.FeatureStore) { roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{ Kind: rbacv1.ServiceAccountKind, Name: services.GetFeastServiceName(auth.Handler.FeatureStore, services.RegistryFeastType), diff --git a/infra/feast-operator/internal/controller/services/repo_config.go b/infra/feast-operator/internal/controller/services/repo_config.go index 23bbaed7f93..55ee6d9817a 100644 --- a/infra/feast-operator/internal/controller/services/repo_config.go +++ b/infra/feast-operator/internal/controller/services/repo_config.go @@ -52,7 +52,7 @@ func getServiceRepoConfig(feastType FeastServiceType, featureStore *feastdevv1al appliedSpec := featureStore.Status.Applied repoConfig := getClientRepoConfig(featureStore) - isLocalReg := isLocalRegistry(featureStore) + isLocalRegistry := IsLocalRegistry(featureStore) if appliedSpec.Services != nil { services := appliedSpec.Services diff --git a/infra/feast-operator/internal/controller/services/services.go b/infra/feast-operator/internal/controller/services/services.go index a627239a647..01fc401b35d 100644 --- a/infra/feast-operator/internal/controller/services/services.go +++ b/infra/feast-operator/internal/controller/services/services.go @@ -502,7 +502,7 @@ func (feast *FeastServices) setRemoteRegistryURL() error { } func (feast *FeastServices) isLocalRegistry() bool { - return isLocalRegistry(feast.Handler.FeatureStore) + return IsLocalRegistry(feast.Handler.FeatureStore) } func (feast *FeastServices) isRemoteRegistry() bool { diff --git a/infra/feast-operator/internal/controller/services/util.go b/infra/feast-operator/internal/controller/services/util.go index a0ad628ee64..0723013c212 100644 --- a/infra/feast-operator/internal/controller/services/util.go +++ b/infra/feast-operator/internal/controller/services/util.go @@ -17,7 +17,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) -func isLocalRegistry(featureStore *feastdevv1alpha1.FeatureStore) bool { +func IsLocalRegistry(featureStore *feastdevv1alpha1.FeatureStore) bool { appliedServices := featureStore.Status.Applied.Services return appliedServices != nil && appliedServices.Registry != nil && appliedServices.Registry.Local != nil } @@ -35,7 +35,7 @@ func hasPvcConfig(featureStore *feastdevv1alpha1.FeatureStore, feastType FeastSe pvcConfig = services.OfflineStore.Persistence.FilePersistence.PvcConfig } case RegistryFeastType: - if isLocalRegistry(featureStore) && services.Registry.Local.Persistence.FilePersistence != nil { + if IsLocalRegistry(featureStore) && services.Registry.Local.Persistence.FilePersistence != nil { pvcConfig = services.Registry.Local.Persistence.FilePersistence.PvcConfig } } From f97ab65eceab79b7071c87cd812b03b28c609606 Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Mon, 25 Nov 2024 16:33:29 +0100 Subject: [PATCH 08/11] fixing generated code Signed-off-by: Daniele Martinoli --- infra/feast-operator/dist/install.yaml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index 4a6d75ae1ff..6c266cf4863 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -62,16 +62,17 @@ spec: properties: kubernetes: description: |- - KubernetesAuth defines the authorization settings using Kubernetes RBAC. + KubernetesAuth provides a way to define the authorization settings using Kubernetes RBAC resources. https://kubernetes.io/docs/reference/access-authn-authz/rbac/ properties: roles: description: |- The Kubernetes RBAC roles to be deployed in the same namespace of the FeatureStore. - See the Feast permission model https://docs.feast.dev/getting-started/concepts/permission - Please note that the feature store admin is not obligated to manage roles using the Feast operator. - Roles can be managed independently. This configuration is only providing a way to automate this step. - Note that the operator cannot ensure that these roles will match the ones used in the configured Feast permissions. + Roles are managed by the operator and created with an empty list of rules. + See the Feast permission model at https://docs.feast.dev/getting-started/concepts/permission + The feature store admin is not obligated to manage roles using the Feast operator, roles can be managed independently. + This configuration option is only providing a way to automate this procedure. + Important note: the operator cannot ensure that these roles will match the ones used in the configured Feast permissions. items: type: string type: array @@ -1074,16 +1075,17 @@ spec: properties: kubernetes: description: |- - KubernetesAuth defines the authorization settings using Kubernetes RBAC. + KubernetesAuth provides a way to define the authorization settings using Kubernetes RBAC resources. https://kubernetes.io/docs/reference/access-authn-authz/rbac/ properties: roles: description: |- The Kubernetes RBAC roles to be deployed in the same namespace of the FeatureStore. - See the Feast permission model https://docs.feast.dev/getting-started/concepts/permission - Please note that the feature store admin is not obligated to manage roles using the Feast operator. - Roles can be managed independently. This configuration is only providing a way to automate this step. - Note that the operator cannot ensure that these roles will match the ones used in the configured Feast permissions. + Roles are managed by the operator and created with an empty list of rules. + See the Feast permission model at https://docs.feast.dev/getting-started/concepts/permission + The feature store admin is not obligated to manage roles using the Feast operator, roles can be managed independently. + This configuration option is only providing a way to automate this procedure. + Important note: the operator cannot ensure that these roles will match the ones used in the configured Feast permissions. items: type: string type: array From 2f7bf155f65c4fc657d245920c5819d1d320b870 Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Tue, 26 Nov 2024 21:07:44 +0100 Subject: [PATCH 09/11] renamed auth condition and types Signed-off-by: Daniele Martinoli --- .../api/v1alpha1/featurestore_types.go | 18 +- .../api/v1alpha1/zz_generated.deepcopy.go | 14 +- .../crd/bases/feast.dev_featurestores.yaml | 8 +- ...v1alpha1_featurestore_kubernetes_auth.yaml | 2 +- infra/feast-operator/dist/install.yaml | 8 +- .../internal/controller/auth/auth.go | 220 ------------------ .../internal/controller/authz/authz.go | 220 ++++++++++++++++++ .../auth_types.go => authz/authz_types.go} | 10 +- .../controller/featurestore_controller.go | 6 +- .../featurestore_controller_ephemeral_test.go | 18 +- ...restore_controller_kubernetes_auth_test.go | 32 +-- ...eaturestore_controller_objectstore_test.go | 8 +- .../featurestore_controller_pvc_test.go | 18 +- .../featurestore_controller_test.go | 42 ++-- .../controller/services/repo_config.go | 8 +- .../controller/services/repo_config_test.go | 34 +-- .../controller/services/services_types.go | 6 +- .../internal/controller/services/util.go | 4 +- .../test/api/featurestore_types_test.go | 6 +- 19 files changed, 341 insertions(+), 341 deletions(-) delete mode 100644 infra/feast-operator/internal/controller/auth/auth.go create mode 100644 infra/feast-operator/internal/controller/authz/authz.go rename infra/feast-operator/internal/controller/{auth/auth_types.go => authz/authz_types.go} (73%) diff --git a/infra/feast-operator/api/v1alpha1/featurestore_types.go b/infra/feast-operator/api/v1alpha1/featurestore_types.go index 247a346dedf..d2d7c9f8d4d 100644 --- a/infra/feast-operator/api/v1alpha1/featurestore_types.go +++ b/infra/feast-operator/api/v1alpha1/featurestore_types.go @@ -28,12 +28,12 @@ const ( FailedPhase = "Failed" // Feast condition types: - ClientReadyType = "Client" - OfflineStoreReadyType = "OfflineStore" - OnlineStoreReadyType = "OnlineStore" - RegistryReadyType = "Registry" - ReadyType = "FeatureStore" - KubernetesAuthReadyType = "KubernetesAuth" + ClientReadyType = "Client" + OfflineStoreReadyType = "OfflineStore" + OnlineStoreReadyType = "OnlineStore" + RegistryReadyType = "Registry" + ReadyType = "FeatureStore" + AuthorizationReadyType = "AuthorizationReadyType" // Feast condition reasons: ReadyReason = "Ready" @@ -62,7 +62,7 @@ type FeatureStoreSpec struct { // FeastProject is the Feast project id. This can be any alphanumeric string with underscores, but it cannot start with an underscore. Required. FeastProject string `json:"feastProject"` Services *FeatureStoreServices `json:"services,omitempty"` - AuthConfig *AuthConfig `json:"auth,omitempty"` + AuthzConfig *AuthzConfig `json:"authz,omitempty"` } // FeatureStoreServices defines the desired feast service deployments. ephemeral registry is deployed by default. @@ -267,8 +267,8 @@ type OptionalConfigs struct { Resources *corev1.ResourceRequirements `json:"resources,omitempty"` } -// AuthConfig defines the authorization settings for the deployed Feast services. -type AuthConfig struct { +// AuthzConfig defines the authorization settings for the deployed Feast services. +type AuthzConfig struct { KubernetesAuth *KubernetesAuth `json:"kubernetes,omitempty"` } diff --git a/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go b/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go index b399128f646..2007e681489 100644 --- a/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -27,7 +27,7 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AuthConfig) DeepCopyInto(out *AuthConfig) { +func (in *AuthzConfig) DeepCopyInto(out *AuthzConfig) { *out = *in if in.KubernetesAuth != nil { in, out := &in.KubernetesAuth, &out.KubernetesAuth @@ -36,12 +36,12 @@ func (in *AuthConfig) DeepCopyInto(out *AuthConfig) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthConfig. -func (in *AuthConfig) DeepCopy() *AuthConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthzConfig. +func (in *AuthzConfig) DeepCopy() *AuthzConfig { if in == nil { return nil } - out := new(AuthConfig) + out := new(AuthzConfig) in.DeepCopyInto(out) return out } @@ -178,9 +178,9 @@ func (in *FeatureStoreSpec) DeepCopyInto(out *FeatureStoreSpec) { *out = new(FeatureStoreServices) (*in).DeepCopyInto(*out) } - if in.AuthConfig != nil { - in, out := &in.AuthConfig, &out.AuthConfig - *out = new(AuthConfig) + if in.AuthzConfig != nil { + in, out := &in.AuthzConfig, &out.AuthzConfig + *out = new(AuthzConfig) (*in).DeepCopyInto(*out) } } 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 9f6debcad44..f7098cb3e73 100644 --- a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +++ b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml @@ -48,8 +48,8 @@ spec: spec: description: FeatureStoreSpec defines the desired state of FeatureStore properties: - auth: - description: AuthConfig defines the authorization settings for the + authz: + description: AuthzConfig defines the authorization settings for the deployed Feast services. properties: kubernetes: @@ -1061,8 +1061,8 @@ spec: description: Shows the currently applied feast configuration, including any pertinent defaults properties: - auth: - description: AuthConfig defines the authorization settings for + authz: + description: AuthzConfig defines the authorization settings for the deployed Feast services. properties: kubernetes: diff --git a/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml b/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml index 17b40e51eea..ed95b41cf47 100644 --- a/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml +++ b/infra/feast-operator/config/samples/v1alpha1_featurestore_kubernetes_auth.yaml @@ -18,7 +18,7 @@ spec: persistence: file: path: /data/registry.db - auth: + authz: kubernetes: roles: - reader diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index 6c266cf4863..8d40c925291 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -56,8 +56,8 @@ spec: spec: description: FeatureStoreSpec defines the desired state of FeatureStore properties: - auth: - description: AuthConfig defines the authorization settings for the + authz: + description: AuthzConfig defines the authorization settings for the deployed Feast services. properties: kubernetes: @@ -1069,8 +1069,8 @@ spec: description: Shows the currently applied feast configuration, including any pertinent defaults properties: - auth: - description: AuthConfig defines the authorization settings for + authz: + description: AuthzConfig defines the authorization settings for the deployed Feast services. properties: kubernetes: diff --git a/infra/feast-operator/internal/controller/auth/auth.go b/infra/feast-operator/internal/controller/auth/auth.go deleted file mode 100644 index 9db7d7a9c95..00000000000 --- a/infra/feast-operator/internal/controller/auth/auth.go +++ /dev/null @@ -1,220 +0,0 @@ -package auth - -import ( - "context" - "slices" - - feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" - "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" - rbacv1 "k8s.io/api/rbac/v1" - apimeta "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/log" -) - -// Deploy the feast authorization -func (auth *FeastAuth) Deploy() error { - authConfig := auth.Handler.FeatureStore.Status.Applied.AuthConfig - if authConfig != nil { - if authConfig.KubernetesAuth != nil { - if err := auth.deployKubernetesAuth(authConfig.KubernetesAuth); err != nil { - return err - } - } else { - auth.removeOrphanedRoles() - _ = auth.Handler.DeleteOwnedFeastObj(auth.initFeastRole()) - _ = auth.Handler.DeleteOwnedFeastObj(auth.initFeastRoleBinding()) - } - } - return nil -} - -func (auth *FeastAuth) deployKubernetesAuth(kubernetesAuth *feastdevv1alpha1.KubernetesAuth) error { - auth.removeOrphanedRoles() - - if err := auth.createFeastRole(); err != nil { - return auth.setFeastKubernetesAuthCondition(err) - } - if err := auth.createFeastRoleBinding(); err != nil { - return auth.setFeastKubernetesAuthCondition(err) - } - - for _, roleName := range kubernetesAuth.Roles { - if err := auth.createAuthRole(roleName); err != nil { - return auth.setFeastKubernetesAuthCondition(err) - } - } - return auth.setFeastKubernetesAuthCondition(nil) -} - -func (auth *FeastAuth) removeOrphanedRoles() { - roleList := &rbacv1.RoleList{} - err := auth.Handler.Client.List(context.TODO(), roleList, &client.ListOptions{ - Namespace: auth.Handler.FeatureStore.Namespace, - LabelSelector: labels.SelectorFromSet(auth.getLabels()), - }) - if err != nil { - return - } - - desiredRoles := []string{} - if auth.Handler.FeatureStore.Status.Applied.AuthConfig.KubernetesAuth != nil { - desiredRoles = auth.Handler.FeatureStore.Status.Applied.AuthConfig.KubernetesAuth.Roles - } - for _, role := range roleList.Items { - roleName := role.Name - if roleName != auth.getFeastRoleName() && !slices.Contains(desiredRoles, roleName) { - _ = auth.Handler.DeleteOwnedFeastObj(auth.initAuthRole(roleName)) - } - } -} - -func (auth *FeastAuth) createFeastRole() error { - logger := log.FromContext(auth.Handler.Context) - role := auth.initFeastRole() - if op, err := controllerutil.CreateOrUpdate(auth.Handler.Context, auth.Handler.Client, role, controllerutil.MutateFn(func() error { - return auth.setFeastRole(role) - })); err != nil { - return err - } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { - logger.Info("Successfully reconciled", "Role", role.Name, "operation", op) - } - - return nil -} - -func (auth *FeastAuth) initFeastRole() *rbacv1.Role { - role := &rbacv1.Role{ - ObjectMeta: metav1.ObjectMeta{Name: auth.getFeastRoleName(), Namespace: auth.Handler.FeatureStore.Namespace}, - } - role.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("Role")) - return role -} - -func (auth *FeastAuth) setFeastRole(role *rbacv1.Role) error { - role.Labels = auth.getLabels() - role.Rules = []rbacv1.PolicyRule{ - { - APIGroups: []string{rbacv1.GroupName}, - Resources: []string{"roles", "rolebindings"}, - Verbs: []string{"get", "list", "watch"}, - }, - } - - return controllerutil.SetControllerReference(auth.Handler.FeatureStore, role, auth.Handler.Scheme) -} - -func (auth *FeastAuth) createFeastRoleBinding() error { - logger := log.FromContext(auth.Handler.Context) - roleBinding := auth.initFeastRoleBinding() - if op, err := controllerutil.CreateOrUpdate(auth.Handler.Context, auth.Handler.Client, roleBinding, controllerutil.MutateFn(func() error { - return auth.setFeastRoleBinding(roleBinding) - })); err != nil { - return err - } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { - logger.Info("Successfully reconciled", "RoleBinding", roleBinding.Name, "operation", op) - } - - return nil -} - -func (auth *FeastAuth) initFeastRoleBinding() *rbacv1.RoleBinding { - roleBinding := &rbacv1.RoleBinding{ - ObjectMeta: metav1.ObjectMeta{Name: auth.getFeastRoleName(), Namespace: auth.Handler.FeatureStore.Namespace}, - } - roleBinding.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("RoleBinding")) - return roleBinding -} - -func (auth *FeastAuth) setFeastRoleBinding(roleBinding *rbacv1.RoleBinding) error { - roleBinding.Labels = auth.getLabels() - roleBinding.Subjects = []rbacv1.Subject{} - if auth.Handler.FeatureStore.Status.Applied.Services.OfflineStore != nil { - roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{ - Kind: rbacv1.ServiceAccountKind, - Name: services.GetFeastServiceName(auth.Handler.FeatureStore, services.OfflineFeastType), - Namespace: auth.Handler.FeatureStore.Namespace, - }) - } - if auth.Handler.FeatureStore.Status.Applied.Services.OnlineStore != nil { - roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{ - Kind: rbacv1.ServiceAccountKind, - Name: services.GetFeastServiceName(auth.Handler.FeatureStore, services.OnlineFeastType), - Namespace: auth.Handler.FeatureStore.Namespace, - }) - } - if services.IsLocalRegistry(auth.Handler.FeatureStore) { - roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{ - Kind: rbacv1.ServiceAccountKind, - Name: services.GetFeastServiceName(auth.Handler.FeatureStore, services.RegistryFeastType), - Namespace: auth.Handler.FeatureStore.Namespace, - }) - } - roleBinding.RoleRef = rbacv1.RoleRef{ - APIGroup: rbacv1.GroupName, - Kind: "Role", - Name: auth.getFeastRoleName(), - } - - return controllerutil.SetControllerReference(auth.Handler.FeatureStore, roleBinding, auth.Handler.Scheme) -} - -func (auth *FeastAuth) createAuthRole(roleName string) error { - logger := log.FromContext(auth.Handler.Context) - role := auth.initAuthRole(roleName) - if op, err := controllerutil.CreateOrUpdate(auth.Handler.Context, auth.Handler.Client, role, controllerutil.MutateFn(func() error { - return auth.setAuthRole(role) - })); err != nil { - return err - } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { - logger.Info("Successfully reconciled", "Role", role.Name, "operation", op) - } - - return nil -} - -func (auth *FeastAuth) initAuthRole(roleName string) *rbacv1.Role { - role := &rbacv1.Role{ - ObjectMeta: metav1.ObjectMeta{Name: roleName, Namespace: auth.Handler.FeatureStore.Namespace}, - } - role.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("Role")) - return role -} - -func (auth *FeastAuth) setAuthRole(role *rbacv1.Role) error { - role.Labels = auth.getLabels() - role.Rules = []rbacv1.PolicyRule{} - - return controllerutil.SetControllerReference(auth.Handler.FeatureStore, role, auth.Handler.Scheme) -} - -func (auth *FeastAuth) getLabels() map[string]string { - return map[string]string{ - services.NameLabelKey: auth.Handler.FeatureStore.Name, - } -} - -func (auth *FeastAuth) setFeastKubernetesAuthCondition(err error) error { - if err != nil { - logger := log.FromContext(auth.Handler.Context) - cond := feastKubernetesAuthConditions[metav1.ConditionFalse] - cond.Message = "Error: " + err.Error() - apimeta.SetStatusCondition(&auth.Handler.FeatureStore.Status.Conditions, cond) - logger.Error(err, "Error deploying the Kubernetes authorization") - return err - } else { - apimeta.SetStatusCondition(&auth.Handler.FeatureStore.Status.Conditions, feastKubernetesAuthConditions[metav1.ConditionTrue]) - } - return nil -} - -func (auth *FeastAuth) getFeastRoleName() string { - return GetFeastRoleName(auth.Handler.FeatureStore) -} - -func GetFeastRoleName(featureStore *feastdevv1alpha1.FeatureStore) string { - return services.GetFeastName(featureStore) -} diff --git a/infra/feast-operator/internal/controller/authz/authz.go b/infra/feast-operator/internal/controller/authz/authz.go new file mode 100644 index 00000000000..28d5cc1ca9a --- /dev/null +++ b/infra/feast-operator/internal/controller/authz/authz.go @@ -0,0 +1,220 @@ +package authz + +import ( + "context" + "slices" + + feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" + rbacv1 "k8s.io/api/rbac/v1" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// Deploy the feast authorization +func (authz *FeastAuthorization) Deploy() error { + authzConfig := authz.Handler.FeatureStore.Status.Applied.AuthzConfig + if authzConfig != nil { + if authzConfig.KubernetesAuth != nil { + if err := authz.deployKubernetesAuth(authzConfig.KubernetesAuth); err != nil { + return err + } + } else { + authz.removeOrphanedRoles() + _ = authz.Handler.DeleteOwnedFeastObj(authz.initFeastRole()) + _ = authz.Handler.DeleteOwnedFeastObj(authz.initFeastRoleBinding()) + } + } + return nil +} + +func (authz *FeastAuthorization) deployKubernetesAuth(kubernetesAuth *feastdevv1alpha1.KubernetesAuth) error { + authz.removeOrphanedRoles() + + if err := authz.createFeastRole(); err != nil { + return authz.setFeastKubernetesAuthCondition(err) + } + if err := authz.createFeastRoleBinding(); err != nil { + return authz.setFeastKubernetesAuthCondition(err) + } + + for _, roleName := range kubernetesAuth.Roles { + if err := authz.createAuthRole(roleName); err != nil { + return authz.setFeastKubernetesAuthCondition(err) + } + } + return authz.setFeastKubernetesAuthCondition(nil) +} + +func (authz *FeastAuthorization) removeOrphanedRoles() { + roleList := &rbacv1.RoleList{} + err := authz.Handler.Client.List(context.TODO(), roleList, &client.ListOptions{ + Namespace: authz.Handler.FeatureStore.Namespace, + LabelSelector: labels.SelectorFromSet(authz.getLabels()), + }) + if err != nil { + return + } + + desiredRoles := []string{} + if authz.Handler.FeatureStore.Status.Applied.AuthzConfig.KubernetesAuth != nil { + desiredRoles = authz.Handler.FeatureStore.Status.Applied.AuthzConfig.KubernetesAuth.Roles + } + for _, role := range roleList.Items { + roleName := role.Name + if roleName != authz.getFeastRoleName() && !slices.Contains(desiredRoles, roleName) { + _ = authz.Handler.DeleteOwnedFeastObj(authz.initAuthRole(roleName)) + } + } +} + +func (authz *FeastAuthorization) createFeastRole() error { + logger := log.FromContext(authz.Handler.Context) + role := authz.initFeastRole() + if op, err := controllerutil.CreateOrUpdate(authz.Handler.Context, authz.Handler.Client, role, controllerutil.MutateFn(func() error { + return authz.setFeastRole(role) + })); err != nil { + return err + } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { + logger.Info("Successfully reconciled", "Role", role.Name, "operation", op) + } + + return nil +} + +func (authz *FeastAuthorization) initFeastRole() *rbacv1.Role { + role := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{Name: authz.getFeastRoleName(), Namespace: authz.Handler.FeatureStore.Namespace}, + } + role.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("Role")) + return role +} + +func (authz *FeastAuthorization) setFeastRole(role *rbacv1.Role) error { + role.Labels = authz.getLabels() + role.Rules = []rbacv1.PolicyRule{ + { + APIGroups: []string{rbacv1.GroupName}, + Resources: []string{"roles", "rolebindings"}, + Verbs: []string{"get", "list", "watch"}, + }, + } + + return controllerutil.SetControllerReference(authz.Handler.FeatureStore, role, authz.Handler.Scheme) +} + +func (authz *FeastAuthorization) createFeastRoleBinding() error { + logger := log.FromContext(authz.Handler.Context) + roleBinding := authz.initFeastRoleBinding() + if op, err := controllerutil.CreateOrUpdate(authz.Handler.Context, authz.Handler.Client, roleBinding, controllerutil.MutateFn(func() error { + return authz.setFeastRoleBinding(roleBinding) + })); err != nil { + return err + } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { + logger.Info("Successfully reconciled", "RoleBinding", roleBinding.Name, "operation", op) + } + + return nil +} + +func (authz *FeastAuthorization) initFeastRoleBinding() *rbacv1.RoleBinding { + roleBinding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{Name: authz.getFeastRoleName(), Namespace: authz.Handler.FeatureStore.Namespace}, + } + roleBinding.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("RoleBinding")) + return roleBinding +} + +func (authz *FeastAuthorization) setFeastRoleBinding(roleBinding *rbacv1.RoleBinding) error { + roleBinding.Labels = authz.getLabels() + roleBinding.Subjects = []rbacv1.Subject{} + if authz.Handler.FeatureStore.Status.Applied.Services.OfflineStore != nil { + roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{ + Kind: rbacv1.ServiceAccountKind, + Name: services.GetFeastServiceName(authz.Handler.FeatureStore, services.OfflineFeastType), + Namespace: authz.Handler.FeatureStore.Namespace, + }) + } + if authz.Handler.FeatureStore.Status.Applied.Services.OnlineStore != nil { + roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{ + Kind: rbacv1.ServiceAccountKind, + Name: services.GetFeastServiceName(authz.Handler.FeatureStore, services.OnlineFeastType), + Namespace: authz.Handler.FeatureStore.Namespace, + }) + } + if services.IsLocalRegistry(authz.Handler.FeatureStore) { + roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{ + Kind: rbacv1.ServiceAccountKind, + Name: services.GetFeastServiceName(authz.Handler.FeatureStore, services.RegistryFeastType), + Namespace: authz.Handler.FeatureStore.Namespace, + }) + } + roleBinding.RoleRef = rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "Role", + Name: authz.getFeastRoleName(), + } + + return controllerutil.SetControllerReference(authz.Handler.FeatureStore, roleBinding, authz.Handler.Scheme) +} + +func (authz *FeastAuthorization) createAuthRole(roleName string) error { + logger := log.FromContext(authz.Handler.Context) + role := authz.initAuthRole(roleName) + if op, err := controllerutil.CreateOrUpdate(authz.Handler.Context, authz.Handler.Client, role, controllerutil.MutateFn(func() error { + return authz.setAuthRole(role) + })); err != nil { + return err + } else if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated { + logger.Info("Successfully reconciled", "Role", role.Name, "operation", op) + } + + return nil +} + +func (authz *FeastAuthorization) initAuthRole(roleName string) *rbacv1.Role { + role := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{Name: roleName, Namespace: authz.Handler.FeatureStore.Namespace}, + } + role.SetGroupVersionKind(rbacv1.SchemeGroupVersion.WithKind("Role")) + return role +} + +func (authz *FeastAuthorization) setAuthRole(role *rbacv1.Role) error { + role.Labels = authz.getLabels() + role.Rules = []rbacv1.PolicyRule{} + + return controllerutil.SetControllerReference(authz.Handler.FeatureStore, role, authz.Handler.Scheme) +} + +func (authz *FeastAuthorization) getLabels() map[string]string { + return map[string]string{ + services.NameLabelKey: authz.Handler.FeatureStore.Name, + } +} + +func (authz *FeastAuthorization) setFeastKubernetesAuthCondition(err error) error { + if err != nil { + logger := log.FromContext(authz.Handler.Context) + cond := feastKubernetesAuthConditions[metav1.ConditionFalse] + cond.Message = "Error: " + err.Error() + apimeta.SetStatusCondition(&authz.Handler.FeatureStore.Status.Conditions, cond) + logger.Error(err, "Error deploying the Kubernetes authorization") + return err + } else { + apimeta.SetStatusCondition(&authz.Handler.FeatureStore.Status.Conditions, feastKubernetesAuthConditions[metav1.ConditionTrue]) + } + return nil +} + +func (authz *FeastAuthorization) getFeastRoleName() string { + return GetFeastRoleName(authz.Handler.FeatureStore) +} + +func GetFeastRoleName(featureStore *feastdevv1alpha1.FeatureStore) string { + return services.GetFeastName(featureStore) +} diff --git a/infra/feast-operator/internal/controller/auth/auth_types.go b/infra/feast-operator/internal/controller/authz/authz_types.go similarity index 73% rename from infra/feast-operator/internal/controller/auth/auth_types.go rename to infra/feast-operator/internal/controller/authz/authz_types.go index d3ee4086821..5e8b167345a 100644 --- a/infra/feast-operator/internal/controller/auth/auth_types.go +++ b/infra/feast-operator/internal/controller/authz/authz_types.go @@ -1,4 +1,4 @@ -package auth +package authz import ( feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" @@ -6,21 +6,21 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// FeastAuth is an interface for configuring feast authorization -type FeastAuth struct { +// FeastAuthorization is an interface for configuring feast authorization +type FeastAuthorization struct { Handler handler.FeastHandler } var ( feastKubernetesAuthConditions = map[metav1.ConditionStatus]metav1.Condition{ metav1.ConditionTrue: { - Type: feastdevv1alpha1.KubernetesAuthReadyType, + Type: feastdevv1alpha1.AuthorizationReadyType, Status: metav1.ConditionTrue, Reason: feastdevv1alpha1.ReadyReason, Message: feastdevv1alpha1.KubernetesAuthReadyMessage, }, metav1.ConditionFalse: { - Type: feastdevv1alpha1.KubernetesAuthReadyType, + Type: feastdevv1alpha1.AuthorizationReadyType, Status: metav1.ConditionFalse, Reason: feastdevv1alpha1.KubernetesAuthFailedReason, }, diff --git a/infra/feast-operator/internal/controller/featurestore_controller.go b/infra/feast-operator/internal/controller/featurestore_controller.go index 01d0d7436dc..b90305b56b7 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller.go +++ b/infra/feast-operator/internal/controller/featurestore_controller.go @@ -36,7 +36,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" - "github.com/feast-dev/feast/infra/feast-operator/internal/controller/auth" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/authz" feasthandler "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" ) @@ -111,7 +111,7 @@ func (r *FeatureStoreReconciler) deployFeast(ctx context.Context, cr *feastdevv1 Message: feastdevv1alpha1.ReadyMessage, } - auth := auth.FeastAuth{ + authz := authz.FeastAuthorization{ Handler: feasthandler.FeastHandler{ Client: r.Client, Context: ctx, @@ -119,7 +119,7 @@ func (r *FeatureStoreReconciler) deployFeast(ctx context.Context, cr *feastdevv1 Scheme: r.Scheme, }, } - if err = auth.Deploy(); err != nil { + if err = authz.Deploy(); err != nil { condition = metav1.Condition{ Type: feastdevv1alpha1.ReadyType, Status: metav1.ConditionFalse, 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 bec48a2f8c5..71713eb872f 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go @@ -127,7 +127,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) - Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) + Expect(resource.Status.Applied.AuthzConfig).To(Equal(&feastdevv1alpha1.AuthzConfig{})) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Persistence).NotTo(BeNil()) @@ -165,7 +165,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(cond.Type).To(Equal(feastdevv1alpha1.ReadyType)) Expect(cond.Message).To(Equal(feastdevv1alpha1.ReadyMessage)) - cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType) Expect(cond).To(BeNil()) cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) @@ -294,7 +294,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { RegistryType: services.RegistryFileConfigType, Path: registryPath, }, - AuthConfig: noAuthConfig(), + AuthzConfig: noAuthzConfig(), } Expect(repoConfig).To(Equal(testConfig)) @@ -331,8 +331,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { OfflineStore: services.OfflineStoreConfig{ Type: services.OfflineFilePersistenceDuckDbConfigType, }, - Registry: regRemote, - AuthConfig: noAuthConfig(), + Registry: regRemote, + AuthzConfig: noAuthzConfig(), } Expect(repoConfigOffline).To(Equal(offlineConfig)) @@ -373,8 +373,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Path: onlineStorePath, Type: services.OnlineSqliteConfigType, }, - Registry: regRemote, - AuthConfig: noAuthConfig(), + Registry: regRemote, + AuthzConfig: noAuthzConfig(), } Expect(repoConfigOnline).To(Equal(onlineConfig)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(3)) @@ -400,8 +400,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Path: fmt.Sprintf("http://feast-%s-online.default.svc.cluster.local:80", resourceName), Type: services.OnlineRemoteConfigType, }, - Registry: regRemote, - AuthConfig: noAuthConfig(), + Registry: regRemote, + AuthzConfig: noAuthzConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) 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 8d2edf7a083..90c24be4de3 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 @@ -39,14 +39,14 @@ import ( "github.com/feast-dev/feast/infra/feast-operator/api/feastversion" feastdevv1alpha1 "github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1" - "github.com/feast-dev/feast/infra/feast-operator/internal/controller/auth" + "github.com/feast-dev/feast/infra/feast-operator/internal/controller/authz" "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler" "github.com/feast-dev/feast/infra/feast-operator/internal/controller/services" ) var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Context("When deploying a resource with all ephemeral services and Kubernetes authorization", func() { - const resourceName = "kubernetes-auth" + const resourceName = "kubernetes-authorization" var pullPolicy = corev1.PullAlways ctx := context.Background() @@ -63,7 +63,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { err := k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{}) - resource.Spec.AuthConfig = &feastdevv1alpha1.AuthConfig{KubernetesAuth: &feastdevv1alpha1.KubernetesAuth{ + resource.Spec.AuthzConfig = &feastdevv1alpha1.AuthzConfig{KubernetesAuth: &feastdevv1alpha1.KubernetesAuth{ Roles: roles, }} @@ -107,12 +107,12 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) - expectedAuthConfig := &feastdevv1alpha1.AuthConfig{ + expectedAuthzConfig := &feastdevv1alpha1.AuthzConfig{ KubernetesAuth: &feastdevv1alpha1.KubernetesAuth{ Roles: roles, }, } - Expect(resource.Status.Applied.AuthConfig).To(Equal(expectedAuthConfig)) + Expect(resource.Status.Applied.AuthzConfig).To(Equal(expectedAuthzConfig)) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Persistence).NotTo(BeNil()) @@ -150,11 +150,11 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Expect(cond.Type).To(Equal(feastdevv1alpha1.ReadyType)) Expect(cond.Message).To(Equal(feastdevv1alpha1.ReadyMessage)) - cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType) Expect(cond).ToNot(BeNil()) Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(feastdevv1alpha1.ReadyReason)) - Expect(cond.Type).To(Equal(feastdevv1alpha1.KubernetesAuthReadyType)) + Expect(cond.Type).To(Equal(feastdevv1alpha1.AuthorizationReadyType)) Expect(cond.Message).To(Equal(feastdevv1alpha1.KubernetesAuthReadyMessage)) cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) @@ -244,7 +244,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { // check Feast Role feastRole := &rbacv1.Role{} err = k8sClient.Get(ctx, types.NamespacedName{ - Name: auth.GetFeastRoleName(resource), + Name: authz.GetFeastRoleName(resource), Namespace: resource.Namespace, }, feastRole) @@ -264,7 +264,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { // check RoleBinding roleBinding := &rbacv1.RoleBinding{} err = k8sClient.Get(ctx, types.NamespacedName{ - Name: auth.GetFeastRoleName(resource), + Name: authz.GetFeastRoleName(resource), Namespace: resource.Namespace, }, roleBinding) @@ -297,7 +297,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { By("Updating the user roled and reconciling") resourceNew := resource.DeepCopy() rolesNew := roles[1:] - resourceNew.Spec.AuthConfig.KubernetesAuth.Roles = rolesNew + resourceNew.Spec.AuthzConfig.KubernetesAuth.Roles = rolesNew err = k8sClient.Update(ctx, resourceNew) Expect(err).NotTo(HaveOccurred()) _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ @@ -335,7 +335,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { By("Clearing the kubernetes authorizatino and reconciling") resourceNew = resource.DeepCopy() - resourceNew.Spec.AuthConfig = &feastdevv1alpha1.AuthConfig{} + resourceNew.Spec.AuthzConfig = &feastdevv1alpha1.AuthzConfig{} err = k8sClient.Update(ctx, resourceNew) Expect(err).NotTo(HaveOccurred()) _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ @@ -362,7 +362,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { // check no RoleBinding roleBinding = &rbacv1.RoleBinding{} err = k8sClient.Get(ctx, types.NamespacedName{ - Name: auth.GetFeastRoleName(resource), + Name: authz.GetFeastRoleName(resource), Namespace: resource.Namespace, }, roleBinding) @@ -444,7 +444,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Path: services.DefaultRegistryEphemeralPath, S3AdditionalKwargs: nil, }, - AuthConfig: services.AuthConfig{ + AuthzConfig: services.AuthzConfig{ Type: services.KubernetesAuthType, }, } @@ -483,7 +483,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Type: services.OfflineDaskConfigType, }, Registry: regRemote, - AuthConfig: services.AuthConfig{ + AuthzConfig: services.AuthzConfig{ Type: services.KubernetesAuthType, }, } @@ -525,7 +525,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Type: services.OnlineSqliteConfigType, }, Registry: regRemote, - AuthConfig: services.AuthConfig{ + AuthzConfig: services.AuthzConfig{ Type: services.KubernetesAuthType, }, } @@ -556,7 +556,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { RegistryType: services.RegistryRemoteConfigType, Path: fmt.Sprintf("feast-%s-registry.default.svc.cluster.local:80", resourceName), }, - AuthConfig: services.AuthConfig{ + AuthzConfig: services.AuthzConfig{ Type: services.KubernetesAuthType, }, } 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 3cf38d20591..dce28f99118 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_objectstore_test.go @@ -122,7 +122,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) - Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) + Expect(resource.Status.Applied.AuthzConfig).To(Equal(&feastdevv1alpha1.AuthzConfig{})) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).To(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore).To(BeNil()) @@ -150,7 +150,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(cond.Type).To(Equal(feastdevv1alpha1.ReadyType)) Expect(cond.Message).To(Equal(feastdevv1alpha1.ReadyMessage)) - cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType) Expect(cond).To(BeNil()) cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) @@ -321,7 +321,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Path: registryPath, S3AdditionalKwargs: &s3AdditionalKwargs, }, - AuthConfig: noAuthConfig(), + AuthzConfig: noAuthzConfig(), } Expect(repoConfig).To(Equal(testConfig)) @@ -365,7 +365,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { RegistryType: services.RegistryRemoteConfigType, Path: fmt.Sprintf("feast-%s-registry.default.svc.cluster.local:80", resourceName), }, - AuthConfig: noAuthConfig(), + AuthzConfig: noAuthzConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) 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 7cfefde02d0..33fde346385 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go @@ -153,7 +153,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) - Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) + Expect(resource.Status.Applied.AuthzConfig).To(Equal(&feastdevv1alpha1.AuthzConfig{})) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Persistence).NotTo(BeNil()) @@ -221,7 +221,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(cond.Type).To(Equal(feastdevv1alpha1.ReadyType)) Expect(cond.Message).To(Equal(feastdevv1alpha1.ReadyMessage)) - cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType) Expect(cond).To(BeNil()) cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) @@ -464,7 +464,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { RegistryType: services.RegistryFileConfigType, Path: registryMountedPath, }, - AuthConfig: noAuthConfig(), + AuthzConfig: noAuthzConfig(), } Expect(repoConfig).To(Equal(testConfig)) @@ -502,8 +502,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { OfflineStore: services.OfflineStoreConfig{ Type: services.OfflineFilePersistenceDuckDbConfigType, }, - Registry: regRemote, - AuthConfig: noAuthConfig(), + Registry: regRemote, + AuthzConfig: noAuthzConfig(), } Expect(repoConfigOffline).To(Equal(offlineConfig)) @@ -544,8 +544,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Path: onlineStoreMountedPath, Type: services.OnlineSqliteConfigType, }, - Registry: regRemote, - AuthConfig: noAuthConfig(), + Registry: regRemote, + AuthzConfig: noAuthzConfig(), } Expect(repoConfigOnline).To(Equal(onlineConfig)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(3)) @@ -571,8 +571,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Path: fmt.Sprintf("http://feast-%s-online.default.svc.cluster.local:80", resourceName), Type: services.OnlineRemoteConfigType, }, - Registry: regRemote, - AuthConfig: noAuthConfig(), + Registry: regRemote, + AuthzConfig: noAuthzConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_test.go b/infra/feast-operator/internal/controller/featurestore_controller_test.go index b59458cac25..980f3e36f5f 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_test.go @@ -133,7 +133,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(resource.Status.ServiceHostnames.OnlineStore).To(BeEmpty()) Expect(resource.Status.ServiceHostnames.Registry).To(Equal(feast.GetFeastServiceName(services.RegistryFeastType) + "." + resource.Namespace + ".svc.cluster.local:80")) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) - Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) + Expect(resource.Status.Applied.AuthzConfig).To(Equal(&feastdevv1alpha1.AuthzConfig{})) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).To(BeNil()) Expect(resource.Status.Applied.Services.OnlineStore).To(BeNil()) @@ -246,7 +246,7 @@ var _ = Describe("FeatureStore Controller", func() { RegistryType: services.RegistryFileConfigType, Path: services.DefaultRegistryEphemeralPath, }, - AuthConfig: noAuthConfig(), + AuthzConfig: noAuthzConfig(), } Expect(repoConfig).To(Equal(testConfig)) @@ -270,7 +270,7 @@ var _ = Describe("FeatureStore Controller", func() { RegistryType: services.RegistryRemoteConfigType, Path: "feast-test-resource-registry.default.svc.cluster.local:80", }, - AuthConfig: noAuthConfig(), + AuthzConfig: noAuthzConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) @@ -377,7 +377,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(cond.Reason).To(Equal(feastdevv1alpha1.FailedReason)) Expect(cond.Message).To(Equal("Error: Object " + resource.Namespace + "/" + name + " is already owned by another Service controller " + name)) - cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType) Expect(cond).To(BeNil()) cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) @@ -458,7 +458,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(resource.Status.FeastVersion).To(Equal(feastversion.FeastVersion)) Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) - Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) + Expect(resource.Status.Applied.AuthzConfig).To(Equal(&feastdevv1alpha1.AuthzConfig{})) Expect(resource.Status.Applied.Services).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Persistence).NotTo(BeNil()) @@ -496,7 +496,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(cond.Type).To(Equal(feastdevv1alpha1.ReadyType)) Expect(cond.Message).To(Equal(feastdevv1alpha1.ReadyMessage)) - cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType) Expect(cond).To(BeNil()) cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) @@ -632,7 +632,7 @@ var _ = Describe("FeatureStore Controller", func() { RegistryType: services.RegistryFileConfigType, Path: services.DefaultRegistryEphemeralPath, }, - AuthConfig: noAuthConfig(), + AuthzConfig: noAuthzConfig(), } Expect(repoConfig).To(Equal(testConfig)) @@ -670,8 +670,8 @@ var _ = Describe("FeatureStore Controller", func() { OfflineStore: services.OfflineStoreConfig{ Type: services.OfflineFilePersistenceDaskConfigType, }, - Registry: regRemote, - AuthConfig: noAuthConfig(), + Registry: regRemote, + AuthzConfig: noAuthzConfig(), } Expect(repoConfigOffline).To(Equal(offlineConfig)) @@ -713,8 +713,8 @@ var _ = Describe("FeatureStore Controller", func() { Path: services.DefaultOnlineStoreEphemeralPath, Type: services.OnlineSqliteConfigType, }, - Registry: regRemote, - AuthConfig: noAuthConfig(), + Registry: regRemote, + AuthzConfig: noAuthzConfig(), } Expect(repoConfigOnline).To(Equal(onlineConfig)) Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(HaveLen(3)) @@ -740,8 +740,8 @@ var _ = Describe("FeatureStore Controller", func() { Path: "http://feast-services-online.default.svc.cluster.local:80", Type: services.OnlineRemoteConfigType, }, - Registry: regRemote, - AuthConfig: noAuthConfig(), + Registry: regRemote, + AuthzConfig: noAuthzConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) @@ -984,7 +984,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).To(HaveOccurred()) err = k8sClient.Get(ctx, nsName, resource) Expect(err).NotTo(HaveOccurred()) - Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType)).To(BeNil()) + Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType)).To(BeNil()) Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType)).To(BeNil()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.ReadyType)).To(BeFalse()) cond := apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.ReadyType) @@ -1000,7 +1000,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(err).To(HaveOccurred()) err = k8sClient.Get(ctx, nsName, resource) Expect(err).NotTo(HaveOccurred()) - Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType)).To(BeNil()) + Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType)).To(BeNil()) Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType)).To(BeNil()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.ReadyType)).To(BeFalse()) cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.ReadyType) @@ -1018,7 +1018,7 @@ var _ = Describe("FeatureStore Controller", func() { err = k8sClient.Get(ctx, nsName, resource) Expect(err).NotTo(HaveOccurred()) - Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType)).To(BeNil()) + Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType)).To(BeNil()) Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType)).To(BeNil()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.ReadyType)).To(BeTrue()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.OnlineStoreReadyType)).To(BeTrue()) @@ -1062,7 +1062,7 @@ var _ = Describe("FeatureStore Controller", func() { RegistryType: services.RegistryRemoteConfigType, Path: "feast-" + referencedRegistry.Name + "-registry.default.svc.cluster.local:80", }, - AuthConfig: noAuthConfig(), + AuthzConfig: noAuthzConfig(), } Expect(repoConfigClient).To(Equal(clientConfig)) @@ -1087,7 +1087,7 @@ var _ = Describe("FeatureStore Controller", func() { err = k8sClient.Get(ctx, nsName, resource) Expect(err).NotTo(HaveOccurred()) Expect(resource.Status.ServiceHostnames.Registry).To(BeEmpty()) - Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType)).To(BeNil()) + Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType)).To(BeNil()) Expect(apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType)).To(BeNil()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.ReadyType)).To(BeFalse()) Expect(apimeta.IsStatusConditionTrue(resource.Status.Conditions, feastdevv1alpha1.OnlineStoreReadyType)).To(BeTrue()) @@ -1165,7 +1165,7 @@ var _ = Describe("FeatureStore Controller", func() { Expect(cond.Reason).To(Equal(feastdevv1alpha1.FailedReason)) Expect(cond.Message).To(Equal("Error: Object " + resource.Namespace + "/" + name + " is already owned by another Service controller " + name)) - cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.KubernetesAuthReadyType) + cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.AuthorizationReadyType) Expect(cond).To(BeNil()) cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) @@ -1286,8 +1286,8 @@ func getFeatureStoreYamlEnvVar(envs []corev1.EnvVar) *corev1.EnvVar { return nil } -func noAuthConfig() services.AuthConfig { - return services.AuthConfig{ +func noAuthzConfig() services.AuthzConfig { + return services.AuthzConfig{ Type: services.NoAuthAuthType, } } diff --git a/infra/feast-operator/internal/controller/services/repo_config.go b/infra/feast-operator/internal/controller/services/repo_config.go index 55ee6d9817a..f7c94c761fe 100644 --- a/infra/feast-operator/internal/controller/services/repo_config.go +++ b/infra/feast-operator/internal/controller/services/repo_config.go @@ -233,13 +233,13 @@ func getClientRepoConfig(featureStore *feastdevv1alpha1.FeatureStore) RepoConfig } } - if status.Applied.AuthConfig.KubernetesAuth == nil { - clientRepoConfig.AuthConfig = AuthConfig{ + if status.Applied.AuthzConfig.KubernetesAuth == nil { + clientRepoConfig.AuthzConfig = AuthzConfig{ Type: NoAuthAuthType, } } else { - if status.Applied.AuthConfig.KubernetesAuth != nil { - clientRepoConfig.AuthConfig = AuthConfig{ + if status.Applied.AuthzConfig.KubernetesAuth != nil { + clientRepoConfig.AuthzConfig = AuthzConfig{ Type: KubernetesAuthType, } } diff --git a/infra/feast-operator/internal/controller/services/repo_config_test.go b/infra/feast-operator/internal/controller/services/repo_config_test.go index 45075702a13..d5f16ee8267 100644 --- a/infra/feast-operator/internal/controller/services/repo_config_test.go +++ b/infra/feast-operator/internal/controller/services/repo_config_test.go @@ -39,21 +39,21 @@ var _ = Describe("Repo Config", func() { var repoConfig RepoConfig repoConfig, err := getServiceRepoConfig(OfflineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) expectedRegistryConfig := RegistryConfig{ @@ -78,21 +78,21 @@ var _ = Describe("Repo Config", func() { ApplyDefaultsToStatus(featureStore) repoConfig, err = getServiceRepoConfig(OfflineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) expectedRegistryConfig = RegistryConfig{ @@ -116,21 +116,21 @@ var _ = Describe("Repo Config", func() { ApplyDefaultsToStatus(featureStore) repoConfig, err = getServiceRepoConfig(OfflineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) @@ -165,7 +165,7 @@ var _ = Describe("Repo Config", func() { ApplyDefaultsToStatus(featureStore) repoConfig, err = getServiceRepoConfig(OfflineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) expectedOfflineConfig := OfflineStoreConfig{ Type: "duckdb", } @@ -175,7 +175,7 @@ var _ = Describe("Repo Config", func() { repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) expectedOnlineConfig := OnlineStoreConfig{ Type: "sqlite", @@ -186,7 +186,7 @@ var _ = Describe("Repo Config", func() { repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore, emptyMockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(NoAuthAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) expectedRegistryConfig = RegistryConfig{ @@ -195,9 +195,9 @@ var _ = Describe("Repo Config", func() { } Expect(repoConfig.Registry).To(Equal(expectedRegistryConfig)) - By("Having kubernetes auth") + By("Having kubernetes authorization") featureStore = minimalFeatureStore() - featureStore.Spec.AuthConfig = &feastdevv1alpha1.AuthConfig{ + featureStore.Spec.AuthzConfig = &feastdevv1alpha1.AuthzConfig{ KubernetesAuth: &feastdevv1alpha1.KubernetesAuth{}, } featureStore.Spec.Services = &feastdevv1alpha1.FeatureStoreServices{ @@ -222,7 +222,7 @@ var _ = Describe("Repo Config", func() { ApplyDefaultsToStatus(featureStore) repoConfig, err = getServiceRepoConfig(OfflineFeastType, featureStore) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(KubernetesAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(KubernetesAuthType)) expectedOfflineConfig = OfflineStoreConfig{ Type: "dask", } @@ -232,7 +232,7 @@ var _ = Describe("Repo Config", func() { repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(KubernetesAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(KubernetesAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) expectedOnlineConfig = OnlineStoreConfig{ Type: "sqlite", @@ -243,7 +243,7 @@ var _ = Describe("Repo Config", func() { repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore) Expect(err).NotTo(HaveOccurred()) - Expect(repoConfig.AuthConfig.Type).To(Equal(KubernetesAuthType)) + Expect(repoConfig.AuthzConfig.Type).To(Equal(KubernetesAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) expectedRegistryConfig = RegistryConfig{ diff --git a/infra/feast-operator/internal/controller/services/services_types.go b/infra/feast-operator/internal/controller/services/services_types.go index a6c91dff03a..bf5c5337f55 100644 --- a/infra/feast-operator/internal/controller/services/services_types.go +++ b/infra/feast-operator/internal/controller/services/services_types.go @@ -171,7 +171,7 @@ type RepoConfig struct { OfflineStore OfflineStoreConfig `yaml:"offline_store,omitempty"` OnlineStore OnlineStoreConfig `yaml:"online_store,omitempty"` Registry RegistryConfig `yaml:"registry,omitempty"` - AuthConfig AuthConfig `yaml:"auth,omitempty"` + AuthzConfig AuthzConfig `yaml:"auth,omitempty"` EntityKeySerializationVersion int `yaml:"entity_key_serialization_version,omitempty"` } @@ -198,8 +198,8 @@ type RegistryConfig struct { DBParameters map[string]interface{} `yaml:",inline,omitempty"` } -// AuthConfig is the RBAC authorization configuration. -type AuthConfig struct { +// AuthzConfig is the RBAC authorization configuration. +type AuthzConfig struct { Type AuthType `yaml:"type,omitempty"` } diff --git a/infra/feast-operator/internal/controller/services/util.go b/infra/feast-operator/internal/controller/services/util.go index 0723013c212..a678e7a2c31 100644 --- a/infra/feast-operator/internal/controller/services/util.go +++ b/infra/feast-operator/internal/controller/services/util.go @@ -53,8 +53,8 @@ func ApplyDefaultsToStatus(cr *feastdevv1alpha1.FeatureStore) { cr.Status.FeastVersion = feastversion.FeastVersion applied := cr.Spec.DeepCopy() - if applied.AuthConfig == nil { - applied.AuthConfig = &feastdevv1alpha1.AuthConfig{} + if applied.AuthzConfig == nil { + applied.AuthzConfig = &feastdevv1alpha1.AuthzConfig{} } if applied.Services == nil { diff --git a/infra/feast-operator/test/api/featurestore_types_test.go b/infra/feast-operator/test/api/featurestore_types_test.go index bb3b315912e..2819cb24243 100644 --- a/infra/feast-operator/test/api/featurestore_types_test.go +++ b/infra/feast-operator/test/api/featurestore_types_test.go @@ -377,11 +377,11 @@ var _ = Describe("FeatureStore API", func() { storage = resource.Status.Applied.Services.Registry.Local.Persistence.FilePersistence.PvcConfig.Create.Resources.Requests.Storage().String() Expect(storage).To(Equal("500Mi")) }) - It("should set the default AuthConfig", func() { + It("should set the default AuthzConfig", func() { resource := featurestore services.ApplyDefaultsToStatus(resource) - Expect(resource.Status.Applied.AuthConfig).ToNot(BeNil()) - Expect(resource.Status.Applied.AuthConfig).To(Equal(&feastdevv1alpha1.AuthConfig{})) + Expect(resource.Status.Applied.AuthzConfig).ToNot(BeNil()) + Expect(resource.Status.Applied.AuthzConfig).To(Equal(&feastdevv1alpha1.AuthzConfig{})) }) }) }) From 13dd5601de32d1060b943e080dfbd566ea5985ad Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Tue, 26 Nov 2024 21:45:40 +0100 Subject: [PATCH 10/11] post rebase fixes Signed-off-by: Daniele Martinoli --- .../featurestore_controller_kubernetes_auth_test.go | 4 ++-- .../internal/controller/services/repo_config.go | 2 +- .../internal/controller/services/repo_config_test.go | 6 +++--- infra/feast-operator/internal/controller/services/util.go | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) 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 90c24be4de3..636c0123782 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 @@ -117,7 +117,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Expect(resource.Status.Applied.Services.OfflineStore).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Persistence).NotTo(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Persistence.FilePersistence).NotTo(BeNil()) - Expect(resource.Status.Applied.Services.OfflineStore.Persistence.FilePersistence.Type).To(Equal(string(services.OfflineDaskConfigType))) + Expect(resource.Status.Applied.Services.OfflineStore.Persistence.FilePersistence.Type).To(Equal(string(services.OfflineFilePersistenceDaskConfigType))) Expect(resource.Status.Applied.Services.OfflineStore.ImagePullPolicy).To(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Resources).To(BeNil()) Expect(resource.Status.Applied.Services.OfflineStore.Image).To(Equal(&services.DefaultImage)) @@ -480,7 +480,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Provider: services.LocalProviderType, EntityKeySerializationVersion: feastdevv1alpha1.SerializationVersion, OfflineStore: services.OfflineStoreConfig{ - Type: services.OfflineDaskConfigType, + Type: services.OfflineFilePersistenceDaskConfigType, }, Registry: regRemote, AuthzConfig: services.AuthzConfig{ diff --git a/infra/feast-operator/internal/controller/services/repo_config.go b/infra/feast-operator/internal/controller/services/repo_config.go index f7c94c761fe..b60847ddaff 100644 --- a/infra/feast-operator/internal/controller/services/repo_config.go +++ b/infra/feast-operator/internal/controller/services/repo_config.go @@ -75,7 +75,7 @@ func getServiceRepoConfig(feastType FeastServiceType, featureStore *feastdevv1al } case RegistryFeastType: // Registry server only has a `registry` section - if isLocalReg { + if isLocalRegistry { err := setRepoConfigRegistry(services, secretExtractionFunc, &repoConfig) if err != nil { return repoConfig, err diff --git a/infra/feast-operator/internal/controller/services/repo_config_test.go b/infra/feast-operator/internal/controller/services/repo_config_test.go index d5f16ee8267..dd93c74586e 100644 --- a/infra/feast-operator/internal/controller/services/repo_config_test.go +++ b/infra/feast-operator/internal/controller/services/repo_config_test.go @@ -220,7 +220,7 @@ var _ = Describe("Repo Config", func() { }, } ApplyDefaultsToStatus(featureStore) - repoConfig, err = getServiceRepoConfig(OfflineFeastType, featureStore) + repoConfig, err = getServiceRepoConfig(OfflineFeastType, featureStore, mockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) Expect(repoConfig.AuthzConfig.Type).To(Equal(KubernetesAuthType)) expectedOfflineConfig = OfflineStoreConfig{ @@ -230,7 +230,7 @@ var _ = Describe("Repo Config", func() { Expect(repoConfig.OnlineStore).To(Equal(emptyOnlineStoreConfig())) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) - repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore) + repoConfig, err = getServiceRepoConfig(OnlineFeastType, featureStore, mockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) Expect(repoConfig.AuthzConfig.Type).To(Equal(KubernetesAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) @@ -241,7 +241,7 @@ var _ = Describe("Repo Config", func() { Expect(repoConfig.OnlineStore).To(Equal(expectedOnlineConfig)) Expect(repoConfig.Registry).To(Equal(emptyRegistryConfig())) - repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore) + repoConfig, err = getServiceRepoConfig(RegistryFeastType, featureStore, mockExtractConfigFromSecret) Expect(err).NotTo(HaveOccurred()) Expect(repoConfig.AuthzConfig.Type).To(Equal(KubernetesAuthType)) Expect(repoConfig.OfflineStore).To(Equal(emptyOfflineStoreConfig())) diff --git a/infra/feast-operator/internal/controller/services/util.go b/infra/feast-operator/internal/controller/services/util.go index a678e7a2c31..8e6df6ee667 100644 --- a/infra/feast-operator/internal/controller/services/util.go +++ b/infra/feast-operator/internal/controller/services/util.go @@ -206,11 +206,11 @@ func checkRegistryDBStorePersistenceType(value string) error { } func (feast *FeastServices) getSecret(secretRef string) (*corev1.Secret, error) { - secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: secretRef, Namespace: feast.FeatureStore.Namespace}} + secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: secretRef, Namespace: feast.Handler.FeatureStore.Namespace}} objectKey := client.ObjectKeyFromObject(secret) - if err := feast.Client.Get(feast.Context, objectKey, secret); err != nil { + if err := feast.Handler.Client.Get(feast.Handler.Context, objectKey, secret); err != nil { if apierrors.IsNotFound(err) || err != nil { - logger := log.FromContext(feast.Context) + logger := log.FromContext(feast.Handler.Context) logger.Error(err, "invalid secret "+secretRef+" for offline store") return nil, err From 3e457f15b52fb40861e808f16396b51eddbb6faf Mon Sep 17 00:00:00 2001 From: Daniele Martinoli Date: Tue, 26 Nov 2024 21:53:18 +0100 Subject: [PATCH 11/11] more renamings Signed-off-by: Daniele Martinoli --- .../api/v1alpha1/featurestore_types.go | 32 +++++++++---------- .../api/v1alpha1/zz_generated.deepcopy.go | 14 ++++---- .../crd/bases/feast.dev_featurestores.yaml | 4 +-- ...ha1_featurestore_all_services_default.yaml | 14 ++++++++ infra/feast-operator/dist/install.yaml | 4 +-- .../internal/controller/authz/authz.go | 10 +++--- .../internal/controller/authz/authz_types.go | 4 +-- ...restore_controller_kubernetes_auth_test.go | 8 ++--- .../controller/services/repo_config.go | 4 +-- .../controller/services/repo_config_test.go | 2 +- .../controller/services/services_types.go | 10 +++--- 11 files changed, 60 insertions(+), 46 deletions(-) create mode 100644 infra/feast-operator/config/samples/v1alpha1_featurestore_all_services_default.yaml diff --git a/infra/feast-operator/api/v1alpha1/featurestore_types.go b/infra/feast-operator/api/v1alpha1/featurestore_types.go index d2d7c9f8d4d..391cc26fca6 100644 --- a/infra/feast-operator/api/v1alpha1/featurestore_types.go +++ b/infra/feast-operator/api/v1alpha1/featurestore_types.go @@ -36,21 +36,21 @@ const ( AuthorizationReadyType = "AuthorizationReadyType" // Feast condition reasons: - ReadyReason = "Ready" - FailedReason = "FeatureStoreFailed" - OfflineStoreFailedReason = "OfflineStoreDeploymentFailed" - OnlineStoreFailedReason = "OnlineStoreDeploymentFailed" - RegistryFailedReason = "RegistryDeploymentFailed" - ClientFailedReason = "ClientDeploymentFailed" - KubernetesAuthFailedReason = "KubernetesAuthorizationDeploymentFailed" + ReadyReason = "Ready" + FailedReason = "FeatureStoreFailed" + OfflineStoreFailedReason = "OfflineStoreDeploymentFailed" + OnlineStoreFailedReason = "OnlineStoreDeploymentFailed" + RegistryFailedReason = "RegistryDeploymentFailed" + ClientFailedReason = "ClientDeploymentFailed" + KubernetesAuthzFailedReason = "KubernetesAuthorizationDeploymentFailed" // Feast condition messages: - ReadyMessage = "FeatureStore installation complete" - OfflineStoreReadyMessage = "Offline Store installation complete" - OnlineStoreReadyMessage = "Online Store installation complete" - RegistryReadyMessage = "Registry installation complete" - ClientReadyMessage = "Client installation complete" - KubernetesAuthReadyMessage = "Kubernetes authorization installation complete" + ReadyMessage = "FeatureStore installation complete" + OfflineStoreReadyMessage = "Offline Store installation complete" + OnlineStoreReadyMessage = "Online Store installation complete" + RegistryReadyMessage = "Registry installation complete" + ClientReadyMessage = "Client installation complete" + KubernetesAuthzReadyMessage = "Kubernetes authorization installation complete" // entity_key_serialization_version SerializationVersion = 3 @@ -269,12 +269,12 @@ type OptionalConfigs struct { // AuthzConfig defines the authorization settings for the deployed Feast services. type AuthzConfig struct { - KubernetesAuth *KubernetesAuth `json:"kubernetes,omitempty"` + KubernetesAuthz *KubernetesAuthz `json:"kubernetes,omitempty"` } -// KubernetesAuth provides a way to define the authorization settings using Kubernetes RBAC resources. +// KubernetesAuthz provides a way to define the authorization settings using Kubernetes RBAC resources. // https://kubernetes.io/docs/reference/access-authn-authz/rbac/ -type KubernetesAuth struct { +type KubernetesAuthz struct { // The Kubernetes RBAC roles to be deployed in the same namespace of the FeatureStore. // Roles are managed by the operator and created with an empty list of rules. // See the Feast permission model at https://docs.feast.dev/getting-started/concepts/permission diff --git a/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go b/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go index 2007e681489..49ebceb3fad 100644 --- a/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -29,9 +29,9 @@ import ( // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AuthzConfig) DeepCopyInto(out *AuthzConfig) { *out = *in - if in.KubernetesAuth != nil { - in, out := &in.KubernetesAuth, &out.KubernetesAuth - *out = new(KubernetesAuth) + if in.KubernetesAuthz != nil { + in, out := &in.KubernetesAuthz, &out.KubernetesAuthz + *out = new(KubernetesAuthz) (*in).DeepCopyInto(*out) } } @@ -220,7 +220,7 @@ func (in *FeatureStoreStatus) DeepCopy() *FeatureStoreStatus { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KubernetesAuth) DeepCopyInto(out *KubernetesAuth) { +func (in *KubernetesAuthz) DeepCopyInto(out *KubernetesAuthz) { *out = *in if in.Roles != nil { in, out := &in.Roles, &out.Roles @@ -229,12 +229,12 @@ func (in *KubernetesAuth) DeepCopyInto(out *KubernetesAuth) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesAuth. -func (in *KubernetesAuth) DeepCopy() *KubernetesAuth { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesAuthz. +func (in *KubernetesAuthz) DeepCopy() *KubernetesAuthz { if in == nil { return nil } - out := new(KubernetesAuth) + out := new(KubernetesAuthz) in.DeepCopyInto(out) return out } 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 f7098cb3e73..3188125e6d2 100644 --- a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +++ b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml @@ -54,7 +54,7 @@ spec: properties: kubernetes: description: |- - KubernetesAuth provides a way to define the authorization settings using Kubernetes RBAC resources. + KubernetesAuthz provides a way to define the authorization settings using Kubernetes RBAC resources. https://kubernetes.io/docs/reference/access-authn-authz/rbac/ properties: roles: @@ -1067,7 +1067,7 @@ spec: properties: kubernetes: description: |- - KubernetesAuth provides a way to define the authorization settings using Kubernetes RBAC resources. + KubernetesAuthz provides a way to define the authorization settings using Kubernetes RBAC resources. https://kubernetes.io/docs/reference/access-authn-authz/rbac/ properties: roles: diff --git a/infra/feast-operator/config/samples/v1alpha1_featurestore_all_services_default.yaml b/infra/feast-operator/config/samples/v1alpha1_featurestore_all_services_default.yaml new file mode 100644 index 00000000000..1dd156378d8 --- /dev/null +++ b/infra/feast-operator/config/samples/v1alpha1_featurestore_all_services_default.yaml @@ -0,0 +1,14 @@ +apiVersion: feast.dev/v1alpha1 +kind: FeatureStore +metadata: + name: sample-all-default +spec: + feastProject: my_project + services: + onlineStore: + image: 'feastdev/feature-server:0.40.0' + offlineStore: + image: 'feastdev/feature-server:0.40.0' + registry: + local: + image: 'feastdev/feature-server:0.40.0' \ No newline at end of file diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index 8d40c925291..c9b4451217c 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -62,7 +62,7 @@ spec: properties: kubernetes: description: |- - KubernetesAuth provides a way to define the authorization settings using Kubernetes RBAC resources. + KubernetesAuthz provides a way to define the authorization settings using Kubernetes RBAC resources. https://kubernetes.io/docs/reference/access-authn-authz/rbac/ properties: roles: @@ -1075,7 +1075,7 @@ spec: properties: kubernetes: description: |- - KubernetesAuth provides a way to define the authorization settings using Kubernetes RBAC resources. + KubernetesAuthz provides a way to define the authorization settings using Kubernetes RBAC resources. https://kubernetes.io/docs/reference/access-authn-authz/rbac/ properties: roles: diff --git a/infra/feast-operator/internal/controller/authz/authz.go b/infra/feast-operator/internal/controller/authz/authz.go index 28d5cc1ca9a..59747d75bd2 100644 --- a/infra/feast-operator/internal/controller/authz/authz.go +++ b/infra/feast-operator/internal/controller/authz/authz.go @@ -19,8 +19,8 @@ import ( func (authz *FeastAuthorization) Deploy() error { authzConfig := authz.Handler.FeatureStore.Status.Applied.AuthzConfig if authzConfig != nil { - if authzConfig.KubernetesAuth != nil { - if err := authz.deployKubernetesAuth(authzConfig.KubernetesAuth); err != nil { + if authzConfig.KubernetesAuthz != nil { + if err := authz.deployKubernetesAuth(authzConfig.KubernetesAuthz); err != nil { return err } } else { @@ -32,7 +32,7 @@ func (authz *FeastAuthorization) Deploy() error { return nil } -func (authz *FeastAuthorization) deployKubernetesAuth(kubernetesAuth *feastdevv1alpha1.KubernetesAuth) error { +func (authz *FeastAuthorization) deployKubernetesAuth(kubernetesAuth *feastdevv1alpha1.KubernetesAuthz) error { authz.removeOrphanedRoles() if err := authz.createFeastRole(); err != nil { @@ -61,8 +61,8 @@ func (authz *FeastAuthorization) removeOrphanedRoles() { } desiredRoles := []string{} - if authz.Handler.FeatureStore.Status.Applied.AuthzConfig.KubernetesAuth != nil { - desiredRoles = authz.Handler.FeatureStore.Status.Applied.AuthzConfig.KubernetesAuth.Roles + if authz.Handler.FeatureStore.Status.Applied.AuthzConfig.KubernetesAuthz != nil { + desiredRoles = authz.Handler.FeatureStore.Status.Applied.AuthzConfig.KubernetesAuthz.Roles } for _, role := range roleList.Items { roleName := role.Name diff --git a/infra/feast-operator/internal/controller/authz/authz_types.go b/infra/feast-operator/internal/controller/authz/authz_types.go index 5e8b167345a..f955f5b40f1 100644 --- a/infra/feast-operator/internal/controller/authz/authz_types.go +++ b/infra/feast-operator/internal/controller/authz/authz_types.go @@ -17,12 +17,12 @@ var ( Type: feastdevv1alpha1.AuthorizationReadyType, Status: metav1.ConditionTrue, Reason: feastdevv1alpha1.ReadyReason, - Message: feastdevv1alpha1.KubernetesAuthReadyMessage, + Message: feastdevv1alpha1.KubernetesAuthzReadyMessage, }, metav1.ConditionFalse: { Type: feastdevv1alpha1.AuthorizationReadyType, Status: metav1.ConditionFalse, - Reason: feastdevv1alpha1.KubernetesAuthFailedReason, + Reason: feastdevv1alpha1.KubernetesAuthzFailedReason, }, } ) 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 636c0123782..6589e181af7 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 @@ -63,7 +63,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { err := k8sClient.Get(ctx, typeNamespacedName, featurestore) if err != nil && errors.IsNotFound(err) { resource := createFeatureStoreResource(resourceName, image, pullPolicy, &[]corev1.EnvVar{}) - resource.Spec.AuthzConfig = &feastdevv1alpha1.AuthzConfig{KubernetesAuth: &feastdevv1alpha1.KubernetesAuth{ + resource.Spec.AuthzConfig = &feastdevv1alpha1.AuthzConfig{KubernetesAuthz: &feastdevv1alpha1.KubernetesAuthz{ Roles: roles, }} @@ -108,7 +108,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Expect(resource.Status.ClientConfigMap).To(Equal(feast.GetFeastServiceName(services.ClientFeastType))) Expect(resource.Status.Applied.FeastProject).To(Equal(resource.Spec.FeastProject)) expectedAuthzConfig := &feastdevv1alpha1.AuthzConfig{ - KubernetesAuth: &feastdevv1alpha1.KubernetesAuth{ + KubernetesAuthz: &feastdevv1alpha1.KubernetesAuthz{ Roles: roles, }, } @@ -155,7 +155,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { Expect(cond.Status).To(Equal(metav1.ConditionTrue)) Expect(cond.Reason).To(Equal(feastdevv1alpha1.ReadyReason)) Expect(cond.Type).To(Equal(feastdevv1alpha1.AuthorizationReadyType)) - Expect(cond.Message).To(Equal(feastdevv1alpha1.KubernetesAuthReadyMessage)) + Expect(cond.Message).To(Equal(feastdevv1alpha1.KubernetesAuthzReadyMessage)) cond = apimeta.FindStatusCondition(resource.Status.Conditions, feastdevv1alpha1.RegistryReadyType) Expect(cond).ToNot(BeNil()) @@ -297,7 +297,7 @@ var _ = Describe("FeatureStore Controller-Kubernetes authorization", func() { By("Updating the user roled and reconciling") resourceNew := resource.DeepCopy() rolesNew := roles[1:] - resourceNew.Spec.AuthzConfig.KubernetesAuth.Roles = rolesNew + resourceNew.Spec.AuthzConfig.KubernetesAuthz.Roles = rolesNew err = k8sClient.Update(ctx, resourceNew) Expect(err).NotTo(HaveOccurred()) _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ diff --git a/infra/feast-operator/internal/controller/services/repo_config.go b/infra/feast-operator/internal/controller/services/repo_config.go index b60847ddaff..6e8bd5f0482 100644 --- a/infra/feast-operator/internal/controller/services/repo_config.go +++ b/infra/feast-operator/internal/controller/services/repo_config.go @@ -233,12 +233,12 @@ func getClientRepoConfig(featureStore *feastdevv1alpha1.FeatureStore) RepoConfig } } - if status.Applied.AuthzConfig.KubernetesAuth == nil { + if status.Applied.AuthzConfig.KubernetesAuthz == nil { clientRepoConfig.AuthzConfig = AuthzConfig{ Type: NoAuthAuthType, } } else { - if status.Applied.AuthzConfig.KubernetesAuth != nil { + if status.Applied.AuthzConfig.KubernetesAuthz != nil { clientRepoConfig.AuthzConfig = AuthzConfig{ Type: KubernetesAuthType, } diff --git a/infra/feast-operator/internal/controller/services/repo_config_test.go b/infra/feast-operator/internal/controller/services/repo_config_test.go index dd93c74586e..5960c1adeae 100644 --- a/infra/feast-operator/internal/controller/services/repo_config_test.go +++ b/infra/feast-operator/internal/controller/services/repo_config_test.go @@ -198,7 +198,7 @@ var _ = Describe("Repo Config", func() { By("Having kubernetes authorization") featureStore = minimalFeatureStore() featureStore.Spec.AuthzConfig = &feastdevv1alpha1.AuthzConfig{ - KubernetesAuth: &feastdevv1alpha1.KubernetesAuth{}, + KubernetesAuthz: &feastdevv1alpha1.KubernetesAuthz{}, } featureStore.Spec.Services = &feastdevv1alpha1.FeatureStoreServices{ OfflineStore: &feastdevv1alpha1.OfflineStore{ diff --git a/infra/feast-operator/internal/controller/services/services_types.go b/infra/feast-operator/internal/controller/services/services_types.go index bf5c5337f55..2e5b1f1a4e5 100644 --- a/infra/feast-operator/internal/controller/services/services_types.go +++ b/infra/feast-operator/internal/controller/services/services_types.go @@ -59,8 +59,8 @@ const ( LocalProviderType FeastProviderType = "local" - NoAuthAuthType AuthType = "no_auth" - KubernetesAuthType AuthType = "kubernetes" + NoAuthAuthType AuthzType = "no_auth" + KubernetesAuthType AuthzType = "kubernetes" ) var ( @@ -140,8 +140,8 @@ var ( } ) -// AuthType defines the authorization type -type AuthType string +// AuthzType defines the authorization type +type AuthzType string // FeastServiceType is the type of feast service type FeastServiceType string @@ -200,7 +200,7 @@ type RegistryConfig struct { // AuthzConfig is the RBAC authorization configuration. type AuthzConfig struct { - Type AuthType `yaml:"type,omitempty"` + Type AuthzType `yaml:"type,omitempty"` } type deploymentSettings struct {