Skip to content

ci(charts): add template-only helm test pipeline - #297

Open
lemaitre-aneo wants to merge 4 commits into
feat/revamp-helmfrom
fl/helm-tests
Open

ci(charts): add template-only helm test pipeline#297
lemaitre-aneo wants to merge 4 commits into
feat/revamp-helmfrom
fl/helm-tests

Conversation

@lemaitre-aneo

@lemaitre-aneo lemaitre-aneo commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Motivation

The Helm charts have no working verification in CI. The linter workflow invokes ct with --chart-dirs charts/armonik, but ct expects parent directories, so it finds no chart there: list-changed returns nothing, and both lint and install silently skip on every run. In practice no chart has ever been linted by this pipeline.

The runners have no Kubernetes cluster available, so chart verification has to be template-only: no kind, no ct install, no smoke deployment.

Description

Adds a four-layer, template-only test pipeline. Every layer is a script that runs identically locally and in CI.

1. Vendoring / dependency freshness (test/vendor.sh) adds the ten chart repositories and vendors each chart's dependencies from its committed Chart.lock, then checks the lock files are unchanged. Vendored .tgz archives are gitignored, so this also produces the artifact the other jobs consume.

2. Chart lint, repaired. The --chart-dirs bug is fixed by moving the configuration into ct.yaml, which now also wires up lintconf.yaml and chart-schema.yaml (both present in the repo but never referenced), disables check-version-increment (chart versions move in lockstep at 0.1.0), and carries the chart-repos list. The first real lint run surfaced pre-existing problems, fixed here: yamllint violations in four values files, and armonik-dependencies/values.yml being misnamed, which meant Helm never loaded it and helm-docs could not process the chart, leaving a README that was a stale copy of the ingress chart's.

3. Unit tests (test/unittest.sh, helm-unittest 1.1.2): suites under charts/<chart>/tests/ for activemq, control-plane, compute-plane, ingress and the umbrella, covering defaults, conditional paths, indexed auth/partition env generation, per-partition fan-out, TLS/mTLS, Gateway API, and the render-time guards. A separate never-deployed harness chart under test/harness/ pins the armonik-common library contract (21 tests): merge-engine semantics (null-is-absent, empty-string-is-absent, tpl-render-on-merge, concatList, overwrite), the conf generators, projected-volume dedup and its shared-path guards, and the nil-safe index helper. A library chart cannot be unit-tested directly, hence the consumer harness.

