Files
rustfs/helm/rustfs/templates/_helpers.tpl
T
Zhengchao An e2f394a897 feat(helm): flexible drivesPerNode topology with backward-compatible per-pool defaults (#4901)
* helm chart: one data drive per node - fix1

* refactor(helm): prevent negative or 0 replicaCount

Co-authored-by: Copilot <copilot@github.com>

* refactor(helm): remove env REPLICA_COUNT

* feat(helm): default no logs directory to force stdout

Co-authored-by: Copilot <copilot@github.com>

* feat(helm): add drivesPerNode

* feat(helm): correct default parity with 4 nodes /  1 drive per node

* conditional render of RUSTFS_OBS_LOG_DIRECTORY

* feat(chart): add table doc for parity

* fix(chart): handle invalid annotation objects

* fix(chart): move logging and obsevability options together; default for kubernetes output to stdout

* feat(chart): better table doc for parity

* fix(chart): leave RUSTFS_OBS_LOG_DIRECTORY empty, or the defaults will attempt to write to readonly fs

* fix(chart): chart defaults as the previous version: 4 nodes with 4 drives per node

* feat(chart): render RUSTFS_STORAGE_CLASS_STANDARD in configmap

* minor fix for pvcAnnotations

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Cristian Chiru <cristi.chiru@gmail.com>

* fix(chart): better drivesPerNode handling

* fix(chart): obs_log_directory default non empty again to preserve previous deployments compatibility

* fix(chart): proper drivesPerNode impl

* fix(chart): values clarification for empty vs unset `obs_log_directory`

* feat(chart): add service externalIPs

* feat(helm): add optional service labels

* fix(helm): merge service labels

* fix(helm): storageclass pvcAnnotations

* fix(chart): move logging and obsevability options together; default for kubernetes output to stdout

* fix(helm): remove duplicate keys

* fix(helm): default drivesPerNode null, keep legacy chart behavior

* feat(helm): add template test for externalIPs

* fix(helm): per-pool drivesPerNode inference, restore regression tests

Repairs the drivesPerNode feature from #2693 so it renders and stays
upgrade-safe:

- define the missing drives inference (rustfs.poolDrives) and compute it
  per pool inside rustfs.pools, so mixed 4x4 + 16x1 pool deployments keep
  their exact legacy volumeClaimTemplates when drivesPerNode is unset
- restore the $poolsEnabled definition dropped in the rebase (chart failed
  to render at all)
- fix .Values references inside the pool range (dot is the pool there) and
  keep per-pool storageclass pvcAnnotations overrides working
- make rustfs.volumes derive the drive range from pool drives instead of
  the pod count, and relax the pools.list 4-or-16 restriction to >= 2
- reject drivesPerNode=0 explicitly instead of silently inferring
- keep default rendering identical to main: storage_class_standard stays
  unrendered by default, obs_endpoint.use_stdout stays false, clusterDomain
  value restored
- restore the clusterDomain/mTLS SAN/explicit-volumes regression tests that
  the branch deleted, keeping the new topology tests

---------

Signed-off-by: Cristian Chiru <cristi.chiru@gmail.com>
Co-authored-by: Cristian Chiru <cristi.chiru@gmail.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-16 09:02:04 +00:00

365 lines
13 KiB
Smarty

{{/*
Expand the name of the chart.
*/}}
{{- define "rustfs.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "rustfs.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "rustfs.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "rustfs.labels" -}}
helm.sh/chart: {{ include "rustfs.chart" . }}
{{ include "rustfs.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Render extra labels for the main Service resource.
Merges (in order of increasing precedence):
- commonLabels
- service.labels
*/}}
{{- define "rustfs.serviceLabels" -}}
{{- $labels := mergeOverwrite (dict) (default (dict) .Values.commonLabels) (default (dict) .Values.service.labels) }}
{{- if $labels }}
{{- toYaml $labels }}
{{- end }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "rustfs.selectorLabels" -}}
app.kubernetes.io/name: {{ include "rustfs.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "rustfs.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "rustfs.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Return the secret name
*/}}
{{- define "rustfs.secretName" -}}
{{- if .Values.secret.existingSecret }}
{{- .Values.secret.existingSecret }}
{{- else }}
{{- printf "%s-secret" (include "rustfs.fullname" .) }}
{{- end }}
{{- end }}
{{/*
Return image pull secret content
*/}}
{{- define "imagePullSecret" }}
{{- with .Values.imageRegistryCredentials }}
{{- printf "{\"auths\":{\"%s\":{\"username\":\"%s\",\"password\":\"%s\",\"email\":\"%s\",\"auth\":\"%s\"}}}" .registry .username .password .email (printf "%s:%s" .username .password | b64enc) | b64enc }}
{{- end }}
{{- end }}
{{/*
Return the default imagePullSecret name
*/}}
{{- define "rustfs.imagePullSecret.name" -}}
{{- printf "%s-registry-secret" (include "rustfs.fullname" .) }}
{{- end }}
{{/*
Render imagePullSecrets for workloads - appends registry secret
*/}}
{{- define "chart.imagePullSecrets" -}}
{{- $secrets := .Values.imagePullSecrets | default list }}
{{- if .Values.imageRegistryCredentials.enabled }}
{{- $secrets = append $secrets (dict "name" (include "rustfs.imagePullSecret.name" .)) }}
{{- end }}
{{- toYaml $secrets }}
{{- end }}
{{/*
Render annotations for the main Service resource.
Merges (in order of increasing precedence):
- service.traefikAnnotations (when ingress.className=traefik)
- ingress.traefikAnnotations (when ingress.className=traefik, backwards-compat alias)
- service.annotations
*/}}
{{- define "rustfs.serviceAnnotations" -}}
{{- $annotations := dict }}
{{- if and .Values.mode.distributed.enabled (eq .Values.ingress.className "traefik") }}
{{- $annotations = merge $annotations (default (dict) .Values.service.traefikAnnotations) }}
{{- $annotations = merge $annotations (default (dict) .Values.ingress.traefikAnnotations) }}
{{- end }}
{{- $annotations = merge $annotations (default (dict) .Values.service.annotations) }}
{{- if and .Values.mode.distributed.enabled .Values.mtls.enabled (eq .Values.ingress.className "traefik") }}
{{- $mtls := dict
"traefik.ingress.kubernetes.io/service.serversscheme" "https"
"traefik.ingress.kubernetes.io/service.serverstransport" (printf "%s-%s-transport@kubernetescrd" .Release.Namespace (include "rustfs.fullname" .))
}}
{{- $annotations = merge $annotations $mtls }}
{{- end }}
{{- if $annotations }}
{{- toYaml $annotations }}
{{- end }}
{{- end }}
{{/*
Render annotations for the headless Service resource.
Merges:
- service.headlessAnnotations
*/}}
{{- define "rustfs.headlessServiceAnnotations" -}}
{{- $annotations := default (dict) .Values.service.headlessAnnotations }}
{{- if $annotations }}
{{- toYaml $annotations }}
{{- end }}
{{- end }}
{{/*
Render annotations for the Ingress resource.
Merges (in order of increasing precedence):
- ingress.nginxAnnotations (when ingress.className=nginx)
- ingress.traefikAnnotations (when ingress.className=traefik)
- ingress.customAnnotations (backwards-compat)
- ingress.annotations
*/}}
{{- define "rustfs.ingressAnnotations" -}}
{{- $annotations := dict }}
{{- if eq .Values.ingress.className "nginx" }}
{{- $annotations = merge $annotations (default (dict) .Values.ingress.nginxAnnotations) }}
{{- else if eq .Values.ingress.className "traefik" }}
{{- $annotations = merge $annotations (default (dict) .Values.ingress.traefikAnnotations) }}
{{- end }}
{{- $annotations = merge $annotations (default (dict) .Values.ingress.customAnnotations) }}
{{- $annotations = merge $annotations (default (dict) .Values.ingress.annotations) }}
{{- if $annotations }}
{{- toYaml $annotations }}
{{- end }}
{{- end }}
{{/*
Resolve the Kubernetes cluster DNS domain (defaults to cluster.local).
Trims any leading/trailing dots so callers can safely append it after `svc.`,
falling back to cluster.local when the value is empty or only dots.
*/}}
{{- define "rustfs.clusterDomain" -}}
{{- .Values.clusterDomain | default "cluster.local" | trimAll "." | default "cluster.local" -}}
{{- end -}}
{{/*
Return the fully qualified name of a server pool.
Pool 0 keeps the legacy single-pool name so that existing deployments can be
expanded in place without renaming their StatefulSet, pods or PVCs.
Expects a dict with keys "root" (the chart root context) and "index".
*/}}
{{- define "rustfs.poolFullname" -}}
{{- if eq (int .index) 0 -}}
{{- include "rustfs.fullname" .root -}}
{{- else -}}
{{- /* Truncate the base name, not the suffix: the pool index must survive
truncation or two long-named pools could collide. */ -}}
{{- $suffix := printf "-pool%d" (int .index) -}}
{{- printf "%s%s" (include "rustfs.fullname" .root | trunc (int (sub 63 (len $suffix))) | trimSuffix "-") $suffix -}}
{{- end -}}
{{- end }}
{{/*
Return the number of data drives (PVCs) per pod for a pool.
When .Values.drivesPerNode is set it applies to every pool. When unset the
value is inferred per pool from its replica count so that rendered output
stays identical to the pre-drivesPerNode chart: 4 replicas keep the legacy
4-drive layout, everything else (16x1 and any new topology) gets one drive.
Expects a dict with keys "root" (the chart root context) and "replicas".
*/}}
{{- define "rustfs.poolDrives" -}}
{{- if kindIs "invalid" .root.Values.drivesPerNode -}}
{{- if eq (int .replicas) 4 -}}
4
{{- else -}}
1
{{- end -}}
{{- else -}}
{{- $d := int .root.Values.drivesPerNode -}}
{{- if lt $d 1 -}}
{{- fail "drivesPerNode must be >= 1 when set" -}}
{{- end -}}
{{- $d -}}
{{- end -}}
{{- end }}
{{/*
Return the normalized list of server pools as JSON.
With pools disabled this is a single pool built from the top-level
replicaCount/storageclass values, which keeps all rendered output identical
to the previous single-pool chart. With pools enabled, each entry of
pools.list becomes one pool; omitted fields inherit the top-level values.
Pools are strictly append-only: the index determines the StatefulSet name,
so entries must never be removed or reordered.
*/}}
{{- define "rustfs.pools" -}}
{{- $pools := list -}}
{{- if and .Values.pools .Values.pools.enabled -}}
{{- if not .Values.mode.distributed.enabled -}}
{{- fail "pools.enabled requires mode.distributed.enabled=true" -}}
{{- end -}}
{{- if not .Values.pools.list -}}
{{- fail "pools.enabled is true but pools.list is empty" -}}
{{- end -}}
{{- range $i, $p := .Values.pools.list -}}
{{- $p = default (dict) $p -}}
{{- $replicas := int (default $.Values.replicaCount $p.replicaCount) -}}
{{- if lt $replicas 2 -}}
{{- fail (printf "pools.list[%d].replicaCount must be >= 2, got %d" $i $replicas) -}}
{{- end -}}
{{- $sc := mergeOverwrite (deepCopy $.Values.storageclass) (default (dict) $p.storageclass) -}}
{{- $drives := int (include "rustfs.poolDrives" (dict "root" $ "replicas" $replicas)) -}}
{{- $pools = append $pools (dict "index" $i "fullname" (include "rustfs.poolFullname" (dict "root" $ "index" $i)) "replicaCount" $replicas "drives" $drives "storageclass" $sc) -}}
{{- end -}}
{{- else -}}
{{- $drives := int (include "rustfs.poolDrives" (dict "root" $ "replicas" (int .Values.replicaCount))) -}}
{{- $pools = append $pools (dict "index" 0 "fullname" (include "rustfs.fullname" .) "replicaCount" (int .Values.replicaCount) "drives" $drives "storageclass" .Values.storageclass) -}}
{{- end -}}
{{- toJson $pools -}}
{{- end }}
{{/*
Render RUSTFS_VOLUMES
One volume expression per server pool, joined with spaces (the server splits
RUSTFS_VOLUMES on spaces, one pool per expression).
*/}}
{{- define "rustfs.volumes" -}}
{{- $protocol := "http" -}}
{{- if .Values.mtls.enabled -}}
{{- $protocol = "https" -}}
{{- end -}}
{{- $headless := printf "%s-headless" (include "rustfs.fullname" .) -}}
{{- $ns := .Release.Namespace -}}
{{- $domain := include "rustfs.clusterDomain" . -}}
{{- $port := .Values.service.endpoint.port | int -}}
{{- $exprs := list -}}
{{- range $pool := include "rustfs.pools" . | fromJsonArray -}}
{{- $n := int $pool.replicaCount -}}
{{- $d := int $pool.drives -}}
{{- if gt $d 1 -}}
{{- $exprs = append $exprs (printf "%s://%s-{0...%d}.%s.%s.svc.%s:%d/data/rustfs{0...%d}" $protocol $pool.fullname (sub $n 1) $headless $ns $domain $port (sub $d 1)) -}}
{{- else -}}
{{- $exprs = append $exprs (printf "%s://%s-{0...%d}.%s.%s.svc.%s:%d/data" $protocol $pool.fullname (sub $n 1) $headless $ns $domain $port) -}}
{{- end -}}
{{- end -}}
{{- join " " $exprs -}}
{{- end }}
{{/*
Render RUSTFS_SERVER_DOMAINS
*/}}
{{- define "rustfs.serverDomains" -}}
{{- $domains := list .Values.config.rustfs.domains -}}
{{- $headless := printf "%s-headless" (include "rustfs.fullname" .) -}}
{{- $servicePort := .Values.service.endpoint.port | default 9000 -}}
{{- range $pool := include "rustfs.pools" . | fromJsonArray -}}
{{- range $i := until (int $pool.replicaCount) -}}
{{- $podDomain := printf "%s-%d.%s:%d" $pool.fullname $i $headless (int $servicePort) -}}
{{- $domains = append $domains $podDomain -}}
{{- end -}}
{{- end -}}
{{- join "," $domains -}}
{{- end -}}
{{/* Render probe command for liveness and readiness
*/}}
{{- define "rustfs.probeCommand" -}}
{{- $endpoint_port := .Values.service.endpoint.port | default 9000 -}}
{{- $console_port := .Values.service.console.port | default 9001 -}}
{{- $args := "-skf" -}}
{{- if and .Values.mtls.enabled -}}
{{- $args = printf "%s --cert %s --key %s" $args .Values.mtls.clientCertPath .Values.mtls.clientKeyPath -}}
{{- end -}}
- /bin/sh
- -c
- |
curl {{ $args }} https://127.0.0.1:{{ $endpoint_port }}/health/ready && \
curl {{ $args }} https://127.0.0.1:{{ $console_port }}/rustfs/console/health
{{- end -}}
{{/*
Render liveness and readiness probe for http and https
*/}}
{{- define "rustfs.probes" -}}
{{- if .Values.livenessProbe.enabled }}
livenessProbe:
{{- if .Values.mtls.enabled }}
exec:
command:
{{ include "rustfs.probeCommand" . | nindent 6 }}
{{- else }}
httpGet:
path: /health
port: {{ .Values.service.endpoint.port | default 9000 }}
scheme: {{ if .Values.mtls.enabled }}HTTPS{{ else }}HTTP{{ end }}
{{- end }}
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds | default 60 }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds | default 5 }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds | default 3 }}
successThreshold: {{ .Values.livenessProbe.successThreshold | default 1 }}
failureThreshold: {{ .Values.livenessProbe.failureThreshold | default 3 }}
{{- end }}
{{- if .Values.readinessProbe.enabled }}
readinessProbe:
{{- if .Values.mtls.enabled }}
exec:
command:
{{ include "rustfs.probeCommand" . | nindent 6 }}
{{- else }}
httpGet:
path: /health/ready
port: {{ .Values.service.endpoint.port | default 9000 }}
scheme: {{ if .Values.mtls.enabled }}HTTPS{{ else }}HTTP{{ end }}
{{- end }}
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds | default 60 }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds | default 5 }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds | default 3 }}
successThreshold: {{ .Values.readinessProbe.successThreshold | default 1 }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold | default 3 }}
{{- end }}
{{- end -}}