Repository navigation
Migrate the workload, Flux and provider validation ClusterPolicies to ValidatingPolicy #4996
Description
🤖 Generated by the Agentic Engineer (interactive Claude Code session)
Evidence and problem
Kyverno 1.20 is planned to remove ClusterPolicy (#4984). These 10 validation policies cover workload hygiene, Flux and the production provider; each must exist as a ValidatingPolicy (policies.kyverno.io) before 1.20 is installed:
k8s/bases/infrastructure/cluster-policies/best-practices/:disallow-latest-tag,validate-host-restrictions,validate-pdb-drain-safe,validate-pod-security,validate-replica-ceiling,validate-replica-floork8s/bases/infrastructure/cluster-policies/flux/:enforce-flux-best-practicesk8s/providers/hetzner/infrastructure/cluster-policies/:require-baseline-workers-for-hcloud-volumes,restrict-arc-openbao-certificate,restrict-backstage-retirement
Four have no Kyverno CLI test today: disallow-latest-tag, validate-pdb-drain-safe, validate-replica-ceiling and restrict-backstage-retirement.
How every migration batch works
- Test first. Of the 49
ClusterPolicymanifests, 28 are named in notests/**/kyverno-test.yaml(counted 2026-10-17 by policy name). Before rewriting a policy, add a Kyverno CLI test that pins its current behaviour — at least one resource it must act on and one it must leave alone — and see it pass against theClusterPolicy. The same test, pointed at the new kind, must pass unchanged in its expected results. - Keep the action. Each rewritten policy keeps its failure action as recorded in
tests/policy-failure-actions.json(Enforce stays Enforce, Audit stays Audit), its match scope and its exclusions. - No gap in enforcement. Add the new policy and delete the
ClusterPolicyin the same PR, so Flux applies the replacement before it prunes the old one. Kinds whose two versions could both act on the same request (mutation or generation) say how double application is avoided. - Confirm the capability first. For each rule, confirm the replacement kind can express it at Kyverno 1.19.1 (the running version). A rule it cannot express is recorded on Migrate Kyverno ClusterPolicy and Policy before Kyverno 1.20 removes them #4984 and blocks only that policy, not the batch.
- Validate with
kyverno test ./testsandscripts/validate-kyverno-fixture-evaluation.sh(CI's⚖️ Validate cluster policiesstep), the layer builds inplatform/AGENTS.md, and after deploy a read-only check that each new policy reports ready and the old one is gone.
Acceptance criteria
- Every policy listed above has a CLI test that passed against the
ClusterPolicybefore the rewrite. - Each is a
ValidatingPolicywith the same failure action, scope and exclusions, and the oldClusterPolicyis deleted in the same PR. -
kyverno test ./testsand the fixture-evaluation check pass in CI. - After deploy, each new policy reports ready and no
ClusterPolicyof these names remains.
Priority High, Effort High, Service class Risk reduction. These rules keep workloads drain-safe, pod security enforced and production storage on the right workers. Effort includes adding the missing tests, the CEL rewrite, review and one production deploy. No prerequisite; #4994 removes the time pressure but is not required first. Part of #4984.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Effort
Projects
- StatusShow more project fields✅ Done
Activity
devantler commentedon Oct 19, 2026
Test-first checkpoint, 19 October: #5080 merged at exact head 14a4e38 through merge commit d4f3e11. Its protected merge-group deployment and queue-membership check succeeded, and the separated platform health readback is OK.
Every policy this issue lists now has a Kyverno CLI test. For restrict-backstage-retirement, two of its five rules are pinned: preserve-retirement-journal and fence-native-targets. preserve-retained-identities, fence-retiring-dependencies and fence-chart-consumers still need allowed and denied cases before that policy is rewritten; those fixtures need writer identities and owner graphs.
When writing those fixtures, keep in mind that kyverno test merges a per-resource request.oldObject into the resource. A key only the new object has is therefore invisible: change a value, never add a key.
chore: agent-claim #4996 nonce=0be22c03016805264c9a5e24339257d7
test(kyverno): pin the Helm history exception of the retained-identit…
devantler commentedon Oct 19, 2026
Test-first checkpoint, 19 October (later): #5081 pins the
preserve-retained-identitiesrule of restrict-backstage-retirement and is in the merge queue at3a4363330bf69a78d4d4b13013931de7c32d32f6. It covers the general case (an untouched retained object is allowed; a label change, a data change, or any update under an unbound journal is denied; an unretained object is out of scope) and the Helm history writer exception (only the recorded Helm controller may move the retained latest release forward to uninstalled).Writer identities are not a blocker for fixtures: a per-resource
request.userInfowithusernameanduidin values.yaml drives the writer variables, and a wronguidalone flips the result.Still unpinned before the rewrite: the CNPG certificate, CNPG role-label and CA-bundle writer exceptions of
preserve-retained-identities, and thefence-retiring-dependenciesandfence-chart-consumersrules.chore: agent-claim #4996 nonce=6de1876ba4562f58fd747be35bdaa943
chore: agent-claim #4996 nonce=bdf978202de7eefb0951f01eb1bbcc67
chore: agent-claim #4996 nonce=a0168e3e59b41d15b804fb6c520beb78
test(kyverno): pin the CA bundle writer exceptions of the Backstage r…
26 remaining items
chore: agent-claim #4996 nonce=b3428558d77b5889b766c6302f2158ee
devantler commentedon Oct 20, 2026
Host-restriction pre-deployment comparison, 20 October UTC: independently evaluated #5141 at
401f4e284d18eef102e09c11ebd74344333861c2with Kyverno 1.19.1.The remaining check is to bind the protected merge and successful deployment, then confirm the rendered replacement is ready with the intended Deny/background behavior and the old kind is gone. #4996 stays open with its original acceptance criteria unchanged. This checkpoint does not take over #5141 or claim its implementation or merge.
chore: agent-claim #4996 nonce=049cc3f31a3ed9b86e905b5b84538475
devantler commentedon Oct 20, 2026
Host-restriction deployment acceptance, 20 October UTC: the pending deployment check from the previous checkpoint now passes.
4c7507df70b4d22d7d79e03e5a466c5bc3a3fdfb. The native merge-group production deployment job executed successfully; the complete 23-job roster and required aggregate were read back.validate-host-restrictionsis a readyValidatingPolicy, its entire specification exactly equals the policy rendered from the merged production infrastructure layer, and it retainsDeny, background evaluation and disabled controller autogen. Both reporting-permission and webhook conditions succeed. A separate successful read confirms that the oldClusterPolicyis absent.53f60460cf9bcb3583f49a5f0240cb541544038d; that change does not touch this policy or its rendering inputs.This completes the deployment portion of the host-restriction checkpoint, alongside the earlier comparison and #5140's delivery receipt. The remaining migrations keep #4996 open with its original acceptance criteria. No production resource was changed by this verification.
devantler commentedon Oct 20, 2026
Progress and a scoping note, 20 October UTC.
require-baseline-workers-for-hcloud-volumesis now tracked by Migrate require-baseline-workers-for-hcloud-volumes to ValidatingPolicy #5150, with draft feat(kyverno): migrate require-baseline-workers-for-hcloud-volumes to ValidatingPolicy #5151 in review. It is the first policy here that looks up other objects at admission. Kyverno 1.19.1 handles that: the new kind can list the namespace claims, and the CLI test supplies them from aContextfile. The audit companion is now its own AuditValidatingPolicy.restrict-backstage-retirement, still has no CLI test. Since Complete Backstage retirement by clearing the terminal journal #5042 closed on 18 October, the portal retirement it fenced is complete. Its five Enforce rules split into two groups:devantler commentedon Oct 21, 2026
Scope decision for
restrict-backstage-retirement, 21 October UTC: remove it, do not migrate it. This is now tracked by #5159, which blocks this issue natively.tests/restrict-backstage-retirement-*directories onmain).retirement.present. That is true only while thebackstagenamespace carries the retirement journal annotation. Completion removed that annotation on 18 October (Complete Backstage retirement by clearing the terminal journal #5042, run 39161262289). A read-only production check just now found the namespace with no annotation. None of the rules can fire, so migrating them would add test and review cost and protect nothing.The other nine policies are unchanged: #5129 (Flux), #5144 (pod security) and #5151 (hcloud volumes) are in the merge queue.
devantler commentedon Oct 21, 2026
Post-deploy readback, 21 October ~02:15Z (read-only, production): #5129 merged 01:42Z and #5151 merged 02:11Z.
ValidatingPolicyClusterPolicyenforce-flux-best-practicesrequire-baseline-workers-for-hcloud-volumesreport-hcloud-claim-lookup-failure, Audit, ready)disallow-latest-tag,validate-pdb-drain-safe,validate-replica-ceiling,validate-replica-floorvalidate-host-restrictions,restrict-arc-openbao-certificatevalidate-pod-securityClusterPolicy(behaviour now pinned by #5144, merged 01:12Z)restrict-backstage-retirementClusterPolicy(removal in #5159 / draft #5161)8 of 10 are done. What remains is the
validate-pod-securityrewrite against the #5144 matrix, and the backstage removal.devantler commentedon Oct 21, 2026
All twelve child issues are closed, and a read-only production check at about 04:59Z confirms the last acceptance criterion: each of the nine migrated policies (
disallow-latest-tag,validate-host-restrictions,validate-pdb-drain-safe,validate-pod-security,validate-replica-ceiling,validate-replica-floor,enforce-flux-best-practices,require-baseline-workers-for-hcloud-volumes,restrict-arc-openbao-certificate) exists as a ValidatingPolicy reporting ready, and none of the ten names remains as a cluster policy. The tenth,restrict-backstage-retirement, was retired rather than migrated (#5159 / #5161). The same read listed 36 other cluster policies, so the absence is a real answer. Closing as complete.