Skip to content

fix(platform): declare the injected _cluster values in system charts - #4102

Merged
Aleksei Sviridkin (lexfrei) merged 1 commit into
mainfrom
fix/system-charts-declare-injected-values
Sep 7, 2026
Merged

Aleksei Sviridkin (lexfrei) merged 1 commit into
mainfrom
fix/system-charts-declare-injected-values

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Ten charts under packages/system read _cluster, and bucket and monitoring also read _namespace. The platform injects both through the cozystack-values Secret, so installs never notice. Rendering a chart on its own does: helm template packages/system/kubevirt dies on index of untyped nil before it emits anything, and the _namespace readers die on nil pointer evaluating interface {}.host.

Each of the ten now declares the keys it reads, with an empty default. Injected values merge over chart defaults, so an installed release renders exactly what it rendered before. Checked per chart against a realistic injected _cluster/_namespace: six came out byte-identical, and the other four differ only in randAlphaNum secrets, which also differ between two renders of the same tree.

The declaration on its own is not worth much, so every chart also gets a suite that renders with nothing injected. The existing suites all set _cluster themselves and cannot see whether values.yaml still declares it. Drop a declaration and exactly that chart's suite goes red, with the error its comment quotes; I ran that for all twelve. None of the new suites carries a suite-level templates: list, on purpose: that list narrows what helm-unittest renders, and would hide the same breakage in a template added later.

kubevirt-cdi had no tests/ and no test: target, so it gets both. etcd-operator reads _cluster too and stays as it is, since its one call site already guards with | default dict. cozystack-basics already declared the key.

Closes #4030.

Not in this PR

No CI job runs helm lint or a standalone helm template, which is why none of this was red. A gate is worth proposing separately. Its value is narrow though: _cluster is always populated on a real install, and eight of these ten already had a suite rendering the affected template with _cluster supplied. The two with nothing were kubevirt, whose vm-exportproxy-* templates no test touched, and kubevirt-cdi, which had no tests at all.

helm lint still exits 1 on keycloak and monitoring with chart metadata is missing these dependencies: cozy-lib. Both carry a charts/cozy-lib symlink that Chart.yaml never declares. That one is not a two-chart item: 82 charts across packages/{system,apps,extra,core} fail lint the same way, which is invisible because no job runs helm lint. Separate defect, untouched here.

Fifteen charts outside packages/system break the same way: apps/{bucket,foundationdb,harbor,kubernetes,mongodb,nats,opensearch,postgres,vpn} and extra/{bootbox,etcd,gateway,info,ingress,seaweedfs}. cozy-lib's own helpers already guard every access with | default dict, so routing those reads through the library would close the class instead of patching fifteen more values files.

kubevirt's suite only pins that the routes stay absent with nothing injected. Nothing pins that they show up once _cluster selects the service.

Screenshots

No UI change: the diff is ten values.yaml files, ten helm-unittest suites, and one package Makefile.

Downstream repositories

Walked the trigger map in docs/agents/contributing.md file by file against this diff. No package is added, renamed or removed under packages/apps or packages/extra; packages/core/platform/values.yaml is untouched; no variant, bundle, component, release asset, namespace or ApplicationDefinition semantic changes; no values.schema.json and no version enum moves, so the provider's hand-written schemas and defaults are unaffected; nothing under hack/ moves and no existing make target changes behaviour. The one Makefile edit adds a test: target to packages/system/kubevirt-cdi, matching the target the other nine already have, so make generate and the ccp skills that drive it see nothing new.

Release note

fix(platform): declare the platform-injected `_cluster` and `_namespace` values in the ten system charts that read them, so each chart renders with `helm template` outside a cluster instead of failing on a nil map. No change to what an installed release renders.

Summary by CodeRabbit

  • Bug Fixes

    • Improved standalone Helm chart rendering when platform-provided values are unavailable.
    • Prevented rendering failures across system charts, including bucket, dashboard, Keycloak, monitoring, KubeVirt, and related components.
  • Tests

    • Added comprehensive standalone rendering coverage for system charts.
    • Added validation for expected ingress, gateway, TLS route, and resource output under different configuration scenarios.
    • Added a chart test command for KubeVirt CDI.

Ten system charts cannot be rendered on their own. They read the
platform-injected _cluster channel, and two of them (bucket,
monitoring) also read _namespace, without declaring either key in
values.yaml. On a cluster the platform supplies both through the
cozystack-values Secret, so installs are unaffected, but rendering a
chart by itself dies before a single template is emitted: on "index of
untyped nil" for a missing _cluster, and on "nil pointer evaluating
interface {}.host" for a missing _namespace.

Declare the keys with empty defaults. Helm merges chart defaults under
the supplied values, so an injected _cluster still wins and rendered
output does not change. Each of the ten also gets a suite that renders
with nothing injected; drop a declaration again and that chart's suite
goes red.

