Skip to content

Commit 8b52810

Browse files
committed
feat(chart): add rustfs-resources chart for declaring CR instances from values
Buckets, Policies and Users declared per release namespace with a shared default connection (clusterRef or secretRef) and per-entry overrides. Users reference an existing secret-key Secret or let the chart create one inline. Entries validated with actionable errors (connection exactly-one, policy document required, user key source exactly-one, deletionPolicy enum, duplicate names). Verified end-to-end on k3s with the released operator image reconciling chart-declared resources against RustFS.
1 parent adf5cf5 commit 8b52810

11 files changed

Lines changed: 296 additions & 5 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rustfs-operator"
3-
version = "0.4.0"
3+
version = "0.5.0"
44
edition = "2024"
55
rust-version = "1.92"
66
license = "MIT OR Apache-2.0"

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ The chart can also bootstrap `ClusterConnection` resources (and their admin
5757
credentials Secrets) from values — see
5858
[`charts/rustfs-operator/README.md`](charts/rustfs-operator/README.md).
5959

60+
Teams can declare their RustFS resources (Buckets, Policies, Users) from
61+
values with the [`rustfs-resources`](charts/rustfs-resources/README.md)
62+
chart — one release per namespace, reconciled by the operator.
63+
6064
The main chart installs the CRDs from its `crds/` directory, which Helm
6165
never upgrades. For Helm-managed, value-controlled CRDs (per-CRD toggles,
6266
keep-on-uninstall policy, upgrades via `helm upgrade`), install the

charts/rustfs-operator-crds/Chart.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ keywords:
1111
- operator
1212
- crds
1313
# version and appVersion are overwritten from the git tag by the release workflow
14-
version: 0.4.0
15-
appVersion: "0.4.0"
14+
version: 0.5.0
15+
appVersion: "0.5.0"

charts/rustfs-operator/Chart.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ keywords:
1010
- s3
1111
- operator
1212
# version and appVersion are overwritten from the git tag by the release workflow
13-
version: 0.4.0
14-
appVersion: "0.4.0"
13+
version: 0.5.0
14+
appVersion: "0.5.0"

charts/rustfs-resources/Chart.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: v2
2+
name: rustfs-resources
3+
description: Declare RustFS resources (Buckets, Users, Policies) from Helm values, reconciled by the rustfs-operator
4+
type: application
5+
home: https://github.com/OpenProjectX/rustfs-operator
6+
sources:
7+
- https://github.com/OpenProjectX/rustfs-operator
8+
keywords:
9+
- rustfs
10+
- s3
11+
- operator
12+
# version and appVersion are overwritten from the git tag by the release workflow
13+
version: 0.5.0
14+
appVersion: "0.5.0"

