From 7af80e8dee8bf43f3c9ca75fd6a42accb3c0898d Mon Sep 17 00:00:00 2001 From: Jeremy Alvis Date: Fri, 4 Sep 2026 11:39:14 -0700 Subject: [PATCH 1/2] Share PostgreSQL with Substrate Configure embedded Substrate to use Kagent's selected PostgreSQL database through a shared Secret and a separate schema. Support bundled, external, shared existing, and independently configured Substrate databases, with examples for each installation mode. Signed-off-by: Jeremy Alvis --- helm/README.md | 38 +++++++++++ .../templates/controller-deployment.yaml | 10 ++- helm/kagent/templates/postgresql-secret.yaml | 26 ++++++++ .../tests/controller-deployment_test.yaml | 65 +++++++++++++++++++ helm/kagent/tests/postgresql_test.yaml | 59 +++++++++++++++++ helm/kagent/values.yaml | 16 ++++- 6 files changed, 211 insertions(+), 3 deletions(-) diff --git a/helm/README.md b/helm/README.md index 8f924b7716..ab782dcc87 100644 --- a/helm/README.md +++ b/helm/README.md @@ -21,6 +21,44 @@ helm install kagent ./helm/kagent/ --namespace kagent --set providers.default=an helm install kagent ./helm/kagent/ --namespace kagent --set providers.default=azureOpenAI --set providers.azureOpenAI.apiKey=your-openai-api-key ``` +### Substrate PostgreSQL + +Enabling Substrate uses Kagent's bundled PostgreSQL by default. Kagent and +Substrate share the database connection but use separate schemas. + +```yaml +substrate: + enabled: true +``` + +To share an external PostgreSQL connection, configure it once for Kagent: + +```yaml +database: + postgres: + url: postgresql://user:password@database:5432/kagent + bundled: + enabled: false +substrate: + enabled: true +``` + +To give Substrate a separate PostgreSQL connection, disable sharing and set +the Substrate connection directly: + +```yaml +substrate: + enabled: true + postgres: + connectionString: postgresql://user:password@substrate-db:5432/substrate + connectionStringSecretRef: + enabled: false +``` + +For a separate Secret-backed connection, leave `enabled: false` and set +`connectionStringSecretRef.name` and `key`. A pod-local +`database.postgres.urlFile` cannot be shared with Substrate. + ### Using Make ```bash diff --git a/helm/kagent/templates/controller-deployment.yaml b/helm/kagent/templates/controller-deployment.yaml index 6c8d8cacf9..cd707d3264 100644 --- a/helm/kagent/templates/controller-deployment.yaml +++ b/helm/kagent/templates/controller-deployment.yaml @@ -101,7 +101,15 @@ spec: - name: AUTH_USER_ID_CLAIM value: {{ .Values.controller.auth.userIdClaim | quote }} {{- end }} - {{- if .Values.database.postgres.urlFile }} + {{- $substratePostgres := get .Values.substrate "postgres" | default dict }} + {{- $connectionStringSecretRef := get $substratePostgres "connectionStringSecretRef" | default dict }} + {{- if and .Values.substrate.enabled (get $connectionStringSecretRef "enabled") }} + - name: POSTGRES_DATABASE_URL + valueFrom: + secretKeyRef: + name: {{ get $connectionStringSecretRef "name" | default (include "substrate.fullname" (list "postgres-connection" .)) }} + key: {{ get $connectionStringSecretRef "key" | default "connectionString" }} + {{- else if .Values.database.postgres.urlFile }} - name: POSTGRES_DATABASE_URL_FILE value: {{ .Values.database.postgres.urlFile | quote }} {{- else if .Values.database.postgres.url }} diff --git a/helm/kagent/templates/postgresql-secret.yaml b/helm/kagent/templates/postgresql-secret.yaml index 3adb5b3c4e..371f06c125 100644 --- a/helm/kagent/templates/postgresql-secret.yaml +++ b/helm/kagent/templates/postgresql-secret.yaml @@ -11,3 +11,29 @@ type: Opaque data: POSTGRES_PASSWORD: {{ "kagent" | b64enc | quote }} {{- end }} +{{- $substratePostgres := get .Values.substrate "postgres" | default dict -}} +{{- $connectionStringSecretRef := get $substratePostgres "connectionStringSecretRef" | default dict -}} +{{- if and .Values.substrate.enabled (get $connectionStringSecretRef "enabled") (not (get $connectionStringSecretRef "name")) }} +{{- $connectionString := "" -}} +{{- if .Values.database.postgres.urlFile -}} +{{- fail "database.postgres.urlFile cannot configure embedded Substrate; set substrate.postgres.connectionStringSecretRef.name to the Secret containing the URL" -}} +{{- else if .Values.database.postgres.url -}} +{{- $connectionString = .Values.database.postgres.url -}} +{{- else if .Values.database.postgres.bundled.enabled -}} +{{- $connectionString = printf "postgres://kagent:kagent@%s.%s.svc:5432/kagent?sslmode=disable" (include "kagent.postgresqlServiceName" .) (include "kagent.namespace" .) -}} +{{- else -}} +{{- fail "No database connection configured. Set database.postgres.url, substrate.postgres.connectionStringSecretRef.name, or enable database.postgres.bundled." -}} +{{- end }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ get $connectionStringSecretRef "name" | default (include "substrate.fullname" (list "postgres-connection" .)) }} + namespace: {{ include "kagent.namespace" . }} + labels: + {{- include "kagent.labels" . | nindent 4 }} + app.kubernetes.io/component: database +type: Opaque +stringData: + {{ get $connectionStringSecretRef "key" | default "connectionString" }}: {{ $connectionString | quote }} +{{- end }} diff --git a/helm/kagent/tests/controller-deployment_test.yaml b/helm/kagent/tests/controller-deployment_test.yaml index 5471b11292..b8056be9a2 100644 --- a/helm/kagent/tests/controller-deployment_test.yaml +++ b/helm/kagent/tests/controller-deployment_test.yaml @@ -448,6 +448,71 @@ tests: name: POSTGRES_DATABASE_URL value: "postgres://user:pass@external-host:5432/db" + - it: should read the bundled database URL from the shared Substrate secret + template: controller-deployment.yaml + set: + substrate: + enabled: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: POSTGRES_DATABASE_URL + valueFrom: + secretKeyRef: + name: RELEASE-NAME-postgres-connection + key: connectionString + - notContains: + path: spec.template.spec.containers[0].env + content: + name: POSTGRES_PASSWORD + + - it: should read an existing shared database secret with Substrate + template: controller-deployment.yaml + set: + substrate: + enabled: true + postgres: + connectionStringSecretRef: + name: shared-db + key: url + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: POSTGRES_DATABASE_URL + valueFrom: + secretKeyRef: + name: shared-db + key: url + + - it: should keep an explicitly separate Substrate database Secret separate + template: controller-deployment.yaml + set: + substrate: + enabled: true + postgres: + connectionStringSecretRef: + enabled: false + name: substrate-db + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: RELEASE-NAME-postgresql + key: POSTGRES_PASSWORD + - notContains: + path: spec.template.spec.containers[0].env + content: + name: POSTGRES_DATABASE_URL + valueFrom: + secretKeyRef: + name: substrate-db + key: connectionString + - it: should set POSTGRES_DATABASE_URL_FILE and omit POSTGRES_PASSWORD when urlFile is set template: controller-deployment.yaml set: diff --git a/helm/kagent/tests/postgresql_test.yaml b/helm/kagent/tests/postgresql_test.yaml index 5d6d40a018..16ed8fc572 100644 --- a/helm/kagent/tests/postgresql_test.yaml +++ b/helm/kagent/tests/postgresql_test.yaml @@ -419,6 +419,65 @@ tests: - hasDocuments: count: 1 + - it: should create a shared Substrate connection secret for bundled postgres + template: postgresql-secret.yaml + documentIndex: 1 + set: + substrate: + enabled: true + asserts: + - isKind: + of: Secret + - equal: + path: metadata.name + value: RELEASE-NAME-postgres-connection + - equal: + path: stringData.connectionString + value: "postgres://kagent:kagent@RELEASE-NAME-postgresql.NAMESPACE.svc:5432/kagent?sslmode=disable" + + - it: should put an external URL in the shared Substrate connection secret + template: postgresql-secret.yaml + set: + database: + postgres: + url: "postgres://user:pass@external-host:5432/db" + bundled: + enabled: false + substrate: + enabled: true + asserts: + - equal: + path: stringData.connectionString + value: "postgres://user:pass@external-host:5432/db" + + - it: should use an existing Substrate connection secret without creating one + template: postgresql-secret.yaml + set: + database: + postgres: + bundled: + enabled: false + substrate: + enabled: true + postgres: + connectionStringSecretRef: + name: shared-db + asserts: + - hasDocuments: + count: 0 + + - it: should reject urlFile when a managed Substrate connection secret is required + template: postgresql-secret.yaml + set: + database: + postgres: + urlFile: /var/secrets/db-url + substrate: + enabled: true + asserts: + - failedTemplate: + errorMessage: "database.postgres.urlFile cannot configure embedded Substrate; set substrate.postgres.connectionStringSecretRef.name to the Secret containing the URL" + - it: should not render imagePullSecret by default template: postgresql.yaml documentIndex: 2 diff --git a/helm/kagent/values.yaml b/helm/kagent/values.yaml index 775faeafe0..29b4b8cf4c 100644 --- a/helm/kagent/values.yaml +++ b/helm/kagent/values.yaml @@ -75,10 +75,12 @@ nodeSelector: {} database: postgres: # -- External PostgreSQL connection string. - # Is always used if set regardless of the `.bundled.enabled` field. + # Used ahead of `.bundled` unless an existing shared Substrate Secret is set. url: "" # -- Path to a file containing the database URL. Takes precedence over url when set. - # Is always used if set regardless of the `.bundled.enabled` field. + # Takes precedence over `.bundled` unless an existing shared Substrate Secret is set. + # Embedded Substrate cannot inherit an arbitrary mounted file; configure its + # connectionStringSecretRef.name instead. urlFile: "" # -- Enable the pgvector migration # Required to use features that depend on database vector capability. (e.g. long-term memory) @@ -664,6 +666,16 @@ kmcp: substrate: enabled: false + postgres: + # Kagent and Substrate use separate schemas in the same database. + enabled: false + schema: substrate + # -- Share Kagent's selected database with Substrate through a Secret. + connectionStringSecretRef: + enabled: true + # -- Existing Secret name. Kagent creates a release-named Secret when empty. + name: "" + key: connectionString # ============================================================================== # BUILT-IN TOOLS From 0d993e8a50a570ec7e3114bfa08d67afcee2e963 Mon Sep 17 00:00:00 2001 From: Jeremy Alvis Date: Fri, 4 Sep 2026 13:23:11 -0700 Subject: [PATCH 2/2] Replace PostgreSQL URL files with Secret references Signed-off-by: Jeremy Alvis --- contrib/cncf/technical-review.md | 2 +- go/core/internal/database/connect.go | 29 --------- go/core/internal/database/connect_test.go | 53 --------------- go/core/pkg/app/app.go | 5 +- helm/README.md | 22 ++++++- helm/kagent/templates/NOTES.txt | 10 +-- .../templates/controller-deployment.yaml | 20 +++--- helm/kagent/templates/postgresql-secret.yaml | 5 +- .../tests/controller-deployment_test.yaml | 64 +++++++++++-------- helm/kagent/tests/postgresql_test.yaml | 21 +++--- helm/kagent/values.yaml | 20 +++--- 11 files changed, 103 insertions(+), 148 deletions(-) diff --git a/contrib/cncf/technical-review.md b/contrib/cncf/technical-review.md index 2c252ecbca..8d680d1934 100644 --- a/contrib/cncf/technical-review.md +++ b/contrib/cncf/technical-review.md @@ -325,7 +325,7 @@ Default values can be found in [helm/kagent/values.yaml](https://github.com/kage **Additional Configurations:** For production use, configure: -- External PostgreSQL connection (set `database.postgres.bundled.enabled=false` and set either `database.postgres.url` or `database.postgres.urlFile`) +- External PostgreSQL connection (set `database.postgres.bundled.enabled=false` and configure `database.postgres.url` or `database.postgres.secretRef`) - LLM API keys via Secrets (`providers.openAI.apiKeySecretRef`) - TLS for external LLM connections (`modelConfig.tls`) - Resource limits based on workload (`agents.*.resources`) diff --git a/go/core/internal/database/connect.go b/go/core/internal/database/connect.go index 47daec9170..49d1730581 100644 --- a/go/core/internal/database/connect.go +++ b/go/core/internal/database/connect.go @@ -4,8 +4,6 @@ import ( "context" "fmt" "log" - "os" - "strings" "time" "github.com/jackc/pgx/v5" @@ -14,9 +12,6 @@ import ( ) // PostgresConfig holds the connection parameters for a Postgres database. -// URL must be a resolved connection string — use ResolveURL to resolve from -// a file path before constructing this config. -// // Pool fields are optional: nil leaves the corresponding pgxpool.Config value // from ParseConfig unchanged (pgx library defaults). type PostgresConfig struct { @@ -109,27 +104,3 @@ func retryDBConnection(ctx context.Context, cfg *PostgresConfig) (*pgxpool.Pool, } } } - -// ResolveURL returns url, unless urlFile is non-empty in which case the URL is -// read from that file. Used by callers (e.g. the migration runner) that need -// the resolved connection string before a pool is created. -func ResolveURL(url, urlFile string) (string, error) { - if urlFile != "" { - return resolveURLFile(urlFile) - } - return url, nil -} - -// resolveURLFile reads a database connection URL from a file and returns the -// trimmed contents. Returns an error if the file cannot be read or is empty. -func resolveURLFile(path string) (string, error) { - content, err := os.ReadFile(path) - if err != nil { - return "", fmt.Errorf("reading URL file: %w", err) - } - url := strings.TrimSpace(string(content)) - if url == "" { - return "", fmt.Errorf("URL file %s is empty or contains only whitespace", path) - } - return url, nil -} diff --git a/go/core/internal/database/connect_test.go b/go/core/internal/database/connect_test.go index 0525301e68..c255704bda 100644 --- a/go/core/internal/database/connect_test.go +++ b/go/core/internal/database/connect_test.go @@ -2,8 +2,6 @@ package database import ( "context" - "os" - "path/filepath" "testing" "time" @@ -53,54 +51,3 @@ func TestApplyPoolConfig(t *testing.T) { assert.Equal(t, 10*time.Minute, config.MaxConnLifetime) }) } - -func TestResolveURLFile(t *testing.T) { - tests := []struct { - name string - fileContent string - wantUrl string - wantErr bool - }{ - { - name: "reads URL from file", - fileContent: "postgres://testuser:testpass@host:5432/testdb", - wantUrl: "postgres://testuser:testpass@host:5432/testdb", - }, - { - name: "trims whitespace and newlines", - fileContent: " postgres://user:pass@host:5432/db\n", - wantUrl: "postgres://user:pass@host:5432/db", - }, - { - name: "empty file returns error", - fileContent: "", - wantErr: true, - }, - { - name: "whitespace-only file returns error", - fileContent: " \n\t\n ", - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tmpFile := filepath.Join(t.TempDir(), "db-url") - err := os.WriteFile(tmpFile, []byte(tt.fileContent), 0600) - assert.NoError(t, err) - - url, err := resolveURLFile(tmpFile) - if tt.wantErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.Equal(t, tt.wantUrl, url) - }) - } - - t.Run("missing file returns error", func(t *testing.T) { - _, err := resolveURLFile("/nonexistent/path/db-url") - assert.Error(t, err) - }) -} diff --git a/go/core/pkg/app/app.go b/go/core/pkg/app/app.go index f2e23e411b..18b45830a7 100644 --- a/go/core/pkg/app/app.go +++ b/go/core/pkg/app/app.go @@ -163,10 +163,7 @@ func Run(ctx context.Context, opts Options) error { } }() - dbURL, err := database.ResolveURL(env("POSTGRES_DATABASE_URL", "postgres://postgres:kagent@kagent-postgresql.kagent.svc.cluster.local:5432/postgres"), os.Getenv("POSTGRES_DATABASE_URL_FILE")) - if err != nil { - return err - } + dbURL := env("POSTGRES_DATABASE_URL", "postgres://postgres:kagent@kagent-postgresql.kagent.svc.cluster.local:5432/postgres") vectorEnabled := kagentenv.DatabaseVectorEnabled.Get() // Appended, not merged: the built-in tracks must reach their final version // before a library consumer's tables, which may reference them. diff --git a/helm/README.md b/helm/README.md index ab782dcc87..30fb55e8ca 100644 --- a/helm/README.md +++ b/helm/README.md @@ -43,6 +43,25 @@ substrate: enabled: true ``` +To share an existing Secret, configure both charts to reference the same +name and key: + +```yaml +database: + postgres: + secretRef: + name: shared-postgres + key: connectionString + bundled: + enabled: false +substrate: + enabled: true + postgres: + connectionStringSecretRef: + name: shared-postgres + key: connectionString +``` + To give Substrate a separate PostgreSQL connection, disable sharing and set the Substrate connection directly: @@ -56,8 +75,7 @@ substrate: ``` For a separate Secret-backed connection, leave `enabled: false` and set -`connectionStringSecretRef.name` and `key`. A pod-local -`database.postgres.urlFile` cannot be shared with Substrate. +`connectionStringSecretRef.name` and `key`. ### Using Make diff --git a/helm/kagent/templates/NOTES.txt b/helm/kagent/templates/NOTES.txt index 12d573a010..4b40741104 100644 --- a/helm/kagent/templates/NOTES.txt +++ b/helm/kagent/templates/NOTES.txt @@ -64,7 +64,7 @@ DOCUMENTATION: {{- end }} {{ if .Values.database.postgres.bundled.enabled -}} ################################################################################ -{{- if and (eq .Values.database.postgres.url "") (eq .Values.database.postgres.urlFile "") }} +{{- if and (eq .Values.database.postgres.url "") (not .Values.database.postgres.secretRef.name) }} # WARNING: BUNDLED DATABASE IN USE # ################################################################################ The bundled PostgreSQL instance is enabled. It is intended for development and @@ -72,15 +72,17 @@ DOCUMENTATION: pod is restarted or rescheduled. To use an external database, set: - database.postgres.url= or database.postgres.urlFile= + database.postgres.url= + or database.postgres.secretRef.name= {{- else }} # NOTE: BUNDLED DATABASE DEPLOYED BUT NOT IN USE BY CONTROLLER # ################################################################################ The bundled PostgreSQL pod is running, but the controller is connected to an - external database (database.postgres.url or database.postgres.urlFile is set). + external database. - To connect the controller to the bundled instance instead, unset url/urlFile: + To connect the controller to the bundled instance instead, unset the external connection: database.postgres.url="" + database.postgres.secretRef.name="" To stop deploying the bundled pod entirely, set: database.postgres.bundled.enabled=false {{- end }} diff --git a/helm/kagent/templates/controller-deployment.yaml b/helm/kagent/templates/controller-deployment.yaml index cd707d3264..09a6f916c1 100644 --- a/helm/kagent/templates/controller-deployment.yaml +++ b/helm/kagent/templates/controller-deployment.yaml @@ -1,3 +1,10 @@ +{{- $databaseConnectionStringSecretRef := .Values.database.postgres.secretRef | default dict -}} +{{- if hasKey .Values.database.postgres "urlFile" -}} +{{- fail "database.postgres.urlFile has been removed; use database.postgres.secretRef.{name,key}" -}} +{{- end -}} +{{- if and .Values.database.postgres.url (get $databaseConnectionStringSecretRef "name") -}} +{{- fail "database.postgres.url and database.postgres.secretRef.name are mutually exclusive" -}} +{{- end -}} apiVersion: apps/v1 kind: Deployment metadata: @@ -101,17 +108,12 @@ spec: - name: AUTH_USER_ID_CLAIM value: {{ .Values.controller.auth.userIdClaim | quote }} {{- end }} - {{- $substratePostgres := get .Values.substrate "postgres" | default dict }} - {{- $connectionStringSecretRef := get $substratePostgres "connectionStringSecretRef" | default dict }} - {{- if and .Values.substrate.enabled (get $connectionStringSecretRef "enabled") }} + {{- if get $databaseConnectionStringSecretRef "name" }} - name: POSTGRES_DATABASE_URL valueFrom: secretKeyRef: - name: {{ get $connectionStringSecretRef "name" | default (include "substrate.fullname" (list "postgres-connection" .)) }} - key: {{ get $connectionStringSecretRef "key" | default "connectionString" }} - {{- else if .Values.database.postgres.urlFile }} - - name: POSTGRES_DATABASE_URL_FILE - value: {{ .Values.database.postgres.urlFile | quote }} + name: {{ get $databaseConnectionStringSecretRef "name" }} + key: {{ get $databaseConnectionStringSecretRef "key" | default "connectionString" }} {{- else if .Values.database.postgres.url }} - name: POSTGRES_DATABASE_URL value: {{ .Values.database.postgres.url | quote }} @@ -124,7 +126,7 @@ spec: - name: POSTGRES_DATABASE_URL value: {{ printf "postgres://kagent:$(POSTGRES_PASSWORD)@%s.%s.svc:5432/kagent?sslmode=disable" (include "kagent.postgresqlServiceName" .) (include "kagent.namespace" .) | quote }} {{- else }} - {{ fail "No database connection configured. Set database.postgres.url, database.postgres.urlFile, or enable database.postgres.bundled." }} + {{ fail "No database connection configured. Set database.postgres.url, database.postgres.secretRef.name, or enable database.postgres.bundled." }} {{- end }} {{- if include "kagent.controller.metricsEnabled" . }} - name: METRICS_BIND_ADDRESS diff --git a/helm/kagent/templates/postgresql-secret.yaml b/helm/kagent/templates/postgresql-secret.yaml index 371f06c125..a8f7ee7e1f 100644 --- a/helm/kagent/templates/postgresql-secret.yaml +++ b/helm/kagent/templates/postgresql-secret.yaml @@ -11,12 +11,13 @@ type: Opaque data: POSTGRES_PASSWORD: {{ "kagent" | b64enc | quote }} {{- end }} +{{- $databaseConnectionStringSecretRef := .Values.database.postgres.secretRef | default dict -}} {{- $substratePostgres := get .Values.substrate "postgres" | default dict -}} {{- $connectionStringSecretRef := get $substratePostgres "connectionStringSecretRef" | default dict -}} {{- if and .Values.substrate.enabled (get $connectionStringSecretRef "enabled") (not (get $connectionStringSecretRef "name")) }} {{- $connectionString := "" -}} -{{- if .Values.database.postgres.urlFile -}} -{{- fail "database.postgres.urlFile cannot configure embedded Substrate; set substrate.postgres.connectionStringSecretRef.name to the Secret containing the URL" -}} +{{- if get $databaseConnectionStringSecretRef "name" -}} +{{- fail "database.postgres.secretRef cannot be inherited by Substrate; set substrate.postgres.connectionStringSecretRef to the same Secret" -}} {{- else if .Values.database.postgres.url -}} {{- $connectionString = .Values.database.postgres.url -}} {{- else if .Values.database.postgres.bundled.enabled -}} diff --git a/helm/kagent/tests/controller-deployment_test.yaml b/helm/kagent/tests/controller-deployment_test.yaml index b8056be9a2..b588383fa0 100644 --- a/helm/kagent/tests/controller-deployment_test.yaml +++ b/helm/kagent/tests/controller-deployment_test.yaml @@ -448,7 +448,7 @@ tests: name: POSTGRES_DATABASE_URL value: "postgres://user:pass@external-host:5432/db" - - it: should read the bundled database URL from the shared Substrate secret + - it: should keep Kagent's bundled database configuration when Substrate is enabled template: controller-deployment.yaml set: substrate: @@ -457,19 +457,25 @@ tests: - contains: path: spec.template.spec.containers[0].env content: - name: POSTGRES_DATABASE_URL + name: POSTGRES_PASSWORD valueFrom: secretKeyRef: - name: RELEASE-NAME-postgres-connection - key: connectionString - - notContains: + name: RELEASE-NAME-postgresql + key: POSTGRES_PASSWORD + - contains: path: spec.template.spec.containers[0].env content: - name: POSTGRES_PASSWORD + name: POSTGRES_DATABASE_URL + value: "postgres://kagent:$(POSTGRES_PASSWORD)@RELEASE-NAME-postgresql.NAMESPACE.svc:5432/kagent?sslmode=disable" - it: should read an existing shared database secret with Substrate template: controller-deployment.yaml set: + database: + postgres: + secretRef: + name: shared-db + key: url substrate: enabled: true postgres: @@ -513,18 +519,23 @@ tests: name: substrate-db key: connectionString - - it: should set POSTGRES_DATABASE_URL_FILE and omit POSTGRES_PASSWORD when urlFile is set + - it: should read an external database URL from a Secret template: controller-deployment.yaml set: database: postgres: - urlFile: "/var/secrets/db-url" + secretRef: + name: external-postgres + key: url asserts: - contains: path: spec.template.spec.containers[0].env content: - name: POSTGRES_DATABASE_URL_FILE - value: "/var/secrets/db-url" + name: POSTGRES_DATABASE_URL + valueFrom: + secretKeyRef: + name: external-postgres + key: url - notContains: path: spec.template.spec.containers[0].env content: @@ -618,28 +629,27 @@ tests: content: name: POSTGRES_PASSWORD - - it: should set POSTGRES_DATABASE_URL_FILE and omit POSTGRES_PASSWORD when urlFile and bundled are both enabled + - it: should reject the removed urlFile value template: controller-deployment.yaml set: database: postgres: - urlFile: "/var/secrets/db-url" - bundled: - enabled: true + urlFile: /var/secrets/db-url asserts: - - contains: - path: spec.template.spec.containers[0].env - content: - name: POSTGRES_DATABASE_URL_FILE - value: "/var/secrets/db-url" - - notContains: - path: spec.template.spec.containers[0].env - content: - name: POSTGRES_PASSWORD - - notContains: - path: spec.template.spec.containers[0].env - content: - name: POSTGRES_DATABASE_URL + - failedTemplate: + errorMessage: "database.postgres.urlFile has been removed; use database.postgres.secretRef.{name,key}" + + - it: should reject both an inline URL and Secret reference + template: controller-deployment.yaml + set: + database: + postgres: + url: postgres://user:pass@external-host:5432/db + secretRef: + name: external-postgres + asserts: + - failedTemplate: + errorMessage: "database.postgres.url and database.postgres.secretRef.name are mutually exclusive" - it: should set external POSTGRES_DATABASE_URL and omit POSTGRES_PASSWORD when url and bundled are both enabled template: controller-deployment.yaml diff --git a/helm/kagent/tests/postgresql_test.yaml b/helm/kagent/tests/postgresql_test.yaml index 16ed8fc572..cc70f66e59 100644 --- a/helm/kagent/tests/postgresql_test.yaml +++ b/helm/kagent/tests/postgresql_test.yaml @@ -4,7 +4,7 @@ templates: - postgresql-secret.yaml tests: # ============================================================================= - # bundled mode (default — url and urlFile both empty, bundled.enabled true) + # bundled mode (default — no external connection, bundled.enabled true) # ============================================================================= - it: should render ServiceAccount, PVC, Deployment, and Service when bundled is enabled @@ -34,12 +34,13 @@ tests: - hasDocuments: count: 4 - - it: should still render resources when urlFile is set and bundled is enabled + - it: should still render resources when an external Secret is set and bundled is enabled template: postgresql.yaml set: database: postgres: - urlFile: "/var/secrets/db-url" + secretRef: + name: external-postgres asserts: - hasDocuments: count: 4 @@ -409,12 +410,13 @@ tests: - hasDocuments: count: 1 - - it: should still create secret when urlFile is set and bundled is enabled + - it: should still create the bundled password secret when an external Secret is set template: postgresql-secret.yaml set: database: postgres: - urlFile: "/var/secrets/db-url" + secretRef: + name: external-postgres asserts: - hasDocuments: count: 1 @@ -466,17 +468,20 @@ tests: - hasDocuments: count: 0 - - it: should reject urlFile when a managed Substrate connection secret is required + - it: should require an explicit Substrate reference for an existing Kagent Secret template: postgresql-secret.yaml set: database: postgres: - urlFile: /var/secrets/db-url + secretRef: + name: shared-db + bundled: + enabled: false substrate: enabled: true asserts: - failedTemplate: - errorMessage: "database.postgres.urlFile cannot configure embedded Substrate; set substrate.postgres.connectionStringSecretRef.name to the Secret containing the URL" + errorMessage: "database.postgres.secretRef cannot be inherited by Substrate; set substrate.postgres.connectionStringSecretRef to the same Secret" - it: should not render imagePullSecret by default template: postgresql.yaml diff --git a/helm/kagent/values.yaml b/helm/kagent/values.yaml index 29b4b8cf4c..1acf5ad137 100644 --- a/helm/kagent/values.yaml +++ b/helm/kagent/values.yaml @@ -75,13 +75,14 @@ nodeSelector: {} database: postgres: # -- External PostgreSQL connection string. - # Used ahead of `.bundled` unless an existing shared Substrate Secret is set. + # Mutually exclusive with `secretRef.name`. url: "" - # -- Path to a file containing the database URL. Takes precedence over url when set. - # Takes precedence over `.bundled` unless an existing shared Substrate Secret is set. - # Embedded Substrate cannot inherit an arbitrary mounted file; configure its - # connectionStringSecretRef.name instead. - urlFile: "" + # -- Source the external PostgreSQL connection string from an existing Secret. + # To share it with embedded Substrate, set the same name and key under + # `substrate.postgres.connectionStringSecretRef`. + secretRef: + name: "" + key: connectionString # -- Enable the pgvector migration # Required to use features that depend on database vector capability. (e.g. long-term memory) # Set to true when using an external PostgreSQL that has the pgvector extension installed. @@ -98,9 +99,9 @@ database: maxConnIdleTime: "" maxConnLifetime: "" # -- Bundled PostgreSQL instance — for development and evaluation only. - # Not suitable for production. Deployed when enabled is true and url/urlFile are not set. + # Not suitable for production. Deployed whenever enabled is true. bundled: - # -- Set to false to disable the bundled database and provide your own via url or urlFile. + # -- Set to false to disable the bundled database and provide an external connection. enabled: true image: # -- Bundled PostgreSQL image registry @@ -670,7 +671,8 @@ substrate: # Kagent and Substrate use separate schemas in the same database. enabled: false schema: substrate - # -- Share Kagent's selected database with Substrate through a Secret. + # -- Read the Substrate connection string from a Secret. + # With no name, Kagent creates this Secret from its bundled or inline URL. connectionStringSecretRef: enabled: true # -- Existing Secret name. Kagent creates a release-named Secret when empty.