diff --git a/infra/feast-operator/api/v1alpha1/featurestore_types.go b/infra/feast-operator/api/v1alpha1/featurestore_types.go index f6898a2a839..8e6bc327be9 100644 --- a/infra/feast-operator/api/v1alpha1/featurestore_types.go +++ b/infra/feast-operator/api/v1alpha1/featurestore_types.go @@ -278,6 +278,7 @@ type FeatureStoreServices struct { // Creates a UI server container UI *ServerConfigs `json:"ui,omitempty"` DeploymentStrategy *appsv1.DeploymentStrategy `json:"deploymentStrategy,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` // Disable the 'feast repo initialization' initContainer DisableInitContainers bool `json:"disableInitContainers,omitempty"` // Volumes specifies the volumes to mount in the FeatureStore deployment. A corresponding `VolumeMount` should be added to whichever feast service(s) require access to said volume(s). diff --git a/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go b/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go index 4fc9b417445..6c0bc66d9df 100644 --- a/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/infra/feast-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -303,6 +303,11 @@ func (in *FeatureStoreServices) DeepCopyInto(out *FeatureStoreServices) { *out = new(appsv1.DeploymentStrategy) (*in).DeepCopyInto(*out) } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } if in.Volumes != nil { in, out := &in.Volumes, &out.Volumes *out = make([]v1.Volume, len(*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 0a08b0a62b3..bfdee79f6b8 100644 --- a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +++ b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml @@ -2125,6 +2125,137 @@ spec: x-kubernetes-validations: - message: One selection required. rule: '[has(self.local), has(self.remote)].exists_one(c, c)' + securityContext: + description: PodSecurityContext holds pod-level security attributes + and common container settings. + properties: + appArmorProfile: + description: appArmorProfile is the AppArmor options to use + by the containers in this pod. + properties: + localhostProfile: + description: localhostProfile indicates a profile loaded + on the node that should be used. + type: string + type: + description: type indicates which kind of AppArmor profile + will be applied. + type: string + required: + - type + type: object + fsGroup: + description: A special supplemental group that applies to + all containers in a pod. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. + type: string + type: + description: type indicates which kind of seccomp profile + will be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsG + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: The Windows specific settings applied to all + containers. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. + type: string + type: object + type: object ui: description: Creates a UI server container properties: @@ -5975,6 +6106,139 @@ spec: - message: One selection required. rule: '[has(self.local), has(self.remote)].exists_one(c, c)' + securityContext: + description: PodSecurityContext holds pod-level security attributes + and common container settings. + properties: + appArmorProfile: + description: appArmorProfile is the AppArmor options to + use by the containers in this pod. + properties: + localhostProfile: + description: localhostProfile indicates a profile + loaded on the node that should be used. + type: string + type: + description: type indicates which kind of AppArmor + profile will be applied. + type: string + required: + - type + type: object + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as + a non-root user. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all + containers. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os. + properties: + localhostProfile: + description: localhostProfile indicates a profile + defined in a file on the node should be used. + type: string + type: + description: type indicates which kind of seccomp + profile will be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsG + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + description: Sysctls hold a list of namespaced sysctls + used for the pod. + items: + description: Sysctl defines a kernel parameter to be + set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: The Windows specific settings applied to + all containers. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of + the GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container + should be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. + type: string + type: object + type: object ui: description: Creates a UI server container properties: diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index d6d31715ec7..2df873249fe 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -2133,6 +2133,137 @@ spec: x-kubernetes-validations: - message: One selection required. rule: '[has(self.local), has(self.remote)].exists_one(c, c)' + securityContext: + description: PodSecurityContext holds pod-level security attributes + and common container settings. + properties: + appArmorProfile: + description: appArmorProfile is the AppArmor options to use + by the containers in this pod. + properties: + localhostProfile: + description: localhostProfile indicates a profile loaded + on the node that should be used. + type: string + type: + description: type indicates which kind of AppArmor profile + will be applied. + type: string + required: + - type + type: object + fsGroup: + description: A special supplemental group that applies to + all containers in a pod. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. + type: string + type: + description: type indicates which kind of seccomp profile + will be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsG + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: The Windows specific settings applied to all + containers. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. + type: string + type: object + type: object ui: description: Creates a UI server container properties: @@ -5983,6 +6114,139 @@ spec: - message: One selection required. rule: '[has(self.local), has(self.remote)].exists_one(c, c)' + securityContext: + description: PodSecurityContext holds pod-level security attributes + and common container settings. + properties: + appArmorProfile: + description: appArmorProfile is the AppArmor options to + use by the containers in this pod. + properties: + localhostProfile: + description: localhostProfile indicates a profile + loaded on the node that should be used. + type: string + type: + description: type indicates which kind of AppArmor + profile will be applied. + type: string + required: + - type + type: object + fsGroup: + description: A special supplemental group that applies + to all containers in a pod. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as + a non-root user. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all + containers. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os. + properties: + localhostProfile: + description: localhostProfile indicates a profile + defined in a file on the node should be used. + type: string + type: + description: type indicates which kind of seccomp + profile will be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in addition + to the container's primary GID, the fsG + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + sysctls: + description: Sysctls hold a list of namespaced sysctls + used for the pod. + items: + description: Sysctl defines a kernel parameter to be + set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: The Windows specific settings applied to + all containers. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of + the GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container + should be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. + type: string + type: object + type: object ui: description: Creates a UI server container properties: diff --git a/infra/feast-operator/docs/api/markdown/ref.md b/infra/feast-operator/docs/api/markdown/ref.md index 0082ede4000..2e90f0c7b91 100644 --- a/infra/feast-operator/docs/api/markdown/ref.md +++ b/infra/feast-operator/docs/api/markdown/ref.md @@ -199,6 +199,7 @@ _Appears in:_ | `registry` _[Registry](#registry)_ | | | `ui` _[ServerConfigs](#serverconfigs)_ | Creates a UI server container | | `deploymentStrategy` _[DeploymentStrategy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#deploymentstrategy-v1-apps)_ | | +| `securityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#podsecuritycontext-v1-core)_ | | | `disableInitContainers` _boolean_ | Disable the 'feast repo initialization' initContainer | | `volumes` _[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#volume-v1-core) array_ | Volumes specifies the volumes to mount in the FeatureStore deployment. A corresponding `VolumeMount` should be added to whichever feast service(s) require access to said volume(s). | diff --git a/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go b/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go index a2cce9e9796..ff273c78648 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_pvc_test.go @@ -68,6 +68,10 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { onlineStoreMountPath := "/online" registryMountPath := "/registry" + securityContext := &corev1.PodSecurityContext{ + FSGroup: int64Ptr(0), + } + accessModes := []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce, corev1.ReadWriteMany} storageClassName := "test" @@ -284,6 +288,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Expect(deploy.Spec.Replicas).To(Equal(int32Ptr(1))) Expect(controllerutil.HasControllerReference(deploy)).To(BeTrue()) Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(3)) + Expect(deploy.Spec.Template.Spec.SecurityContext).NotTo(Equal(securityContext)) Expect(deploy.Spec.Template.Spec.Volumes).To(HaveLen(3)) Expect(deploy.Spec.Template.Spec.Volumes).NotTo(ContainElement(ephemeralVolume)) name := feast.GetFeastServiceName(services.RegistryFeastType) @@ -420,12 +425,20 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { Scheme: k8sClient.Scheme(), } - _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + resource := &feastdevv1alpha1.FeatureStore{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + resource.Spec.Services.SecurityContext = securityContext + + err = k8sClient.Update(ctx, resource) + 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()) @@ -466,6 +479,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() { }, deploy) Expect(err).NotTo(HaveOccurred()) Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(3)) + Expect(deploy.Spec.Template.Spec.SecurityContext).To(Equal(securityContext)) registryContainer := services.GetRegistryContainer(*deploy) Expect(registryContainer.Env).To(HaveLen(1)) env := getFeatureStoreYamlEnvVar(registryContainer.Env) diff --git a/infra/feast-operator/internal/controller/featurestore_controller_test.go b/infra/feast-operator/internal/controller/featurestore_controller_test.go index b6ed2ee5bf9..efbd69c78e9 100644 --- a/infra/feast-operator/internal/controller/featurestore_controller_test.go +++ b/infra/feast-operator/internal/controller/featurestore_controller_test.go @@ -1407,3 +1407,7 @@ func strPtr(str string) *string { func int32Ptr(value int32) *int32 { return &value } + +func int64Ptr(value int64) *int64 { + return &value +} diff --git a/infra/feast-operator/internal/controller/services/services.go b/infra/feast-operator/internal/controller/services/services.go index eef18825500..5cc9cc11267 100644 --- a/infra/feast-operator/internal/controller/services/services.go +++ b/infra/feast-operator/internal/controller/services/services.go @@ -331,6 +331,7 @@ func (feast *FeastServices) createPVC(pvcCreate *feastdevv1alpha1.PvcCreate, fea } func (feast *FeastServices) setDeployment(deploy *appsv1.Deployment) error { + cr := feast.Handler.FeatureStore replicas := deploy.Spec.Replicas deploy.Labels = feast.getLabels() @@ -344,13 +345,14 @@ func (feast *FeastServices) setDeployment(deploy *appsv1.Deployment) error { }, Spec: corev1.PodSpec{ ServiceAccountName: feast.initFeastSA().Name, + SecurityContext: cr.Status.Applied.Services.SecurityContext, }, }, } if err := feast.setPod(&deploy.Spec.Template.Spec); err != nil { return err } - return controllerutil.SetControllerReference(feast.Handler.FeatureStore, deploy, feast.Handler.Scheme) + return controllerutil.SetControllerReference(cr, deploy, feast.Handler.Scheme) } func (feast *FeastServices) setPod(podSpec *corev1.PodSpec) error {