From 974b7d40c9b0a0dd372e9d8ae324dd5e478d371c Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Mon, 29 Jun 2026 14:26:20 +0200 Subject: [PATCH 01/17] Closes: #399 - Migrate away from bitnami/elasticsearch --- .github/ct.yaml | 1 + .github/kubeconform.sh | 7 +- .github/workflows/ci.yaml | 8 +++ zammad/Chart.yaml | 13 ++-- zammad/README.md | 58 ++++++++++++---- zammad/ci/full-values.yaml | 7 +- zammad/templates/_helpers.tpl | 37 ++++++++++- zammad/templates/configmap-init.yaml | 4 +- zammad/templates/job-init.yaml | 4 +- zammad/values.yaml | 99 +++++++++++++++------------- 10 files changed, 170 insertions(+), 68 deletions(-) diff --git a/.github/ct.yaml b/.github/ct.yaml index 17ab77f2..b0f3cd5d 100644 --- a/.github/ct.yaml +++ b/.github/ct.yaml @@ -2,6 +2,7 @@ chart-dirs: . chart-repos: - bitnami=https://charts.bitnami.com/bitnami + - elastic=https://helm.elastic.co check-version-increment: true debug: true # upgrade: true # Causes issues because of low performance on default GitHub actions servers. diff --git a/.github/kubeconform.sh b/.github/kubeconform.sh index 86bfbd60..ed1c401a 100755 --- a/.github/kubeconform.sh +++ b/.github/kubeconform.sh @@ -18,5 +18,10 @@ for CHART_DIR in ${CHART_DIRS};do helm dependency build "${CHART_DIR}" echo "kubeconform(ing) ${CHART_DIR##charts/} chart…" - helm template "${CHART_DIR}" | kubeconform --strict --verbose --kubernetes-version "${KUBERNETES_VERSION#v}" + # The second schema location resolves CRD-based kinds (e.g. the ECK-managed + # Elasticsearch resource) from the datree CRDs catalog. + helm template "${CHART_DIR}" | kubeconform --strict --verbose \ + --schema-location default \ + --schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \ + --kubernetes-version "${KUBERNETES_VERSION#v}" done diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4bba6055..41f0035f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -123,6 +123,14 @@ jobs: config: .github/kind-config.yaml node_image: kindest/node:${{ matrix.k8s }} + - name: Install ECK operator (CRDs + operator) + if: steps.list-changed.outputs.changed == 'true' + run: | + helm repo add elastic https://helm.elastic.co + helm repo update + helm install elastic-operator elastic/eck-operator \ + --namespace elastic-system --create-namespace --wait + - name: Create Namespace 'zammad' run: kubectl create namespace zammad diff --git a/zammad/Chart.yaml b/zammad/Chart.yaml index 75ec83d0..1b09579b 100755 --- a/zammad/Chart.yaml +++ b/zammad/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: zammad -version: 17.0.2 +version: 18.0.0 appVersion: 7.1.2-0013 description: Zammad is a web based open source helpdesk/customer support system with many features to manage customer communication via several channels like telephone, facebook, twitter, chat and e-mails. home: https://zammad.org @@ -14,9 +14,14 @@ maintainers: - name: mgruner email: enjoy@zammad.com dependencies: - - name: elasticsearch - repository: https://charts.bitnami.com/bitnami - version: 21.6.3 + # Official Elastic way to run Elasticsearch: the eck-elasticsearch chart deploys + # an Elasticsearch custom resource that is reconciled by the ECK operator. + # The ECK operator (+ CRDs) must be installed cluster-wide as a prerequisite, + # see README. Aliased to "elasticsearch" to keep the values key stable. + - name: eck-elasticsearch + alias: elasticsearch + repository: https://helm.elastic.co + version: 0.19.1 condition: zammadConfig.elasticsearch.enabled - name: minio version: 14.10.5 diff --git a/zammad/README.md b/zammad/README.md index a3e15346..f6cc9d71 100644 --- a/zammad/README.md +++ b/zammad/README.md @@ -41,9 +41,24 @@ Be aware that the Zammad Helm chart version is different from the actual Zammad ## Prerequisites -- Kubernetes 1.19+ +- Kubernetes 1.21+ - Helm 3.8.0+ - Cluster with at least 4GB of free RAM +- The [ECK operator](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-install-helm.html) + must be installed in the cluster when using the bundled Elasticsearch + (`zammadConfig.elasticsearch.enabled: true`, the default). The bundled + Elasticsearch is deployed via the official `eck-elasticsearch` chart, which + creates an `Elasticsearch` custom resource that is reconciled by the operator. + This is **not** required when connecting to an external Elasticsearch service. + + Install the operator once per cluster: + + ```console + helm repo add elastic https://helm.elastic.co + helm repo update + helm install elastic-operator elastic/eck-operator \ + --namespace elastic-system --create-namespace + ``` ## Installing the Chart @@ -114,7 +129,11 @@ To deploy on OpenShift unprivileged and with [arbitrary UIDs and GIDs](https://c - [Delete the default key](https://helm.sh/docs/chart_template_guide/values_files/#deleting-a-default-key) `securityContext` and `zammadConfig.initContainers.zammad.securityContext.runAsUser` with `null`. - Disable if used: - also `podSecurityContext` in all subcharts. - - the privileged [sysctlImage](https://github.com/bitnami/charts/tree/main/bitnami/elasticsearch#default-kernel-settings) in elasticsearch subchart. + +The bundled Elasticsearch uses `node.store.allow_mmap: false` by default (see +`elasticsearch.nodeSets[].config`), so no privileged sysctl init container is +required. If you raise `vm.max_map_count` for production instead, do so on the +node level rather than via a privileged init container. ```yaml securityContext: null @@ -130,15 +149,6 @@ zammadConfig: emptyDir: medium: memory -elasticsearch: - sysctlImage: - enabled: false - master: - podSecurityContext: - enabled: false - containerSecurityContext: - enabled: false - memcached: podSecurityContext: enabled: false @@ -201,6 +211,32 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. ## Upgrading +### From Chart Version 17.x to 18.0.0 + +- The bundled Elasticsearch was migrated from the `bitnami/elasticsearch` subchart to the + official [`eck-elasticsearch`](https://artifacthub.io/packages/helm/elastic/eck-elasticsearch) + chart, which is managed by the [ECK operator](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-overview.html). + This is the deployment method officially supported by Elastic. +- **The ECK operator (and its CRDs) must now be installed in the cluster** before upgrading + when `zammadConfig.elasticsearch.enabled` is `true` (the default). See [Prerequisites](#prerequisites). +- The `elasticsearch.*` values changed completely. The previous bitnami values (`master`, `data`, + `ingest`, `coordinating`, `sysctlImage`, `image`, `global.security.allowInsecureImages`, …) no + longer apply. Review the new `elasticsearch.*` block in `values.yaml` (`version`, `nodeSets`, + `http`, …). +- Authentication is now mandatory: ECK creates a superuser `elastic` and stores its password in + the auto-generated secret `{{ .Release.Name }}-elasticsearch-es-elastic-user`. The chart wires + this up automatically, so `secrets.elasticsearch.*` is only relevant for an **external** + Elasticsearch now. +- The in-cluster service name changed from `{{ .Release.Name }}-elasticsearch` to + `{{ .Release.Name }}-elasticsearch-es-http`. +- TLS on the HTTP layer is disabled by default (`elasticsearch.http.tls.selfSignedCertificate.disabled: true`) + to keep the plain-`http` connection behaviour. Set it to `false` and `zammadConfig.elasticsearch.schema: https` + to use the operator-managed self-signed certificate. +- The StatefulSet and its `PersistentVolumeClaim`s are recreated under new names. As the search + index can be rebuilt from PostgreSQL, the recommended path is to delete the old bitnami + Elasticsearch PVCs and let the index be recreated. To force a full rebuild after the upgrade, + set `zammadConfig.elasticsearch.reindex: true` for the upgrade run. + ### From Chart Version 16.x to 17.0.0 - The bitnami PostgreSQL subchart was replaced with [cloudpirates-postgres/postgres](https://artifacthub.io/packages/helm/cloudpirates-postgres/postgres). diff --git a/zammad/ci/full-values.yaml b/zammad/ci/full-values.yaml index cd1669e6..5df0a1c4 100755 --- a/zammad/ci/full-values.yaml +++ b/zammad/ci/full-values.yaml @@ -23,9 +23,10 @@ secrets: autoWizard: enabled: true -elasticsearch: - security: - existingSecret: elasticsearch-existing-secret +# The bundled Elasticsearch is managed by the ECK operator and uses its own +# auto-generated credentials secret, so no existingSecret config is needed here. +# (The secrets.elasticsearch.* existing-secret path only applies to an external +# Elasticsearch, i.e. when zammadConfig.elasticsearch.enabled is false.) ingress: enabled: true diff --git a/zammad/templates/_helpers.tpl b/zammad/templates/_helpers.tpl index 29325f26..9935536d 100755 --- a/zammad/templates/_helpers.tpl +++ b/zammad/templates/_helpers.tpl @@ -105,17 +105,52 @@ autowizard secret name {{- end -}} {{- end -}} +{{/* +elasticsearch service host (in-cluster ECK service or external host) +*/}} +{{- define "zammad.elasticsearchHost" -}} +{{- if .Values.zammadConfig.elasticsearch.enabled -}} +{{ .Release.Name }}-elasticsearch-es-http +{{- else -}} +{{ .Values.zammadConfig.elasticsearch.host }} +{{- end -}} +{{- end -}} + +{{/* +elasticsearch user (ECK creates the "elastic" superuser for the in-cluster service) +*/}} +{{- define "zammad.elasticsearchUser" -}} +{{- if .Values.zammadConfig.elasticsearch.enabled -}} +elastic +{{- else -}} +{{ .Values.zammadConfig.elasticsearch.user }} +{{- end -}} +{{- end -}} + {{/* elasticsearch secret name */}} {{- define "zammad.elasticsearchSecretName" -}} -{{- if .Values.secrets.elasticsearch.useExisting -}} +{{- if .Values.zammadConfig.elasticsearch.enabled -}} +{{ .Release.Name }}-elasticsearch-es-elastic-user +{{- else if .Values.secrets.elasticsearch.useExisting -}} {{ .Values.secrets.elasticsearch.secretName }} {{- else -}} {{ include "zammad.fullname" . }}-{{ .Values.secrets.elasticsearch.secretName }} {{- end -}} {{- end -}} +{{/* +elasticsearch secret key (ECK stores the password under the user name "elastic") +*/}} +{{- define "zammad.elasticsearchSecretKey" -}} +{{- if .Values.zammadConfig.elasticsearch.enabled -}} +elastic +{{- else -}} +{{ .Values.secrets.elasticsearch.secretKey }} +{{- end -}} +{{- end -}} + {{/* postgresql secret name */}} diff --git a/zammad/templates/configmap-init.yaml b/zammad/templates/configmap-init.yaml index a8ec91c3..286ed3c2 100644 --- a/zammad/templates/configmap-init.yaml +++ b/zammad/templates/configmap-init.yaml @@ -45,10 +45,10 @@ data: #!/bin/bash set -e - ELASTICSEARCH_URL={{ .Values.zammadConfig.elasticsearch.schema }}://{{ if .Values.zammadConfig.elasticsearch.enabled }}{{ .Release.Name }}-elasticsearch{{ else }}{{ .Values.zammadConfig.elasticsearch.host }}{{ end }}:{{ .Values.zammadConfig.elasticsearch.port }} + ELASTICSEARCH_URL={{ .Values.zammadConfig.elasticsearch.schema }}://{{ include "zammad.elasticsearchHost" . }}:{{ .Values.zammadConfig.elasticsearch.port }} bundle exec rails r "Setting.set('es_url', '${ELASTICSEARCH_URL}')" - ELASTICSEARCH_USER=${ELASTICSEARCH_USER:-{{ .Values.zammadConfig.elasticsearch.user }}} + ELASTICSEARCH_USER=${ELASTICSEARCH_USER:-{{ include "zammad.elasticsearchUser" . }}} if [ -n "${ELASTICSEARCH_USER}" ] && [ -n "${ELASTICSEARCH_PASSWORD}" ]; then bundle exec rails r "Setting.set('es_user', '${ELASTICSEARCH_USER}'); Setting.set('es_password', '${ELASTICSEARCH_PASSWORD}')" fi diff --git a/zammad/templates/job-init.yaml b/zammad/templates/job-init.yaml index cae12839..71e49114 100755 --- a/zammad/templates/job-init.yaml +++ b/zammad/templates/job-init.yaml @@ -91,12 +91,12 @@ spec: env: {{- include "zammad.env" . | nindent 12 }} {{- include "zammad.env.failOnPendingMigrations" . | nindent 12 }} - {{- if or .Values.zammadConfig.elasticsearch.pass .Values.secrets.elasticsearch.useExisting }} + {{- if or .Values.zammadConfig.elasticsearch.enabled .Values.zammadConfig.elasticsearch.pass .Values.secrets.elasticsearch.useExisting }} - name: ELASTICSEARCH_PASSWORD valueFrom: secretKeyRef: name: {{ include "zammad.elasticsearchSecretName" . }} - key: {{ .Values.secrets.elasticsearch.secretKey }} + key: {{ include "zammad.elasticsearchSecretKey" . }} {{- end }} volumeMounts: {{- include "zammad.volumeMounts" . | nindent 12 }} diff --git a/zammad/values.yaml b/zammad/values.yaml index 33877abe..9d4afdec 100644 --- a/zammad/values.yaml +++ b/zammad/values.yaml @@ -85,10 +85,11 @@ podDisruptionBudget: zammadConfig: elasticsearch: - # enable/disable elasticsearch chart dependency + # enable/disable the bundled elasticsearch (ECK) chart dependency enabled: true - # host env var is only used when zammadConfig.elasticsearch.enabled is false - host: zammad-elasticsearch-master + # host is only used when zammadConfig.elasticsearch.enabled is false + # (i.e. when connecting to an external Elasticsearch service) + host: external-elasticsearch-host initialisation: true pass: "" port: 9200 @@ -710,48 +711,58 @@ initContainers: [] # dependency charts config -# Settings for the elasticsearch subchart +# Settings for the elasticsearch subchart (eck-elasticsearch). +# +# This deploys an Elasticsearch custom resource that is managed by the ECK operator. +# The ECK operator (and its CRDs) MUST already be installed in the cluster, see README. +# +# ECK names the resource "-elasticsearch" (nameOverride below), which +# results in the in-cluster service "-elasticsearch-es-http" and the +# auto-generated superuser secret "-elasticsearch-es-elastic-user" +# (user "elastic"). The Zammad templates pick these up automatically. elasticsearch: - # Workaround: switch to bitnami legacy image repository (https://github.com/bitnami/containers/issues/83267) - image: - repository: bitnamilegacy/elasticsearch - sysctlImage: - repository: bitnamilegacy/os-shell - volumePermissions: - image: - repository: bitnamilegacy/os-shell - metrics: - image: - repository: bitnamilegacy/elasticsearch-exporter - global: - security: - allowInsecureImages: true - - clusterName: zammad - coordinating: - replicaCount: 0 - data: - replicaCount: 0 - ingest: - replicaCount: 0 - master: - heapSize: 512m - masterOnly: false - replicaCount: 1 - resourcesPreset: medium - resources: {} - # requests: - # cpu: 50m - # memory: 512Mi - # limits: - # cpu: 100m - # memory: 1024Mi - - # To use an existing Kubernetes secret containing the credentials, - # remove the comments on the lines below and adjust them accordingly - # - # security: - # existingSecret: elastic-credentials + nameOverride: elasticsearch + # Elasticsearch version. Kept in sync with the version Zammad is tested against. + version: 8.18.0 + http: + tls: + selfSignedCertificate: + # Disable TLS on the HTTP layer (Variant A): Zammad talks plain http to ES. + # Native authentication (user "elastic" + generated password) stays enabled. + # Set to false and adjust zammadConfig.elasticsearch.schema to "https" to + # use the operator-managed self-signed certificate instead. + disabled: true + nodeSets: + - name: default + count: 1 + config: + # Single-node default deployment without raising vm.max_map_count. + # For production, raise the kernel setting vm.max_map_count to 1048576 via + # an initContainer and remove this line instead. See: + # https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-virtual-memory.html + node.store.allow_mmap: false + volumeClaimTemplates: + - metadata: + name: elasticsearch-data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi + # storageClassName: "" + podTemplate: + spec: + containers: + - name: elasticsearch + env: + - name: ES_JAVA_OPTS + value: "-Xms512m -Xmx512m" + resources: + requests: + memory: 1Gi + limits: + memory: 1Gi # settings for the memcached subchart memcached: From 408da89c866ae552931f9dcc03923579a92a61a1 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Mon, 29 Jun 2026 14:38:15 +0200 Subject: [PATCH 02/17] Fix for super linter --- .editorconfig | 5 +++++ .github/kubeconform.sh | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..e38f1d3a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*.sh] +indent_style = space +indent_size = 2 diff --git a/.github/kubeconform.sh b/.github/kubeconform.sh index ed1c401a..564ec5cd 100755 --- a/.github/kubeconform.sh +++ b/.github/kubeconform.sh @@ -13,7 +13,7 @@ curl --silent --show-error --fail --location --output /tmp/kubeconform.tar.gz ht sudo tar -C /usr/local/bin -xf /tmp/kubeconform.tar.gz kubeconform # validate charts -for CHART_DIR in ${CHART_DIRS};do +for CHART_DIR in ${CHART_DIRS}; do echo "helm dependency build…" helm dependency build "${CHART_DIR}" From e14a4aeb054910a984af25356e32daf8cf945c7d Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Tue, 30 Jun 2026 10:24:48 +0200 Subject: [PATCH 03/17] Solve copilot comments --- zammad/templates/_helpers.tpl | 23 +++++++++++++++++++++-- zammad/templates/secrets.yaml | 2 +- zammad/values.yaml | 10 ++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/zammad/templates/_helpers.tpl b/zammad/templates/_helpers.tpl index 9935536d..7a83bb50 100755 --- a/zammad/templates/_helpers.tpl +++ b/zammad/templates/_helpers.tpl @@ -105,12 +105,31 @@ autowizard secret name {{- end -}} {{- end -}} +{{/* +Name of the ECK Elasticsearch resource. Mirrors the eck-elasticsearch chart's +"elasticsearch.fullname" logic so that ECK-derived names (services, secrets) +stay correct even if elasticsearch.nameOverride/fullnameOverride are changed. +*/}} +{{- define "zammad.elasticsearchName" -}} +{{- $es := .Values.elasticsearch | default dict -}} +{{- if $es.fullnameOverride -}} +{{- $es.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default "eck-elasticsearch" $es.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + {{/* elasticsearch service host (in-cluster ECK service or external host) */}} {{- define "zammad.elasticsearchHost" -}} {{- if .Values.zammadConfig.elasticsearch.enabled -}} -{{ .Release.Name }}-elasticsearch-es-http +{{ include "zammad.elasticsearchName" . }}-es-http {{- else -}} {{ .Values.zammadConfig.elasticsearch.host }} {{- end -}} @@ -132,7 +151,7 @@ elasticsearch secret name */}} {{- define "zammad.elasticsearchSecretName" -}} {{- if .Values.zammadConfig.elasticsearch.enabled -}} -{{ .Release.Name }}-elasticsearch-es-elastic-user +{{ include "zammad.elasticsearchName" . }}-es-elastic-user {{- else if .Values.secrets.elasticsearch.useExisting -}} {{ .Values.secrets.elasticsearch.secretName }} {{- else -}} diff --git a/zammad/templates/secrets.yaml b/zammad/templates/secrets.yaml index 16430602..237de7ed 100644 --- a/zammad/templates/secrets.yaml +++ b/zammad/templates/secrets.yaml @@ -12,7 +12,7 @@ type: Opaque data: {{ .Values.secrets.autowizard.secretKey }}: {{ .Values.autoWizard.config | b64enc | quote }} {{ end }} -{{ if and .Values.zammadConfig.elasticsearch.pass (not .Values.secrets.elasticsearch.useExisting) }} +{{ if and (not .Values.zammadConfig.elasticsearch.enabled) .Values.zammadConfig.elasticsearch.pass (not .Values.secrets.elasticsearch.useExisting) }} --- apiVersion: v1 kind: Secret diff --git a/zammad/values.yaml b/zammad/values.yaml index 9d4afdec..28ab2f67 100644 --- a/zammad/values.yaml +++ b/zammad/values.yaml @@ -87,8 +87,10 @@ zammadConfig: elasticsearch: # enable/disable the bundled elasticsearch (ECK) chart dependency enabled: true - # host is only used when zammadConfig.elasticsearch.enabled is false - # (i.e. when connecting to an external Elasticsearch service) + # host, user and pass are only used when zammadConfig.elasticsearch.enabled is + # false (i.e. when connecting to an external Elasticsearch service). For the + # bundled ECK Elasticsearch, the host, the "elastic" user and the operator- + # generated password are resolved automatically and these values are ignored. host: external-elasticsearch-host initialisation: true pass: "" @@ -737,8 +739,8 @@ elasticsearch: count: 1 config: # Single-node default deployment without raising vm.max_map_count. - # For production, raise the kernel setting vm.max_map_count to 1048576 via - # an initContainer and remove this line instead. See: + # For production, raise the kernel setting vm.max_map_count to 1048576 at + # the node level and remove this line instead. See: # https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-virtual-memory.html node.store.allow_mmap: false volumeClaimTemplates: From fe4905da098c3cace732c9a63f0ba9ad1a158096 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Tue, 30 Jun 2026 10:52:47 +0200 Subject: [PATCH 04/17] Switch to ES 9 and add test case to CI --- zammad/ci/full-values.yaml | 6 ++---- zammad/values.yaml | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/zammad/ci/full-values.yaml b/zammad/ci/full-values.yaml index 5df0a1c4..f4bba191 100755 --- a/zammad/ci/full-values.yaml +++ b/zammad/ci/full-values.yaml @@ -23,10 +23,8 @@ secrets: autoWizard: enabled: true -# The bundled Elasticsearch is managed by the ECK operator and uses its own -# auto-generated credentials secret, so no existingSecret config is needed here. -# (The secrets.elasticsearch.* existing-secret path only applies to an external -# Elasticsearch, i.e. when zammadConfig.elasticsearch.enabled is false.) +elasticsearch: + nameOverride: my-local-elasticsearch ingress: enabled: true diff --git a/zammad/values.yaml b/zammad/values.yaml index 28ab2f67..86a82c6f 100644 --- a/zammad/values.yaml +++ b/zammad/values.yaml @@ -724,8 +724,7 @@ initContainers: [] # (user "elastic"). The Zammad templates pick these up automatically. elasticsearch: nameOverride: elasticsearch - # Elasticsearch version. Kept in sync with the version Zammad is tested against. - version: 8.18.0 + version: 9.4.2 http: tls: selfSignedCertificate: From 42d089080761207131c573a987efff41b6ece012 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Tue, 30 Jun 2026 11:10:05 +0200 Subject: [PATCH 05/17] Fix test case --- zammad/ci/full-values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zammad/ci/full-values.yaml b/zammad/ci/full-values.yaml index f4bba191..7914c339 100755 --- a/zammad/ci/full-values.yaml +++ b/zammad/ci/full-values.yaml @@ -24,7 +24,7 @@ autoWizard: enabled: true elasticsearch: - nameOverride: my-local-elasticsearch + nameOverride: my-local-es ingress: enabled: true From 4a013047f7183ddfbfa9222cd2c0b88925b8ad90 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Wed, 12 Aug 2026 11:21:59 +0200 Subject: [PATCH 06/17] Reactivate updatecli --- .github/updatecli.yaml | 47 ++++++++++++++++++++++++------------------ zammad/README.md | 3 +-- zammad/values.yaml | 9 +++++++- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/.github/updatecli.yaml b/.github/updatecli.yaml index cc94b8f1..2c41ab4d 100644 --- a/.github/updatecli.yaml +++ b/.github/updatecli.yaml @@ -18,14 +18,21 @@ sources: versionfilter: pattern: '3.x.x' kind: semver - # elasticsearch: - # name: elasticsearch - # kind: helmchart - # spec: - # url: https://charts.bitnami.com/bitnami - # name: elasticsearch - # versionfilter: - # pattern: "21.x.x" + elasticsearch: + name: elasticsearch + kind: helmchart + spec: + url: https://helm.elastic.co + name: eck-elasticsearch + # Patch level only: this subchart provides the Elasticsearch version (which we do not + # pin, see values.yaml) and it may change the Elasticsearch minor or major version in + # a chart minor release, as the chart itself stays on 0.x. Chart patches only ever + # carry Elasticsearch patch updates. + # To perform a minor update, raise the pattern below and check that the ECK operator + # installed in the cluster (a prerequisite outside of this chart, see README) supports + # the resulting Elasticsearch version. + versionfilter: + pattern: "0.19.x" # minio: # name: minio # kind: helmchart @@ -102,18 +109,18 @@ targets: file: zammad/values.yaml matchpattern: ' tag: "\d+\.\d+\.\d+"' replacepattern: ' tag: "{{ source `alpine` }}"' - # elasticsearch: - # name: elasticsearch - # kind: helmchart - # sourceid: elasticsearch - # # {{ if .github.enabled }} - # scmid: zammad-helm - # # {{ end }} - # spec: - # name: "zammad" - # file: "Chart.yaml" - # key: "$.dependencies[0].version" - # versionincrement: patch + elasticsearch: + name: elasticsearch + kind: helmchart + sourceid: elasticsearch + # {{ if .github.enabled }} + scmid: zammad-helm + # {{ end }} + spec: + name: "zammad" + file: "Chart.yaml" + key: "$.dependencies[0].version" + versionincrement: patch # minio: # name: minio # kind: helmchart diff --git a/zammad/README.md b/zammad/README.md index f6cc9d71..17a2a95e 100644 --- a/zammad/README.md +++ b/zammad/README.md @@ -221,8 +221,7 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. when `zammadConfig.elasticsearch.enabled` is `true` (the default). See [Prerequisites](#prerequisites). - The `elasticsearch.*` values changed completely. The previous bitnami values (`master`, `data`, `ingest`, `coordinating`, `sysctlImage`, `image`, `global.security.allowInsecureImages`, …) no - longer apply. Review the new `elasticsearch.*` block in `values.yaml` (`version`, `nodeSets`, - `http`, …). + longer apply. Review the new `elasticsearch.*` block in `values.yaml` (`nodeSets`, `http`, …). - Authentication is now mandatory: ECK creates a superuser `elastic` and stores its password in the auto-generated secret `{{ .Release.Name }}-elasticsearch-es-elastic-user`. The chart wires this up automatically, so `secrets.elasticsearch.*` is only relevant for an **external** diff --git a/zammad/values.yaml b/zammad/values.yaml index 86a82c6f..2ddbb06a 100644 --- a/zammad/values.yaml +++ b/zammad/values.yaml @@ -44,6 +44,9 @@ secrets: useExisting: false secretKey: autowizard secretName: autowizard + # These values are only used when zammadConfig.elasticsearch.enabled is false + # (i.e. for an external Elasticsearch service). For the bundled ECK Elasticsearch, + # the operator-generated secret is used automatically and these values are ignored. elasticsearch: useExisting: false secretKey: password @@ -722,9 +725,13 @@ initContainers: [] # results in the in-cluster service "-elasticsearch-es-http" and the # auto-generated superuser secret "-elasticsearch-es-elastic-user" # (user "elastic"). The Zammad templates pick these up automatically. +# +# The Elasticsearch version is not pinned here on purpose: it comes from the subchart's +# default, so it is carried along with the subchart version. Set "version" here if you +# need to pin a specific Elasticsearch version (it must be supported by the installed +# ECK operator). elasticsearch: nameOverride: elasticsearch - version: 9.4.2 http: tls: selfSignedCertificate: From 88d38b5b0b8ca3dfa21e41fdb9e71e77559365b9 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Wed, 12 Aug 2026 12:15:08 +0200 Subject: [PATCH 07/17] Update es chart --- .github/updatecli.yaml | 2 +- zammad/Chart.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/updatecli.yaml b/.github/updatecli.yaml index 2c41ab4d..f6a4abb3 100644 --- a/.github/updatecli.yaml +++ b/.github/updatecli.yaml @@ -32,7 +32,7 @@ sources: # installed in the cluster (a prerequisite outside of this chart, see README) supports # the resulting Elasticsearch version. versionfilter: - pattern: "0.19.x" + pattern: "0.20.x" # minio: # name: minio # kind: helmchart diff --git a/zammad/Chart.yaml b/zammad/Chart.yaml index 1b09579b..f32ee75c 100755 --- a/zammad/Chart.yaml +++ b/zammad/Chart.yaml @@ -21,7 +21,7 @@ dependencies: - name: eck-elasticsearch alias: elasticsearch repository: https://helm.elastic.co - version: 0.19.1 + version: 0.20.0 condition: zammadConfig.elasticsearch.enabled - name: minio version: 14.10.5 From 48db6165c1fa5df9a1ebb7d337f778578e1ec769 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Thu, 13 Aug 2026 09:00:14 +0200 Subject: [PATCH 08/17] Formatting and simplification --- zammad/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/zammad/README.md b/zammad/README.md index 17a2a95e..cec31c11 100644 --- a/zammad/README.md +++ b/zammad/README.md @@ -56,8 +56,7 @@ Be aware that the Zammad Helm chart version is different from the actual Zammad ```console helm repo add elastic https://helm.elastic.co helm repo update - helm install elastic-operator elastic/eck-operator \ - --namespace elastic-system --create-namespace + helm install elastic-operator elastic/eck-operator --namespace elastic-system --create-namespace ``` ## Installing the Chart @@ -233,8 +232,8 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. to use the operator-managed self-signed certificate. - The StatefulSet and its `PersistentVolumeClaim`s are recreated under new names. As the search index can be rebuilt from PostgreSQL, the recommended path is to delete the old bitnami - Elasticsearch PVCs and let the index be recreated. To force a full rebuild after the upgrade, - set `zammadConfig.elasticsearch.reindex: true` for the upgrade run. + Elasticsearch PVCs and let the index be recreated. This usually happens automatically when the + zammad-init job runs and finds no index to be available. ### From Chart Version 16.x to 17.0.0 From ce229dd426155797416bb474a7568cede8b86610 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Thu, 13 Aug 2026 09:29:02 +0200 Subject: [PATCH 09/17] Include also change for securityContext.fsGroupChangePolicy --- zammad/README.md | 11 +++++++++++ zammad/values.yaml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/zammad/README.md b/zammad/README.md index cec31c11..69ee5f39 100644 --- a/zammad/README.md +++ b/zammad/README.md @@ -212,6 +212,8 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. ### From Chart Version 17.x to 18.0.0 +#### Elasticsearch subchart migrated to the ECK operator + - The bundled Elasticsearch was migrated from the `bitnami/elasticsearch` subchart to the official [`eck-elasticsearch`](https://artifacthub.io/packages/helm/elastic/eck-elasticsearch) chart, which is managed by the [ECK operator](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-overview.html). @@ -235,6 +237,15 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. Elasticsearch PVCs and let the index be recreated. This usually happens automatically when the zammad-init job runs and finds no index to be available. +#### Faster volume permission fixing + +- `securityContext.fsGroupChangePolicy` now defaults to `OnRootMismatch` instead of `Always`. This + avoids a lengthy startup delay on volumes with a large number of files, since Kubernetes only + recurses into a volume to fix ownership if the volume's root directory doesn't already have the + expected group ownership. This is safe for existing volumes (their ownership was already set by + a previous `Always` run) and only changes behaviour on future pod restarts. If you rely on the + old, more thorough behaviour, set `securityContext.fsGroupChangePolicy: Always` explicitly. + ### From Chart Version 16.x to 17.0.0 - The bitnami PostgreSQL subchart was replaced with [cloudpirates-postgres/postgres](https://artifacthub.io/packages/helm/cloudpirates-postgres/postgres). diff --git a/zammad/values.yaml b/zammad/values.yaml index 2ddbb06a..006026c0 100644 --- a/zammad/values.yaml +++ b/zammad/values.yaml @@ -71,7 +71,7 @@ secrets: securityContext: fsGroup: 1000 # https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods - fsGroupChangePolicy: Always + fsGroupChangePolicy: OnRootMismatch runAsUser: 1000 runAsNonRoot: true runAsGroup: 1000 From 9d0b4087e41ce97acd0435bd6b0c5729af552840 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Thu, 13 Aug 2026 13:59:17 +0200 Subject: [PATCH 10/17] QA - improve docs --- zammad/README.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/zammad/README.md b/zammad/README.md index 69ee5f39..1a54b55c 100644 --- a/zammad/README.md +++ b/zammad/README.md @@ -44,12 +44,15 @@ Be aware that the Zammad Helm chart version is different from the actual Zammad - Kubernetes 1.21+ - Helm 3.8.0+ - Cluster with at least 4GB of free RAM -- The [ECK operator](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-install-helm.html) - must be installed in the cluster when using the bundled Elasticsearch - (`zammadConfig.elasticsearch.enabled: true`, the default). The bundled - Elasticsearch is deployed via the official `eck-elasticsearch` chart, which - creates an `Elasticsearch` custom resource that is reconciled by the operator. - This is **not** required when connecting to an external Elasticsearch service. +- The [ECK operator](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-install-helm.html), + **version 3.5.0 or newer**, must be installed in the cluster when using the bundled + Elasticsearch (`zammadConfig.elasticsearch.enabled: true`, the default). The bundled + Elasticsearch is deployed via the official `eck-elasticsearch` chart (pinned to `0.20.0` + in `Chart.yaml`), which creates an `Elasticsearch` custom resource that is reconciled by + the operator. If your cluster already runs an + older ECK operator for other workloads, upgrade it first — an older operator's webhook may + reject the `Elasticsearch` resource this chart creates. This is **not** required when + connecting to an external Elasticsearch service. Install the operator once per cluster: @@ -218,8 +221,9 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. official [`eck-elasticsearch`](https://artifacthub.io/packages/helm/elastic/eck-elasticsearch) chart, which is managed by the [ECK operator](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-overview.html). This is the deployment method officially supported by Elastic. -- **The ECK operator (and its CRDs) must now be installed in the cluster** before upgrading - when `zammadConfig.elasticsearch.enabled` is `true` (the default). See [Prerequisites](#prerequisites). +- **The ECK operator (and its CRDs), version 3.5.0+, must now be installed in the cluster** + before upgrading when `zammadConfig.elasticsearch.enabled` is `true` (the default). See + [Prerequisites](#prerequisites). - The `elasticsearch.*` values changed completely. The previous bitnami values (`master`, `data`, `ingest`, `coordinating`, `sysctlImage`, `image`, `global.security.allowInsecureImages`, …) no longer apply. Review the new `elasticsearch.*` block in `values.yaml` (`nodeSets`, `http`, …). @@ -230,8 +234,12 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. - The in-cluster service name changed from `{{ .Release.Name }}-elasticsearch` to `{{ .Release.Name }}-elasticsearch-es-http`. - TLS on the HTTP layer is disabled by default (`elasticsearch.http.tls.selfSignedCertificate.disabled: true`) - to keep the plain-`http` connection behaviour. Set it to `false` and `zammadConfig.elasticsearch.schema: https` - to use the operator-managed self-signed certificate. + to keep the plain-`http` connection behaviour. Enabling the operator-managed self-signed + certificate (`selfSignedCertificate.disabled: false` with `zammadConfig.elasticsearch.schema: https`) + is **not supported yet**: Zammad verifies TLS certificates by default and the chart does not mount + the ECK-generated CA certificate into the Zammad pods, so the zammad-init job will fail on + certificate verification. Leave TLS disabled unless you terminate it yourself and configure + Zammad's Elasticsearch client accordingly. - The StatefulSet and its `PersistentVolumeClaim`s are recreated under new names. As the search index can be rebuilt from PostgreSQL, the recommended path is to delete the old bitnami Elasticsearch PVCs and let the index be recreated. This usually happens automatically when the From 04d35c782e3beaefe20051e80f0abec1df084151 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Thu, 13 Aug 2026 14:12:28 +0200 Subject: [PATCH 11/17] QA - name handling --- zammad/README.md | 10 ++++++---- zammad/templates/_helpers.tpl | 17 ++++++++++++----- zammad/values.yaml | 16 +++++++++------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/zammad/README.md b/zammad/README.md index 1a54b55c..1900b6e6 100644 --- a/zammad/README.md +++ b/zammad/README.md @@ -228,11 +228,13 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. `ingest`, `coordinating`, `sysctlImage`, `image`, `global.security.allowInsecureImages`, …) no longer apply. Review the new `elasticsearch.*` block in `values.yaml` (`nodeSets`, `http`, …). - Authentication is now mandatory: ECK creates a superuser `elastic` and stores its password in - the auto-generated secret `{{ .Release.Name }}-elasticsearch-es-elastic-user`. The chart wires - this up automatically, so `secrets.elasticsearch.*` is only relevant for an **external** - Elasticsearch now. + the auto-generated secret `{{ .Release.Name }}-es-es-elastic-user`. The chart wires this up + automatically, so `secrets.elasticsearch.*` is only relevant for an **external** Elasticsearch + now. - The in-cluster service name changed from `{{ .Release.Name }}-elasticsearch` to - `{{ .Release.Name }}-elasticsearch-es-http`. + `{{ .Release.Name }}-es-es-http`. Note that ECK caps the Elasticsearch resource name itself at + 36 characters; the chart fails fast with a clear error at template/install time if your release + name is too long to fit. - TLS on the HTTP layer is disabled by default (`elasticsearch.http.tls.selfSignedCertificate.disabled: true`) to keep the plain-`http` connection behaviour. Enabling the operator-managed self-signed certificate (`selfSignedCertificate.disabled: false` with `zammadConfig.elasticsearch.schema: https`) diff --git a/zammad/templates/_helpers.tpl b/zammad/templates/_helpers.tpl index 7a83bb50..817fd615 100755 --- a/zammad/templates/_helpers.tpl +++ b/zammad/templates/_helpers.tpl @@ -109,19 +109,26 @@ autowizard secret name Name of the ECK Elasticsearch resource. Mirrors the eck-elasticsearch chart's "elasticsearch.fullname" logic so that ECK-derived names (services, secrets) stay correct even if elasticsearch.nameOverride/fullnameOverride are changed. +Also fails fast if the result exceeds ECK's 36-character resource name limit, +since the admission webhook's own rejection of an over-long name is opaque. */}} {{- define "zammad.elasticsearchName" -}} {{- $es := .Values.elasticsearch | default dict -}} +{{- $name := "" -}} {{- if $es.fullnameOverride -}} -{{- $es.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- $name = $es.fullnameOverride | trunc 63 | trimSuffix "-" -}} {{- else -}} -{{- $name := default "eck-elasticsearch" $es.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- $base := default "elasticsearch" $es.nameOverride -}} +{{- if contains $base .Release.Name -}} +{{- $name = .Release.Name | trunc 63 | trimSuffix "-" -}} {{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- $name = printf "%s-%s" .Release.Name $base | trunc 63 | trimSuffix "-" -}} +{{- end -}} {{- end -}} +{{- if gt (len $name) 36 -}} +{{- fail (printf "Elasticsearch resource name %q is %d characters, but the ECK operator caps names at 36. Shorten the release name or set a shorter elasticsearch.nameOverride/fullnameOverride." $name (len $name)) -}} {{- end -}} +{{- $name -}} {{- end -}} {{/* diff --git a/zammad/values.yaml b/zammad/values.yaml index 006026c0..696f8e07 100644 --- a/zammad/values.yaml +++ b/zammad/values.yaml @@ -721,24 +721,26 @@ initContainers: [] # This deploys an Elasticsearch custom resource that is managed by the ECK operator. # The ECK operator (and its CRDs) MUST already be installed in the cluster, see README. # -# ECK names the resource "-elasticsearch" (nameOverride below), which -# results in the in-cluster service "-elasticsearch-es-http" and the -# auto-generated superuser secret "-elasticsearch-es-elastic-user" -# (user "elastic"). The Zammad templates pick these up automatically. +# ECK names the resource "-es" (nameOverride below), which results in the +# in-cluster service "-es-es-http" and the auto-generated superuser secret +# "-es-es-elastic-user" (user "elastic"). The Zammad templates pick these up +# automatically. Kept short on purpose: ECK caps the Elasticsearch resource name itself at +# 36 characters, so a short nameOverride leaves more of that budget for the release name. # # The Elasticsearch version is not pinned here on purpose: it comes from the subchart's # default, so it is carried along with the subchart version. Set "version" here if you # need to pin a specific Elasticsearch version (it must be supported by the installed # ECK operator). elasticsearch: - nameOverride: elasticsearch + nameOverride: es http: tls: selfSignedCertificate: # Disable TLS on the HTTP layer (Variant A): Zammad talks plain http to ES. # Native authentication (user "elastic" + generated password) stays enabled. - # Set to false and adjust zammadConfig.elasticsearch.schema to "https" to - # use the operator-managed self-signed certificate instead. + # Enabling this (selfSignedCertificate.disabled: false) is not supported yet: Zammad + # verifies TLS certificates by default and the chart does not mount the ECK-generated + # CA into the Zammad pods, so the zammad-init job will fail on certificate verification. disabled: true nodeSets: - name: default From 309aedec25a13063e439ab1c7893ed7fd4c96abb Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Thu, 13 Aug 2026 14:24:04 +0200 Subject: [PATCH 12/17] QA - preset --- zammad/values.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/zammad/values.yaml b/zammad/values.yaml index 696f8e07..fe976c6b 100644 --- a/zammad/values.yaml +++ b/zammad/values.yaml @@ -770,6 +770,7 @@ elasticsearch: value: "-Xms512m -Xmx512m" resources: requests: + cpu: 500m memory: 1Gi limits: memory: 1Gi From 8467208ca0a15360c4db97fd84ef72b5158e0337 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Thu, 13 Aug 2026 14:46:46 +0200 Subject: [PATCH 13/17] QA - release name docs --- zammad/README.md | 15 +++++++++------ zammad/values.yaml | 13 ++++++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/zammad/README.md b/zammad/README.md index 1900b6e6..8b20cdfb 100644 --- a/zammad/README.md +++ b/zammad/README.md @@ -228,12 +228,15 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. `ingest`, `coordinating`, `sysctlImage`, `image`, `global.security.allowInsecureImages`, …) no longer apply. Review the new `elasticsearch.*` block in `values.yaml` (`nodeSets`, `http`, …). - Authentication is now mandatory: ECK creates a superuser `elastic` and stores its password in - the auto-generated secret `{{ .Release.Name }}-es-es-elastic-user`. The chart wires this up - automatically, so `secrets.elasticsearch.*` is only relevant for an **external** Elasticsearch - now. -- The in-cluster service name changed from `{{ .Release.Name }}-elasticsearch` to - `{{ .Release.Name }}-es-es-http`. Note that ECK caps the Elasticsearch resource name itself at - 36 characters; the chart fails fast with a clear error at template/install time if your release + an auto-generated secret, typically named `{{ .Release.Name }}-es-es-elastic-user` (depending + on your release name, see below). The chart wires this up automatically, so + `secrets.elasticsearch.*` is only relevant for an **external** Elasticsearch now. +- The in-cluster service name changed from `{{ .Release.Name }}-elasticsearch` to typically + `{{ .Release.Name }}-es-es-http`. The exact name depends on your release name: if it already + contains `es` as a substring (e.g. `zammad-helpdesk`), it's used as-is instead of getting `-es` + appended, giving `{{ .Release.Name }}-es-http` instead. Either way, the chart's own templates + resolve this automatically. Note that ECK caps the Elasticsearch resource name itself at 36 + characters; the chart fails fast with a clear error at template/install time if your release name is too long to fit. - TLS on the HTTP layer is disabled by default (`elasticsearch.http.tls.selfSignedCertificate.disabled: true`) to keep the plain-`http` connection behaviour. Enabling the operator-managed self-signed diff --git a/zammad/values.yaml b/zammad/values.yaml index fe976c6b..7f41d7d9 100644 --- a/zammad/values.yaml +++ b/zammad/values.yaml @@ -721,11 +721,14 @@ initContainers: [] # This deploys an Elasticsearch custom resource that is managed by the ECK operator. # The ECK operator (and its CRDs) MUST already be installed in the cluster, see README. # -# ECK names the resource "-es" (nameOverride below), which results in the -# in-cluster service "-es-es-http" and the auto-generated superuser secret -# "-es-es-elastic-user" (user "elastic"). The Zammad templates pick these up -# automatically. Kept short on purpose: ECK caps the Elasticsearch resource name itself at -# 36 characters, so a short nameOverride leaves more of that budget for the release name. +# ECK names the resource "-es" (nameOverride below), which usually results in +# the in-cluster service "-es-es-http" and the auto-generated superuser secret +# "-es-es-elastic-user" (user "elastic") - the exact names depend on your release +# name, since a release name that already contains "es" as a substring (e.g. "zammad-helpdesk") +# is used as-is instead of getting "-es" appended. The Zammad templates pick these up +# automatically regardless. Kept short on purpose: ECK caps the Elasticsearch resource name +# itself at 36 characters, so a short nameOverride leaves more of that budget for the release +# name. # # The Elasticsearch version is not pinned here on purpose: it comes from the subchart's # default, so it is carried along with the subchart version. Set "version" here if you From b2ab718f768d88520e8db84726337c706cf9567b Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Thu, 13 Aug 2026 14:48:25 +0200 Subject: [PATCH 14/17] QA - deletePolicy doc --- zammad/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zammad/README.md b/zammad/README.md index 8b20cdfb..66644fa3 100644 --- a/zammad/README.md +++ b/zammad/README.md @@ -249,6 +249,12 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. index can be rebuilt from PostgreSQL, the recommended path is to delete the old bitnami Elasticsearch PVCs and let the index be recreated. This usually happens automatically when the zammad-init job runs and finds no index to be available. +- **Behaviour change:** the ECK operator's default `volumeClaimDeletePolicy` is + `DeleteOnScaledownAndClusterDeletion`, so uninstalling the release (or setting + `zammadConfig.elasticsearch.enabled: false`) now also deletes the Elasticsearch data volume. + The old bitnami PVCs were left behind on uninstall. This is harmless in practice since the + index rebuilds from PostgreSQL, but if you rely on the volume surviving an uninstall, set + `elasticsearch.volumeClaimDeletePolicy: DeleteOnScaledownOnly` explicitly. #### Faster volume permission fixing From 58be9bd88e5655968041483f241f72be1e409088 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Fri, 14 Aug 2026 10:15:40 +0200 Subject: [PATCH 15/17] QA - Added plaintext pw comment --- zammad/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zammad/README.md b/zammad/README.md index 66644fa3..754c673f 100644 --- a/zammad/README.md +++ b/zammad/README.md @@ -230,7 +230,10 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. - Authentication is now mandatory: ECK creates a superuser `elastic` and stores its password in an auto-generated secret, typically named `{{ .Release.Name }}-es-es-elastic-user` (depending on your release name, see below). The chart wires this up automatically, so - `secrets.elasticsearch.*` is only relevant for an **external** Elasticsearch now. + `secrets.elasticsearch.*` is only relevant for an **external** Elasticsearch now. Since TLS on + the HTTP layer is disabled by default (see below), this password still crosses the cluster + network in plaintext on every request, same as the previous unauthenticated setup did for all + traffic — authentication alone does not protect it in transit. - The in-cluster service name changed from `{{ .Release.Name }}-elasticsearch` to typically `{{ .Release.Name }}-es-es-http`. The exact name depends on your release name: if it already contains `es` as a substring (e.g. `zammad-helpdesk`), it's used as-is instead of getting `-es` From de35e668cb20ef08397acd995b77bbab7a62fdc2 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Fri, 14 Aug 2026 11:27:47 +0200 Subject: [PATCH 16/17] Drop obsolete initJob.randomName setting --- zammad/README.md | 19 ++++++++++++++----- zammad/templates/job-init.yaml | 6 ++---- zammad/values.yaml | 6 ------ 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/zammad/README.md b/zammad/README.md index 754c673f..a9cf6cf0 100644 --- a/zammad/README.md +++ b/zammad/README.md @@ -179,19 +179,15 @@ redis: ### Deploying with ArgoCD Due to the way Argo CD syncs Helm charts into the cluster and this chart deploying the initialization job, the default configuration can lead to Sync loops where Argo CD will create an infinite amount of initialization jobs. -To prevent this, disable the random name for the initialization job and add the according annotation to the job to let Argo CD treat it as a Sync Hook. +To prevent this, add the according annotation to the job to let Argo CD treat it as a Sync Hook. ```yaml zammadConfig: initJob: - randomName: false annotations: argocd.argoproj.io/hook: Sync ``` -Note that `randomName` is a bit of a misnomer: the job name suffix is the Helm release revision, not a random value. -Tools that render the chart statically instead of performing a real Helm install/upgrade (Argo CD, `helm template`, `helm diff`) always see revision `1`, so with `randomName: true` the job name never changes across syncs and the job will not re-run on its own - hence the `false` + Sync Hook workaround above. Flux's helm-controller uses the Helm SDK directly and is unaffected by this. - ## Maintenance Tasks If you have to run any maintenance commands inside of the Zammad stack, you can do that by @@ -268,6 +264,19 @@ and `zammadConfig.cronJob.reindex.schedule` if you want to run it periodically. a previous `Always` run) and only changes behaviour on future pod restarts. If you rely on the old, more thorough behaviour, set `securityContext.fsGroupChangePolicy: Always` explicitly. +#### `initJob.randomName` removed + +- The `zammadConfig.initJob.randomName` setting was removed. The init Job name is now always + suffixed with the Helm release revision, which is what `randomName: true` (the default) already + did in practice. +- If you had set `randomName: false`, e.g. for the Argo CD workaround described in + [Deploying with ArgoCD](#deploying-with-argocd), remove that line - it is now a no-op. Argo CD + and other tools that render the chart statically (also `helm template`, `helm diff`) always see + revision `1`, so the job name is stable there regardless. It's the Sync Hook annotation that + actually prevents the sync loop, and that is unaffected by this change. +- On upgrade, installs that previously had `randomName: false` will see their init Job renamed once + (`-init` to `-init-`); this is a harmless one-time replacement. + ### From Chart Version 16.x to 17.0.0 - The bitnami PostgreSQL subchart was replaced with [cloudpirates-postgres/postgres](https://artifacthub.io/packages/helm/cloudpirates-postgres/postgres). diff --git a/zammad/templates/job-init.yaml b/zammad/templates/job-init.yaml index 71e49114..9e6f7700 100755 --- a/zammad/templates/job-init.yaml +++ b/zammad/templates/job-init.yaml @@ -2,12 +2,10 @@ apiVersion: batch/v1 kind: Job metadata: - {{- if .Values.zammadConfig.initJob.randomName }} # Use a different job name on each run to ensure a new job always runs once. + # This will generate static names in environments like ArgoCD which + # always use Revision 1. name: {{ include "zammad.fullname" . }}-init-{{ .Release.Revision }} - {{- else }} - name: {{ include "zammad.fullname" . }}-init - {{- end}} namespace: {{ .Release.Namespace }} # Helm post-install/post-upgrade hooks cannot be used here, because # helm's --wait flag causes a deadlock: the job waits for all resources to be ready, diff --git a/zammad/values.yaml b/zammad/values.yaml index 7f41d7d9..f69d9e34 100644 --- a/zammad/values.yaml +++ b/zammad/values.yaml @@ -542,12 +542,6 @@ zammadConfig: # initialised, e.g. while restoring a dump (see the PostgreSQL migration notes in the README). # Zammad will not work correctly if this is left disabled permanently. enabled: true - # Appends the release revision to the job name so a new job always runs on - # install/upgrade. Note that `helm template` and GitOps tools that render - # manifests statically (e.g. Argo CD) always see revision 1, so the name - # stays constant there and the job will not re-run on subsequent syncs - - # see the Argo CD section in the README. - randomName: true # specify how long after finish pods are deleted ttlSecondsAfterFinished: 300 annotations: {} From 1abb11135d2c739fbf7996a7c36494aef69e5c83 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Tue, 18 Aug 2026 07:29:14 +0200 Subject: [PATCH 17/17] Add comment and visual separation of secrets --- zammad/templates/secrets.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zammad/templates/secrets.yaml b/zammad/templates/secrets.yaml index 237de7ed..20519980 100644 --- a/zammad/templates/secrets.yaml +++ b/zammad/templates/secrets.yaml @@ -12,6 +12,8 @@ type: Opaque data: {{ .Values.secrets.autowizard.secretKey }}: {{ .Values.autoWizard.config | b64enc | quote }} {{ end }} + +{{/* Only needed for an external Elasticsearch; the bundled ECK operator manages its own secret. */}} {{ if and (not .Values.zammadConfig.elasticsearch.enabled) .Values.zammadConfig.elasticsearch.pass (not .Values.secrets.elasticsearch.useExisting) }} --- apiVersion: v1 @@ -26,6 +28,7 @@ type: Opaque data: {{ .Values.secrets.elasticsearch.secretKey }}: {{ .Values.zammadConfig.elasticsearch.pass | b64enc | quote }} {{ end }} + {{ if not .Values.secrets.postgresql.useExisting }} --- apiVersion: v1 @@ -40,6 +43,7 @@ type: Opaque data: {{ .Values.secrets.postgresql.secretKey }}: {{ .Values.zammadConfig.postgresql.pass | b64enc | quote }} {{ end }} + {{ if and .Values.zammadConfig.redis.pass (not .Values.secrets.redis.useExisting) }} --- apiVersion: v1