|
| 1 | +# Feature request: optionally create ClusterConnection(s) + admin Secret from Helm values |
| 2 | + |
| 3 | +## Context |
| 4 | +The `rustfs-operator` chart currently deploys only the operator Deployment + RBAC. |
| 5 | +The admin credentials Secret and the cluster-scoped `ClusterConnection` CR must be |
| 6 | +created by hand (see `deploy/example.yaml`). In GitOps/helmfile setups this forces a |
| 7 | +second, out-of-chart mechanism just to bootstrap the connection. |
| 8 | + |
| 9 | +Add **optional** support to the chart so a `ClusterConnection` (and, optionally, its |
| 10 | +admin credentials Secret) can be declared inline in `values.yaml`. Feature must be |
| 11 | +**off by default** and must not change any existing rendered output when unset. |
| 12 | + |
| 13 | +## Chart facts to respect |
| 14 | +- CRD: group `rustfs.com`, version `v1alpha1`, kind `ClusterConnection`, **cluster-scoped**. |
| 15 | +- `ClusterConnection.spec`: `endpoint` (required), `credentialsSecretRef` (required — name |
| 16 | + of a Secret **in the operator's namespace** with keys `accessKey`/`secretKey`), |
| 17 | + optional `region`, `insecure`, `allowedNamespaces` (string list; absent = all). |
| 18 | +- The credentials Secret must live in the operator's own namespace (`.Release.Namespace`); |
| 19 | + a namespaced Role already grants the operator read access there. |
| 20 | +- `rbac.clusterWideSecrets` already exists; when only `ClusterConnection` is used it can be |
| 21 | + `false`. Do not change its default. |
| 22 | + |
| 23 | +## Values API (new) |
| 24 | +Add a top-level `clusterConnections` **list** (support zero or more servers): |
| 25 | + |
| 26 | +```yaml |
| 27 | +clusterConnections: [] |
| 28 | +# clusterConnections: |
| 29 | +# - name: prod # required; metadata.name of the ClusterConnection (cluster-scoped) |
| 30 | +# endpoint: http://rustfs-svc.storage.svc.cluster.local:9000 # required |
| 31 | +# allowedNamespaces: [] # optional; omit/empty -> render nothing (operator treats absent = all) |
| 32 | +# region: "" # optional |
| 33 | +# insecure: false # optional |
| 34 | +# credentials: |
| 35 | +# # Provide EITHER inline creds (chart creates the Secret in the operator ns) ... |
| 36 | +# create: true # default true |
| 37 | +# secretName: "" # optional override; default: "<fullname>-<name>-admin" |
| 38 | +# accessKey: "" |
| 39 | +# secretKey: "" |
| 40 | +# # ... OR reference an existing Secret (chart creates no Secret): |
| 41 | +# existingSecret: "" # if set, credentials.create is ignored and this name |
| 42 | +# # is used as credentialsSecretRef (must have accessKey/secretKey) |
| 43 | +``` |
| 44 | + |
| 45 | +### Behavior |
| 46 | +- `clusterConnections: []` (default) => render nothing new; byte-for-byte identical output to today. |
| 47 | +- For each entry: |
| 48 | + - Always render a `ClusterConnection` (cluster-scoped, no namespace) with `spec.endpoint`, |
| 49 | + `spec.credentialsSecretRef` = resolved secret name (see below), and — only when set — |
| 50 | + `region`, `insecure`, and `allowedNamespaces`. |
| 51 | + - Resolve `credentialsSecretRef`: |
| 52 | + - if `credentials.existingSecret` is non-empty => use it; render **no** Secret. |
| 53 | + - else if `credentials.create` (default true) => render a `Secret` of `type: Opaque` in |
| 54 | + `.Release.Namespace` named `credentials.secretName | default "<fullname>-<name>-admin"`, |
| 55 | + with `stringData.accessKey` / `stringData.secretKey`; use that name as `credentialsSecretRef`. |
| 56 | + - `allowedNamespaces`: omit the field entirely when the list is empty/unset (so operator's |
| 57 | + "absent = all" semantics apply); render the list when provided. |
| 58 | + |
| 59 | +## Validation (fail template with a clear message) |
| 60 | +- `name` and `endpoint` are required per entry. |
| 61 | +- Exactly one credentials source: reject if `existingSecret` is set **and** inline |
| 62 | + `accessKey`/`secretKey` are also set. |
| 63 | +- When `credentials.create` is true and `existingSecret` is empty, both `accessKey` and |
| 64 | + `secretKey` must be non-empty (do not silently emit an empty-keyed Secret). |
| 65 | +- Duplicate `name` values across the list should fail (they'd collide on a cluster-scoped object). |
| 66 | + |
| 67 | +## Templates / conventions |
| 68 | +- New file `templates/clusterconnection.yaml` (range over `.Values.clusterConnections`, |
| 69 | + `---` separated). |
| 70 | +- Use existing `_helpers.tpl` fullname/labels helpers; apply the same standard labels the |
| 71 | + other objects use. |
| 72 | +- CRDs in `crds/` are applied by Helm before `templates/`, so the CR renders fine on a fresh |
| 73 | + install — but please confirm ordering on first `helm install` (no CRD-not-found race). |
| 74 | + |
| 75 | +## RBAC note |
| 76 | +No change to defaults. Please document that when a deployment uses **only** ClusterConnection |
| 77 | +(no per-namespace `secretRef`, no `User.secretKeyRef` in app namespaces), users can set |
| 78 | +`rbac.clusterWideSecrets: false` for least privilege. |
| 79 | + |
| 80 | +## Docs |
| 81 | +- Document the new `clusterConnections` values block in the chart README with one inline-creds |
| 82 | + example and one `existingSecret` example. |
| 83 | +- Note the security trade-off: inline `accessKey`/`secretKey` land in the release values; |
| 84 | + recommend `existingSecret` (or a sealed/external secret) for production. |
| 85 | + |
| 86 | +## Acceptance criteria |
| 87 | +1. `helm lint` passes; `helm template` with default values produces **no** ClusterConnection/Secret |
| 88 | + and is unchanged from current output. |
| 89 | +2. `helm template` with one inline-creds entry renders: a Secret in the release namespace |
| 90 | + (keys `accessKey`/`secretKey`) **and** a ClusterConnection whose `credentialsSecretRef` |
| 91 | + equals that Secret's name. |
| 92 | +3. Same with `existingSecret` set renders the ClusterConnection referencing that name and |
| 93 | + **no** Secret. |
| 94 | +4. Multiple entries render multiple independent ClusterConnections/Secrets. |
| 95 | +5. `allowedNamespaces` present only when provided. |
| 96 | +6. Validation failures produce actionable error messages. |
| 97 | +7. Bump chart `version`; update README. |
0 commit comments