fix: manage Karpenter CRDs in lockstep with the controller#325
Conversation
Install the karpenter-crd chart as a second helm.cattle.io/v1 HelmChart pinned to the same karpenter_version as the controller, so a controller bump can never run against stale, hand-applied CRDs. Pre-stamp the existing CRDs with Helm ownership metadata so the chart can adopt them on existing clusters; the stamp is a no-op on greenfield. Order CR creation with dependsOn (CRD chart first). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the manual CustomResourcePatch ownership pre-stamp with the helm-controller's native spec.takeOwnership (helm --take-ownership). Bump helm-controller v0.16.10 -> v0.16.14 (adds takeOwnership) on both AWS and Azure clusters, and add the takeOwnership boolean to PTD's embedded HelmChart CRD schema so the field is not pruned. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Condense the inline comment in awsHelmKarpenter to the essentials and move the full rationale (CRD staleness, takeOwnership adoption, version requirements, limitations, rollout) to docs/infrastructure/karpenter-crds.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@claude review please |
This comment was marked as outdated.
This comment was marked as outdated.
The helm job image is pinned to Helm 3.16.4 (klipper-helm:v0.9.5-build20250306), which predates --take-ownership, so spec.takeOwnership can't adopt the controller chart's pre-existing, Helm-unowned CRDs. Drop the takeOwnership field and the helm-controller v0.16.14 bump; adopt via a documented one-time manual kubectl stamp instead (docs/infrastructure/karpenter-crds.md). The karpenter-crd chart and dependsOn ordering are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@claude review please |
|
Claude finished @amdove's task in 1m 47s —— View job Code Review
The approach is sound and follows the recommended Karpenter pattern. The implementation is minimal and consistent with the existing codebase. A few things worth calling out:
|
Condense the inline comment to what/where — the full rationale and migration procedure live in docs/infrastructure/karpenter-crds.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use a for loop instead of an unquoted $CRDS variable, which zsh does not word-split (bash does), so the label/annotate ran against one mashed-together argument. The loop works in both shells. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a one-phrase explanation for readers unfamiliar with the NodeOverlay CRD (added in Karpenter v1.7.1; absent on older installs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @amdove's task in 56s —— View job Code Review
The implementation is correct and minimal. One substantive issue and two small doc nits:
|
Stamp the karpenter-crd chart's CRDs with resource-policy: keep (via its additionalAnnotations value) so a helm uninstall — the HelmChart CR being deleted, renamed, or the code reverted — never cascade-deletes the CRDs and every NodePool/NodeClaim/EC2NodeClass. Restores the delete-safety the controller chart's bundled crds/ had, while keeping upgrade management. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Manage Karpenter's CRDs in lockstep with the controller, so a
karpenter_versionbump can never run against stale CRDs.The controller chart bundles its CRDs in
crds/, which Helm installs once and never upgrades. So akarpenter_versionbump moves the controller but leaves the CRDs frozen — and a controller >= 1.9 emitting aGte/Lterequirement (normalized from a NodePoolGt/Ltfloor) against a pre-1.9 CRD gets its NodeClaim rejected, so no nodes provision.What changed
karpenter-crdchart as a secondhelm.cattle.io/v1HelmChart, pinned to the samekarpenter_versionas the controller. Its CRDs are templated, so Helm upgrades them on every bump (Karpenter's recommended approach).dependsOnorders it ahead of the controller chart and the NodePool/EC2NodeClass CRs.helm.sh/resource-policy: keepso ahelm uninstall(the HelmChart CR being deleted/renamed, or the code reverted) never cascade-deletes the CRDs and every NodePool/NodeClaim/EC2NodeClass — restoring the delete-safety the controller chart's bundledcrds/had.Why adoption is a one-time manual step
Existing clusters already have the Karpenter CRDs — the controller chart's
crds/created them — without Helm ownership metadata. A plainkarpenter-crdinstall fails withinvalid ownership metadata. Helm's fix is--take-ownership, but PTD's helm job image is intentionally pinned toklipper-helm:v0.9.5-build20250306(Helm 3.16.4, predating that flag; newer Helm-4 builds regressed install/upgrade detection). So adoption can't be automated in the chart.Instead it's a one-time manual stamp per existing cluster (label + annotate the CRDs with Helm ownership metadata), after which the
karpenter-crdinstall adopts them cleanly. Greenfield clusters skip it. Full procedure: docs/infrastructure/karpenter-crds.md.Testing
just check-go,just cli,just test-libpass.Validated end-to-end on a staging cluster in the stale-CRD state (Karpenter controller 1.12.1, pre-1.9 CRDs): applied the one-time ownership stamp, installed the
karpenter-crdchart, and confirmed it adopted the existing CRDs (now owned by the release, noinvalid ownership metadataerror), upgraded their schema (theoperatorenum gainedGte/Lte), and left the existing NodePools/EC2NodeClasses and the running controller untouched. A new Workbench session then triggered Karpenter to provision a fresh node and scheduled onto it successfully, confirming live provisioning against the adopted/upgraded CRDs. Per-cluster migration is still required before broad rollout.Future
When the fleet's helm job image moves to a fixed Helm-4 klipper-helm build (
>= v0.11.1-build20260615) + helm-controller>= v0.16.14, this can switch tospec.takeOwnership: trueand drop the manual step. Tracked separately.🤖 Generated with Claude Code