feat(scripts): add reproducible Kyverno and CNPG demo clusters - #1402
Open
nadaverell wants to merge 5 commits into
Open
feat(scripts): add reproducible Kyverno and CNPG demo clusters#1402nadaverell wants to merge 5 commits into
nadaverell wants to merge 5 commits into
Conversation
Bootstraps a kind cluster with Kyverno 1.18.2 and curated policy fixtures,
following the gitops-demo pattern. Without it, anyone testing policy UI is
testing against whatever cluster they happen to be pointed at, and several
non-obvious states are not reproducible by guesswork.
The fixtures encode the states that are hard to get right rather than a
minimal smoke set:
- The four enforcement-posture cases. A policy declaring Deny with
admission evaluation disabled (blocks nothing), and one with NO
validationActions at all (which Kyverno treats as Deny, undocumented
upstream). Both are verifiable by hand and the README shows how.
- A MutatingPolicy with mutateExisting and admission off — the case that
used to render "Inactive" while rewriting the cluster. Note its default
is false, unlike admission/background, so it has to be set explicitly.
- Both API families installed together, since family selection, the
sidebar grouping and the legacy-vs-modern renderer split only have
meaning during migration. PolicyException exists in both, same Kind and
plural, different specs.
- Two DeletingPolicies on six-hourly and every-minute crons, so the table
shows "Never run" beside a recent timestamp within two minutes of
bootstrap rather than needing a hand-patched status.
- Real workloads, one passing and one failing, so the background scanner
produces ~40 reports across four distinct results[].source values —
which is the engine-taxonomy case made visible.
Two subcommands cover states that need a chart reconfiguration:
`openreports` leaves wgpolicyk8s.io served but empty while the data lands in
openreports.io (verified: 0 vs 37), and `modern-only` reproduces the 1.20 API
surface.
`modern-only` prints a warning before it runs, and the README leads with it:
Kyverno 1.18.2's admission controller crashloops without the legacy CRDs
because it sanity-checks for them at startup. That is upstream behaviour, and
anyone who hits it without warning will think they broke the cluster.
The webhook readiness gate is not incidental. `helm --wait` returns before the
admission webhook accepts connections, and applying a policy in that window
fails with "connection refused" — which is how the first run of this script
failed. The gate waits for endpoints and then round-trips a real policy
through a server-side dry-run, since endpoint readiness alone still races the
TLS cert.
CNPG had the problem `make gitops-demo` exists to solve and no answer: anyone testing CNPG UI changes was testing against whatever cluster was in their current context, usually one healthy Postgres with none of the variety that matters. `make cnpg-demo` builds a kind cluster with four clusters that all reach 2/2 Ready and render four different badges — Healthy, WAL Archiving Failing, and Unrecoverable twice. The uniform instance count is the point: every difference is invisible to counts, which is the class of bug the fixture exists to catch. Plus a Pooler, a plugin-method ScheduledBackup, three Backups, and Velero and KubeBlocks CRs for the shared `backups` and `clusters` plurals — with one engine installed, a guard that matches everything and a guard that matches the right group behave identically. The payload is the ordering, and every constraint below was found by running the script rather than by reasoning about it. Each one fails in a way that looks like success: Freezing the operator does not hold a status. Each Postgres pod runs an instance manager that writes cluster status independently, so a patched ContinuousArchiving=False reverts within ~10 minutes with the operator scaled to zero. `status.phase` survives; conditions and readyInstances do not. So the WAL failure is caused, not asserted — but only after the cluster is up. CNPG verifies the object store during bootstrap, so a cluster created with an unroutable endpoint never becomes Ready at all. That still yields ContinuousArchiving=False, which is the convincing wrong answer: the condition looks right on a cluster that is simply broken. The check therefore requires both the condition AND 2/2 Ready, because asserting the condition alone passes on the broken version — as it did, and reported success. Backups must be created after the freeze, not merely patched after it. The operator attempts a Backup, and one pointed at a cluster with no backup configuration fails and stamps LastBackupSucceeded=False on it. The pristine baseline silently rendered "Backup Failed" — via a resource attached to it rather than anything wrong with it. A Pooler must not share a name with one of its cluster's Services. CNPG creates <cluster>-rw/-ro/-r, and a Pooler creates a Service of its own name, so pg-healthy-rw refused to reconcile forever with "invalid ownership for managed resources" — no status, no condition, no event, just a CR that looks slow. Thawing is not scaling back up. The operator builds its PKI from its own webhook configurations at startup, so with them deleted it crash-loops on ensurePKI. `thaw` re-applies the manifest first. Timestamps are computed relative to now. A hardcoded expiry rendered "Expired" rather than "24d remaining" within months of being written. `live` skips the freeze for v2/v3 work that needs a reconciling controller — real failovers and backup runs, at the cost of hand-written phases. Verified end-to-end from an empty machine, then against Radar: four badges as documented, audit evaluated 4 / passed 1 with only the plugin-method cluster passing, four CNPG issues, and both backup engines rendering their own column sets on one cluster.
Same slot as the GitOps and Crossplane entries, with the instance-manager gotcha called out — anyone editing the fixtures needs to know it before they patch a condition and watch it heal itself ten minutes later.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c3c25f7. Configure here.
This was referenced Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Safeguards
Verification
make testmake tscmake buildbash -n scripts/kyverno-demo.sh scripts/cnpg-demo.shVisual testing is skipped because this changes developer fixture tooling and documentation, not application UI.
Tradeoffs
modern-onlyintentionally leaves the current Kyverno admission controller crashlooping after legacy CRDs are removed; it exists to model the future API surface for read-path testingNote
Low Risk
Developer-only bash/YAML and documentation; no runtime application or production deployment paths change.
Overview
Adds kind-based developer demos alongside the existing GitOps/Crossplane flows so policy and CloudNativePG UI work can be visual-tested against curated cluster state instead of arbitrary kube contexts.
Kyverno (
scripts/kyverno-demo.sh+ fixtures): bootstraps Kyverno 1.18.2 with legacy and modern policy APIs, enforcement-posture edge cases, dual-shapePolicyExceptions, workloads that drive real PolicyReports, plus optionalopenreports(emptywgpolicyk8s.iovs populatedopenreports.io) andmodern-only(legacy CRD removal for 1.20-style detection, with documented admission crashloop). Make targets:kyverno-demo,-down,-status.CloudNativePG (
scripts/cnpg-demo.sh+ fixtures): four 2/2 clusters with distinct badges (healthy, real WAL-archiving failure, two frozen unrecoverable phases), Pooler/ScheduledBackup/Backups, and Velero/KubeBlocks plural-collision CRs. Supports frozen (operator scaled down, status patched), live (operator up, no synthetic terminal/backup rows), refreeze/thaw, with guards around backup ordering and baseline contamination. Make targets:cnpg-demo,cnpg-demo-live,-down,-status.Docs:
CLAUDE.mddocuments when to use each demo;Makefilehelplists demo clusters;docs/STRUCTURE.mdbroadensscripts/description to “demo-cluster” orchestration.Reviewed by Cursor Bugbot for commit abd558e. Bugbot is set up for automated code reviews on this repo. Configure here.