This makes `helm template` succeed for all ten. `helm lint` also
reports a missing cozy-lib dependency on keycloak and monitoring,
which is a separate pre-existing defect and survives this change.

etcd-operator reads _cluster too and stays undeclared here: its single
call site already guards with `| default dict`, so the chart renders
standalone as it is.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
@github-actions github-actions Bot added size/L This PR changes 100-499 lines, ignoring generated files area/platform Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) kind/bug Categorizes issue or PR as related to a bug labels Sep 6, 2026
@coderabbitai

coderabbitai Bot commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 97654b94-ac54-41c0-9487-cd4329801155

📥 Commits

Reviewing files that changed from the base of the PR and between cd675e1 and eef5fe6.

📒 Files selected for processing (21)
  • packages/system/bucket/tests/standalone_render_test.yaml
  • packages/system/bucket/values.yaml
  • packages/system/cert-manager-issuers/tests/standalone_render_test.yaml
  • packages/system/cert-manager-issuers/values.yaml
  • packages/system/cozystack-api/tests/standalone_render_test.yaml
  • packages/system/cozystack-api/values.yaml
  • packages/system/dashboard/tests/standalone_render_test.yaml
  • packages/system/dashboard/values.yaml
  • packages/system/keycloak-configure/tests/standalone_render_test.yaml
  • packages/system/keycloak-configure/values.yaml
  • packages/system/keycloak/tests/standalone_render_test.yaml
  • packages/system/keycloak/values.yaml
  • packages/system/kubevirt-cdi/Makefile
  • packages/system/kubevirt-cdi/tests/standalone_render_test.yaml
  • packages/system/kubevirt-cdi/values.yaml
  • packages/system/kubevirt/tests/standalone_render_test.yaml
  • packages/system/kubevirt/values.yaml
  • packages/system/linstor-gui/tests/standalone_render_test.yaml
  • packages/system/linstor-gui/values.yaml
  • packages/system/monitoring/tests/standalone_render_test.yaml
  • packages/system/monitoring/values.yaml

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The charts add empty defaults for platform-injected values and new standalone Helm render tests. The tests cover absent _cluster and _namespace values, rendered document counts, and KubeVirt CDI ingress or TLSRoute selection.

Changes

Standalone Helm rendering

Layer / File(s) Summary
Chart defaults and render tests
packages/system/{bucket,cert-manager-issuers,cozystack-api,dashboard,keycloak-configure,keycloak,kubevirt,linstor-gui,monitoring}/...
Charts declare empty _cluster or _namespace defaults. Helm-unittest suites verify rendering without platform-injected values.
KubeVirt CDI route selection
packages/system/kubevirt-cdi/...
The chart declares an empty _cluster default. Tests verify ingress and TLSRoute output for gateway-disabled and gateway-enabled configurations. The Makefile adds a helm unittest . target.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to eef5f

