Skip to content

Commit 8fa87fc

Browse files
authored
Merge branch 'release' into claude/quirky-hawking-72b0d2
2 parents 847e36a + 5e6bb9e commit 8fa87fc

10 files changed

Lines changed: 728 additions & 14 deletions

File tree

deploy/helm/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ sources:
1111
- https://github.com/appsmithorg/appsmith
1212
home: https://www.appsmith.com/
1313
icon: https://assets.appsmith.com/appsmith-icon.png
14-
version: 3.8.1
14+
version: 3.9.0
1515
dependencies:
1616
- condition: redis.enabled
1717
name: redis

deploy/helm/README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ for prerequisites, step-by-step setup, and platform-specific instructions (EKS,
6464
| Key | Type | Default | Description |
6565
|-----|------|---------|-------------|
6666
| redis.enabled | bool | `true` | Deploy the bundled Bitnami Redis subchart |
67-
| redis.auth.enabled | bool | `false` | Enable Redis authentication |
67+
| redis.auth.enabled | bool | `true` | Enable Redis authentication |
68+
| redis.auth.existingSecret | string | `"appsmith-redis-secret"` | Name of the Secret holding the Redis password. Bootstrapped by the redisAuth.passwordInit Job when absent (see redisAuth below) |
69+
| redis.auth.existingSecretPasswordKey | string | `"redis-password"` | Key within existingSecret that holds the Redis password |
6870
| redis.master.nodeSelector | object | `{}` | Node selector for Redis master pods |
6971
| redis.master.disableCommands | list | `[]` | Commands to disable on Redis master |
7072
| redis.master.affinity | object | `{}` | Affinity rules for Redis master pods |
@@ -78,6 +80,15 @@ for prerequisites, step-by-step setup, and platform-specific instructions (EKS,
7880
| redis.image.repository | string | `"redis"` | Redis image repository |
7981
| redis.image.tag | string | `"7.4.9"` | Redis image tag |
8082

83+
### Redis Auth
84+
85+
| Key | Type | Default | Description |
86+
|-----|------|---------|-------------|
87+
| redisAuth.passwordInit.image.registry | string | `"docker.io"` | Registry for the kubectl image |
88+
| redisAuth.passwordInit.image.repository | string | `"alpine/kubectl"` | Repository path for the kubectl image |
89+
| redisAuth.passwordInit.image.tag | string | `"latest"` | Image tag. "latest" is used by default to sidestep upstream tag retention; pin for reproducibility. |
90+
| redisAuth.passwordInit.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy for the Job |
91+
8192
### MongoDB (Bitnami subchart)
8293

8394
| Key | Type | Default | Description |

deploy/helm/templates/_helpers.tpl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,57 @@ Uses existing secret if provided, otherwise derives from the CR name
240240
{{- end -}}
241241
{{- end -}}
242242

243+
{{/*
244+
Redis: password secret name
245+
Uses existing secret if provided, otherwise derives "{release}-redis-secret"
246+
*/}}
247+
{{- define "appsmith.redisSecretName" -}}
248+
{{- .Values.redis.auth.existingSecret | default (printf "%s-redis-secret" .Release.Name) -}}
249+
{{- end -}}
250+
251+
{{/*
252+
Redis: validate the redis.auth.password configuration.
253+
254+
redis.auth.password is a Bitnami subchart passthrough that the Appsmith
255+
templates never read on their own. There is exactly ONE supported way to use
256+
it: the fully self-managed path, where the operator also disables the chart's
257+
bootstrap secret (existingSecret: "") and hands the app a matching connection
258+
string via applicationConfig.APPSMITH_REDIS_URL. Any other use silently splits
259+
the password between Redis and the app, so we fail fast instead.
260+
261+
Invoked from a template that always renders (configMap.yaml) so it evaluates on
262+
every `helm template`/install/upgrade.
263+
*/}}
264+
{{- define "appsmith.validateRedisAuth" -}}
265+
{{- if .Values.redis.auth.password -}}
266+
{{- if or .Values.redis.auth.existingSecret (not .Values.applicationConfig.APPSMITH_REDIS_URL) -}}
267+
{{ fail (printf "redis.auth.password is set, which is only supported on the self-managed path. Choose one of:\n 1. Leave redis.auth.password unset and let the chart bootstrap a password (default), or supply your own secret via redis.auth.existingSecret / redis.auth.existingSecretPasswordKey.\n 2. Self-manage the password: set redis.auth.password, set redis.auth.existingSecret: \"\", and set applicationConfig.APPSMITH_REDIS_URL=redis://:<password>@%s-redis-master:6379 so the app uses the same credential." .Release.Name) }}
268+
{{- end -}}
269+
{{- end -}}
270+
{{- end -}}
271+
272+
{{/*
273+
Redis: master service hostname (FQDN inside the cluster).
274+
Reuses the bundled `common.names.fullname` helper (the one the subchart's master
275+
Service uses), evaluated in the redis subchart's context (.Subcharts.redis), so the
276+
host always matches the Service it renders — including the edge cases where the
277+
release name contains "redis" (the subchart collapses its fullname to just the
278+
release name) or redis.nameOverride / redis.fullnameOverride is set.
279+
Only valid when redis.enabled (the subchart context exists); all callers gate on it.
280+
*/}}
281+
{{- define "appsmith.redisMasterHost" -}}
282+
{{- printf "%s-master.%s.svc.cluster.local" (include "common.names.fullname" .Subcharts.redis) (include "appsmith.namespace" .) -}}
283+
{{- end -}}
284+
285+
{{/*
286+
Redis: kubectl image used by the password-init Job.
287+
Kept independent from the MongoDB equivalent so the two bootstraps don't share config.
288+
*/}}
289+
{{- define "appsmith.redisPasswordInitImage" -}}
290+
{{- $img := .Values.redisAuth.passwordInit.image -}}
291+
{{- printf "%s/%s:%s" $img.registry $img.repository $img.tag -}}
292+
{{- end -}}
293+
243294
{{/*
244295
Renders a value that contains template.
245296
*/}}

deploy/helm/templates/configMap.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
{{- $postgresqlPassword := .Values.postgresql.auth.password -}}
77
{{- $postgresqlDatabase := .Values.postgresql.auth.database -}}
88
{{- $releaseName := .Release.Name -}}
9+
{{- include "appsmith.validateRedisAuth" . -}}
910
apiVersion: v1
1011
kind: ConfigMap
1112
metadata:
@@ -37,8 +38,8 @@ data:
3738
{{- end }}
3839

3940
{{- if and (eq "APPSMITH_REDIS_URL" $key) ( not $value) }}
40-
{{- if $.Values.redis.enabled }}
41-
{{ $key }}: redis://{{ $releaseName }}-redis-master.{{ $nameSpace }}.svc.cluster.local:6379
41+
{{- if and $.Values.redis.enabled (not $.Values.redis.auth.enabled) }}
42+
{{ $key }}: redis://{{ include "appsmith.redisMasterHost" $ }}:6379
4243
{{- end }}
4344
{{- end }}
4445

deploy/helm/templates/deployment.yaml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,21 @@ spec:
7979
{{- else }}
8080
image: "{{ .Values.redis.image.registry }}/{{ .Values.redis.image.repository }}:{{ .Values.redis.image.tag }}"
8181
{{- end }}
82-
command: ['sh', '-c', "until redis-cli -h {{.Release.Name}}-redis-master.{{.Release.Namespace}}.svc.cluster.local ping ; do echo waiting for redis; sleep 2; done"]
82+
command: ['sh', '-c', "until redis-cli -h {{ include "appsmith.redisMasterHost" . }} ping ; do echo waiting for redis; sleep 2; done"]
83+
{{- if and .Values.redis.auth.enabled (not .Values.applicationConfig.APPSMITH_REDIS_URL) }}
84+
# Pull the password from the chart-managed Secret so the readiness ping can
85+
# authenticate. Skipped when the operator supplies their own APPSMITH_REDIS_URL
86+
# (the self-managed redis.auth.password path), because then no chart Secret exists
87+
# to reference and an unresolvable secretKeyRef would wedge the pod in
88+
# CreateContainerConfigError. The wait still works unauthenticated: `redis-cli ping`
89+
# against an auth-required server replies NOAUTH but exits 0, satisfying the loop.
90+
env:
91+
- name: REDISCLI_AUTH
92+
valueFrom:
93+
secretKeyRef:
94+
name: {{ include "appsmith.redisSecretName" . }}
95+
key: {{ .Values.redis.auth.existingSecretPasswordKey }}
96+
{{- end }}
8397
{{- end }}
8498
{{- if .Values.mongodb.enabled }}
8599
- name: mongo-init-container
@@ -175,6 +189,21 @@ spec:
175189
name: {{ include "appsmith.mongoOperatorSecretName" . }}
176190
key: connectionString.standardSrv
177191
{{- end }}
192+
{{- if and .Values.redis.enabled .Values.redis.auth.enabled (not .Values.applicationConfig.APPSMITH_REDIS_URL) }}
193+
# Assemble the authenticated Redis URL for the bundled subchart. Skipped when the
194+
# user supplied applicationConfig.APPSMITH_REDIS_URL (which is honored via the
195+
# ConfigMap) — an explicit env entry here would otherwise take precedence over
196+
# envFrom and silently override it. The password is pulled from the Secret and
197+
# injected by reference, so the cleartext never lands in the ConfigMap; order
198+
# matters — the password var must precede the URL that references it via $(VAR).
199+
- name: APPSMITH_REDIS_PASSWORD
200+
valueFrom:
201+
secretKeyRef:
202+
name: {{ include "appsmith.redisSecretName" . }}
203+
key: {{ .Values.redis.auth.existingSecretPasswordKey }}
204+
- name: APPSMITH_REDIS_URL
205+
value: "redis://:$(APPSMITH_REDIS_PASSWORD)@{{ include "appsmith.redisMasterHost" . }}:6379"
206+
{{- end }}
178207
envFrom:
179208
- configMapRef:
180209
name: {{ include "appsmith.fullname" . }}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
{{/*
2+
Creates the Redis password secret via a pre-install/pre-upgrade Job when the
3+
bundled redis subchart is enabled with auth on.
4+
5+
The Job is idempotent: if the secret already exists, it exits without change.
6+
This keeps the password stable across Helm upgrades and ArgoCD re-syncs —
7+
unlike a template-based `randAlphaNum` approach, which changes on every render
8+
and would either lock clients out or force an ArgoCD ignoreDifferences rule.
9+
10+
The runtime "secret already exists -> exit 0" check is also the migration and
11+
bring-your-own-secret guard: an upgrade of an install that already has the
12+
secret (or a user who pre-created their own) is left untouched.
13+
14+
The resulting Secret has no Helm release labels/annotations and no
15+
ownerReferences, so ArgoCD does not track or diff it.
16+
*/}}
17+
{{/*
18+
Skip the bootstrap entirely when redis.auth.password is set: on that path the
19+
operator self-manages the credential (Bitnami uses redis.auth.password directly)
20+
and there is no chart secret to create. Safe ONLY because appsmith.validateRedisAuth
21+
(see _helpers.tpl, invoked from configMap.yaml) rejects every redis.auth.password
22+
configuration except the self-managed one (existingSecret: "" + a matching
23+
APPSMITH_REDIS_URL) — so this can no longer leave a non-empty existingSecret
24+
pointing at a secret the hook never creates.
25+
*/}}
26+
{{- if and .Values.redis.enabled .Values.redis.auth.enabled (not .Values.redis.auth.password) }}
27+
{{- $secretName := include "appsmith.redisSecretName" . -}}
28+
{{- $passwordKey := .Values.redis.auth.existingSecretPasswordKey -}}
29+
{{- $jobName := printf "%s-redis-password-init" (include "appsmith.fullname" .) | trunc 63 | trimSuffix "-" -}}
30+
{{- $namespace := include "appsmith.namespace" . -}}
31+
---
32+
apiVersion: v1
33+
kind: ServiceAccount
34+
metadata:
35+
name: {{ $jobName }}
36+
namespace: {{ $namespace }}
37+
labels:
38+
{{- include "appsmith.labels" . | nindent 4 }}
39+
annotations:
40+
helm.sh/hook: pre-install,pre-upgrade
41+
helm.sh/hook-weight: "-5"
42+
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
43+
---
44+
apiVersion: rbac.authorization.k8s.io/v1
45+
kind: Role
46+
metadata:
47+
name: {{ $jobName }}
48+
namespace: {{ $namespace }}
49+
labels:
50+
{{- include "appsmith.labels" . | nindent 4 }}
51+
annotations:
52+
helm.sh/hook: pre-install,pre-upgrade
53+
helm.sh/hook-weight: "-5"
54+
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
55+
rules:
56+
# Check whether the password secret already exists (scoped to one name).
57+
- apiGroups: [""]
58+
resources: ["secrets"]
59+
resourceNames: [{{ $secretName | quote }}]
60+
verbs: ["get"]
61+
# Create it if it doesn't. resourceNames can't scope `create` (the name
62+
# is in the request body, not the URL), so this is namespace-scoped.
63+
- apiGroups: [""]
64+
resources: ["secrets"]
65+
verbs: ["create"]
66+
---
67+
apiVersion: rbac.authorization.k8s.io/v1
68+
kind: RoleBinding
69+
metadata:
70+
name: {{ $jobName }}
71+
namespace: {{ $namespace }}
72+
labels:
73+
{{- include "appsmith.labels" . | nindent 4 }}
74+
annotations:
75+
helm.sh/hook: pre-install,pre-upgrade
76+
helm.sh/hook-weight: "-5"
77+
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
78+
subjects:
79+
- kind: ServiceAccount
80+
name: {{ $jobName }}
81+
namespace: {{ $namespace }}
82+
roleRef:
83+
kind: Role
84+
name: {{ $jobName }}
85+
apiGroup: rbac.authorization.k8s.io
86+
---
87+
apiVersion: batch/v1
88+
kind: Job
89+
metadata:
90+
name: {{ $jobName }}
91+
namespace: {{ $namespace }}
92+
labels:
93+
{{- include "appsmith.labels" . | nindent 4 }}
94+
annotations:
95+
helm.sh/hook: pre-install,pre-upgrade
96+
helm.sh/hook-weight: "0"
97+
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
98+
spec:
99+
ttlSecondsAfterFinished: 60
100+
backoffLimit: 3
101+
template:
102+
metadata:
103+
labels:
104+
{{- include "appsmith.labels" . | nindent 8 }}
105+
spec:
106+
serviceAccountName: {{ $jobName }}
107+
restartPolicy: Never
108+
{{- if .Values.image.pullSecrets }}
109+
imagePullSecrets:
110+
- name: {{ .Values.image.pullSecrets }}
111+
{{- end }}
112+
containers:
113+
- name: create-password
114+
image: {{ include "appsmith.redisPasswordInitImage" . | quote }}
115+
imagePullPolicy: {{ .Values.redisAuth.passwordInit.image.pullPolicy }}
116+
command:
117+
- /bin/sh
118+
- -c
119+
- |
120+
set -eu
121+
SECRET_NAME={{ $secretName | quote }}
122+
NAMESPACE={{ $namespace | quote }}
123+
PASSWORD_KEY={{ $passwordKey | quote }}
124+
if kubectl -n "$NAMESPACE" get secret "$SECRET_NAME" >/dev/null 2>&1; then
125+
echo "Secret $SECRET_NAME already exists; leaving it untouched."
126+
exit 0
127+
fi
128+
PASSWORD=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 24)
129+
kubectl -n "$NAMESPACE" create secret generic "$SECRET_NAME" \
130+
--from-literal="$PASSWORD_KEY"="$PASSWORD"
131+
echo "Created secret $SECRET_NAME."
132+
securityContext:
133+
allowPrivilegeEscalation: false
134+
readOnlyRootFilesystem: true
135+
runAsNonRoot: true
136+
runAsUser: 1000
137+
capabilities:
138+
drop: ["ALL"]
139+
{{- end }}

0 commit comments

Comments
 (0)