fix(fluxcd): omit empty distribution.artifact in FluxInstance for guest clusters - #3284
Conversation
📝 WalkthroughWalkthroughThe FluxCD template now conditionally renders ChangesFluxCD artifact rendering
Estimated code review effort: 1 (Trivial) | ~5 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 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 |
cc3324b to
8a5ac8c
Compare
2dfaa08 to
9bd727e
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 addresses a deployment failure in guest Kubernetes clusters where the fluxcd addon would remain in an unready state. By conditionally omitting the distribution artifact field, the operator is now able to correctly fall back to its embedded manifests, satisfying CRD validation requirements while preserving air-gapped default behavior. 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 updates the packages/system/fluxcd/Makefile to apply a patch (patches/guard-distribution-artifact.diff) during the update target. Feedback suggests removing the --no-backup-if-mismatch option from the patch command, as it is a GNU extension that is not supported by the default BSD patch utility on macOS, which would cause the build to fail on that platform.
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.
| update: | ||
| rm -rf charts | ||
| helm pull oci://ghcr.io/controlplaneio-fluxcd/charts/flux-instance --untar --untardir charts | ||
| patch --no-backup-if-mismatch -p1 < patches/guard-distribution-artifact.diff |
There was a problem hiding this comment.
The --no-backup-if-mismatch option is a GNU patch extension and is not supported by the default BSD patch utility on macOS. This will cause make update to fail on macOS with patch: unrecognized option '--no-backup-if-mismatch'.
To ensure portability across development environments and to align with the convention used in other packages (like fluxcd-operator), we should omit this option and use standard patch -p1.
patch -p1 < patches/guard-distribution-artifact.diff
There was a problem hiding this comment.
Keeping --no-backup-if-mismatch here: it is the convention across all 12 vendored-patch Makefiles under packages/system/*, including the sibling fluxcd-operator (patch --no-backup-if-mismatch -p1 < patches/*.diff). Dropping it only from this package would make it the sole outlier, and make update in this repo runs under GNU patch. A repo-wide move to BSD-portable flags would be better as a separate PR.
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM — the diagnosis holds under checking and the fix does what it claims.
Business context: guest Kubernetes clusters with the fluxcd addon enabled never reach Ready, because the vendored flux-instance chart renders spec.distribution.artifact unconditionally and the umbrella's empty default turns into YAML null, which the CRD rejects (#3283).
I verified each load-bearing claim rather than taking the PR body's word for it:
- The render. Templating
packages/system/fluxcdwith default values onmainemits a bareartifact:(YAMLnull); on this branch the key is absent entirely. The guard does exactly what it says. - The CRD. flux-operator v0.50.0 types
distribution.artifactastype: stringwithpattern: ^oci://.*$and nonullable, anddistribution.requiredis[registry, version]. Sonullis genuinely rejected and omitting the key is genuinely valid — both halves of the diagnosis are sound. - The blast radius.
packages/system/fluxcdis referenced only frompackages/core/platform/sources/kubernetes-application.yaml(askubernetes-fluxcd) and appears in no management-cluster bundle. The management cluster never renders this chart, so it really is unaffected — which is the obvious "then why isn't the management cluster broken too?" objection, and it survives. - The patch mechanics. The diff applies at
-p1from the package directory, both the vendored template and the patch file are committed, and theupdatetarget matches the siblingfluxcd-operatorpackage's convention verbatim.
Non-blocking follow-ups
-
The root cause is upstream, and nothing here points at it.
controlplaneio-fluxcd/charts/flux-instancerendersartifact:unconditionally while guarding every sibling optional field (artifactPullSecret,imagePullSecret,variant) — that is an upstream template bug, and there is no way to express "no artifact" through the chart as shipped. Carrying it as a local patch is the right immediate move, since guest clusters are hard-broken today. But no upstream issue or PR is referenced anywhere in this change, which makes the patch permanent by default. Please file it upstream and link it from the patch file or the Makefile, so the next person to bump this chart knows when the hunk can be dropped rather than re-resolved. -
Nothing pins the rendered shape, which is the entire content of this fix.
packages/system/fluxcdhas notests/directory and notest:target, so there is zero automated coverage here — and the PR itself notes the affected path is off by default and unexercised by CI, while E2E did not run at all on this fork PR. Two helm-unittest assertions cover it:notExists: spec.distribution.artifactunder default values, andequalwheninstance.distribution.artifactis set. That also guards the patch: if a futuremake updatesilently loses the hunk, a test catches it, whereas today nothing would. -
helm pullin theupdatetarget pins no--version, somake updatealways fetches the latestflux-instance. Pre-existing, but it enlarges the new patch's exposure — a hunk against an unpinned moving target. Worth pinning while this Makefile is open.
On CI: the red Build Talos is a fork-PR artifact, not a code failure. The image built fine; only the registry push was denied (denied: Anonymous users are only allowed read access on public repos), because fork PRs don't receive the registry credentials. It does not gate this review.
| update: | ||
| rm -rf charts | ||
| helm pull oci://ghcr.io/controlplaneio-fluxcd/charts/flux-instance --untar --untardir charts | ||
| patch --no-backup-if-mismatch -p1 < patches/guard-distribution-artifact.diff |
There was a problem hiding this comment.
Two things worth doing while this line is being added.
Point the patch at its upstream. The bug being patched is upstream's: flux-instance's instance.yaml renders artifact: unconditionally while guarding artifactPullSecret, imagePullSecret and variant, so there is no way to express "no artifact" through the chart. Carrying the fix locally is right for now — guest clusters are broken today — but with no upstream issue or PR referenced anywhere, this patch is permanent by default. A link in the patch file or here tells whoever next bumps the chart whether to drop the hunk or re-resolve it.
helm pull above pins no --version, so make update always fetches the latest chart. That was already true, but a hunk against an unpinned moving target is a more fragile thing than a hunk against a pinned one. Worth pinning the version while you are here.
There was a problem hiding this comment.
Thanks for the check. Addressed both:
- Pinned
helm pull --version 0.50.0(matches the vendoredChart.yaml), so the hunk targets a stable chart version instead of latest. - Added an upstream-reference header to the patch (chart source + "drop this hunk once upstream guards the field").
Not filed upstream yet — can open a one-line guard PR against controlplaneio-fluxcd/charts to retire this patch on a future bump.
There was a problem hiding this comment.
Verified both: the pin matches the vendored Chart.yaml (0.50.0), and the header explains the bug well.
On --no-backup-if-mismatch — agreed, keep it. It is used in 13 of the vendored-patch Makefiles, including the sibling fluxcd-operator, so dropping it only here would make this package the sole outlier for no gain. A BSD-portable sweep is worth doing, but as its own PR across all of them.
Please do open the upstream guard PR. Right now the header says "not yet reported upstream", which makes the patch permanent by default: nothing tells a future bump that the hunk can go away, so it will be carried forever by inertia. A one-line guard against controlplaneio-fluxcd/charts, with its link in the header, turns this into a temporary carry with a retire condition someone can actually check. That is the shape we want for every vendored patch.
The flux-instance template rendered spec.distribution.artifact unconditionally, unlike the sibling optional fields (artifactPullSecret, imagePullSecret, variant). The umbrella default is empty to use the operator's embedded manifests (the air-gapped default from cozystack#964), but an empty value renders as YAML null. In the flux-operator v0.50.0 CRD the field is optional yet typed 'string' with pattern ^oci://.*$ and is not nullable, so the rendered null (like an empty string) fails schema validation and the API rejects the FluxInstance. Guest Kubernetes clusters with the fluxcd addon enabled could therefore never install it: the sub-release looped install/uninstall, the parent kubernetes release stayed InProgress, and the cluster never reached Ready. Guard the field so an empty value is omitted (the operator falls back to its embedded manifests, preserving the air-gapped default) while a non-empty artifact still renders. The template lives in the vendored subchart, so the change is carried as a patch applied by 'make update'. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com>
9bd727e to
da47b70
Compare
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM — re-reviewing after the force-push (which dismissed my earlier approval). The delta addresses two of the three follow-ups, and I verified both actually work rather than taking the diff at face value.
What changed since my last pass (9bd727e5 → da47b70e): the helm pull in the update target is now pinned to --version 0.50.0, and the patch file gained a header explaining the upstream bug and stating the hunk should be dropped once upstream guards the field.
Both of those are exactly the class of change that can silently break make update, so I replayed the target end-to-end rather than eyeballing it:
patchstill applies. The new#-prefixed header sits ahead of thediff --gitline;patchskips leading garbage, so it applies cleanly — exit 0, no.rej, no.orig.- The pin matches reality.
--version 0.50.0is the same version already vendored undercharts/flux-instance(Chart.yaml: version: 0.50.0), so the pin freezes the current state rather than silently moving the chart on the next update. - No vendored drift. I regenerated
charts/flux-instance/templates/instance.yamlfrom a freshhelm pull 0.50.0+ the patch, and it is byte-identical to the file committed here. What is in the tree is whatmake updateproduces.
Everything I verified last time still holds: the render omits artifact under default values, the CRD types it as a non-nullable string and does not list it in distribution.required, and the chart ships only to guest clusters (absent from every management-cluster bundle).
Remaining follow-up (non-blocking, unchanged)
Nothing pins the rendered shape. packages/system/fluxcd still has no tests/ directory and no test: target, so the fix — which is a render-shape change — has zero automated coverage, on a path the PR itself notes is off by default and unexercised by CI. Two helm-unittest assertions cover it: notExists: spec.distribution.artifact under default values, and equal when instance.distribution.artifact is set. That would also guard the patch itself: if a future make update loses the hunk, a test catches it; today nothing would.
The patch header is a good compromise on the upstream point — it tells the next maintainer what the hunk is for and when to drop it. Filing it upstream is still worth doing so the hunk can actually go away.
On CI: Build Talos remains red for the fork-PR reason (image builds, registry push is denied without secrets), not for anything in this diff.
5d881f1
into
cozystack:main
|
Successfully created backport PR for |
What this PR does
Guest Kubernetes clusters (
apps/kubernetes) with thefluxcdaddon enabled never becameReady. Theflux-instancetemplate rendersspec.distribution.artifactunconditionally,unlike the sibling optional fields (
artifactPullSecret,imagePullSecret,variant, whichare all
{{- if }}-guarded). The umbrella default is empty (artifact: "") so the operatoruses its embedded manifests, which is the intended air-gapped default (#964, and the upstream
recommendation). But an empty value renders as YAML
null, and the flux-operator v0.50.0 CRDtypes
distribution.artifactas a non-nullablestring(pattern^oci://.*$), so the APIrejects the
FluxInstance.Result: the addon's HelmRelease looped install/uninstall (
remediation.retries: -1), theparent
kubernetesrelease stayedInProgress, and the cluster CR was stuckReady=Unknown.This guards the field so an empty value is omitted entirely. Behaviour:
default preserved, and the CRD accepts it (artifact is not in
distribution.required);valuesOverridestill applies.The template is a vendored subchart (
make updaterunshelm pull), so the change is carriedas
patches/guard-distribution-artifact.diffand applied by theupdatetarget, matching theconvention of the sibling
fluxcd-operatorpackage (patch -p1 < patches/*.diff).Verified with
helm template packages/system/fluxcd:artifact:line (before this PR:artifact: null);--set flux-instance.instance.distribution.artifact=oci://example.com/x:1-> renders.The affected path is off by default (
addons.fluxcd.enabled: false) and not exercised by thein-repo CI values, which is why default e2e stayed green.
Closes #3283
Release note
Summary by CodeRabbit