Skip to content

Tenant addon CRD HelmReleases remediate by uninstall, and their CRDs carry no resource-policy: keep #3555

Description

Four tenant addon HelmReleases in packages/apps/kubernetes/templates/helmreleases/ ship CRDs from a chart's templates/ directory and carry remediation.retries: -1 with no install.strategy. Helm owns anything rendered from templates/, so an uninstall deletes it, and deleting a CRD makes the API server garbage-collect every custom resource of that kind. The default install strategy remediates a failed install by uninstalling the release.

Only a failed install gets there. A failed upgrade does not: with Remediation unset, UpgradeRemediation.GetStrategy() returns the rollback strategy (api/v2/helmrelease_types.go:957-961), so a failed chart bump rolls back and leaves the CRDs. An absent release does not either. retries: -1 never exhausts, so the absent branch returns an install instead of refusing one (internal/reconcile/atomic_release.go:389). The exposure is that this reinstall can fail, and then it is an install failure. Line references are helm-controller v1.5.0, the version this repo installs (internal/fluxinstall/manifests/fluxcd.yaml:8097).

Second limiter. On a first install no custom resources exist, so a teardown destroys nothing. To lose anything the install has to run while custom resources already exist, which means the release went absent after a successful install (deleting its Helm storage Secret is the simplest way there) and the reinstall then failed. Whether that combination has ever occurred is not shown here. I collected no evidence of an occurrence, and the instrument that would record one is limited anyway: the e2e remediation-cycle guard watches only the parent HelmRelease and treats a failed attempt the same as a teardown (#3553).

Release CRD source resource-policy: keep dependsOn timeout
gateway-api-crds packages/system/gateway-api-crds/templates/ (1 file) no none 10m
vertical-pod-autoscaler-crds packages/system/vertical-pod-autoscaler-crds/templates/ (1 file) no none inherited default
prometheus-operator-crds subchart named crds, CRDs in its templates/ (10 files) no none inherited default
volumesnapshot-crd packages/system/vsnap-crd/templates/ (3 files) no none 10m
cert-manager-crds packages/system/cert-manager-crds/templates/ (6 files) yes, crds.keep: true by default none 10m

cert-manager-crds is in the table as the precedent. Every one of its CRD templates is annotated, so an uninstall leaves the definitions and their custom resources alone. That shape already exists in the tree.

Two entries inherit the HelmRelease default timeout of 5m instead of setting 10m (api/v2/helmrelease_types.go:110, GetTimeout at :1503-1504), so they have the narrowest budget and no protection. None of the five declares dependsOn. The dependency gate does protect the other tenant addons: a release waiting on a dependency returns before the release reconciler runs (internal/controller/helmrelease_controller.go:262) and never increments a failure count, since IncrementFailureCount is only reached from the install, upgrade and test reconcilers. The five without dependsOn are the ones that gate misses.

coredns is a lighter case. Same remediation-only shape, and it is the tenant cluster's only DNS, so an uninstall drops the Deployment and its Service while every running pod keeps resolving against an address that stopped routing. The chart pins the Service address (packages/apps/kubernetes/templates/helmreleases/coredns.yaml, cozystack.defaultCoreDNSValues), so a reinstall comes back on the same address and pods recover without a restart. Without the pin a reallocated address would leave every existing pod pointing at a dead one for good. It also declares dependsOn on the CNI, so the gate covers that failure path.

Two directions, and they are not exclusive. Annotating the CRD templates with helm.sh/resource-policy: keep follows cert-manager-crds and stops the deletion, at the cost of leaving definitions behind on a deliberate uninstall. Setting install.strategy.name: RetryOnFailure on both actions follows #3552 and takes the teardown out of the retry path without changing what a deliberate uninstall does. #3554 covers a related trap on the remediation values that the second direction has to account for.

Activity

  1. lexfrei commented on Aug 5, 2026

    @lexfrei
    ContributorAuthor

    I went through CI artifacts looking for the occurrence this issue says is not shown. The sample is ten recent red e2e runs, nine nightly and one from a pull request. Seven had a usable instrument. One died during install, so no tenant clusters ever existed and there was nothing to observe. Two had no artifacts at all.

    The remediation-by-uninstall path is not theoretical in this repo. It fires in all seven runs, on sibling releases: Normal UninstallSucceeded helmrelease/kubernetes-test-previous-version-cilium Helm uninstall remediation for release cozy-cilium/cilium.v1 with chart [email protected]+02ff94ddaf23 succeeded. Each run has two or three distinct occurrences, all on cilium, and one run also has them on csi. Counting needs care: the diagnostics block prints the event dump more than once per run, so the same occurrence shows up again under a different age, and a naive line count roughly doubles it.

    Those two releases are configured the same way as the ones in the table. In packages/apps/kubernetes/templates/helmreleases/, cilium.yaml and csi.yaml both carry install.remediation.retries: -1 and upgrade.remediation.retries: -1, same as gateway-api-crds.yaml, vertical-pod-autoscaler-crds.yaml and volumesnapshot-crd.yaml. What separates them in these runs is how often the install fails. The configuration is the same.

    No uninstall event shows up on any release in the table, but for half of them that says nothing. The tenant addon set is identical in every run I looked at: cilium, coredns, csi, ingress-nginx, metrics-server, ouroboros, prometheus-operator-crds, vsnap-crd. So gateway-api-crds and vertical-pod-autoscaler-crds never get installed in an e2e tenant at all, and CI cannot observe them either way. prometheus-operator-crds and vsnap-crd are installed and visible to the instrument, both show InstallSucceeded in the same dumps, and neither shows an uninstall.

    Three limits on what these artifacts can answer, all measured. Events are the only surviving trace: in a full bundle of 6337 files the string is in exactly one of them, the host event dump, and the oldest event there is 59 minutes old in a run that lasted over two hours. HelmRelease objects are missing from the bundle completely. No file matches kind: HelmRelease, and although the collector's own apis.json lists helm.toolkit.fluxcd.io, no namespace directory contains that group, so .status.history is never collected. Snapshots are taken once per failed test, at failure time, so there is no second window where a custom resource could be seen to disappear.

  2. lexfrei commented on Aug 5, 2026

    @lexfrei
    ContributorAuthor

    Correction to my previous comment. I claimed HelmRelease objects are never collected into the report bundle, and that .status.history is therefore unavailable. That is wrong. It holds for two of the six bundles I have, where that API group is missing from an otherwise complete collection, but the other four carry between 108 and 242 HelmRelease objects each, including the tenant addon releases. I checked a single bundle and generalized from it.

    With the history available the check this issue actually wants can be run, and it comes out the same way the events did, only on firmer ground. Across all six bundles exactly four HelmRelease objects carry an uninstall entry in .status.history, and all four are cilium: action: uninstall-remediation, status: uninstalling, deleted: 2026-08-04T05:22:26Z. No release from the table has one. kubernetes-test-latest-version-vsnap-crd, for example, carries a single entry, action: install with status: deployed.

    The conclusion is unchanged and now rests on the durable record instead of events alone. What I wrote about event lifetime and about there being one snapshot window per failed test still holds.

  3. lexfrei commented on Aug 7, 2026

    @lexfrei
    ContributorAuthor

    The keep half of this landed: #3586 stamps resource-policy: keep on the four addon CRD packages, #3590 pins the annotation with tests, and #3595 makes the vendoring step fail loudly when the stamp misses instead of shipping unprotected CRDs. Remediation-by-uninstall is still there: csi got RetryOnFailure in #3581, the other addons still uninstall on a failed install.

  4. lexfrei commented on Aug 7, 2026

    @lexfrei
    ContributorAuthor

    Correction to my previous comment: cilium also carries RetryOnFailure, via #3552, same shape as the csi one from #3581. So two addon HelmReleases retry in place now and the rest still remediate by uninstall.

  5. added
    kind/bugCategorizes issue or PR as related to a bug
    area/kubernetesIssues or PRs related to the tenant Kubernetes app
    triage/acceptedIndicates an issue is ready to be actively worked on
    priority/important-longtermImportant over the long term, but may not be staffed and/or may need multiple releases to complete
    and removed
    triage/needs-triageIndicates an issue needs triage by a maintainer
    on Sep 23, 2026
  6. lexfrei commented on Oct 5, 2026

    @lexfrei
    ContributorAuthor

    One more thing this chart needs is a test that every tenant addon release either retries in place or remediates without limit. The e2e HelmRelease history guard from #3594 depends on that, and nothing holds it today. The guard fails a run on a failed Snapshot only when the release sets install.strategy.name: RetryOnFailure, and prints a note for every other addon. For an addon with remediation.retries: -1 that is fine, since remediation is its declared recovery path.

    An addon added later with retries: 0 or no remediation block would slip through. That release never remediates, so its failed Snapshot is the only record of the failure. If the suite does not wait on it, the run stays green. The comment that states the assumption is at hack/e2e-chainsaw/_lib/run-kubernetes.sh:5204 on b42805b.

    The property holds on main at 94abf69. cilium.yaml, csi.yaml and csi-proxmox.yaml set RetryOnFailure. Every file in packages/apps/kubernetes/templates/helmreleases/ sets retries: -1 and no other retries value. The only tests that read these fields are cilium_install_retry_test.yaml and csi_install_retry_test.yaml, and they cover those two releases only.

    The fix is a helm-unittest test in packages/apps/kubernetes/tests/ that renders every addon and requires each HelmRelease to carry RetryOnFailure or retries: -1 on both actions. I'm adding this here because the fix this issue proposes changes the same fields on the CRD addons, and the test would hold whichever direction lands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/kubernetesIssues or PRs related to the tenant Kubernetes appkind/bugCategorizes issue or PR as related to a bugpriority/important-longtermImportant over the long term, but may not be staffed and/or may need multiple releases to completetriage/acceptedIndicates an issue is ready to be actively worked on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions