Platform engineering

Kyverno's CLI learns to fake production so offline policy tests stop lying

Kyverno's CLI learns to fake production so offline policy tests stop lying

The first time a Kyverno CLI test lied to me, I did not know it was lying. My suite went green. My PR merged. And the rule that was supposed to block a suspiciously permissive Deployment simply had not run against it in a cluster, because the policy referenced a GlobalContextEntry lookup and the CLI had no live Kubernetes API to resolve it against. So the engine did what the engine had always done in that spot: it skipped, and kyverno apply did not tell me. A CNCF blog post from July 22 walks through how a Kyverno contributor finally taught the CLI to fake production convincingly enough that offline tests for policies with those lookups stop lying.

The gap the CLI had been papering over

The post frames the underlying problem as an architectural mismatch. Kyverno's policy engine wants to talk to a live informer cache: unstructured resources wrapped in []interface{} slices, in exactly the shape a running controller receives them. In kyverno apply mode there was no informer cache, and any policy that reached for one, most commonly through GlobalContextEntry, would either panic outright or silently skip the rule. The tests kept passing. The rule kept not running.

If you have ever added a lookup to a validating policy and then wondered why your local test never exercised it, this is the shape of the bug you were living with.

The disguise, not a rewrite

The author's move here is that the fix does not belong in the engine. The engine already knows how to talk to a live cache. What was missing was a translation layer between test data on disk and the shape the engine expects in memory.

So they wrote one. Mock resources are decoded through runtime.RawExtension inside a new resolveResourcesMockData function, packed into the same []interface{} slices a real informer would return, and fronted by an in-memory fake dynamic client that stands in for the API server during kyverno apply. Nothing in the engine changes. It thinks it is talking to a cluster.

I like this shape a lot. When a test tool starts hacking the code under test to be more testable, the tests get faster and the production behaviour starts to drift. This goes the other way.

What lands in your test manifests

The post lists the new fields your Kyverno test YAML can now carry:

  • apiCallResponses, for canned responses to arbitrary API calls a policy makes.
  • globalContextEntries, for the exact class of lookup that used to force a skip.
  • resources and resourceFiles, for the mock inventory the engine reads through the fake client.
  • jsonPayloads, an array of payloads for tests that need to fan out over multiple inputs.
  • generatedResources, for verifying the multiple objects a generate policy actually produces.

Two new CLI flags widen the surface further: --http-payload and --envoy-payload let you drive tests with the payload shapes Kyverno already handles at runtime. And CleanupPolicy and ClusterCleanupPolicy are now testable in dry-run mode, which closes a lifecycle gap the CLI had been carrying for a while.

Why this matters at PR time

This is about tests that mean something. A CI stage that runs your Kyverno policies against representative resources and either fails your PR or does not, without needing a live cluster or a kind sidecar, is the difference between finding out from your editor and finding out from the platform team next Wednesday. Every offline gate that quietly stopped running was a foot-gun. Closing that class of foot-gun is worth more than any raw-speed improvement I can remember shipping in this space.

The honest rough edges

A mock is still a mock. What the CLI now models faithfully is what the engine sees, not what a busy cluster actually does. Admission chain ordering, races between generate policies and their targets, and quota interactions that only bite under real controller latency are not in scope for this. You still want a smoke test in a real (probably ephemeral) cluster before you flip a policy to enforce.

The other rough edge is that your test manifests get busier. Any fixture that used to pass by not exercising a lookup now has to declare the context that lookup would have found. That is more code to maintain, in exchange for tests that tell the truth.

What I am watching next

Whether the "fake the informer cache, do not touch the engine" pattern spreads. OPA/Gatekeeper has its own unit-test story built around conftest and Rego fixtures, and the trade-offs it makes for offline testing are different. If admission-control tooling across the ecosystem converges on standing up an in-memory API shape rather than skipping past the interesting rules, the meaning of "unit-testable policy" quietly changes underneath us. I would take that quiet change over another benchmark chart any day.

Source: CNCF Blog (cncf.io)

Related
Platform engineering

Sandboxing your agent isn't the hard part, keeping it cheap is

A CNCF post from Solo.io's Lin Sun argues Kubernetes' agent-sandbox project delivers the isolation piece of hosting AI agents, while a sibling project, agent-substrate, is aimed at the resource-efficiency gap a per-agent pod leaves behind.

July 7, 2026
Security & supply chain

Confidential Containers hits CNCF incubation, and every pipeline is on notice

The CNCF Technical Oversight Committee voted Confidential Containers into incubation on July 22, moving TEE-backed pods one step closer to being a normal part of the Kubernetes stack. If your pipeline already signs and attests images, the runtime side is about to demand the same discipline.

July 25, 2026
Runners & infrastructure

Dynamic Resource Allocation hits GA in Kubernetes 1.35, and GPU CI jobs finally get a real API

DRA reached general availability in Kubernetes v1.35, and the NVIDIA DRA driver has dropped its Beta label. For teams running GPU workloads through Kubernetes-backed pipelines, it replaces a decade of nodeSelector gymnastics with a first-class request primitive.

July 2, 2026

Turn this into your pipeline. Build it on Buddy.

Start free