diff --git a/changelog/55.feature.md b/changelog/55.feature.md new file mode 100644 index 0000000..ce292bc --- /dev/null +++ b/changelog/55.feature.md @@ -0,0 +1 @@ +Adds a chart-wide `podLabels` value, applied to every pod the chart renders. diff --git a/helm/README.md b/helm/README.md index 9534151..03f1ea3 100644 --- a/helm/README.md +++ b/helm/README.md @@ -208,6 +208,10 @@ For ephemeral test deployments (no persistence across upgrades), `/ref` can also | `imagePullSecrets` | Docker registry secrets | `[]` | | `nameOverride` | Override chart name | `""` | | `fullnameOverride` | Override full release name | `""` | +| `podLabels` | Labels added to every pod | `{}` | + +`podLabels` covers every pod the chart renders. +A component's own `podLabels` is applied on top of it so they take precendence. ### Diagnostic providers diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index f4b5bba..d6c04f4 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -42,6 +42,16 @@ app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} +{{/* +Labels added to every pod the chart renders, from the chart-wide `podLabels`. +Rendered before a component's own podLabels, so a component can still override one. +*/}} +{{- define "ref.podLabels" -}} +{{- with .Values.podLabels -}} +{{- toYaml . -}} +{{- end -}} +{{- end -}} + {{/* Selector labels */}} diff --git a/helm/templates/api/deployment.yaml b/helm/templates/api/deployment.yaml index a824398..44f5660 100644 --- a/helm/templates/api/deployment.yaml +++ b/helm/templates/api/deployment.yaml @@ -29,6 +29,9 @@ spec: labels: app.kubernetes.io/component: api {{- include "ref.labels" . | nindent 8 }} + {{- with (include "ref.podLabels" .) }} + {{- . | nindent 8 }} + {{- end }} {{- with .Values.api.podLabels }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/helm/templates/flower/deployment.yaml b/helm/templates/flower/deployment.yaml index 0aa04fb..6153c8f 100644 --- a/helm/templates/flower/deployment.yaml +++ b/helm/templates/flower/deployment.yaml @@ -24,6 +24,9 @@ spec: labels: app.kubernetes.io/component: flower {{- include "ref.labels" . | nindent 8 }} + {{- with (include "ref.podLabels" .) }} + {{- . | nindent 8 }} + {{- end }} {{- with .Values.flower.podLabels }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/helm/templates/migrate-job.yaml b/helm/templates/migrate-job.yaml index 4e46be8..4bc2997 100644 --- a/helm/templates/migrate-job.yaml +++ b/helm/templates/migrate-job.yaml @@ -31,6 +31,9 @@ spec: metadata: labels: {{- include "ref.selectorLabels" . | nindent 8 }} + {{- with (include "ref.podLabels" .) }} + {{- . | nindent 8 }} + {{- end }} spec: {{- with $migrate.priorityClassName }} priorityClassName: {{ . | quote }} diff --git a/helm/templates/providers/deployment.yaml b/helm/templates/providers/deployment.yaml index 7abd100..8315e80 100644 --- a/helm/templates/providers/deployment.yaml +++ b/helm/templates/providers/deployment.yaml @@ -78,6 +78,9 @@ spec: labels: app.kubernetes.io/component: {{ $instance }} {{- include "ref.labels" $ | nindent 8 }} + {{- with (include "ref.podLabels" $) }} + {{- . | nindent 8 }} + {{- end }} {{- with $spec.podLabels }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/helm/values.yaml b/helm/values.yaml index 5436ce4..2cf8337 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -2,6 +2,9 @@ imagePullSecrets: [] nameOverride: "" fullnameOverride: "" +# Labels added to every pod the chart renders, on top of each component's own podLabels. +podLabels: {} + api: enabled: true replicaCount: 1 diff --git a/tests/test_helm_render.py b/tests/test_helm_render.py index 9fcfe0b..f3573fc 100644 --- a/tests/test_helm_render.py +++ b/tests/test_helm_render.py @@ -1064,3 +1064,48 @@ def test_the_migrate_job_follows_an_orchestrator_priority_class_override(): "orchestrator.priorityClassName=ref-orchestrator", ) assert _pod_spec(docs, "migrate", kind="Job")["priorityClassName"] == "ref-orchestrator" + + +def _pod_labels(docs: list[dict], component: str, kind: str = "Deployment") -> dict: + return find(docs, kind, f"-{component}")["spec"]["template"]["metadata"]["labels"] + + +POD_TEMPLATES = [ + ("api", "Deployment"), + ("flower", "Deployment"), + ("orchestrator", "Deployment"), + ("pmp", "Deployment"), + ("migrate", "Job"), +] + + +@pytest.mark.parametrize("component,kind", POD_TEMPLATES) +def test_chart_wide_pod_labels_reach_every_pod(component, kind): + docs = render("flower.enabled=true", "podLabels.environment=production") + assert _pod_labels(docs, component, kind=kind)["environment"] == "production" + + +@pytest.mark.parametrize("component,kind", POD_TEMPLATES) +def test_no_chart_wide_pod_labels_by_default(component, kind): + docs = render("flower.enabled=true") + assert "environment" not in _pod_labels(docs, component, kind=kind) + + +def test_chart_wide_pod_labels_stay_off_the_selectors(): + # A Deployment's selector is immutable + # A label that reached it would make the next upgrade of an existing release fail. + docs = render("podLabels.environment=production") + for component in ("api", "orchestrator", "pmp"): + selector = find(docs, "Deployment", f"-{component}")["spec"]["selector"]["matchLabels"] + assert "environment" not in selector + + +def test_a_component_overrides_a_chart_wide_pod_label(): + docs = render( + "podLabels.environment=production", + "api.podLabels.environment=staging", + "providers.pmp.podLabels.environment=staging", + ) + assert _pod_labels(docs, "api")["environment"] == "staging" + assert _pod_labels(docs, "pmp")["environment"] == "staging" + assert _pod_labels(docs, "orchestrator")["environment"] == "production"