fix(chart): make pre-upgrade-4-0-0 also delete the v3 Deployment and DaemonSets

This commit is contained in:
Thibault VINCENT
2026-05-05 18:19:05 +02:00
parent ffcff3bd58
commit 6ec3aeeac9
2 changed files with 61 additions and 16 deletions
@@ -14,7 +14,13 @@
{{- $chartLabel := index $existingResource.metadata.labels "helm.sh/chart" }}
{{- if $chartLabel }}
{{- $prevVersion := trimPrefix "x509-certificate-exporter-" $chartLabel }}
{{- if semverCompare "<3.20.0" $prevVersion }}
{{/* Only fire when both the source release is pre-3.20.0 AND the
target release is still in the v3 line. Upgrades that cross
into v4+ are handled by pre-upgrade-4-0-0, which subsumes
the same Deployment/DaemonSet recreation alongside its own
Service migration. Without this gate the two hooks would run
in parallel and double-delete the same resources. */}}
{{- if and (semverCompare "<3.20.0" $prevVersion) (semverCompare "<4.0.0" .Chart.Version) }}
{{- $needsMigration = true }}
{{- end }}
{{- end }}
@@ -1,25 +1,55 @@
{{/*
Pre-upgrade hook for v3 → v4 chart upgrades.
Why: v4 flipped `service.headless` to `true` by default, which sets
`clusterIP: None` on the Service. Kubernetes refuses to mutate a
Service's clusterIP in place — `spec.clusterIPs[0]: may not change once
set` — so an `helm upgrade` from a v3 release with an assigned ClusterIP
to a v4 default install fails before any other reconciliation.
Two unrelated immutable-field changes force a delete-and-recreate cycle:
Strategy: if the existing Service carries a `helm.sh/chart` label
matching a chart version <4.0.0, AND the new release will render a
Service (`.Values.service.create` is true), delete it pre-upgrade so
helm can recreate it from the new template — headless or otherwise,
whichever the new values dictate.
1. Service.spec.clusterIP — v4 flipped `service.headless` to `true` by
default, which sets `clusterIP: None`. Kubernetes refuses to mutate
a Service's clusterIP in place (`spec.clusterIPs[0]: may not
change once set`).
2. Deployment.spec.selector and DaemonSet.spec.selector — v4 reworked
the per-component label set, which propagates to the immutable
selector. `helm upgrade` reports `field is immutable` on these.
Strategy: if any v4-managed resource (Service, secrets-exporter
Deployment, or any host-paths-exporter DaemonSet) still carries a
`helm.sh/chart` label from a chart version <4.0.0, delete all of them
pre-upgrade so helm can recreate them with the new schema. The pods
they back are stateless: a few seconds of unavailability during the
upgrade is the only side-effect.
This hook also subsumes the responsibility of pre-upgrade-3-20-0 when
the upgrade target is v4+ — see the version-gate inside that hook.
*/}}
{{- $needsMigration := false }}
{{- if and .Release.IsUpgrade .Values.service.create }}
{{- if .Release.IsUpgrade }}
{{- $ns := include "x509-certificate-exporter.namespace" . }}
{{- $svcName := include "x509-certificate-exporter.fullname" . }}
{{- $existingSvc := lookup "v1" "Service" $ns $svcName }}
{{- if $existingSvc }}
{{- $chartLabel := index $existingSvc.metadata.labels "helm.sh/chart" }}
{{/* Find any chart-managed resource still on chart <4.0.0. Probe the
Service first (always present unless the user disabled it), then
fall back to the Deployment, then any DaemonSet — covers users
who run with service.create=false. */}}
{{- $existingResource := dict }}
{{- if .Values.service.create }}
{{- $svc := lookup "v1" "Service" $ns (include "x509-certificate-exporter.fullname" .) }}
{{- if $svc }}{{ $existingResource = $svc }}{{ end }}
{{- end }}
{{- if not $existingResource }}
{{- $dep := lookup "apps/v1" "Deployment" $ns (include "x509-certificate-exporter.secretsExporterName" .) }}
{{- if $dep }}{{ $existingResource = $dep }}{{ end }}
{{- end }}
{{- if not $existingResource }}
{{- range $name, $_ := .Values.hostPathsExporter.daemonSets }}
{{- if not $existingResource }}
{{- $dsName := printf "%s-%s" (include "x509-certificate-exporter.fullname" $) $name }}
{{- $ds := lookup "apps/v1" "DaemonSet" $ns $dsName }}
{{- if $ds }}{{ $existingResource = $ds }}{{ end }}
{{- end }}
{{- end }}
{{- end }}
{{- if $existingResource }}
{{- $chartLabel := index $existingResource.metadata.labels "helm.sh/chart" }}
{{- if $chartLabel }}
{{- $prevVersion := trimPrefix "x509-certificate-exporter-" $chartLabel }}
{{- if semverCompare "<4.0.0" $prevVersion }}
@@ -57,6 +87,9 @@ rules:
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "delete"]
- apiGroups: ["apps"]
resources: ["deployments", "daemonsets"]
verbs: ["get", "list", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
@@ -125,5 +158,11 @@ spec:
- --ignore-not-found=true
- -n
- {{ include "x509-certificate-exporter.namespace" . }}
{{- if .Values.service.create }}
- service/{{ include "x509-certificate-exporter.fullname" . }}
{{- end }}
- deployment/{{ include "x509-certificate-exporter.secretsExporterName" . }}
{{- range $name, $_ := .Values.hostPathsExporter.daemonSets }}
- daemonset/{{ printf "%s-%s" (include "x509-certificate-exporter.fullname" $) $name }}
{{- end }}
{{- end }}