Is this related to a problem? Please describe.
Charts in this repo render bootstrap and migration work as Helm lifecycle hooks (helm.sh/hook plus hook-weight). That ties the chart to installing through the Helm controller. Declarative reconcilers (Argo CD, Flux, plain CI kubectl apply) then either have to strip the hook annotations themselves or lose the ordering the hooks gave them. This came up in the Cassandra bitnami-removal work (#317, PR #328): its initdb and migrations run as post-install,post-upgrade hooks, with no way to render them for a non-Helm installer and no way to turn them off, for example during datacenter expansion, where the new datacenter must not re-run schema-init or migrations.
The same coupling exists in every chart that has bootstrap or migration jobs, so it is worth solving once as a shared convention rather than chart by chart.
Describe the solution you'd like
A generic set of values that renders the same bootstrap and migration work as either Helm hooks or plain Kubernetes resources, or not at all, with no installer-specific behavior in the chart: no argo.enabled, no Argo or Flux annotations, no controller branching.
lifecycleJobs:
mode: hook # hook | resource | disabled
ttlSecondsAfterFinished: 300
podAnnotations: {}
annotations: {}
deletePolicy: before-hook-creation # hook mode only
Modes:
hook: current behavior. Jobs render with helm.sh/hook annotations and weights. For direct Helm or Helmfile.
resource: jobs render as normal Kubernetes resources with stable names and no hook annotations. For Argo, Flux, CI apply, or any declarative reconciler.
disabled: the chart renders no bootstrap or migration jobs, and an external operator or runbook owns them. This covers the datacenter-expansion case.
Acceptance criteria:
- A shared helper renders bootstrap and migration jobs the same way across charts in all three modes, rather than each chart implementing its own.
- The
lifecycleJobs values are implemented: mode, ttlSecondsAfterFinished, podAnnotations, annotations, and deletePolicy (hook mode only).
resource mode keeps the init-then-migration ordering without depending on a specific reconciler, and defines what happens on upgrade re-runs.
disabled mode renders no bootstrap or migration jobs and is documented for the datacenter-expansion path.
- No installer-specific fields or annotations exist in any chart.
- The Cassandra chart is the first to adopt it, as the reference implementation, and the GitOps example values show
mode: resource.
- Docs: a values reference for
lifecycleJobs, and a note in the Cassandra upgrade guide pointing datacenter-expansion operators at mode: disabled.
- The other hook-using charts (
openbao, nvca-operator, cert-manager, function-autoscaler, llm-request-router) are triaged to identify which of their hooks are bootstrap or migration jobs, and a follow-up issue is filed for each chart that qualifies. Converting them is out of scope here.
Describe alternatives you've considered
- Installer-specific behavior in the chart (
argo.enabled, sync-wave annotations, controller branching). Rejected: it hardcodes one orchestrator into a chart that should stay neutral, and it does nothing for Flux or plain apply.
- A script in the GitOps layer that strips
helm.sh/hook annotations at apply time. Rejected: it is a workaround in each consumer, easy to get wrong, and it still loses the ordering the hooks encoded.
- A one-off toggle per chart (for example
cassandra.hooks.*.enabled). Rejected: it only handles the disable case for one chart and becomes a second mechanism the shared approach then has to migrate off.
Additional context
Most of the design work is in resource mode, which is why it needs its own pass:
- Ordering. Hook-weights currently run the pre-install RBAC and configmap first, then
initdb, then migrations. As plain resources that ordering has to be rebuilt so it holds under a plain kubectl apply (for example init-container gates or readiness preconditions), not only under a reconciler that has sync-waves.
- Re-run and immutability. Hooks re-run on each upgrade (
before-hook-creation deletes the prior job first). A Job with a fixed name is immutable and will not re-run on upgrade, so the migration silently stops running. This needs a decision: suffix the job name with a content hash, use a TTL and recreate, or make migrations apply-once by contract.
deletePolicy only applies in hook mode.
References:
By submitting this issue, you agree to follow our code of conduct and our contributing guidelines.
Is this related to a problem? Please describe.
Charts in this repo render bootstrap and migration work as Helm lifecycle hooks (
helm.sh/hookplushook-weight). That ties the chart to installing through the Helm controller. Declarative reconcilers (Argo CD, Flux, plain CIkubectl apply) then either have to strip the hook annotations themselves or lose the ordering the hooks gave them. This came up in the Cassandra bitnami-removal work (#317, PR #328): itsinitdbandmigrationsrun aspost-install,post-upgradehooks, with no way to render them for a non-Helm installer and no way to turn them off, for example during datacenter expansion, where the new datacenter must not re-run schema-init or migrations.The same coupling exists in every chart that has bootstrap or migration jobs, so it is worth solving once as a shared convention rather than chart by chart.
Describe the solution you'd like
A generic set of values that renders the same bootstrap and migration work as either Helm hooks or plain Kubernetes resources, or not at all, with no installer-specific behavior in the chart: no
argo.enabled, no Argo or Flux annotations, no controller branching.Modes:
hook: current behavior. Jobs render withhelm.sh/hookannotations and weights. For direct Helm or Helmfile.resource: jobs render as normal Kubernetes resources with stable names and no hook annotations. For Argo, Flux, CI apply, or any declarative reconciler.disabled: the chart renders no bootstrap or migration jobs, and an external operator or runbook owns them. This covers the datacenter-expansion case.Acceptance criteria:
lifecycleJobsvalues are implemented:mode,ttlSecondsAfterFinished,podAnnotations,annotations, anddeletePolicy(hook mode only).resourcemode keeps the init-then-migration ordering without depending on a specific reconciler, and defines what happens on upgrade re-runs.disabledmode renders no bootstrap or migration jobs and is documented for the datacenter-expansion path.mode: resource.lifecycleJobs, and a note in the Cassandra upgrade guide pointing datacenter-expansion operators atmode: disabled.openbao,nvca-operator,cert-manager,function-autoscaler,llm-request-router) are triaged to identify which of their hooks are bootstrap or migration jobs, and a follow-up issue is filed for each chart that qualifies. Converting them is out of scope here.Describe alternatives you've considered
argo.enabled, sync-wave annotations, controller branching). Rejected: it hardcodes one orchestrator into a chart that should stay neutral, and it does nothing for Flux or plain apply.helm.sh/hookannotations at apply time. Rejected: it is a workaround in each consumer, easy to get wrong, and it still loses the ordering the hooks encoded.cassandra.hooks.*.enabled). Rejected: it only handles the disable case for one chart and becomes a second mechanism the shared approach then has to migrate off.Additional context
Most of the design work is in
resourcemode, which is why it needs its own pass:initdb, thenmigrations. As plain resources that ordering has to be rebuilt so it holds under a plainkubectl apply(for example init-container gates or readiness preconditions), not only under a reconciler that has sync-waves.before-hook-creationdeletes the prior job first). A Job with a fixed name is immutable and will not re-run on upgrade, so the migration silently stops running. This needs a decision: suffix the job name with a content hash, use a TTL and recreate, or make migrations apply-once by contract.deletePolicyonly applies inhookmode.References:
initdbandmigrations, and the review note about disabling hooks for datacenter expansiondependsOn, as generic references for the three rendering modesBy submitting this issue, you agree to follow our code of conduct and our contributing guidelines.