System charts can now render without platform-injected cluster or namespace values while retaining their injected-value configuration path. No current merge-blocking risk is identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary change: declaring injected _cluster values in system charts to fix standalone rendering.
Linked Issues check ✅ Passed The PR satisfies issue [#4030] by declaring _cluster: {} in kubevirt/values.yaml and adding standalone render tests that verify the VM export routes render zero documents without injected platform…
Out of Scope Changes check ✅ Passed The changes are within scope. They add injected-value defaults, standalone render tests, and one requested Makefile test target. No unrelated CI, dependency, or feature changes are included.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/system-charts-declare-injected-values

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict

LGTM with non-blocking notes

Reviewed at eef5fe690 against merge-base 86d62bbb2.

The mechanism holds up. I could not find a way for this to change what an installed release renders: across five configuration corners times ten charts, base and head produce identical manifests apart from randAlphaNum secrets, and those differ between two renders of the same tree anyway. Both notes below are about the standalone render this PR creates, not about anything that reaches a cluster.

Findings

  • [MINOR] packages/system/monitoring/values.yaml:4, the empty-map default renders %!s(<nil>) where a host belongs
  • [MINOR] packages/system/kubevirt/tests/standalone_render_test.yaml:16, the two asserts on vm-exportproxy cannot fail if those templates go permanently dead

Claim mismatches

[PARTIAL] "82 charts across packages/{system,apps,extra,core} fail lint the same way". I count 33, and the tier breakdown differs. find packages -maxdepth 4 -path "*/charts/cozy-lib" returns 35 charts, 33 of which never declare the dependency in Chart.yaml, and helm lint puts those at 22 in apps, 8 in extra, 2 in system, 1 in tests, none in core. 85 charts fail helm lint for one reason or another on helm v4.0.4, which is probably where 82 came from. Does not change the decision to defer it: 33 is still not a two-chart item.

Caveats

  • I did not replay an upgrade through helm-controller, and a green render does not prove one converges. The argument standing in for it: since no rendered field changes, there is no newly set or newly conditional field for server-side apply to conflict on, no immutable field to diverge, no admission-defaulted field to drift.
  • The new monitoring suite sets the release namespace to cozy-monitoring, while the chart's existing suites use tenant-root, which is where its HelmRelease actually gets created. No assert in the new suite depends on the namespace, so nothing is wrong today. A content assert added there later would be exercising a namespace the chart does not install into.
  • Versions: helm v4.0.4, helm-unittest v1.0.3, kubeconform v0.7.0. All local against the clone, no cluster contacted.

Recommended follow-ups

  • packages/system/kubevirt-cdi/Makefile's update: target still opens with rm -rf templates, and templates/ holds cdi-uploadproxy-ingress.yaml and cdi-uploadproxy-tlsroute.yaml next to the upstream cdi-cr.yaml, so a refresh deletes both cozystack-authored templates. The sibling kubevirt Makefile solved this with an idempotent re-apply plus per-marker sanity checks. Adding test: here at least turns that loss into a red suite instead of a silent one, but the destructive update: deserves its own change.
  • The body names the structural fix, routing these reads through cozy-lib whose helpers already guard with | default dict, then declares the keys per chart instead. Reasonable for ten charts. The fifteen remaining ones in apps/ and extra/ are where the per-chart shape stops paying, and I confirmed that list matches the one the body gives.

# Injected by the platform from the cozystack-values Secret. The empty
# defaults keep the chart renderable on its own, outside a cluster.
_cluster: {}
_namespace: {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MINOR] the empty-map default renders %!s(<nil>) where a host belongs

A bare {} makes .Values._namespace.host an untyped nil, and the call sites that build hostnames with printf "%s" write Go's own formatting error into the manifest rather than an empty string. Nine occurrences across three of the ten charts:

$ helm template monitoring packages/system/monitoring --namespace cozy-monitoring | grep -n '%!'
197:        - "alerta.%!s(<nil>)"
200:    - host: "alerta.%!s(<nil>)"
280:      root_url: "https://grafana.%!s(<nil>)"
337:        - host: "grafana.%!s(<nil>)"
349:        - "grafana.%!s(<nil>)"
$ helm template bucket packages/system/bucket --namespace cozy-bucket | grep -n '%!'
36:          value: "s3.%!s(<nil>)"
$ helm template keycloak packages/system/keycloak --namespace cozy-keycloak | grep -n '%!'
133:              value: https://keycloak.%!s(<nil>)
181:      - keycloak.%!s(<nil>)
184:  - host: keycloak.%!s(<nil>)

Inside bucket the two code paths disagree about what unset looks like: the Ingress rule comes out host: cozystack. because {{ $host }} interpolates nil as empty, while the Deployment env comes out s3.%!s(<nil>) because that one goes through printf. Nothing in the PR notices either: the new suites assert document counts only, and kubeconform -strict -ignore-missing-schemas reports every one of these renders valid, since a formatting error is still a legal string. So the follow-up helm template gate the PR proposes would go green on this too.

Seeding the one key each printf path reads fixes it. I ran _cluster: {root-host: ""} and _namespace: {host: ""} on the three charts in isolated checkouts: the artifact count drops to zero, every suite stays green (keycloak 6/6, monitoring 8/8, bucket 3/3), and the render under a realistic injected _cluster/_namespace stays byte-identical to this PR's, because an injected key still wins the merge.

# same breakage unseen.

tests:
- it: exposes no vm-exportproxy route when _cluster is absent

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MINOR] the two asserts on vm-exportproxy cannot fail if those templates go permanently dead

kubevirt-cdi got a positive counterpart in this PR and kubevirt did not, and kubevirt is the one chart where nothing else in the tree renders the templates in question (grep -rl vm-exportproxy packages/system/kubevirt/tests/ matches only the new suite). I forced both exposure gates to false so the templates emit nothing under any input, then ran each chart's whole suite:

$ review-helper mutate /tmp/pr-review-cozystack-cozystack-4102 --mutations gatemut.json
GAPS: 1   answered: 2 of 2
kubevirt: vm-exportproxy ingress can never render GAP: the suite stayed green without the fix
| Test Suites: 2 passed, 2 total
kubevirt-cdi: uploadproxy ingress can never render covered: the suite went red
| Test Suites: 1 failed, 0 passed, 1 total

The count: 0 asserts are not entirely inert: I renamed a template out from under one and helm-unittest failed with template "..." not exists or not selected in test suite, so a delete or rename is caught. What they cannot separate is "correctly absent because _cluster is empty" from "never renders again under any input". The fourteen lines that close it are already written next door in kubevirt-cdi/tests/standalone_render_test.yaml.

@lexfrei
Aleksei Sviridkin (lexfrei) merged commit d0fce92 into main Sep 7, 2026
22 checks passed
@lexfrei
Aleksei Sviridkin (lexfrei) deleted the fix/system-charts-declare-injected-values branch September 7, 2026 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/platform Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) kind/bug Categorizes issue or PR as related to a bug size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kubevirt: helm lint fails on the chart because _cluster is missing from its values

2 participants