4. Render matrix + schema validation (test/matrix.sh): 19 render cases across defaults and conditional paths, plus 5 cases asserting that the render-time guards fail with the expected message. Every successful render is schema-validated with kubeconform (strict, CRD catalog) and screened for unrendered {{ leftovers.

No reference outputs or snapshots are committed anywhere: assertions target specific keys, so the suite does not need re-recording on every unrelated change.

Fixture convention. Values files that render successfully live in charts/<name>/ci/*-values.yaml, where ct picks them up automatically and lints the chart once per fixture, so one file serves both layers. Fixtures that must not be linted (expected-failure inputs, kube-version variants) live in test/fixtures/<chart>/.

Unrendered-template screening. A leftover {{ in rendered output usually means a tpl value that never got evaluated. Screening for it naively produces false positives, because upstream Grafana dashboards and Prometheus rules legitimately ship {{ }} that their own consumers evaluate later. Rather than exempting whole renders, test/check-unrendered.awk decides per document: documents originating from third-party subcharts are skipped by provenance, ExternalSecret documents may carry the operator's own {{ .KEY }} target-template references, and the ArmoniK-owned dashboard ConfigMaps may carry Grafana's {{label}} legend syntax. Anything else fails the case, so ArmoniK-owned output stays strictly checked, umbrella included.

Known-broken render paths keep their tests written but disabled, each skip naming the defect, so fixing the bug is a one-line enablement:

  • compute-plane with a PodDisruptionBudget enabled: pdb.yaml calls an undefined helper (armonik.compute.pdb.apiVersion; the library defines armonik.pdb.apiVersion);
  • umbrella with control-plane.enabled=false: NOTES.txt dereferences the control-plane values unconditionally;
  • ingress standalone defaults: the chart tpl-renders global.environment.{name,description} but ships no defaults for them, so a bare render nil-pointers. Its ci/default-values.yaml supplies the block until this is fixed.

Tests for in-flight PRs, also written and skipped, with each skip naming the PR that unblocks it (14 tests): #294 (operator split) gets the operators guard, the ESO gate moving to global.armonik.operators.externalSecrets, the partitions guard, KEDA gating via the operator flags, PodMonitor/ServiceMonitor gating on both planes, the cert-manager availability guards in ingress and activemq, and the relocated cert-manager wait job; #295 gets the two Grafana root_url guards. test/matrix.sh carries the matching ready-to-swap case lines in comments, and charts/armonik/ci/minimal-values.yaml already declares the operator flags (inert until #294 lands). This keeps the merge-day work mechanical.

Bugs found by the new checks and fixed here:

  • duplicate automountServiceAccountToken key in both plane init Job pod specs, rejected by strict schema validation (both occurrences set true, so behaviour is unchanged);
  • a single-brace {{namespace} typo in a dashboard legendFormat, which Grafana would have rendered as literal text.

CI. A new workflow runs the three script layers: a vendoring job that publishes the archives, feeding parallel unit-test and render-matrix jobs; tool versions and action SHAs are pinned. The linter workflow is repaired: config-driven ct, the kind and ct install steps removed since no cluster is available, and chart-testing-action moved to v2.8.0, whose bundled yamale is the first release that runs on Python 3.12+ (earlier versions crash in ast.Num against the Python that setup-python resolves today).

All test scripts are POSIX sh, verified under dash. .helmignore entries keep the new test material out of packaged chart archives, and the check-yaml pre-commit hook now also excludes the harness chart's templates, which are Helm templates rather than YAML.

charts/update-charts.sh is deliberately untouched here; its flag interface is #296.

Testing

The pipeline is the deliverable, so it was verified by construction and by deliberately breaking things.

Current state, locally and in CI: ct lint clean over all 10 charts; 69 unit tests passing with 17 skipped (3 broken paths, 14 awaiting #294/#295); 24 matrix cases passing with 4 skipped; kubeconform clean.

Each layer was checked to actually fail when it should. A wrong image tag turns the unit tests red; an invalid manifest field turns kubeconform red; a Chart.yaml/lock mismatch turns vendoring red; a weakened guard message turns the matrix guard case red; an injected {{ .Values.oops }} turns the unrendered screening red. The screening rules were additionally exercised against synthetic documents covering each accept/reject decision: a leak in an owned document, a leak inside an ExternalSecret, Grafana syntax outside a dashboard, third-party content, and an owned chart nested under a dependency.

That exercise also caught a bug in the screening itself: with pipefail set, grep -q closing the pipe early made the producer die on SIGPIPE and inverted the result on large renders, so a match read as no-match. Removing pipefail (the POSIX conversion) fixed it, and the umbrella typo above is what the now-working check found first.

Impact

  • Charts get verified on every PR for the first time; expect two workflows as checks.
  • No behaviour change to the charts beyond the two fixes listed above.
  • Running the suite locally needs the helm-unittest plugin and kubeconform; test/README.md documents the entrypoints and versions.
  • armonik-dependencies/Chart.lock is refreshed to current upstream versions.

Additional Information

Ordering with the in-flight PRs. #296 should merge first: until then the vendoring step runs the current update-charts.sh, which re-resolves and rewrites Chart.lock instead of reproducing it, so the freshness check degrades into "update and diff" and will go red whenever an upstream chart publishes a new version inside the pinned x.x range. Once #296 provides the build mode, that step becomes a true freshness check. #294 and #295 then unblock the skipped tests; both touch files this branch also touches, so the merge is not conflict-free either way.

Two observations from writing the fixtures, neither addressed here: gateway.gatewayClassName has no chart default and renders a schema-invalid null Gateway when unset (the fixture supplies one, and a render-time failure would fit the fail-fast convention better), and mtls.trustedCommonNames is currently consumed by no template.

kubeconform runs with -ignore-missing-schemas, so CRDs absent from the catalog pass silently; worth revisiting with a pinned schema directory if that proves too permissive.

Checklist

  • My code adheres to the coding and style guidelines of the project.
  • I have performed a self-review of my code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have made corresponding changes to the documentation.
  • I have thoroughly tested my modifications and added tests when necessary.
  • Tests pass locally and in the CI.
  • I have assessed the performance impact of my modifications.

@lemaitre-aneo
lemaitre-aneo force-pushed the fl/helm-tests branch 2 times, most recently from 638bb5b to da7a16e Compare August 3, 2026 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant