diff --git a/.changeset/roll-hyperdx-on-config-changes.md b/.changeset/roll-hyperdx-on-config-changes.md new file mode 100644 index 0000000..22b1762 --- /dev/null +++ b/.changeset/roll-hyperdx-on-config-changes.md @@ -0,0 +1,5 @@ +--- +"helm-charts": minor +--- + +Automatically roll HyperDX pods when the chart-managed ConfigMap or Secret content changes. The first upgrade containing this change adds checksum annotations and triggers a one-time HyperDX rollout. diff --git a/charts/clickstack/templates/_helpers.tpl b/charts/clickstack/templates/_helpers.tpl index 687cd43..098e135 100644 --- a/charts/clickstack/templates/_helpers.tpl +++ b/charts/clickstack/templates/_helpers.tpl @@ -227,4 +227,42 @@ ClickHouse headless service name. The operator creates a headless service named */}} {{- define "clickstack.clickhouse.svc" -}} {{- printf "%s-clickhouse-headless" (include "clickstack.clickhouse.fullname" .) -}} -{{- end }} \ No newline at end of file +{{- end }} + +{{/* +Render the chart-managed HyperDX ConfigMap from one canonical template so the +manifest and the Deployment rollout checksum cannot drift. +*/}} +{{- define "clickstack.hyperdx.configmap" -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: clickstack-config + labels: + {{- include "clickstack.labels" . | nindent 4 }} +data: + {{- range $k, $v := .Values.hyperdx.config }} + {{ $k }}: {{ tpl (toString $v) $ | quote }} + {{- end }} + {{- if and .Values.global.otelCollector.customConfig (not (hasKey .Values.hyperdx.config "CUSTOM_OTELCOL_CONFIG_FILE")) }} + CUSTOM_OTELCOL_CONFIG_FILE: "/etc/otelcol-contrib/custom/custom.config.yaml" + {{- end }} +{{- end }} + +{{/* +Render the chart-managed HyperDX Secret from one canonical template so the +manifest and the Deployment rollout checksum cannot drift. +*/}} +{{- define "clickstack.hyperdx.secret" -}} +apiVersion: v1 +kind: Secret +metadata: + name: clickstack-secret + labels: + {{- include "clickstack.labels" . | nindent 4 }} +type: Opaque +stringData: + {{- range $k, $v := .Values.hyperdx.secrets }} + {{ $k }}: {{ tpl (toString $v) $ | quote }} + {{- end }} +{{- end }} diff --git a/charts/clickstack/templates/hyperdx/configmap.yaml b/charts/clickstack/templates/hyperdx/configmap.yaml index 4b37b3a..b28761a 100644 --- a/charts/clickstack/templates/hyperdx/configmap.yaml +++ b/charts/clickstack/templates/hyperdx/configmap.yaml @@ -1,13 +1 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: clickstack-config - labels: - {{- include "clickstack.labels" . | nindent 4 }} -data: - {{- range $k, $v := .Values.hyperdx.config }} - {{ $k }}: {{ tpl (toString $v) $ | quote }} - {{- end }} - {{- if and .Values.global.otelCollector.customConfig (not (hasKey .Values.hyperdx.config "CUSTOM_OTELCOL_CONFIG_FILE")) }} - CUSTOM_OTELCOL_CONFIG_FILE: "/etc/otelcol-contrib/custom/custom.config.yaml" - {{- end }} +{{- include "clickstack.hyperdx.configmap" . }} diff --git a/charts/clickstack/templates/hyperdx/deployment.yaml b/charts/clickstack/templates/hyperdx/deployment.yaml index 1ff3834..39b44c0 100644 --- a/charts/clickstack/templates/hyperdx/deployment.yaml +++ b/charts/clickstack/templates/hyperdx/deployment.yaml @@ -1,4 +1,8 @@ {{- $podAnnotations := mergeOverwrite (dict) (default (dict) .Values.hyperdx.deployment.annotations) (default (dict) .Values.hyperdx.deployment.podAnnotations) -}} +{{- $_ := set $podAnnotations "checksum/clickstack-config" (include "clickstack.hyperdx.configmap" . | sha256sum) -}} +{{- if ne .Values.hyperdx.secrets nil -}} +{{- $_ := set $podAnnotations "checksum/clickstack-secret" (include "clickstack.hyperdx.secret" . | sha256sum) -}} +{{- end -}} {{- $dashboards := default (dict) .Values.hyperdx.dashboards -}} {{- include "clickstack.hyperdx.validateDashboards" . -}} apiVersion: apps/v1 diff --git a/charts/clickstack/templates/hyperdx/secret.yaml b/charts/clickstack/templates/hyperdx/secret.yaml index 212f7a5..bfb87cc 100644 --- a/charts/clickstack/templates/hyperdx/secret.yaml +++ b/charts/clickstack/templates/hyperdx/secret.yaml @@ -2,15 +2,5 @@ {{- fail "hyperdx.secrets cannot be null when mongodb, clickhouse, or otel-collector is enabled -- these subcharts require the clickstack-secret for credentials" }} {{- end }} {{- if ne .Values.hyperdx.secrets nil }} -apiVersion: v1 -kind: Secret -metadata: - name: clickstack-secret - labels: - {{- include "clickstack.labels" . | nindent 4 }} -type: Opaque -stringData: - {{- range $k, $v := .Values.hyperdx.secrets }} - {{ $k }}: {{ tpl (toString $v) $ | quote }} - {{- end }} +{{- include "clickstack.hyperdx.secret" . }} {{- end }} diff --git a/charts/clickstack/tests/hyperdx-rollout-checksums_test.yaml b/charts/clickstack/tests/hyperdx-rollout-checksums_test.yaml new file mode 100644 index 0000000..c1356b8 --- /dev/null +++ b/charts/clickstack/tests/hyperdx-rollout-checksums_test.yaml @@ -0,0 +1,129 @@ +suite: Test HyperDX Rollout Checksums +templates: + - hyperdx/deployment.yaml + - hyperdx/configmap.yaml + - hyperdx/secret.yaml +# Pin chart metadata so release version bumps do not invalidate content-change assertions. +chart: + version: 3.3.0 + appVersion: 2.36.0 +tests: + - it: should render deterministic checksums for chart-managed configuration + template: hyperdx/deployment.yaml + asserts: + - equal: + path: spec.template.metadata.annotations["checksum/clickstack-config"] + value: 0587572216112d61fd7d298001d71ed852194038be79415116a447e260753058 + - equal: + path: spec.template.metadata.annotations["checksum/clickstack-secret"] + value: 7d047b36293876f9242bce55bbd3ce9a255e51a425934a7cea4f2ab04f86895d + + - it: should change only the ConfigMap checksum when config content changes + template: hyperdx/deployment.yaml + set: + hyperdx: + config: + FRONTEND_URL: https://changed.example + asserts: + - equal: + path: spec.template.metadata.annotations["checksum/clickstack-config"] + value: 8149416c5dff3a76c98e11c8f1fef4ab3a6eca7cd68e79d6a1bd956c196499a3 + - equal: + path: spec.template.metadata.annotations["checksum/clickstack-secret"] + value: 7d047b36293876f9242bce55bbd3ce9a255e51a425934a7cea4f2ab04f86895d + + - it: should change only the Secret checksum when independent secret content changes + template: hyperdx/deployment.yaml + set: + hyperdx: + secrets: + HYPERDX_API_KEY: changed + asserts: + - equal: + path: spec.template.metadata.annotations["checksum/clickstack-config"] + value: 0587572216112d61fd7d298001d71ed852194038be79415116a447e260753058 + - equal: + path: spec.template.metadata.annotations["checksum/clickstack-secret"] + value: a94315e89cfbbe97c9d0f11a48fa845d1f324473ee64666032c9151ffb3a4349 + + - it: should omit the Secret checksum when using external secrets + template: hyperdx/deployment.yaml + set: + mongodb: + enabled: false + clickhouse: + enabled: false + otel-collector: + enabled: false + hyperdx: + secrets: null + config: + MONGO_URI: mongodb://external + deployment: + env: + - name: HYPERDX_API_KEY + valueFrom: + secretKeyRef: + name: external-hyperdx + key: api-key + asserts: + - matchRegex: + path: spec.template.metadata.annotations["checksum/clickstack-config"] + pattern: ^[a-f0-9]{64}$ + - notExists: + path: spec.template.metadata.annotations["checksum/clickstack-secret"] + - notExists: + path: spec.template.spec.containers[0].envFrom[1] + - contains: + path: spec.template.spec.containers[0].env + content: + name: HYPERDX_API_KEY + valueFrom: + secretKeyRef: + name: external-hyperdx + key: api-key + + - it: should preserve user annotations while reserving generated checksum keys + template: hyperdx/deployment.yaml + set: + hyperdx: + deployment: + annotations: + example.com/legacy: preserved + podAnnotations: + example.com/pod: preserved + checksum/clickstack-config: caller-value + checksum/clickstack-secret: caller-value + asserts: + - equal: + path: spec.template.metadata.annotations["example.com/legacy"] + value: preserved + - equal: + path: spec.template.metadata.annotations["example.com/pod"] + value: preserved + - equal: + path: spec.template.metadata.annotations["checksum/clickstack-config"] + value: 0587572216112d61fd7d298001d71ed852194038be79415116a447e260753058 + - equal: + path: spec.template.metadata.annotations["checksum/clickstack-secret"] + value: 7d047b36293876f9242bce55bbd3ce9a255e51a425934a7cea4f2ab04f86895d + - notEqual: + path: spec.template.metadata.annotations["checksum/clickstack-config"] + value: caller-value + - notEqual: + path: spec.template.metadata.annotations["checksum/clickstack-secret"] + value: caller-value + + - it: should keep checksums stable when unrelated values change + template: hyperdx/deployment.yaml + set: + hyperdx: + deployment: + replicas: 3 + asserts: + - equal: + path: spec.template.metadata.annotations["checksum/clickstack-config"] + value: 0587572216112d61fd7d298001d71ed852194038be79415116a447e260753058 + - equal: + path: spec.template.metadata.annotations["checksum/clickstack-secret"] + value: 7d047b36293876f9242bce55bbd3ce9a255e51a425934a7cea4f2ab04f86895d diff --git a/charts/clickstack/values.yaml b/charts/clickstack/values.yaml index 91371ec..f962aca 100644 --- a/charts/clickstack/values.yaml +++ b/charts/clickstack/values.yaml @@ -100,6 +100,7 @@ hyperdx: # Shared non-sensitive environment variables. Used by HyperDX and OTEL collector via envFrom. # All values support Helm template expressions (rendered via tpl). # Override any entry with a plain string to point at an external service. + # Changes to the rendered ConfigMap automatically roll the HyperDX Deployment. config: APP_PORT: "3000" API_PORT: "8000" @@ -140,6 +141,9 @@ hyperdx: # managing secrets externally via deployment.env valueFrom entries. Requires # mongodb, clickhouse, and otel-collector to all be disabled. Users must # provide all required environment variables through their own secret management. + # Changes to this chart-managed Secret automatically roll the HyperDX Deployment. + # External Secret changes require an explicit rollout because Helm cannot hash + # resources that are not rendered by this chart. secrets: HYPERDX_API_KEY: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" CLICKHOUSE_PASSWORD: "otelcollectorpass" @@ -175,6 +179,8 @@ hyperdx: # Annotations applied to the HyperDX Deployment metadata. deploymentAnnotations: {} # Annotations applied to the HyperDX pod template metadata. + # checksum/clickstack-config and checksum/clickstack-secret are reserved for + # chart-generated rollout checksums and cannot be overridden. podAnnotations: {} # Deprecated: use podAnnotations. Retained for backward compatibility; # podAnnotations takes precedence when both maps contain the same key.