mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-13 08:36:54 +00:00
e2f394a897
* 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>
365 lines
13 KiB
Smarty
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 -}}
|