Skip to content

feat(chart): expose deployment/job/cronJob annotations for workload metadata#75

Merged
bryantbiggs merged 1 commit into
mainfrom
feat/chart-deployment-annotations
May 1, 2026
Merged

feat(chart): expose deployment/job/cronJob annotations for workload metadata#75
bryantbiggs merged 1 commit into
mainfrom
feat/chart-deployment-annotations

Conversation

@bryantbiggs

@bryantbiggs bryantbiggs commented May 1, 2026

Copy link
Copy Markdown
Member

Closes #74.

Summary

Adds three mode-gated values rendered onto metadata.annotations of the top-level workload, distinct from podAnnotations:

Value Renders on Active when
deploymentAnnotations Deployment metadata mode: watch
cronJobAnnotations CronJob metadata mode: cronjob
jobAnnotations Job metadata mode: job

Each is independently mode-gated, so values for inactive modes never render. Pod-template annotations remain unchanged under podAnnotations.

Naming and shape

The original issue proposed a single workloadAnnotations. After surveying mainstream charts the three-value shape is the established convention for charts that render multiple workload kinds:

  • kubernetes-sigs/descheduler — direct precedent: same multi-mode (Deployment / CronJob) pattern, exposes deploymentAnnotations + cronJobAnnotations + jobAnnotations + podAnnotations side by side.
  • cert-managerstartupapicheck.jobAnnotations defaults to helm.sh/hook: post-install plus helm.sh/hook-weight and helm.sh/hook-delete-policy. Helm hook annotations only function on the workload's own metadata, not the pod template.
  • bitnami/superset, devtron-labs/cronjob-chart, wiz-sec/charts, and 800+ other values.yaml files in public repos use one or more of the same three keys.

workloadAnnotations has no precedent in any popular chart. A bare top-level annotations: was rejected because serviceAccount.annotations already exists at values.yaml:24 and would create an ambiguous pair next to podAnnotations.

Why workload-level annotations matter for Job/CronJob (not just Deployment)

Reloader does not watch Jobs or CronJobs, so its motivation does not extend. The justification for the Job/CronJob slots is independent:

  • Helm lifecycle hooks (helm.sh/hook: post-install, helm.sh/hook-delete-policy, helm.sh/hook-weight) — these only function on the workload's own metadata.annotations. cert-manager's startupapicheck Job is the canonical example.
  • Argo CDargocd.argoproj.io/sync-wave and argocd.argoproj.io/hook are read from the resource's own metadata. Sync-wave on a CronJob (e.g. ordering DB migration before app rollout) is a normal Argo pattern.
  • Policy controllers — Kyverno and OPA Gatekeeper select on workload-level annotations for cost-center tags, owner tags, and exemption markers.
  • Backup/cost tooling — Velero, Kubecost, and similar tools key off resource-level annotations for inclusion/exclusion and rollups.

None of these need Reloader. All of them need the workload's own metadata, not the pod template's.

Mid-sync runtime safety

Editing metadata.annotations does not signal the running Pod or restart any in-flight sync. For each mode:

  • mode: watch — annotations on the Deployment are not part of the pod-template hash, so kubectl patch does not roll the workload. The exception is reloader.stakater.com/auto: "true" paired with a ConfigMap/Secret rotation, which causes Reloader to mutate the pod-template annotation and roll the Deployment. Safe-by-design: ocync's transfer state cache and digest skip-detection allow the new pod to resume work and skip already-uploaded blobs.
  • mode: cronjob — annotations on the CronJob's metadata are independent of any spawned Job. Patching cronJobAnnotations while a sync runs has no effect on the running Job. Affects only Argo's reconcile view, Helm's hook lifecycle, policy controllers, etc.
  • mode: job — annotations on the Job's metadata are read-only from the runtime's perspective. Patching mid-run does not signal the pod or restart anything.

Example

# values.yaml — Argo CD-managed sync Job ordered before app rollout
mode: job

jobAnnotations:
  helm.sh/hook: post-install
  helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
  argocd.argoproj.io/sync-wave: "5"

Test plan

  • helm-unittest covers all three modes with positive (real annotation values: helm.sh/hook, argocd.argoproj.io/sync-wave, reloader.stakater.com/auto) and negative cases — 60/60 pass (added 8 new cases)
  • Cross-render guards: deploymentAnnotations is not rendered in mode: cronjob, jobAnnotations is not rendered in mode: watch
  • helm template end-to-end render verified for each mode with a real annotation set
  • helm template with no override produces no metadata.annotations block on any workload
  • helm lint charts/ocync clean
  • helm-docs regenerated; charts/ocync/README.md includes all three new values

@bryantbiggs
bryantbiggs force-pushed the feat/chart-deployment-annotations branch from 5a6e467 to be1e04d Compare May 1, 2026 22:18
@bryantbiggs bryantbiggs changed the title feat(chart): expose deploymentAnnotations for top-level workload metadata feat(chart): expose deployment/job/cronJob annotations for workload metadata May 1, 2026
…etadata

Adds three mode-gated values rendered onto metadata.annotations of the
top-level workload:

  deploymentAnnotations - Deployment (mode: watch)
  cronJobAnnotations    - CronJob    (mode: cronjob)
  jobAnnotations        - Job        (mode: job)

Pod-template annotations remain unchanged via podAnnotations.

Each value targets concrete real-world use cases at the workload level,
which are distinct from pod-template annotations and would silently fail
under the latter:

  Deployment - Stakater Reloader, Argo Rollouts notification subscriptions
  CronJob/Job - Helm lifecycle hooks (helm.sh/hook), Argo CD sync-wave
                and hook annotations, Kyverno/Gatekeeper policy selectors,
                cost-allocation tags

Naming and shape mirror the kubernetes-sigs/descheduler chart, which
exposes the same triad alongside podAnnotations across the same multi-
mode rendering pattern. cert-manager exposes startupapicheck.jobAnnotations
defaulting to Helm hook annotations for the same reason.

Closes #74.
@bryantbiggs
bryantbiggs force-pushed the feat/chart-deployment-annotations branch from be1e04d to 2b1be65 Compare May 1, 2026 22:37
@bryantbiggs
bryantbiggs merged commit d733620 into main May 1, 2026
16 checks passed
@bryantbiggs
bryantbiggs deleted the feat/chart-deployment-annotations branch May 1, 2026 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose workloadAnnotations for the top-level Deployment/CronJob/Job

1 participant