From bb7ffbbfdde8677030f0a3e5515f96fb8a7fc95c Mon Sep 17 00:00:00 2001 From: ntkathole Date: Wed, 13 May 2026 20:34:46 +0530 Subject: [PATCH] fix: Add missing feast init templates to operator CRD and enhance persistence documentation Signed-off-by: ntkathole --- .../feast-operator/02-persistence.md | 703 ++++++++++++++++++ .../api/v1/featurestore_types.go | 2 +- .../api/v1alpha1/featurestore_types.go | 2 +- .../manifests/feast.dev_featurestores.yaml | 16 + .../crd/bases/feast.dev_featurestores.yaml | 16 + infra/feast-operator/dist/install.yaml | 16 + 6 files changed, 753 insertions(+), 2 deletions(-) diff --git a/docs/how-to-guides/feast-operator/02-persistence.md b/docs/how-to-guides/feast-operator/02-persistence.md index 1097e68a6ce..fe8c445e77e 100644 --- a/docs/how-to-guides/feast-operator/02-persistence.md +++ b/docs/how-to-guides/feast-operator/02-persistence.md @@ -294,6 +294,709 @@ services: --- +## Overriding the Secret key name + +By default the operator looks up the Secret key that matches `persistence.store.type` (e.g. +`type: postgres` → key `postgres`). To use a different key, set `secretKeyName`: + +```yaml +services: + onlineStore: + persistence: + store: + type: postgres + secretRef: + name: feast-data-stores + secretKeyName: my_custom_key # reads key "my_custom_key" instead of "postgres" +``` + +This is useful when a single Secret holds configuration for multiple stores of the same type, +or when you want a more descriptive key name. + +--- + +## Validation rules + +The operator enforces these rules on Secret values at reconciliation time: + +1. The Secret key value must be **valid YAML** that deserializes to a map. +2. If the YAML contains a `type` field, its value **must match** the CR's `persistence.store.type`. + Otherwise the operator rejects it with an error. Best practice: omit `type` from the Secret. +3. If the YAML contains a `registry_type` field (for registry stores), the same matching rule applies. +4. The Secret must exist in the **same namespace** as the FeatureStore CR. +5. Only **one** of `file` or `store` may be set under each persistence block (enforced by CRD validation). + +--- + +## Complete Secret examples by store type + +Below are copy-paste-ready Secret YAML snippets for every operator-supported store type. +Each snippet shows the Secret data key and the YAML value the operator expects. + +> **Note**: omit the `type` field from Secret values — the operator injects it from +> `persistence.store.type`. Including a matching `type` value is tolerated but not +> recommended. + +### Online store Secrets + +#### Redis + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + redis: | + connection_string: redis.feast.svc.cluster.local:6379 +``` + +Redis Cluster with SSL: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + redis: | + redis_type: redis_cluster + connection_string: "redis1:6379,redis2:6379,ssl=true,password=my_password" +``` + +CR snippet: + +```yaml +services: + onlineStore: + persistence: + store: + type: redis + secretRef: + name: feast-online-store +``` + +SDK reference: [Redis](../reference/online-stores/redis.md) + +--- + +#### Postgres + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + postgres: | + host: postgres.feast.svc.cluster.local + port: 5432 + database: feast + db_schema: public + user: feast + password: feast +``` + +With SSL: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + postgres: | + host: postgres.feast.svc.cluster.local + port: 5432 + database: feast + db_schema: public + user: feast + password: feast + sslmode: verify-ca + sslrootcert_path: /path/to/server-ca.pem +``` + +CR snippet: + +```yaml +services: + onlineStore: + persistence: + store: + type: postgres + secretRef: + name: feast-online-store +``` + +SDK reference: [Postgres](../reference/online-stores/postgres.md) + +--- + +#### Cassandra + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + cassandra: | + hosts: + - 192.168.1.1 + - 192.168.1.2 + - 192.168.1.3 + keyspace: KeyspaceName + port: 9042 + username: user + password: secret + protocol_version: 5 + load_balancing: + local_dc: datacenter1 + load_balancing_policy: TokenAwarePolicy(DCAwareRoundRobinPolicy) + read_concurrency: 100 + write_concurrency: 100 +``` + +CR snippet: + +```yaml +services: + onlineStore: + persistence: + store: + type: cassandra + secretRef: + name: feast-online-store +``` + +SDK reference: [Cassandra](../reference/online-stores/cassandra.md) + +--- + +#### Snowflake (online) + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + snowflake.online: | + account: snowflake_deployment.us-east-1 + user: user_login + password: user_password + role: SYSADMIN + warehouse: COMPUTE_WH + database: FEAST +``` + +CR snippet: + +```yaml +services: + onlineStore: + persistence: + store: + type: snowflake.online + secretRef: + name: feast-online-store +``` + +SDK reference: [Snowflake](../reference/online-stores/snowflake.md) + +--- + +#### DynamoDB + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + dynamodb: | + region: us-west-2 + batch_size: 100 +``` + +CR snippet: + +```yaml +services: + onlineStore: + persistence: + store: + type: dynamodb + secretRef: + name: feast-online-store +``` + +SDK reference: [DynamoDB](../reference/online-stores/dynamodb.md) + +--- + +#### Bigtable + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + bigtable: | + project_id: my_gcp_project + instance: my_bigtable_instance +``` + +CR snippet: + +```yaml +services: + onlineStore: + persistence: + store: + type: bigtable + secretRef: + name: feast-online-store +``` + +SDK reference: [Bigtable](../reference/online-stores/bigtable.md) + +--- + +#### Datastore + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + datastore: | + project_id: my_gcp_project + namespace: my_datastore_namespace +``` + +CR snippet: + +```yaml +services: + onlineStore: + persistence: + store: + type: datastore + secretRef: + name: feast-online-store +``` + +SDK reference: [Datastore](../reference/online-stores/datastore.md) + +--- + +#### MySQL + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + mysql: | + host: mysql.feast.svc.cluster.local + port: 3306 + database: feast + user: feast + password: feast +``` + +CR snippet: + +```yaml +services: + onlineStore: + persistence: + store: + type: mysql + secretRef: + name: feast-online-store +``` + +SDK reference: [MySQL](../reference/online-stores/mysql.md) + +--- + +#### Hazelcast + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + hazelcast: | + cluster_name: dev + cluster_members: + - "localhost:5701" + key_ttl_seconds: 36000 +``` + +CR snippet: + +```yaml +services: + onlineStore: + persistence: + store: + type: hazelcast + secretRef: + name: feast-online-store +``` + +SDK reference: [Hazelcast](../reference/online-stores/hazelcast.md) + +--- + +#### HBase + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-online-store +stringData: + hbase: | + host: hbase-thrift.feast.svc.cluster.local + port: "9090" + connection_pool_size: 4 +``` + +CR snippet: + +```yaml +services: + onlineStore: + persistence: + store: + type: hbase + secretRef: + name: feast-online-store +``` + +SDK reference: [HBase](../reference/online-stores/hbase.md) + +--- + +#### Other supported online store types + +The following types also use the same pattern (`persistence.store.type` + `secretRef`). +Place the driver-specific YAML keys from the SDK docs under the matching Secret key: + +| `type` | Secret key | SDK docs | +|--------|------------|----------| +| `sqlite` | `sqlite` | [SQLite](../reference/online-stores/sqlite.md) | +| `singlestore` | `singlestore` | [SingleStore](../reference/online-stores/singlestore.md) | +| `elasticsearch` | `elasticsearch` | [Elasticsearch](../reference/online-stores/elasticsearch.md) | +| `qdrant` | `qdrant` | [Qdrant](../reference/online-stores/qdrant.md) | +| `couchbase.online` | `couchbase.online` | [Couchbase](../reference/online-stores/couchbase.md) | +| `milvus` | `milvus` | [Milvus](../reference/online-stores/milvus.md) | +| `mongodb` | `mongodb` | [MongoDB](../reference/online-stores/mongodb.md) | +| `hybrid` | `hybrid` | [Hybrid](../reference/online-stores/hybrid.md) | + +--- + +### Offline store Secrets + +Offline DB stores follow the same pattern. The `type` field tells the operator which +store driver to use; the Secret value holds the connection parameters. + +#### Snowflake (offline) + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-offline-store +stringData: + snowflake.offline: | + account: snowflake_deployment.us-east-1 + user: user_login + password: user_password + role: SYSADMIN + warehouse: COMPUTE_WH + database: FEAST + schema: PUBLIC +``` + +CR snippet: + +```yaml +services: + offlineStore: + persistence: + store: + type: snowflake.offline + secretRef: + name: feast-offline-store +``` + +SDK reference: [Snowflake](../reference/offline-stores/snowflake.md) + +--- + +#### BigQuery + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-offline-store +stringData: + bigquery: | + dataset: feast_bq_dataset + project_id: my_gcp_project +``` + +CR snippet: + +```yaml +services: + offlineStore: + persistence: + store: + type: bigquery + secretRef: + name: feast-offline-store +``` + +SDK reference: [BigQuery](../reference/offline-stores/bigquery.md) + +--- + +#### Postgres (offline) + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-offline-store +stringData: + postgres: | + host: postgres.feast.svc.cluster.local + port: 5432 + database: feast + db_schema: public + user: feast + password: feast +``` + +CR snippet: + +```yaml +services: + offlineStore: + persistence: + store: + type: postgres + secretRef: + name: feast-offline-store +``` + +SDK reference: [Postgres](../reference/offline-stores/postgres.md) + +--- + +#### Other supported offline store types + +| `type` | Secret key | SDK docs | +|--------|------------|----------| +| `redshift` | `redshift` | [Redshift](../reference/offline-stores/redshift.md) | +| `spark` | `spark` | [Spark](../reference/offline-stores/spark.md) | +| `trino` | `trino` | [Trino](../reference/offline-stores/trino.md) | +| `athena` | `athena` | [Athena](../reference/offline-stores/athena.md) | +| `mssql` | `mssql` | [MSSQL](../reference/offline-stores/mssql.md) | +| `couchbase.offline` | `couchbase.offline` | [Couchbase](../reference/offline-stores/couchbase.md) | +| `clickhouse` | `clickhouse` | [ClickHouse](../reference/offline-stores/clickhouse.md) | +| `ray` | `ray` | [Ray](../reference/offline-stores/ray.md) | +| `oracle` | `oracle` | [Oracle](../reference/offline-stores/oracle.md) | + +--- + +### Registry Secrets + +#### SQL (SQLAlchemy) registry + +The most common production registry. Uses a SQLAlchemy URL to connect to PostgreSQL, +MySQL, or SQLite: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-registry-store +stringData: + sql: | + path: postgresql+psycopg://feast:feast@postgres.feast.svc.cluster.local:5432/feast #pragma: allowlist secret + cache_ttl_seconds: 60 + sqlalchemy_config_kwargs: + echo: false + pool_pre_ping: true +``` + +CR snippet: + +```yaml +services: + registry: + local: + persistence: + store: + type: sql + secretRef: + name: feast-registry-store +``` + +SDK reference: [SQL Registry](../reference/registries/sql.md) + +--- + +#### Snowflake registry + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-registry-store +stringData: + snowflake.registry: | + account: snowflake_deployment.us-east-1 + user: user_login + password: user_password + role: SYSADMIN + warehouse: COMPUTE_WH + database: FEAST + schema: PUBLIC + cache_ttl_seconds: 60 +``` + +CR snippet: + +```yaml +services: + registry: + local: + persistence: + store: + type: snowflake.registry + secretRef: + name: feast-registry-store +``` + +SDK reference: [Snowflake Registry](../reference/registries/snowflake.md) + +--- + +## Multi-store Secret (single Secret for all components) + +You can combine all store configurations into a single Secret: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: feast-data-stores +stringData: + redis: | + connection_string: redis.feast.svc.cluster.local:6379 + snowflake.offline: | + account: snowflake_deployment.us-east-1 + user: user_login + password: user_password + role: SYSADMIN + warehouse: COMPUTE_WH + database: FEAST + schema: PUBLIC + sql: | + path: postgresql+psycopg://feast:feast@postgres.feast.svc.cluster.local:5432/feast #pragma: allowlist secret + cache_ttl_seconds: 60 +--- +apiVersion: feast.dev/v1 +kind: FeatureStore +metadata: + name: production-store +spec: + feastProject: my_project + services: + onlineStore: + persistence: + store: + type: redis + secretRef: + name: feast-data-stores + offlineStore: + persistence: + store: + type: snowflake.offline + secretRef: + name: feast-data-stores + registry: + local: + persistence: + store: + type: sql + secretRef: + name: feast-data-stores +``` + +--- + +## ConfigMap usage (batch engine) + +The `batchEngine` is the only operator component that uses a **ConfigMap** rather than a +Secret for its configuration. The ConfigMap must contain a YAML value under key `config` +(default) or the key specified in `configMapKey`. + +Unlike store Secrets, the batch engine ConfigMap value **must include the `type` field**: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: feast-batch-engine +data: + config: | + type: spark + master: local + spark_conf: + spark.executor.memory: 4g +--- +apiVersion: feast.dev/v1 +kind: FeatureStore +metadata: + name: sample +spec: + feastProject: my_project + batchEngine: + configMapRef: + name: feast-batch-engine + # configMapKey: config # optional, defaults to "config" +``` + +See [Guide 6 — Batch & Jobs](06-batch-and-jobs.md) for full details. + +--- + +## Troubleshooting + +| Symptom | Likely cause | Fix | +|---------|-------------|-----| +| `secret key X doesn't exist in secret Y` | The Secret key name doesn't match the store `type` | Either rename the Secret key to match `type`, or set `secretKeyName` in the CR | +| `secret X contains invalid value` | The Secret value is not valid YAML | Check indentation and quoting in the `stringData` value | +| `contains tag named type with value X` | The Secret includes a `type` field that doesn't match the CR's `persistence.store.type` | Remove `type` from the Secret value, or correct it to match | +| `invalid secret X for offline store` | The referenced Secret doesn't exist | Create the Secret in the same namespace as the FeatureStore CR | +| `One selection required between file or store` | Both `file` and `store` are set under a persistence block | Keep only one — choose either `file` persistence or `store` (DB) persistence | + +--- + ## See also - [API reference — `OnlineStorePersistence`](https://github.com/feast-dev/feast/blob/stable/infra/feast-operator/docs/api/markdown/ref.md#onlinestorepersistence) diff --git a/infra/feast-operator/api/v1/featurestore_types.go b/infra/feast-operator/api/v1/featurestore_types.go index 81e1dfa14c1..deeff9b4e52 100644 --- a/infra/feast-operator/api/v1/featurestore_types.go +++ b/infra/feast-operator/api/v1/featurestore_types.go @@ -170,7 +170,7 @@ type GitCloneOptions struct { type FeastInitOptions struct { Minimal bool `json:"minimal,omitempty"` // Template for the created project - // +kubebuilder:validation:Enum=local;gcp;aws;snowflake;spark;postgres;hbase;cassandra;hazelcast;couchbase;clickhouse + // +kubebuilder:validation:Enum=local;gcp;aws;snowflake;spark;postgres;hbase;cassandra;hazelcast;couchbase;clickhouse;milvus;ray;ray_rag;pytorch_nlp Template string `json:"template,omitempty"` } diff --git a/infra/feast-operator/api/v1alpha1/featurestore_types.go b/infra/feast-operator/api/v1alpha1/featurestore_types.go index d9c85c93136..87d13003805 100644 --- a/infra/feast-operator/api/v1alpha1/featurestore_types.go +++ b/infra/feast-operator/api/v1alpha1/featurestore_types.go @@ -105,7 +105,7 @@ type GitCloneOptions struct { type FeastInitOptions struct { Minimal bool `json:"minimal,omitempty"` // Template for the created project - // +kubebuilder:validation:Enum=local;gcp;aws;snowflake;spark;postgres;hbase;cassandra;hazelcast;couchbase;clickhouse + // +kubebuilder:validation:Enum=local;gcp;aws;snowflake;spark;postgres;hbase;cassandra;hazelcast;couchbase;clickhouse;milvus;ray;ray_rag;pytorch_nlp Template string `json:"template,omitempty"` } diff --git a/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml b/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml index 2241b4c5f2e..41651161cc4 100644 --- a/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml +++ b/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml @@ -735,6 +735,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object @@ -6693,6 +6697,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object @@ -12791,6 +12799,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object @@ -17054,6 +17066,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object 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 e1a1adfabe8..4c09756f2c5 100644 --- a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +++ b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml @@ -735,6 +735,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object @@ -6696,6 +6700,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object @@ -12797,6 +12805,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object @@ -17060,6 +17072,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index c466442b8e8..a1fb4f75b98 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -743,6 +743,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object @@ -6704,6 +6708,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object @@ -12805,6 +12813,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object @@ -17068,6 +17080,10 @@ spec: - hazelcast - couchbase - clickhouse + - milvus + - ray + - ray_rag + - pytorch_nlp type: string type: object type: object