diff --git a/charts/substrate/README.md b/charts/substrate/README.md index 35d7bc96c8..ca5c447b3f 100644 --- a/charts/substrate/README.md +++ b/charts/substrate/README.md @@ -89,6 +89,7 @@ See `values.yaml` for the full set; the important keys: | `atenetEgress.extraCredentialProviders` | `[]` | Credential providers you deploy, added after the bundled `k8s.io` provider, each `{name, address}`. `name` is the host of the `ate-secret://` URIs it serves; `address` is the `host:port` the egress gateway dials over mTLS. Its serving certificate must chain to the servicedns CA. See [egress credential injection](../../docs/guides/egress-credential-injection.md#the-plugin-api) | | `atenetEgress.agentgateway.logLevel` | `info` | Egress proxy `RUST_LOG` filter, e.g. `debug` or `info,agentgateway=debug` when troubleshooting | | `rustfs.enabled` | `true` | Deploy an in-cluster S3-compatible RustFS bucket for snapshots | +| `rustfs.existingSecret` | `""` | Secret with `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` for RustFS, its bucket-init Job and the object-store plugin, instead of the literal `rustfs.accessKey`/`rustfs.secretKey`. The atelet chart's `objectStore.credentialsSecret` takes the same keys | | `ateApi.snapshotLocation` | `""` | Cluster-wide snapshot URI; defaults to `gs:///` with bundled RustFS | | `atelet.enabled` | `true` | Deprecated. Set to `false` and install atelet with the [substrate-atelet](../substrate-atelet/README.md) chart | | `atelet.storageBackend` | `s3` | Default snapshot backend, wired to RustFS when `rustfs.enabled=true` | diff --git a/charts/substrate/templates/_helpers.tpl b/charts/substrate/templates/_helpers.tpl index 35d435d350..a6a7889a04 100644 --- a/charts/substrate/templates/_helpers.tpl +++ b/charts/substrate/templates/_helpers.tpl @@ -250,16 +250,43 @@ definition so the fallback cannot drift between pods. value: http://{{ include "substrate.fullname" (list "rustfs" .) }}.{{ .Release.Namespace }}.svc:9000 - name: AWS_S3_USE_PATH_STYLE value: "true" -- name: AWS_ACCESS_KEY_ID - value: {{ .Values.rustfs.accessKey | quote }} -- name: AWS_SECRET_ACCESS_KEY - value: {{ .Values.rustfs.secretKey | quote }} +{{ include "substrate.rustfs.credentialsEnv" (list "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY" .) }} {{- end }} {{- with .Values.objectStorePlugin.extraEnv }} {{ toYaml . }} {{- end }} {{- end }} +{{/* +RustFS credential env vars. Usage: + include "substrate.rustfs.credentialsEnv" (list "" "" .) +With rustfs.existingSecret the values come from that Secret's +AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY keys, otherwise from +rustfs.accessKey and rustfs.secretKey. +*/}} +{{- define "substrate.rustfs.credentialsEnv" -}} +{{- $ctx := index . 2 -}} +{{- $secret := $ctx.Values.rustfs.existingSecret -}} +- name: {{ index . 0 }} +{{- if $secret }} + valueFrom: + secretKeyRef: + name: {{ $secret | quote }} + key: AWS_ACCESS_KEY_ID +{{- else }} + value: {{ $ctx.Values.rustfs.accessKey | quote }} +{{- end }} +- name: {{ index . 1 }} +{{- if $secret }} + valueFrom: + secretKeyRef: + name: {{ $secret | quote }} + key: AWS_SECRET_ACCESS_KEY +{{- else }} + value: {{ $ctx.Values.rustfs.secretKey | quote }} +{{- end }} +{{- end -}} + {{/* The egress gateway's credentialProviders: the bundled k8s.io provider when credentialProvider.enabled, then atenetEgress.extraCredentialProviders. Renders diff --git a/charts/substrate/templates/rustfs.yaml b/charts/substrate/templates/rustfs.yaml index 95a5b10053..f252342533 100644 --- a/charts/substrate/templates/rustfs.yaml +++ b/charts/substrate/templates/rustfs.yaml @@ -94,10 +94,7 @@ spec: value: "true" - name: RUSTFS_VOLUMES value: "/data" - - name: RUSTFS_ACCESS_KEY - value: {{ .Values.rustfs.accessKey | quote }} - - name: RUSTFS_SECRET_KEY - value: {{ .Values.rustfs.secretKey | quote }} + {{- include "substrate.rustfs.credentialsEnv" (list "RUSTFS_ACCESS_KEY" "RUSTFS_SECRET_KEY" .) | nindent 8 }} volumeMounts: - name: data mountPath: /data @@ -124,10 +121,7 @@ spec: resources: {{ toYaml .Values.rustfs.bucketInit.resources | indent 10 }} env: - - name: AWS_ACCESS_KEY_ID - value: {{ .Values.rustfs.accessKey | quote }} - - name: AWS_SECRET_ACCESS_KEY - value: {{ .Values.rustfs.secretKey | quote }} + {{- include "substrate.rustfs.credentialsEnv" (list "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY" .) | nindent 8 }} - name: AWS_REGION value: us-east-1 - name: AWS_ENDPOINT_URL diff --git a/charts/substrate/tests/rustfs_credentials_test.yaml b/charts/substrate/tests/rustfs_credentials_test.yaml new file mode 100644 index 0000000000..8e508dc4d6 --- /dev/null +++ b/charts/substrate/tests/rustfs_credentials_test.yaml @@ -0,0 +1,123 @@ +# Copyright 2026 The Agent Substrate Authors +# +# 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. + +suite: RustFS credentials +templates: +- rustfs.yaml +- ate-api-server-envvars.yaml +- ate-api-server.yaml +tests: +- it: renders the inline credentials by default + template: rustfs.yaml + documentSelector: + path: kind + value: Deployment + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: RUSTFS_ACCESS_KEY + value: rustfsadmin + - contains: + path: spec.template.spec.containers[0].env + content: + name: RUSTFS_SECRET_KEY + value: rustfsadmin + +- it: renders the inline object-store plugin credentials by default + template: ate-api-server.yaml + documentSelector: + path: kind + value: Deployment + asserts: + - contains: + path: spec.template.spec.initContainers[?(@.name == "objectstore-plugin")].env + content: + name: AWS_SECRET_ACCESS_KEY + value: rustfsadmin + +- it: reads the RustFS server credentials from rustfs.existingSecret + template: rustfs.yaml + documentSelector: + path: kind + value: Deployment + set: + rustfs.existingSecret: rustfs-credentials + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: RUSTFS_ACCESS_KEY + valueFrom: + secretKeyRef: + name: rustfs-credentials + key: AWS_ACCESS_KEY_ID + - contains: + path: spec.template.spec.containers[0].env + content: + name: RUSTFS_SECRET_KEY + valueFrom: + secretKeyRef: + name: rustfs-credentials + key: AWS_SECRET_ACCESS_KEY + +- it: reads the bucket-init credentials from rustfs.existingSecret + template: rustfs.yaml + documentSelector: + path: kind + value: Job + set: + rustfs.existingSecret: rustfs-credentials + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AWS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: rustfs-credentials + key: AWS_ACCESS_KEY_ID + - contains: + path: spec.template.spec.containers[0].env + content: + name: AWS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: rustfs-credentials + key: AWS_SECRET_ACCESS_KEY + +- it: reads the object-store plugin credentials from rustfs.existingSecret + template: ate-api-server.yaml + documentSelector: + path: kind + value: Deployment + set: + rustfs.existingSecret: rustfs-credentials + asserts: + - contains: + path: spec.template.spec.initContainers[?(@.name == "objectstore-plugin")].env + content: + name: AWS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: rustfs-credentials + key: AWS_ACCESS_KEY_ID + - contains: + path: spec.template.spec.initContainers[?(@.name == "objectstore-plugin")].env + content: + name: AWS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: rustfs-credentials + key: AWS_SECRET_ACCESS_KEY diff --git a/charts/substrate/values.yaml b/charts/substrate/values.yaml index 5d0f7a4dce..3e2e71b64f 100644 --- a/charts/substrate/values.yaml +++ b/charts/substrate/values.yaml @@ -50,6 +50,10 @@ rustfs: bucket: ate-snapshots accessKey: rustfsadmin secretKey: rustfsadmin + # Name of an existing Secret in the release namespace with the keys + # AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. When set, every RustFS + # consumer reads the credentials from it and accessKey/secretKey are ignored. + existingSecret: "" resources: requests: cpu: 50m