CLI Policy Testing#
The Kyverno CLI simulates Kubernetes admission requests when evaluating policies via kyverno apply and kyverno test. The central orchestrator is PolicyProcessor, which holds all policy types, the resource under test, user info, subresource definitions, and the operation to simulate. Its ApplyPoliciesOnResource() method drives the full pipeline: it runs legacy Kyverno policies first (mutate β verify images β validate β generate), then each CEL-based engine in sequence (MutatingAdmissionPolicy, MutatingPolicy, ValidatingAdmissionPolicy, ValidatingPolicy, GeneratingPolicy).
Admission Request Construction for CEL Engines#
For all CEL-based policy types, the CLI builds a synthetic admissionv1.AdmissionRequest via two helpers:
-
AdmissionRequestShape(operation, resource)β maps the operation string to the correct (op,object,oldObject) triple, mirroring API server semantics:CREATE(default):object = resource,oldObject = nilUPDATE: both are the resource (oldObject is deep-copied to prevent mutation leakage)DELETE:object = nil,oldObject = resource(deep-copied)
-
celengine.Request(...)β constructs the fullEngineRequest, which wraps anadmissionv1.AdmissionRequestcontaining GVK, GVR,SubResource,Name,Namespace,Operation,UserInfo,Object, andOldObject. This is the object passed to each CEL engine'sHandle()method.
The operation is resolved by resolveOperation(): explicit p.Operation (set per test result entry) wins, then falls back to request.operation from the values file, then defaults to CREATE.
Subresource Context#
When the --cluster flag is not set, subresource context is derived from the Subresources field of PolicyProcessor (populated from the test values file). At the top of ApplyPoliciesOnResource(), the code matches the resource's GVK against declared subresource entries β if a match is found, it rewrites gvk to the parent resource's GVK and sets subresource to the path segment after the / (e.g., pods/status β subresource = "status").
This resolved subresource string is passed into celengine.Request() as SubResource and RequestSubResource . Prior to PR #16639 and PR #16787, both the operation and subresource were hardcoded to CREATE and "" in all CEL engine call sites, causing policies targeting specific operations or subresources (e.g., pods/status, pods/scale) to silently mismatch .
Known gap: The
ValidatingPolicyCEL call site still has a// TODO: how to manage subresource ?comment and passes""as the subresource, meaning validating policies do not yet receive subresource context.
Policy Type Routing#
| Policy Type | Engine Package | Request Path |
|---|---|---|
MutatingPolicy / NamespacedMutatingPolicy | pkg/cel/policies/mpol | celengine.Request() + mpolengine.Handle() |
ValidatingPolicy / NamespacedValidatingPolicy (K8s mode) | pkg/cel/policies/vpol | celengine.Request() + vpolengine.Handle() |
ValidatingPolicy (JSON mode) | pkg/cel/policies/vpol | celengine.RequestFromJSON() + vpolengine.Handle() |
GeneratingPolicy | pkg/cel/policies/gpol | celengine.Request() + gpolengine.Handle() |
ValidatingAdmissionPolicy (native K8s) | pkg/admissionpolicy | Direct admissionpolicy.Validate() |
MutatingAdmissionPolicy (native K8s) | pkg/admissionpolicy | Direct admissionpolicy.Mutate() |
The ValidatingPolicy engine also supports a JSON evaluation mode (spec.evaluation.mode: JSON), which skips admission attributes entirely and evaluates against the raw resource as JSON payload .
MutateExisting Support#
For MutatingPolicy policies with targetMatchConstraints, the processor also iterates over TargetResources . For each target, it builds an admission.NewAttributesRecord and calls mutExistEng.Evaluate() rather than Handle(). CEL-expression-based target selection is pre-computed by discoverCELTargets(), which compiles and evaluates the targetMatchConstraints.expression against the trigger resource to produce a map[string]bool of matching target keys.
Key Files#
| File | Purpose |
|---|---|
cmd/cli/kubectl-kyverno/processor/policy_processor.go | Main PolicyProcessor and ApplyPoliciesOnResource() orchestration |
cmd/cli/kubectl-kyverno/processor/operation.go | AdmissionRequestShape, resolveOperation, NormalizeOperation |
pkg/cel/engine/request.go | EngineRequest type and Request() / RequestFromJSON() constructors |