diff --git a/.secrets.baseline b/.secrets.baseline index 0eefabade70..cc84f3f96b7 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -957,7 +957,7 @@ "filename": "infra/feast-operator/api/v1/featurestore_types.go", "hashed_secret": "44e17306b837162269a410204daaa5ecee4ec22c", "is_verified": false, - "line_number": 936 + "line_number": 941 } ], "infra/feast-operator/api/v1/zz_generated.deepcopy.go": [ @@ -1555,5 +1555,5 @@ } ] }, - "generated_at": "2026-06-26T06:19:05Z" + "generated_at": "2026-07-20T18:19:30Z" } diff --git a/infra/feast-operator/api/v1/featurestore_types.go b/infra/feast-operator/api/v1/featurestore_types.go index f0331ecb1b4..bbee3ed204b 100644 --- a/infra/feast-operator/api/v1/featurestore_types.go +++ b/infra/feast-operator/api/v1/featurestore_types.go @@ -544,6 +544,11 @@ type OnlineStore struct { // Controls metrics granularity, offline push batching, and MCP. // +optional Serving *ServingConfig `json:"serving,omitempty"` + // Disabled skips deploying the online store service entirely, including its + // serving pod and persistence. Omitting the online store block, or setting + // this to false, deploys the online store with defaults as before. + // +optional + Disabled bool `json:"disabled,omitempty"` } // ServingConfig configures the feature_server section of the generated feature_store.yaml. 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 11650995e04..b8354fe618a 100644 --- a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +++ b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml @@ -2258,6 +2258,11 @@ spec: onlineStore: description: OnlineStore configures the online store service properties: + disabled: + description: |- + Disabled skips deploying the online store service entirely, including its + serving pod and persistence. + type: boolean persistence: description: OnlineStorePersistence configures the persistence settings for the online store service @@ -8561,6 +8566,11 @@ spec: onlineStore: description: OnlineStore configures the online store service properties: + disabled: + description: |- + Disabled skips deploying the online store service entirely, including its + serving pod and persistence. + type: boolean persistence: description: OnlineStorePersistence configures the persistence settings for the online store service diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index 57853e4b83f..b9c1ae30cc6 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -2266,6 +2266,11 @@ spec: onlineStore: description: OnlineStore configures the online store service properties: + disabled: + description: |- + Disabled skips deploying the online store service entirely, including its + serving pod and persistence. + type: boolean persistence: description: OnlineStorePersistence configures the persistence settings for the online store service @@ -8569,6 +8574,11 @@ spec: onlineStore: description: OnlineStore configures the online store service properties: + disabled: + description: |- + Disabled skips deploying the online store service entirely, including its + serving pod and persistence. + type: boolean persistence: description: OnlineStorePersistence configures the persistence settings for the online store service diff --git a/infra/feast-operator/docs/api/markdown/ref.md b/infra/feast-operator/docs/api/markdown/ref.md index 0a7782feb2a..27dcf56a307 100644 --- a/infra/feast-operator/docs/api/markdown/ref.md +++ b/infra/feast-operator/docs/api/markdown/ref.md @@ -654,6 +654,9 @@ _Appears in:_ | `persistence` _[OnlineStorePersistence](#onlinestorepersistence)_ | | | `serving` _[ServingConfig](#servingconfig)_ | Serving configures the Feast feature_server section written into feature_store.yaml for the online serve pod. Controls metrics granularity, offline push batching, and MCP. | +| `disabled` _boolean_ | Disabled skips deploying the online store service entirely, including its +serving pod and persistence. Omitting the online store block, or setting +this to false, deploys the online store with defaults as before. | #### OnlineStoreDBStorePersistence diff --git a/infra/feast-operator/internal/controller/services/services.go b/infra/feast-operator/internal/controller/services/services.go index c6271c28d3a..0290ba2a0b0 100644 --- a/infra/feast-operator/internal/controller/services/services.go +++ b/infra/feast-operator/internal/controller/services/services.go @@ -1274,7 +1274,7 @@ func (feast *FeastServices) isOnlineServer() bool { func (feast *FeastServices) isOnlineStore() bool { appliedServices := feast.Handler.FeatureStore.Status.Applied.Services - return appliedServices != nil && appliedServices.OnlineStore != nil + return appliedServices != nil && appliedServices.OnlineStore != nil && !appliedServices.OnlineStore.Disabled } func (feast *FeastServices) noLocalCoreServerConfigured() bool { diff --git a/infra/feast-operator/internal/controller/services/util.go b/infra/feast-operator/internal/controller/services/util.go index 387ccdb631b..7b04a75183d 100644 --- a/infra/feast-operator/internal/controller/services/util.go +++ b/infra/feast-operator/internal/controller/services/util.go @@ -163,30 +163,32 @@ func ApplyDefaultsToStatus(cr *feastdevv1.FeatureStore) { } } - // default to onlineStore service deployment + // default to onlineStore service deployment unless it is explicitly disabled if services.OnlineStore == nil { services.OnlineStore = &feastdevv1.OnlineStore{} } - if services.OnlineStore.Persistence == nil { - services.OnlineStore.Persistence = &feastdevv1.OnlineStorePersistence{} - } - - if services.OnlineStore.Persistence.DBPersistence == nil { - if services.OnlineStore.Persistence.FilePersistence == nil { - services.OnlineStore.Persistence.FilePersistence = &feastdevv1.OnlineStoreFilePersistence{} + if !services.OnlineStore.Disabled { + if services.OnlineStore.Persistence == nil { + services.OnlineStore.Persistence = &feastdevv1.OnlineStorePersistence{} } - if len(services.OnlineStore.Persistence.FilePersistence.Path) == 0 { - services.OnlineStore.Persistence.FilePersistence.Path = defaultOnlineStorePath(cr) - } + if services.OnlineStore.Persistence.DBPersistence == nil { + if services.OnlineStore.Persistence.FilePersistence == nil { + services.OnlineStore.Persistence.FilePersistence = &feastdevv1.OnlineStoreFilePersistence{} + } - ensurePVCDefaults(services.OnlineStore.Persistence.FilePersistence.PvcConfig, OnlineFeastType) - } + if len(services.OnlineStore.Persistence.FilePersistence.Path) == 0 { + services.OnlineStore.Persistence.FilePersistence.Path = defaultOnlineStorePath(cr) + } + + ensurePVCDefaults(services.OnlineStore.Persistence.FilePersistence.PvcConfig, OnlineFeastType) + } - if services.OnlineStore.Server == nil { - services.OnlineStore.Server = &feastdevv1.ServerConfigs{} + if services.OnlineStore.Server == nil { + services.OnlineStore.Server = &feastdevv1.ServerConfigs{} + } + setDefaultCtrConfigs(&services.OnlineStore.Server.ContainerConfigs.DefaultCtrConfigs) } - setDefaultCtrConfigs(&services.OnlineStore.Server.ContainerConfigs.DefaultCtrConfigs) if services.UI != nil { setDefaultCtrConfigs(&services.UI.ContainerConfigs.DefaultCtrConfigs) diff --git a/infra/feast-operator/internal/controller/services/util_test.go b/infra/feast-operator/internal/controller/services/util_test.go new file mode 100644 index 00000000000..89c3fa55a72 --- /dev/null +++ b/infra/feast-operator/internal/controller/services/util_test.go @@ -0,0 +1,83 @@ +/* +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 services + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + feastdevv1 "github.com/feast-dev/feast/infra/feast-operator/api/v1" +) + +var _ = Describe("ApplyDefaultsToStatus", func() { + It("deploys the online store with defaults when it is not declared", func() { + cr := &feastdevv1.FeatureStore{ + Spec: feastdevv1.FeatureStoreSpec{ + FeastProject: "test_project", + Services: &feastdevv1.FeatureStoreServices{}, + }, + } + + ApplyDefaultsToStatus(cr) + + online := cr.Status.Applied.Services.OnlineStore + Expect(online).ToNot(BeNil()) + Expect(online.Disabled).To(BeFalse()) + Expect(online.Persistence).ToNot(BeNil()) + Expect(online.Server).ToNot(BeNil()) + }) + + It("applies online store defaults when it is declared", func() { + cr := &feastdevv1.FeatureStore{ + Spec: feastdevv1.FeatureStoreSpec{ + FeastProject: "test_project", + Services: &feastdevv1.FeatureStoreServices{ + OnlineStore: &feastdevv1.OnlineStore{}, + }, + }, + } + + ApplyDefaultsToStatus(cr) + + online := cr.Status.Applied.Services.OnlineStore + Expect(online).ToNot(BeNil()) + Expect(online.Persistence).ToNot(BeNil()) + Expect(online.Server).ToNot(BeNil()) + }) + + // #6586: disabling the online store opts out of its persistence and serving + // pod, letting a registry-only or offline-only FeatureStore skip it while + // leaving the default-on behavior unchanged for everyone else. + It("does not apply persistence or server defaults when the online store is disabled", func() { + cr := &feastdevv1.FeatureStore{ + Spec: feastdevv1.FeatureStoreSpec{ + FeastProject: "test_project", + Services: &feastdevv1.FeatureStoreServices{ + OnlineStore: &feastdevv1.OnlineStore{Disabled: true}, + }, + }, + } + + ApplyDefaultsToStatus(cr) + + online := cr.Status.Applied.Services.OnlineStore + Expect(online).ToNot(BeNil()) + Expect(online.Disabled).To(BeTrue()) + Expect(online.Persistence).To(BeNil()) + Expect(online.Server).To(BeNil()) + }) +})