Skip to content

ci(flux): every overlay is built, and the CRDs are checked against real schemas - #1323

Merged
ExtraToast merged 1 commit into
mainfrom
worktree-ci+manifests-schema-checked
Sep 18, 2026
Merged

ExtraToast merged 1 commit into
mainfrom
worktree-ci+manifests-schema-checked

Conversation

@ExtraToast

@ExtraToast ExtraToast commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Closes #1292. Part of #1289.

flux-kustomize-check built four overlays, named in a hardcoded list, while the cluster applies eight. apps/stateless, apps/data, apps/mail and apps/vso-secrets were never rendered, so a manifest in any of them could merge without CI having built it once. What it did build, it only built: rendering proves the YAML parses and says nothing about whether a CRD field exists.

What this achieves

Every overlay Flux applies is built, and the rendered objects are validated against the CRD schemas the charts install — Flagger, Traefik, VSO, cert-manager and Flux's own.

The overlay list is derived from the spec.path of every Flux Kustomization CR rather than written down beside it, so what CI builds and what the cluster applies cannot drift. A directory that builds but that no Kustomization CR applies is reported: it is either dead or about to be applied by nobody.

How

scripts/check-flux-manifests.sh, following check-migration-order.sh's example of putting the logic in a script rather than in the workflow, so it can be run locally. kubeconform -strict against the datreeio CRDs-catalog; -strict is what turns a misspelled key into a failed build rather than a field the cluster silently drops.

The trap worth knowing about: kubeconform skips a resource it has no schema for, and says nothing. A lost schema location or one -ignore flag too many yields a green run in which nothing was checked. Two things guard that. Kinds that must be checked are listed explicitly, and a skip among them is an error rather than a notice. And --self-test pushes a deliberately broken Canary through the same validator and fails if it passes — the fixture lives in the script rather than in the Flux tree, where an intentionally invalid Canary would read as a mistake.

The check also prints every kind it validated and every kind it skipped, so the coverage is visible in the log rather than assumed.

Not in scope

Schemas are fetched from raw.githubusercontent.com on each run and not cached, which adds a network dependency to every pull request. Worth a cache if it ever flakes; not worth the moving parts before then.

CustomResourceDefinition has no schema in the catalog, so the eleven in Flux's vendored gotk-components.yaml are reported as unchecked. That file is generated by flux bootstrap, not hand-edited, so it is not in the required-kind list.

Worth a reviewer's attention

  • The job keeps its id, so the Validate complete gate and any branch protection keyed to it are untouched. Only its display name changed, from Flux kustomize build to Flux manifests, since it now does more than build.
  • Required kinds absent from every overlay produce a notice, not a failure. On main that notice currently reads Canary — correct, because the Canaries are in feat(platform): the site's images name a version, and Flagger promotes the pair #1316 and not merged. Once that lands the notice goes away and Canary becomes a validated kind.
  • kubeconform is pinned to v0.8.0 and installed by curl | tar rather than an action, matching how the job already installs kubectl.

Verification

Run locally in both modes against the real tree. Eight overlays build, 22 kinds validate — including IngressRoute, Middleware, TLSStore, VaultStaticSecret, HelmRelease, Certificate and Kustomization.

Each acceptance criterion was demonstrated with a deliberate break, then reverted:

break result
a Kustomization CR pointing at a non-existent path exit 1, a Kustomization CR points at … which does not exist
entryPoints misspelled as entryPointz in the real api.yaml exit 1, additional properties 'entryPointz' not allowed
the --self-test Canary, run against the validator exit 1 as designed, so the self-test passes

Every schema the required-kind list names was confirmed to resolve in the catalog before relying on it.


Diff breakdown added removed, scaled to the largest row.

ci                                                +220    -12    2
  build & config     █████████████████████████░   +220    -12    2

──────────────────────────────────────────────────────────────────
total (hand-written)                              +220    -12  2 files

…al schemas

The job built four of the eight overlays, named in a list beside the eight
the cluster applies, so apps/stateless, apps/data, apps/mail and
apps/vso-secrets could merge without CI ever rendering them. The list is
now derived from the Flux Kustomization CRs, so what CI builds and what the
cluster applies cannot drift, and a directory that builds but that no CR
applies is reported rather than ignored.

Rendering only proved the YAML parsed. kubeconform now validates the output
against the CRD schemas the charts install — Flagger, Traefik, VSO,
cert-manager and Flux itself — with -strict, so a misspelled key fails the
build instead of becoming a field the cluster silently drops. kubeconform
skips a resource it has no schema for and says nothing, so kinds that must
be checked are listed and a skip among them is an error.

A --self-test mode runs a deliberately broken Canary through the same
validator and fails if it passes, because a validator that has quietly
stopped validating is green on everything.
@ExtraToast ExtraToast self-assigned this Sep 18, 2026
@ExtraToast
ExtraToast merged commit 28653ab into main Sep 18, 2026
33 checks passed
@ExtraToast
ExtraToast deleted the worktree-ci+manifests-schema-checked branch September 18, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Every cluster manifest is built and schema-checked before it merges

1 participant