charts/rustfs-resources/README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# rustfs-resources Helm chart
2+
3+
Declare RustFS resources — `Bucket`, `Policy`, `User` custom resources —
4+
from Helm values. One release per team/app/namespace; the
5+
[rustfs-operator](https://github.com/OpenProjectX/rustfs-operator)
6+
reconciles them against the RustFS server. Resources are created in the
7+
release namespace.
8+
9+
```sh
10+
helm repo add rustfs-operator https://openprojectx.github.io/rustfs-operator
11+
helm install app-storage rustfs-operator/rustfs-resources \
12+
--namespace team-a -f my-resources.yaml
13+
```
14+
15+
## Example
16+
17+
```yaml
18+
# my-resources.yaml
19+
connection: # default for every entry; per-entry override possible
20+
clusterRef: prod # or secretRef: <connection Secret in this namespace>
21+
22+
buckets:
23+
- name: app-data
24+
versioning: true
25+
quotaBytes: 10737418240 # 10 GiB
26+
deletionPolicy: Retain # keep the bucket if the CR is deleted
27+
28+
policies:
29+
- name: app-data-rw
30+
document:
31+
Version: "2012-10-17"
32+
Statement:
33+
- Effect: Allow
34+
Action: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket"]
35+
Resource: ["arn:aws:s3:::app-data", "arn:aws:s3:::app-data/*"]
36+
37+
users:
38+
- name: app-user
39+
policies: ["app-data-rw"]
40+
secretKeyRef: # existing Secret with key `secretKey`
41+
name: app-user-creds
42+
```
43+
44+
## Values
45+
46+
| Key | Description |
47+
|-----|-------------|
48+
| `connection.clusterRef` / `connection.secretRef` | default connection (exactly one) |
49+
| `buckets[]` | `name` (required), `bucketName`, `versioning`, `quotaBytes`, `deletionPolicy`, `connection` |
50+
| `policies[]` | `name` (required), `document` (required), `policyName`, `deletionPolicy`, `connection` |
51+
| `users[]` | `name` (required), `accessKey`, `secretKeyRef` **or** inline `secretKey`, `policies`, `enabled`, `deletionPolicy`, `connection` |
52+
53+
Fields you omit stay unmanaged (e.g. no `versioning` key means the operator
54+
never touches versioning). `deletionPolicy` defaults to `Delete` — the
55+
remote resource is removed when the CR is deleted; use `Retain` to keep it.
56+
57+
**User secret keys**: `secretKeyRef` points at an existing Secret in the
58+
release namespace. Alternatively set `secretKey` inline and the chart
59+
creates `<release>-user-<name>` — but the key then lives in the Helm
60+
release values; prefer `secretKeyRef` in production. The secret key is only
61+
applied when the user is first created (RustFS cannot update it in place).
62+
63+
## Prerequisites
64+
65+
The operator and CRDs must be installed (main `rustfs-operator` chart), and
66+
the referenced `ClusterConnection` or connection Secret must exist.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{{- define "rustfs-resources.labels" -}}
2+
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
3+
app.kubernetes.io/name: {{ .Chart.Name }}
4+
app.kubernetes.io/instance: {{ .Release.Name }}
5+
app.kubernetes.io/managed-by: {{ .Release.Service }}
6+
{{- end }}
7+
8+
{{/*
9+
Render the spec.connection block for an entry, validating that exactly one
10+
of clusterRef/secretRef is set after applying the top-level default.
11+
Context: dict "root" $ "entry" <entry> "id" "<kind>[<name>]"
12+
*/}}
13+
{{- define "rustfs-resources.connection" -}}
14+
{{- $conn := .entry.connection | default .root.Values.connection | default dict -}}
15+
{{- $secretRef := $conn.secretRef | default "" -}}
16+
{{- $clusterRef := $conn.clusterRef | default "" -}}
17+
{{- if and (eq $secretRef "") (eq $clusterRef "") -}}
18+
{{- fail (printf "%s: no connection; set top-level 'connection' or a per-entry 'connection' with clusterRef or secretRef" .id) -}}
19+
{{- end -}}
20+
{{- if and (ne $secretRef "") (ne $clusterRef "") -}}
21+
{{- fail (printf "%s: connection.clusterRef and connection.secretRef are mutually exclusive" .id) -}}
22+
{{- end -}}
23+
connection:
24+
{{- if ne $clusterRef "" }}
25+
clusterRef: {{ $clusterRef | quote }}
26+
{{- else }}
27+
secretRef: {{ $secretRef | quote }}
28+
{{- end }}
29+
{{- end }}
30+
31+
{{/*
32+
Validate and render deletionPolicy when set.
33+
Context: dict "entry" <entry> "id" "<kind>[<name>]"
34+
*/}}
35+
{{- define "rustfs-resources.deletionPolicy" -}}
36+
{{- with .entry.deletionPolicy -}}
37+
{{- if not (has . (list "Delete" "Retain")) -}}
38+
{{- fail (printf "%s: deletionPolicy must be Delete or Retain, got %q" $.id .) -}}
39+
{{- end -}}
40+
deletionPolicy: {{ . }}
41+
{{- end -}}
42+
{{- end }}
43+
44+
{{/*
45+
Require a unique non-empty .name per entry.
46+
Context: dict "entry" <entry> "seen" <dict> "kind" "<kind>"
47+
*/}}
48+
{{- define "rustfs-resources.entryName" -}}
49+
{{- $name := .entry.name | default "" -}}
50+
{{- if eq $name "" -}}
51+
{{- fail (printf "%s: every entry needs a non-empty 'name'" .kind) -}}
52+
{{- end -}}
53+
{{- if hasKey .seen $name -}}
54+
{{- fail (printf "%s: duplicate name %q" .kind $name) -}}
55+
{{- end -}}
56+
{{- $_ := set .seen $name true -}}
57+
{{- $name -}}
58+
{{- end }}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{{- $seen := dict }}
2+
{{- range .Values.buckets }}
3+
{{- $name := include "rustfs-resources.entryName" (dict "entry" . "seen" $seen "kind" "buckets") }}
4+
{{- $id := printf "buckets[%s]" $name }}
5+
---
6+
apiVersion: rustfs.com/v1alpha1
7+
kind: Bucket
8+
metadata:
9+
name: {{ $name }}
10+
labels:
11+
{{- include "rustfs-resources.labels" $ | nindent 4 }}
12+
spec:
13+
{{- include "rustfs-resources.connection" (dict "root" $ "entry" . "id" $id) | nindent 2 }}
14+
{{- with .bucketName }}
15+
bucketName: {{ . | quote }}
16+
{{- end }}
17+
{{- if hasKey . "versioning" }}
18+
versioning: {{ .versioning }}
19+
{{- end }}
20+
{{- if hasKey . "quotaBytes" }}
21+
quotaBytes: {{ .quotaBytes | int64 }}
22+
{{- end }}
23+
{{- include "rustfs-resources.deletionPolicy" (dict "entry" . "id" $id) | nindent 2 }}
24+
{{- end }}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{{- $seen := dict }}
2+
{{- range .Values.policies }}
3+
{{- $name := include "rustfs-resources.entryName" (dict "entry" . "seen" $seen "kind" "policies") }}
4+
{{- $id := printf "policies[%s]" $name }}
5+
{{- if not .document }}
6+
{{- fail (printf "%s: 'document' is required" $id) }}
7+
{{- end }}
8+
---
9+
apiVersion: rustfs.com/v1alpha1
10+
kind: Policy
11+
metadata:
12+
name: {{ $name }}
13+
labels:
14+
{{- include "rustfs-resources.labels" $ | nindent 4 }}
15+
spec:
16+
{{- include "rustfs-resources.connection" (dict "root" $ "entry" . "id" $id) | nindent 2 }}
17+
{{- with .policyName }}
18+
policyName: {{ . | quote }}
19+
{{- end }}
20+
document:
21+
{{- toYaml .document | nindent 4 }}
22+
{{- include "rustfs-resources.deletionPolicy" (dict "entry" . "id" $id) | nindent 2 }}
23+
{{- end }}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{{- $seen := dict }}
2+
{{- range .Values.users }}
3+
{{- $name := include "rustfs-resources.entryName" (dict "entry" . "seen" $seen "kind" "users") }}
4+
{{- $id := printf "users[%s]" $name }}
5+
{{- $inlineKey := .secretKey | default "" }}
6+
{{- $ref := .secretKeyRef | default dict }}
7+
{{- $refName := $ref.name | default "" }}
8+
{{- if and (ne $inlineKey "") (ne $refName "") }}
9+
{{- fail (printf "%s: set either secretKeyRef or inline secretKey, not both" $id) }}
10+
{{- end }}
11+
{{- if and (eq $inlineKey "") (eq $refName "") }}
12+
{{- fail (printf "%s: a secret key source is required; set secretKeyRef (existing Secret) or secretKey (chart-created)" $id) }}
13+
{{- end }}
14+
{{- $secretName := ternary $refName (printf "%s-user-%s" $.Release.Name $name) (ne $refName "") }}
15+
---
16+
apiVersion: rustfs.com/v1alpha1
17+
kind: User
18+
metadata:
19+
name: {{ $name }}
20+
labels:
21+
{{- include "rustfs-resources.labels" $ | nindent 4 }}
22+
spec:
23+
{{- include "rustfs-resources.connection" (dict "root" $ "entry" . "id" $id) | nindent 2 }}
24+
{{- with .accessKey }}
25+
accessKey: {{ . | quote }}
26+
{{- end }}
27+
secretKeyRef:
28+
name: {{ $secretName }}
29+
{{- if ne $refName "" }}
30+
{{- with $ref.key }}
31+
key: {{ . }}
32+
{{- end }}
33+
{{- else }}
34+
key: secretKey
35+
{{- end }}
36+
{{- with .policies }}
37+
policies:
38+
{{- toYaml . | nindent 4 }}
39+
{{- end }}
40+
{{- if hasKey . "enabled" }}
41+
enabled: {{ .enabled }}
42+
{{- end }}
43+
{{- include "rustfs-resources.deletionPolicy" (dict "entry" . "id" $id) | nindent 2 }}
44+
{{- if eq $refName "" }}
45+
---
46+
apiVersion: v1
47+
kind: Secret
48+
metadata:
49+
name: {{ $secretName }}
50+
namespace: {{ $.Release.Namespace }}
51+
labels:
52+
{{- include "rustfs-resources.labels" $ | nindent 4 }}
53+
type: Opaque
54+
stringData:
55+
secretKey: {{ $inlineKey | quote }}
56+
{{- end }}
57+
{{- end }}

0 commit comments

Comments
 (0)