feat(kubernetes)!: remove FluxCD addon - #3379
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRemoves the FluxCD addon from Kubernetes API types, chart values, schemas, platform sources, and system package references. Adds migration 54→55 cleanup for existing FluxCD HelmReleases and advances the migration target. ChangesFluxCD addon removal
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
85f2765 to
42a316b
Compare
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request removes the optional FluxCD addon from the Kubernetes application to eliminate AGPL-3.0 licensed components, addressing a key governance requirement for CNCF Incubation. The change includes a migration script to ensure that existing tenant clusters with the addon enabled maintain their current Flux installation by orphaning the management-side HelmReleases, preventing unintended uninstalls. Highlights
New Features🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request removes the FluxCD addon from the Kubernetes application as a breaking change, deleting the associated packages, templates, schemas, and documentation. To handle existing installations, a new migration script (54 -> 55) is introduced to orphan the FluxCD HelmReleases in tenant clusters during upgrade. Feedback on the migration script highlights three issues: first, the use of pipefail in a #!/bin/sh script is a bashism that will cause failures on POSIX-compliant shells like dash; second, the JSON patch to remove /metadata/finalizers can fail if the field is already absent, so a merge patch setting it to null is recommended; and third, using printf instead of echo when piping the list of HelmReleases prevents an unnecessary loop execution when no releases are found.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| # reaches the tenant HelmReleases, so the finalizer is removed before any | ||
| # pruning can fire. | ||
|
|
||
| set -euo pipefail |
There was a problem hiding this comment.
The shell script uses set -euo pipefail with a #!/bin/sh shebang. However, pipefail is a non-standard shell option (a bashism) and is not supported by POSIX-compliant shells like dash (which is the default /bin/sh on Debian and Ubuntu). Running this script in an environment where /bin/sh points to dash will cause it to fail immediately with set: pipefail: bad option, blocking the platform upgrade. Since the pipeline exit status is already safely handled by the || true guard in the command substitution, we can safely remove pipefail and use set -eu.
| set -euo pipefail | |
| set -eu |
| kubectl -n "$ns" patch helmrelease "$name" --type=json \ | ||
| -p='[{"op":"remove","path":"/metadata/finalizers"}]' 2>/dev/null || true |
There was a problem hiding this comment.
Using a JSON patch with the remove operation on /metadata/finalizers will fail if the finalizers field is already absent or null on the HelmRelease object. Although the error is ignored via || true, it can clutter the migration logs with unnecessary errors. Using a merge patch to set metadata.finalizers to null is safer, idempotent, and will always succeed regardless of whether the finalizers field currently exists.
| kubectl -n "$ns" patch helmrelease "$name" --type=json \ | |
| -p='[{"op":"remove","path":"/metadata/finalizers"}]' 2>/dev/null || true | |
| kubectl -n "$ns" patch helmrelease "$name" --type=merge \ | |
| -p '{"metadata":{"finalizers":null}}' 2>/dev/null || true |
| or .spec.chartRef.name == "cozystack-kubernetes-application-kubevirt-kubernetes-fluxcd-operator") | ||
| | "\(.metadata.namespace) \(.metadata.name)"' || true) | ||
|
|
||
| echo "$hrs" | while read -r ns name; do |
There was a problem hiding this comment.
If hrs is empty, echo "$hrs" will output a blank line, causing the while read loop to execute once with empty variables (which is then skipped by the [ -n "$ns" ] check). Using printf '%s' instead of echo avoids generating this extra blank line when hrs is empty, preventing the loop from executing unnecessarily.
| echo "$hrs" | while read -r ns name; do | |
| printf '%s' "$hrs" | while read -r ns name; do |
|
Kingdon Barrett (@kingdonb) FYI — alongside this removal we have asked CNCF for guidance on a Debian-style "contrib" repository (ServiceDesk ticket CNCFSD-3698): Apache-2.0 integration charts under project governance, with non-Apache components such as ControlPlane's flux-operator fetched by the user's cluster from vendor sources at deploy time. That would give the flux-operator-based delivery of Flux a proper home — including satisfying the platform's Flux dependency from ControlPlane's enterprise distribution as an opt-in alternative, with vanilla Apache-2.0 Flux staying the self-contained default in main (Debian's alternatives model). We would be glad to have you maintain that integration if you are interested; we will follow up once CNCF responds. |
The fluxcd addon pin was a holdover from the #3150-era model where app charts rendered nested HelmReleases into the target cluster and needed an in-cluster helm-controller. Consumers now keep their releases on the management cluster and remote-apply via spec.kubeConfig, so the management Flux resolves charts and applies manifests — nothing inside the ComputePlane reconciles HelmReleases. This also decouples the module from the FluxCD addon removal (#3379). certManager and ingressNginx stay pinned: Certificate and Ingress objects land inside the cluster and need their controllers there. Assisted-By: Claude <[email protected]> Signed-off-by: Andrei Kvapil <[email protected]>
|
I was worried that AGPL would be a blocker somewhere before too long - Flux distributes the operator as AGPL to prevent hyperscalers from bundling it as a closed source product, not to prevent GPL or Apache projects from integrating. I think I need to review the implications of that exclusion, but it sounds like there is a plan. |
|
Yes, please sign me up - I'll be glad to help part to maintain the contrib repo of external or differently licensed projects 🎉 |
42a316b to
1e2d87d
Compare
IvanHunters
left a comment
There was a problem hiding this comment.
Reviewed with the cozy-review methodology, focusing on the breaking-change aspects. Verdict: LGTM with non-blocking notes. No blocking findings. The FluxCD addon removal is complete and consistent, and the upgrade path for existing clusters is safe.
Breaking-change review
- Migration 54 removes the finalizer and deletes the child tenant HelmReleases by two hardcoded
chartRef.namevalues that match the removedpackages/apps/kubernetes/templates/helmreleases/fluxcd.yamltemplate byte-for-byte. - Ordering is guaranteed:
migration-hook.yaml:34useshelm.sh/hook: pre-upgrade,pre-install, so the migration runs before the platform re-applies the fluxcd-less ExternalArtifacts. targetVersionbumped from 54 to 55 via the shared version-stamp helper. No rebase collision (max on main is 53).- Upgrade does not hard-fail: the generated
openAPISchemainkubernetes-rd/cozyrds/kubernetes.yamldoes not setadditionalProperties: false, so a leftoveraddons.fluxcdfield in a stored CR is not rejected at upgrade time and is pruned on the next manual apply. make generateis up to date (schema / types.go / README), Go builds, no dangling references.- Both bootstrap signals are false positives: the
removed-identifiersenum isFluxInstance.spec.cluster.sizeinside the fully-removed vendored CRD (not a cozystack values enum); thecharts-direct-editfiles are all deleted in full (status D), so there is no in-place edit to be reverted bymake update.
Non-blocking notes
- No test for migration 54 (a destructive operation across all tenant namespaces). A bats/e2e test is recommended.
- The
|| trueon the kubectl calls in the migration masks a possible finalizer-removal failure (low likelihood, since an apiserver failure would fail the whole run).
The fluxcd addon pin was a holdover from the #3150-era model where app charts rendered nested HelmReleases into the target cluster and needed an in-cluster helm-controller. Consumers now keep their releases on the management cluster and remote-apply via spec.kubeConfig, so the management Flux resolves charts and applies manifests — nothing inside the ComputePlane reconciles HelmReleases. This also decouples the module from the FluxCD addon removal (#3379). certManager and ingressNginx stay pinned: Certificate and Ingress objects land inside the cluster and need their controllers there. Assisted-By: Claude <[email protected]> Signed-off-by: Andrei Kvapil <[email protected]>
The fluxcd addon pin was a holdover from the #3150-era model where app charts rendered nested HelmReleases into the target cluster and needed an in-cluster helm-controller. Consumers now keep their releases on the management cluster and remote-apply via spec.kubeConfig, so the management Flux resolves charts and applies manifests — nothing inside the ComputePlane reconciles HelmReleases. This also decouples the module from the FluxCD addon removal (#3379). certManager and ingressNginx stay pinned: Certificate and Ingress objects land inside the cluster and need their controllers there. Assisted-By: Claude <[email protected]> Signed-off-by: Andrei Kvapil <[email protected]>
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
NOT LGTM. The removal itself is clean and the ordering is right, but migration 54 swallows the one failure it exists to prevent, and it ships without the test the other migrations in this tree have.
Blockers
Migration 54 deletes the HelmRelease even when the finalizer patch failed
The migration's whole point is stated in its own header: drop the Flux finalizer so the delete does not trigger a Helm uninstall inside the tenant. Both steps carry || true, and the delete is unconditional, so a finalizer patch that fails for any reason (webhook, RBAC, conflict, transient apiserver error) is followed by a delete that sets deletionTimestamp on an object that still has the finalizer. helm-controller then does exactly the uninstall the migration was written to avoid, and the tenant loses its Flux.
The suspend patch is not a second line of defence here. It is the finalizer, and only the finalizer, that keeps the delete inert.
Same shape one level up. hrs=$(kubectl get ... | jq ... || true) with set -eu and no pipefail means a failed kubectl get produces an empty list, the loop runs zero times, and stamp_cozystack_version 55 runs anyway. Migrations never re-run, so those tenants keep their finalizers, and the next reconcile of the addon-less Kubernetes chart prunes the HelmReleases through the normal path with the finalizer intact. Same uninstall, just later and with nothing in the logs pointing at it. An earlier review read this as low risk because an apiserver failure would fail the whole run. Without pipefail, and with || true on the assignment, it does not.
Every migration from 42 through 53 uses set -euo pipefail, and none of them uses || true except where the swallowed case is named in a comment. 54 is the only one on set -eu.
Verify the finalizer is gone before deleting, and let the migration fail if it is not. A migration that stops is recoverable; one that stamps 55 on a half-done fleet is not.
No test, and this tree tests its migrations
hack/migration-50-etcd-adopt.bats, hack/migration-seaweedfs-db-adopt.bats, hack/monitoring-pvc-backfill-migration.bats and hack/kubernetes-md0-migration.bats all drive the real migration scripts against a fake kubectl inside the migrations image's own base. The seaweedfs one pins fail-closed behaviour as an explicit property, with the reasoning spelled out in its header: a swallowed error permanently leaves at-risk tenants exposed, because migrations never re-run. That is the property 54 gets wrong, and the harness for testing it already exists.
Two cases are enough: the ordering (suspend, then finalizer, then delete, with the delete gated on the finalizer being gone) and the selection by chartRef.name across namespaces.
Non-blocking
.github/CODEOWNERS:78-79 still owns /packages/system/fluxcd/ and /packages/system/fluxcd-operator/, both deleted here.
The PR body says grep -riE 'affero|AGPL' packages/ returns nothing after the change. On the branch it returns two hits in packages/system/monitoring — comments explaining why Grafana is not rebuilt. No AGPL payload, but the claim as written does not hold.
A tenant that had the addon keeps a Flux the platform no longer manages and no longer exposes a toggle for. enabled: false used to uninstall it. Now nothing does, and neither the release note nor any doc says how an operator removes it by hand. One line in the release note would cover it.
d34670a to
e8aa0fa
Compare
|
Thanks — both blockers addressed. B1 (migration 54 deleted even when the finalizer patch failed): rewrote it fail-closed — B2 (no test): added Non-blocking: dropped the removed fluxcd entries from CODEOWNERS; corrected the AGPL-grep claim in the description (the two matches are monitoring comments, no payload); added a release-note line on removing a leftover tenant Flux by hand. Rebased on main. |
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
NOT LGTM. Two of the three round-1 items are properly closed, but the fail-closed rewrite has one || true left in it, on exactly the line the whole revision exists to protect.
Business context: Removes the last AGPL-3.0 vendored content (the default-off tenant FluxCD addon) as a breaking change for CNCF governance; tenants that had it enabled keep their running Flux via an orphaning migration.
Blockers
The verification read is fail-open
The gate added in this revision reads the finalizer back with 2>/dev/null || true (packages/core/platform/images/migrations/migrations/54:63-64). A read that fails yields an empty remaining, and empty passes the gate: delete and stamp proceed on an object whose state was never checked. Reproduced against the real script inside the pinned alpine base with a verify read that exits 1: exit code 0, the delete ran, version stamped to 55. The header says "no || true" and "the delete is gated on the finalizer actually being gone"; line 64 disagrees with both.
On blast radius, to be fair: helm-controller v1.5.0's reconcileDelete skips the uninstall for suspended objects and drops the finalizer itself, so on the shipped controller even the worst case does not uninstall the tenant's Flux. But that fence lives in the controller, not in this script, and the header sells the finalizer gate as the guarantee.
--ignore-not-found closes it in one line: a concurrently-deleted object still reads as empty with exit 0, a real API error aborts under set -e.
remaining=$(kubectl -n "$ns" get helmrelease "$name" --ignore-not-found \
-o jsonpath='{.metadata.finalizers}')While in the file: the header's claim that removing the finalizer is the ONLY thing keeping the delete inert is not accurate for this controller version, suspend alone protects too. Worth rewording so the next reader does not re-derive it.
The failure knobs exist and nothing uses them
The fake (hack/testdata/migration-54-fluxcd/kubectl:13-15) plumbs FAKE_LIST_FAIL, FAKE_PATCH_FAIL and FAKE_DELETE_FAIL, and its own header says a fake that can only succeed cannot test fail-closed. Neither test uses any of them, and there is no knob for the verify read failing, which is how the blocker above stayed invisible. Two tests close this: a verify-read-fail knob pinning the fix above (read fails, migration aborts before delete and before stamping), and a FAKE_LIST_FAIL case (scan fails, abort, no stamp); that one also pins the busybox-ash pipefail behavior the bats header calls load-bearing. The seaweedfs sibling this file imitates pins exactly these shapes.
The e2e harness still submits the removed addon
hack/e2e-chainsaw/_lib/run-kubernetes.sh:290-292 still puts fluxcd: {enabled: false, valuesOverride: {}} into the Kubernetes CR it applies. It no-ops silently since the schema is not strict, but it references a field this PR removes. Delete the three lines.
Non-blocking
The patch, get and delete calls use bare helmrelease while the fleet scan uses the fully-qualified helmreleases.helm.toolkit.fluxcd.io. Qualifying all of them keeps the migration immune to short-name collisions.
Everything else from round 1 checks out. The ordering holds: the migration is a pre-upgrade hook and the manifests that update the tenant-consumed artifacts apply only after hooks succeed, so a failed migration blocks the upgrade with the old chart still in force. The chartRef selection matches the removed template literally and that form shipped unchanged since before v1.0.0. The metadata-only finalizer strip triggers no reconcile on this controller (the watch predicate ignores it), so there is no re-add race. Leftover addons.fluxcd in stored tenant values is inert. CODEOWNERS is fixed, the AGPL grep claim in the body now holds, and the release note documents manual removal.
| # still carries it, and deleting now would set a deletionTimestamp that makes | ||
| # helm-controller run the very uninstall this migration exists to avoid. | ||
| remaining=$(kubectl -n "$ns" get helmrelease "$name" \ | ||
| -o jsonpath='{.metadata.finalizers}' 2>/dev/null || true) |
There was a problem hiding this comment.
A read that fails yields an empty remaining and empty passes the gate, so the delete and the stamp proceed unverified. --ignore-not-found instead of 2>/dev/null || true keeps the concurrently-deleted case working while a real API error aborts under set -e.
| # rows with any other chartRef must be listed but ignored. | ||
| # FAKE_LIST_FAIL non-empty => the -A fleet scan exits 1 with this stderr | ||
| # FAKE_PATCH_FAIL non-empty => every `patch` exits 1 with this stderr | ||
| # FAKE_DELETE_FAIL non-empty => every `delete` exits 1 with this stderr |
There was a problem hiding this comment.
These three knobs are unused by the tests, and there is no knob for the verify read failing. A verify-read-fail case pins the fail-open fix; a FAKE_LIST_FAIL case pins the pipefail behavior the bats header calls load-bearing.
875f8ef to
4f91ced
Compare
|
Round 2 addressed. Fail-open verify read (blocker): the finalizer verify now uses Unused failure knobs (blocker): added a Removed addon in the harness (blocker): dropped the Non-blocking: qualified all the calls. Rebased on main. |
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
NOT LGTM. The migration is now right; the tests that pin it are not. Every negated assertion in the new bats file is a no-op, so the half of the fail-closed contract that says "and it must not have deleted or stamped" is not actually enforced.
Blockers
The ! assertions never fail the test
cozytest.sh runs each test body under set -e and injects return 0 at the closing brace, and POSIX set -e ignores failures of commands negated with !. So ! grep -qE -- "^DELETE " "$FAKE_CMDLOG" does nothing when the DELETE line IS there: grep succeeds, the negation yields rc 1, set -e skips it by design, and the body falls through to the injected return 0. Verified on the harness itself: a scratch test whose log contains a DELETE line and whose body is exactly that negation reports Test OK.
Concretely: the finalizer-sticks and verify-read-fail tests enforce only [ "$rc" -ne 0 ], so a regression that deletes or stamps before aborting still passes. The list-fail test's "nothing touched, nothing stamped" clauses are all vacuous, leaving the rc check as its only real assertion. Test 1's ! grep some-other-app is backstopped by the exact -eq 4 counts, so selection survives. The comments say "and it deleted nothing and stamped nothing"; the checks behind those words do not exist.
The fix is mechanical, per line:
if grep -qE -- "^DELETE " "$FAKE_CMDLOG"; then echo "unexpected DELETE" >&2; return 1; fiAn explicit return 1 propagates fine; only fall-through hits the injected return 0. For the record, the same ! pattern predates this PR in the seaweedfs and etcd adopt suites; that is tracked separately and is not this PR's to fix.
Round provenance in committed text
The comment above the verify-read-fail test says "Round 2 of this migration replaced 2>/dev/null || true on that read with --ignore-not-found", and the last commit's message says "pinning the round-2 fix". A reader of the tree or git log has no round 2. Describe the property instead: the read uses --ignore-not-found rather than 2>/dev/null || true so a failed read aborts instead of reading as finalizer-gone. Same reword for the commit message.
Non-blocking
The testdata realignment sits in the last commit, so at the previous commit the suite is red (its own message says the verify read matched the fleet-scan branch). This repo merges with merge commits, so that intermediate lands in history and breaks bisect over the range; you are rewriting these commits anyway, fold it in.
What held up: the fail-open verify read is properly closed (--ignore-not-found, no || true left in code, header now matches the code); all kubectl calls are fully qualified; the e2e harness block is gone; the fake's matching order is right (the verify read is matched before the fleet scan by the jsonpath token) and the verify-read-fail pin itself is non-vacuous, since reintroducing || true makes the enforced [ "$rc" -ne 0 ] fail; all four tests are green on a linux docker host. A stale addons.fluxcd left in a parent's values passes schema validation (the schema sets no additionalProperties: false), so no parent-values cleanup step is needed in the migration. The red "Unit & controller tests" on this PR is inherited: main's own run-kubernetes-talos-diagnostics_test.bats is missing from the frozen EXIT-trap list in hack/cozyreport.bats, and both files are byte-identical between this branch and main. Not this diff's doing, but it needs a one-line fix on main before either of the open migration PRs can merge green.
| grep -qE -- "^FINALIZER-PATCH tenant-a kubevirt-kubernetes-fluxcd$" "$FAKE_CMDLOG" | ||
| grep -qF -- "refusing to delete" "$WORK/out" | ||
| # ...and it deleted nothing and stamped nothing. | ||
| ! grep -qE -- "^DELETE " "$FAKE_CMDLOG" |
There was a problem hiding this comment.
This negation never fails the test: under set -e a !-negated command's failure is ignored by POSIX rule, and cozytest.sh injects return 0 at the closing brace, so when a DELETE line IS present the body still falls through green. Verified with a scratch test on the harness. if grep -qE -- "^DELETE " "$FAKE_CMDLOG"; then echo "unexpected DELETE" >&2; return 1; fi enforces it; same for every ! grep in this file.
| rm -rf "$WORK" | ||
| } | ||
|
|
||
| # The verify READ that gates the delete errors out (RBAC, conflict, a transient |
There was a problem hiding this comment.
"Round 2 of this migration replaced..." is review-process context a reader of this file cannot resolve. Describe the property instead: the read uses --ignore-not-found rather than 2>/dev/null || true so a failed read aborts instead of reading as finalizer-gone.
231c9d6 to
130701b
Compare
|
Round 3 addressed. No-op Round provenance (blocker): removed the "Round 2 …" comment — it now describes the property (the verify read uses Fold (NB): rebased on main and reconstructed into two clean commits — the addon removal, then migration 54 with its bats test + fake kubectl together — so the fake realignment is never in a commit after the migration, no red intermediate over the range. The inherited "Unit & controller tests" red is resolved by the rebase: main removed the talos-diagnostics EXIT traps (6cfeadd / 575b41e), so there is nothing left to record in a frozen list. Rebased on main. |
82add2b to
5103215
Compare
2c2328e to
8126088
Compare
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM. Both blockers are gone: every negation is now if grep ...; then ...; return 1; fi, and the round wording is out of both the test comment and the commit message. The testdata realignment is folded in, so the two commits stand on their own.
I checked the load-bearing parts of the migration rather than just the wording. The chartRef selector matches the two names the removed template actually emitted, the migration hook is pre-upgrade,pre-install so the finalizer drop really does precede any pruning, targetVersion: 55 lines up with stamp_cozystack_version 55, and the new bats file is picked up by the hack/*.bats wildcard in bats-unit-tests. Nothing named addons.fluxcd survives outside the changelogs. Migration 53 also bumped targetVersion without touching the migrations image digest, so leaving that pin alone matches precedent.
E2E is red on the install flake, not on this diff: tenant-root/monitoring and seaweedfs time out behind vlstorage-db-vlstorage-generic-0 failing autoplace with "Not enough available nodes", with a lineage.cozystack.io webhook deadline alongside. The API owner gate is the other red check, since api/apps/v1alpha1/kubernetes loses a field. The two downstream items in the description need issues, otherwise they are a paragraph in a merged PR.
The optional `addons.fluxcd` toggle of the Kubernetes app deployed a Flux Operator + FluxInstance into a tenant cluster. It was served by two vendored charts, packages/system/fluxcd-operator and packages/system/ fluxcd, which were the last AGPL-3.0 content in this Apache-2.0 repository and a governance blocker for CNCF Incubation. The management cluster does not use these charts (it runs classic Flux via internal/fluxinstall; migration 21 already removed the operator from it), and the addon defaulted to disabled. Rather than maintain an AGPL payload for an unused feature, drop it entirely: - delete both system packages and their platform source entries - remove the addons.fluxcd HelmReleases, values and generated schema from the Kubernetes app - drop the addons.fluxcd block still submitted by the e2e harness - drop the removed fluxcd package entries from CODEOWNERS BREAKING CHANGE: the addons.fluxcd option of the Kubernetes app is removed. Tenant clusters that had it enabled keep their running Flux release; see the companion migration for the upgrade path. Assisted-By: Claude <[email protected]> Signed-off-by: Andrei Kvapil <[email protected]>
Add migration 54 for the removed addons.fluxcd feature. For every tenant that had it enabled, the FluxCD HelmReleases in the management cluster are suspended, stripped of their Flux finalizer and deleted, so the delete does not trigger a Helm uninstall inside the tenant cluster. The Flux release already running in the tenant keeps running untouched. Runs as a pre-upgrade hook, before the addon-less Kubernetes chart reaches the tenant HelmReleases. Mirrors the orphan idiom of migration 29. Bumps migrations.targetVersion to 55. Fail closed. Dropping the finalizer is the only thing that keeps the delete inert, and migrations never re-run, so the script runs under `set -euo pipefail` with no `|| true` on the critical path and gates the delete on the finalizer actually being gone. The verify read uses `--ignore-not-found` rather than `2>/dev/null || true`, so a failed read aborts instead of reading as an empty finalizer list that passes the gate on an object whose state was never checked. A migration that stops is recoverable; one that half-orphans a fleet is not. Cover it with a docker + fake-kubectl unit test running the real script by path inside the migrations image's own alpine base (busybox ash, jq baked on), so it exercises the shipped interpreter and load-bearing pipefail. The suite pins ordering and selection (only the addon HelmReleases are touched, each suspend -> finalizer-drop -> delete in order) and the fail-closed paths (a surviving finalizer, a failing verify read, and a failing fleet scan each abort before deleting and before stamping). Assisted-By: Claude <[email protected]> Signed-off-by: Andrei Kvapil <[email protected]>
8126088 to
851a86d
Compare
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM. Rebased this onto main myself after #3406 landed, since both PRs claimed migration 54.
What the rebase changed, nothing else: the migration file is renumbered 54 to 55, its header and stamp_cozystack_version now read 56, targetVersion goes 55 to 56, and hack/migration-54-fluxcd-orphan.bats plus its testdata directory are renamed to 55 with the numbers inside them updated. The redis migration that came in with #3406 stays at 54, untouched.
Worth knowing for the next collision: migrations/54 conflicted add/add and was loud, but values.yaml merged clean on its own because both branches wrote the same 55. Resolving only what git points at would have left targetVersion: 55 next to a migration 55 that stamps 56, and the runner loops seq 54 54, so the migration would never run. make migrations-target-check catches exactly that; it reports targetVersion=56 >= 56 (latest migration 55) now, and the platform suite is 138/138.
## What this PR does The Kubernetes app's optional `addons.fluxcd` toggle deployed a Flux Operator + FluxInstance into tenant clusters, served by two vendored Helm charts (`packages/system/fluxcd-operator`, `packages/system/fluxcd`). Those charts are **AGPL-3.0** — the last AGPL content in this Apache-2.0 repository and a governance blocker for CNCF Incubation. The management cluster does not depend on them: it runs classic Flux via `internal/fluxinstall`, and migration 21 already removed the Flux Operator from it. The addon defaulted to `enabled: false`. Rather than maintain an AGPL payload for an unused feature, this PR removes it entirely as a breaking change: - Delete both system packages and their `platform` source entries. - Remove the `addons.fluxcd` HelmReleases, values, Go types and generated schema/README from the Kubernetes app. - Add **migration 54**: for any tenant that had the addon enabled, the FluxCD HelmReleases in the management cluster are **suspended, stripped of their Flux finalizer and deleted**, so the delete does not trigger a Helm uninstall inside the tenant cluster. The Flux release already running inside the tenant keeps running untouched (mirrors the orphan idiom of migration 29). It runs as a `pre-upgrade` hook, before the addon-less Kubernetes chart reaches the tenant HelmReleases. After the change the only `affero`/`AGPL` matches under `packages/` are comments in `packages/system/monitoring` explaining why Grafana is not rebuilt — no AGPL payload ships. **Verification:** the Kubernetes app helm-unittests (181) and platform tests (88) pass; `make generate` was run with the CI-pinned `cozyvalues-gen v1.6.0` (schema, README, Go types, CRD); `migrations-target-check` and the `cozystack-version-stamp` bats pass; the `api/apps/v1alpha1` module builds. ### Downstream repositories This removes a user-facing addon and drops the `addons.fluxcd` field from the Kubernetes app's `values.schema.json`. Boxes are left unticked pending follow-ups (not opened from this PR): - **terraform-provider-cozystack** — models the Kubernetes app schema; the removed `addons.fluxcd` field should be checked/dropped there. - **website** — any hand-written mention of the FluxCD addon in the tenant Kubernetes docs should be removed (the managed-app reference regenerates from the README). ### Release note ```release-note feat(kubernetes)!: remove the FluxCD addon (`addons.fluxcd`). It relied on AGPL-3.0 vendored charts and was disabled by default; tenant clusters that had enabled it keep their running Flux release via an automatic upgrade migration. To remove that orphaned Flux from such a tenant, uninstall its `flux-operator`/`flux-instance` Helm releases inside the tenant cluster by hand. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Breaking Changes** * Removed FluxCD addon support from tenant cluster configuration, including the Kubernetes tenant CRD schema, chart values schema, defaults, and published “Cluster Addons” parameters. * FluxCD-related Helm manifests are no longer rendered as part of the Kubernetes package. * FluxCD configuration is no longer accepted for new or updated tenant clusters. * **Migration** * During upgrade, existing FluxCD Helm releases are cleaned up automatically by suspending them, clearing finalizers, and deleting the management-side releases. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
What this PR does
The Kubernetes app's optional
addons.fluxcdtoggle deployed a Flux Operator + FluxInstance into tenant clusters, served by two vendored Helm charts (packages/system/fluxcd-operator,packages/system/fluxcd). Those charts are AGPL-3.0 — the last AGPL content in this Apache-2.0 repository and a governance blocker for CNCF Incubation.The management cluster does not depend on them: it runs classic Flux via
internal/fluxinstall, and migration 21 already removed the Flux Operator from it. The addon defaulted toenabled: false. Rather than maintain an AGPL payload for an unused feature, this PR removes it entirely as a breaking change:platformsource entries.addons.fluxcdHelmReleases, values, Go types and generated schema/README from the Kubernetes app.pre-upgradehook, before the addon-less Kubernetes chart reaches the tenant HelmReleases.After the change the only
affero/AGPLmatches underpackages/are comments inpackages/system/monitoringexplaining why Grafana is not rebuilt — no AGPL payload ships.Verification: the Kubernetes app helm-unittests (181) and platform tests (88) pass;
make generatewas run with the CI-pinnedcozyvalues-gen v1.6.0(schema, README, Go types, CRD);migrations-target-checkand thecozystack-version-stampbats pass; theapi/apps/v1alpha1module builds.Downstream repositories
This removes a user-facing addon and drops the
addons.fluxcdfield from the Kubernetes app'svalues.schema.json. Boxes are left unticked pending follow-ups (not opened from this PR):addons.fluxcdfield should be checked/dropped there.Release note
Summary by CodeRabbit
Breaking Changes
Migration