Skip to content

Kyverno CLI

FEATURE STATE:Stable•Kyverno v1.19

The Kyverno Command Line Interface (CLI) is designed to validate and test policy behavior to resources prior to adding them to a cluster.

The CLI can be used in CI/CD pipelines to assist with the resource authoring process to ensure they conform to standards prior to them being deployed. It can be used as a kubectl plugin or as a standalone CLI.

The CLI, although composed of the same Kyverno codebase, is a purpose-built binary available via multiple installation methods but is distinct from the Kyverno container image which runs as a Pod in a target Kubernetes cluster.

This page covers the main Kyverno CLI commands, for other commands please refer to the Reference documentation

The Kyverno CLI can also be installed with Homebrew as a formula.

Terminal window
brew install kyverno

You can use Krew to install the Kyverno CLI:

Terminal window
# Install Kyverno CLI using kubectl krew plugin manager
kubectl krew install kyverno
# test the Kyverno CLI
kubectl kyverno version

You can install the Kyverno CLI via your favorite AUR helper (e.g. yay)

Terminal window
yay -S kyverno-git

The Kyverno CLI can be installed in GitHub Actions using kyverno-cli-installer from the GitHub Marketplace. Please refer to kyverno-cli-installer for more information.

The Kyverno CLI may also be installed by manually downloading the compiled binary available on the releases page. An example of installing the Kyverno CLI v1.12.0 on a Linux x86_64 system is shown below.

Terminal window
curl -LO https://github.com/kyverno/kyverno/releases/download/v1.12.0/kyverno-cli_v1.12.0_linux_x86_64.tar.gz
tar -xvf kyverno-cli_v1.12.0_linux_x86_64.tar.gz
sudo cp kyverno /usr/local/bin/

You can also build the CLI binary from the Git repository (requires Go).

Terminal window
git clone https://github.com/kyverno/kyverno
cd kyverno
make build-cli
sudo mv ./cmd/cli/kubectl-kyverno/kubectl-kyverno /usr/local/bin/

The apply command evaluates supported CEL-based Kyverno policies and Kubernetes-native admission policies against resource manifests, or against resources in a cluster when --cluster is specified. It can also show mutations and generate policy reports for supported policy types.

Apply a policy to local resources:

Terminal window
kyverno apply /path/to/policy.yaml --resource /path/to/resources.yaml

Evaluate policies against resources in the current Kubernetes context:

Terminal window
kyverno apply /path/to/policy.yaml --cluster

The CLI supports Kubernetes ValidatingAdmissionPolicy and MutatingAdmissionPolicy, as well as Kyverno CEL policies such as ValidatingPolicy. The following examples show how to apply these policy types locally and against a cluster.

The kyverno apply command can be used to apply native Kubernetes policies and their corresponding bindings to resources, allowing you to test them locally without a cluster.

With the apply command, Kubernetes ValidatingAdmissionPolicies can be applied to resources as follows:

Policy manifest (check-deployment-replicas.yaml):

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: check-deployments-replicas
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ['apps']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['deployments']
validations:
- expression: 'object.spec.replicas <= 3'
message: 'Replicas must be less than or equal 3'

Resource manifest (deployment.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-pass
spec:
replicas: 2
selector:
matchLabels:
app: nginx-pass
template:
metadata:
labels:
app: nginx-pass
spec:
containers:
- name: nginx-server
image: nginx

Apply the ValidatingAdmissionPolicy to the resource:

Terminal window
kyverno apply /path/to/check-deployment-replicas.yaml --resource /path/to/deployment.yaml

The following output will be generated:

Terminal window
Applying 1 policy rule(s) to 1 resource(s)...
pass: 1, fail: 0, warn: 0, error: 0, skip: 0

The below example applies a ValidatingAdmissionPolicyBinding along with the policy to all resources in the cluster.

Policy manifest (check-deployment-replicas.yaml):

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: 'check-deployment-replicas'
spec:
matchConstraints:
resourceRules:
- apiGroups:
- apps
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- deployments
validations:
- expression: object.spec.replicas <= 5
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: 'check-deployment-replicas-binding'
spec:
policyName: 'check-deployment-replicas'
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
environment: staging

The above policy verifies that the number of deployment replicas is not greater than 5 and is limited to a namespace labeled environment: staging.

Create a Namespace with the label environment: staging:

Terminal window
kubectl create ns staging
kubectl label ns staging environment=staging

Create two Deployments, one of them in the staging namespace, which violates the policy.

Terminal window
kubectl create deployment nginx-1 --image=nginx --replicas=6 -n staging
kubectl create deployment nginx-2 --image=nginx --replicas=6

Get all Deployments from the cluster:

Terminal window
kubectl get deployments -A
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
default nginx-2 6/6 6 6 7m26s
kube-system coredns 2/2 2 2 13m
local-path-storage local-path-provisioner 1/1 1 1 13m
staging nginx-1 6/6 6 6 7m44s

Apply the ValidatingAdmissionPolicy with its binding to all resources in the cluster:

Terminal window
kyverno apply /path/to/check-deployment-replicas.yaml --cluster --policy-report

The following output will be generated:

Terminal window
Applying 1 policy rule(s) to 4 resource(s)...
----------------------------------------------------------------------
POLICY REPORT:
----------------------------------------------------------------------
apiVersion: wgpolicyk8s.io/v1alpha2
kind: ClusterPolicyReport
metadata:
creationTimestamp: null
name: merged
results:
- message: 'failed expression: object.spec.replicas <= 5'
policy: check-deployment-replicas
resources:
- apiVersion: apps/v1
kind: Deployment
name: nginx-1
namespace: staging
uid: a95d1594-44a7-4c8a-9225-04ac34cb9494
result: fail
scored: true
source: kyverno
timestamp:
nanos: 0
seconds: 1707394871
summary:
error: 0
fail: 1
pass: 0
skip: 0
warn: 0

As expected, the policy is only applied to nginx-1 as it matches both the policy definition and its binding.

Similarly, you can test a MutatingAdmissionPolicy to preview the changes it would make to a resource. The CLI will output the final, mutated resource.

For instance, you can test a MutatingAdmissionPolicy that adds a label to a ConfigMap.

Policy manifest (add-label-to-configmap.yaml):

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: 'add-label-to-configmap'
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE']
resources: ['configmaps']
failurePolicy: Fail
reinvocationPolicy: Never
mutations:
- patchType: 'ApplyConfiguration'
applyConfiguration:
expression: >
object.metadata.?labels["lfx-mentorship"].hasValue() ?
Object{} :
Object{ metadata: Object.metadata{ labels: {"lfx-mentorship": "kyverno"}}}

Resource manifest (configmap.yaml):

apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
labels:
app: game
data:
player_initial_lives: '3'

Now, apply the MutatingAdmissionPolicy to the ConfigMap resource:

Terminal window
kyverno apply /path/to/add-label-to-configmap.yaml --resource /path/to/configmap.yaml

The output will show the mutated ConfigMap with the added label:

Terminal window
Applying 1 policy rule(s) to 1 resource(s)...
policy add-label-to-configmap applied to default/ConfigMap/game-demo:
apiVersion: v1
data:
player_initial_lives: "3"
kind: ConfigMap
metadata:
labels:
app: game
lfx-mentorship: kyverno
name: game-demo
namespace: default
---
Mutation has been applied successfully.
pass: 1, fail: 0, warn: 0, error: 0, skip: 0

The output displays the ConfigMap with the new lfx-mentorship: kyverno label, confirming the mutation was applied correctly.

Example 2: Mutation with a Binding and Namespace Selector
Section titled “Example 2: Mutation with a Binding and Namespace Selector”

You can also test policies that include a MutatingAdmissionPolicyBinding to control where the policy is applied. This example makes use of a namespace selector to apply the policy only to ConfigMaps in a specific namespace.

To do this, you must provide a values.yaml file to simulate the labels of the Namespaces your resources belong to.

Policy manifest (add-label-to-configmap.yaml):

This file defines a policy to add a label and a binding that restricts it to Namespaces labeled environment: staging or environment: production.

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: 'add-label-to-configmap'
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE']
resources: ['configmaps']
failurePolicy: Fail
reinvocationPolicy: Never
mutations:
- patchType: 'ApplyConfiguration'
applyConfiguration:
expression: >
object.metadata.?labels["lfx-mentorship"].hasValue() ?
Object{} :
Object{ metadata: Object.metadata{ labels: {"lfx-mentorship": "kyverno"}}}
---
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicyBinding
metadata:
name: 'add-label-to-configmap-binding'
spec:
policyName: 'add-label-to-configmap'
matchResources:
namespaceSelector:
matchExpressions:
- key: environment
operator: In
values:
- staging
- production

Resource manifest (configmaps.yaml):

This file contains three ConfigMap resources in different Namespaces. Only the ones in staging and production should be mutated.

apiVersion: v1
kind: ConfigMap
metadata:
name: matched-cm-1
namespace: staging
labels:
color: red
data:
player_initial_lives: '3'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: matched-cm-2
namespace: production
labels:
color: red
data:
player_initial_lives: '3'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: unmatched-cm
namespace: testing
labels:
color: blue
data:
player_initial_lives: '3'

Values file (values.yaml):

This file provides the necessary context. It tells the Kyverno CLI what labels are associated with the staging, production, and testing Namespaces so it can correctly evaluate the namespaceSelector in the binding.

apiVersion: cli.kyverno.io/v1alpha1
kind: Value
metadata:
name: values
namespaceSelector:
- labels:
environment: staging
name: staging
- labels:
environment: production
name: production
- labels:
environment: testing
name: testing

Now, apply the MutatingAdmissionPolicy and its binding to the ConfigMaps:

Terminal window
kyverno apply /path/to/add-label-to-configmap.yaml --resource /path/to/configmaps.yaml -f /path/to/values.yaml

The output will show the mutated ConfigMaps in the staging and production Namespaces, while the one in the testing Namespace remains unchanged:

Terminal window
Applying 1 policy rule(s) to 3 resource(s)...
policy add-label-to-configmap applied to staging/ConfigMap/matched-cm-1:
apiVersion: v1
data:
player_initial_lives: "3"
kind: ConfigMap
metadata:
labels:
color: red
lfx-mentorship: kyverno
name: matched-cm-1
namespace: staging
---
Mutation has been applied successfully.
policy add-label-to-configmap applied to production/ConfigMap/matched-cm-2:
apiVersion: v1
data:
player_initial_lives: "3"
kind: ConfigMap
metadata:
labels:
color: red
lfx-mentorship: kyverno
name: matched-cm-2
namespace: production
---
Mutation has been applied successfully.
pass: 2, fail: 0, warn: 0, error: 0, skip: 0

In this example, we will apply a ValidatingPolicy against two Deployment manifests: one that complies with the policy and one that violates it.

First, we define a ValidatingPolicy that ensures any Deployment has no more than two replicas.

Policy manifest (check-deployment-replicas.yaml):

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-deployment-replicas
spec:
matchConstraints:
resourceRules:
- apiGroups: ['apps']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['deployments']
validations:
- expression: 'object.spec.replicas <= 2'
message: 'Deployment replicas must be less than or equal to 2'

Next, we have two Deployment manifests. The good-deployment is compliant with 2 replicas, while the bad-deployment is non-compliant with 3 replicas.

Resource manifest (deployments.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
name: good-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bad-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest

Now, we use the kyverno apply command to test the policy against both resources.

Terminal window
kyverno apply /path/to/check-deployment-replicas.yaml --resource /path/to/deployments.yaml --policy-report

The following output will be generated:

Terminal window
apiVersion: openreports.io/v1alpha1
kind: ClusterReport
metadata:
creationTimestamp: null
name: merged
results:
- message: Deployment replicas must be less than or equal 2
policy: check-deployment-replicas
properties:
process: background scan
resources:
- apiVersion: apps/v1
kind: Deployment
name: bad-deployment
namespace: default
result: fail
scored: true
source: KyvernoValidatingPolicy
timestamp:
nanos: 0
seconds: 1752755472
- message: success
policy: check-deployment-replicas
properties:
process: background scan
resources:
- apiVersion: apps/v1
kind: Deployment
name: good-deployment
namespace: default
result: pass
scored: true
source: KyvernoValidatingPolicy
timestamp:
nanos: 0
seconds: 1752755472
source: ""
summary:
error: 0
fail: 1
pass: 1
skip: 0
warn: 0

In addition to testing local YAML files, you can use the kyverno apply command to validate policies against resources that are already running in a Kubernetes cluster. Instead of specifying resource files with the --resource flag, you can use the --cluster flag.

For example, to test the check-deployment-replicas policy against all Deployment resources in your currently active cluster, you would run:

Terminal window
kyverno apply /path/to/check-deployment-replicas.yaml --cluster --policy-report

Many advanced policies need to look up the state of other resources in the cluster using Kyverno’s custom CEL functions like resource.Get(). When testing such policies locally with the kyverno apply command, the CLI cannot connect to the cluster to retrieve the required resources so you have to provide these resources as input via the --context-path flag.

This flag allows you to specify the resources that the policy will reference. The CLI will then use these resources to evaluate the policy.

This example demonstrates how to test a policy that validates an incoming Pod by checking its name against a value stored in a ConfigMap.

First, we define a ValidatingPolicy that uses resource.Get() to fetch a ConfigMap named policy-cm. The policy then validates that the incoming Pod’s name matches the name key in the ConfigMap’s data.

Policy manifest (check-pod-name-from-configmap.yaml):

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-pod-name-from-configmap
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['pods']
variables:
# This variable uses a Kyverno CEL function to get a ConfigMap from the cluster.
- name: cm
expression: >-
resource.Get("v1", "configmaps", object.metadata.namespace, "policy-cm")
validations:
# This rule validates that the Pod's name matches the 'name' key in the ConfigMap's data.
- expression: >-
object.metadata.name == variables.cm.data.name

Next, we define two Pod manifests: good-pod, which should pass the validation, and bad-pod, which should fail.

Resource manifest (pods.yaml):

apiVersion: v1
kind: Pod
metadata:
name: good-pod
spec:
containers:
- name: nginx
image: nginx
---
apiVersion: v1
kind: Pod
metadata:
name: bad-pod
spec:
containers:
- name: nginx
image: nginx

Because the CLI cannot connect to a cluster to fetch the policy-cm ConfigMap, we must provide it in a context file. This file contains a mock ConfigMap that resource.Get() will use during local evaluation.

Context file (context.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Context
metadata:
name: context
spec:
# The resources defined here will be available to functions like resource.Get()
resources:
- apiVersion: v1
kind: ConfigMap
metadata:
namespace: default
name: policy-cm
data:
# According to this, the valid pod name is 'good-pod'.
name: good-pod

Now, we can run the kyverno apply command, providing the policy, the resources, and the context file. We also use the -p (or --policy-report) flag to generate a ClusterReport detailing the results.

Terminal window
kyverno apply /path/to/policy.yaml --resource /path/to/pods.yaml --context-file /path/to/context.yaml -p

The following output will be generated:

apiVersion: openreports.io/v1alpha1
kind: ClusterReport
metadata:
creationTimestamp: null
name: merged
results:
- message: success
policy: check-pod-name-from-configmap
properties:
process: background scan
resources:
- apiVersion: v1
kind: Pod
name: good-pod
namespace: default
result: pass
scored: true
source: KyvernoValidatingPolicy
timestamp:
nanos: 0
seconds: 1752756617
- policy: check-pod-name-from-configmap
properties:
process: background scan
resources:
- apiVersion: v1
kind: Pod
name: bad-pod
namespace: default
result: fail
scored: true
source: KyvernoValidatingPolicy
timestamp:
nanos: 0
seconds: 1752756617
source: ''
summary:
error: 0
fail: 1
pass: 1
skip: 0
warn: 0
  • The good-pod resource resulted in a pass because its name matches the value in the ConfigMap provided by the context file.

  • The bad-pod resource resulted in a fail because its name does not match, and the report includes the validation error message from the policy.

When using the --cluster flag, the CLI connects to your active Kubernetes cluster, so a local context file is not needed. The resource.Get() function will fetch the live ConfigMap directly from the cluster so you have to ensure the ConfigMap and the Pod resources exist in your cluster before running the command.

Terminal window
kyverno apply /path/to/check-pod-name-from-configmap.yaml --cluster --policy-report

In case of applying a CEL ValidatingPolicy with a policies.kyverno.io PolicyException, use the --exception flag to specify the exception manifest. The CLI will then apply the policy and its exception together.

In this example, we will test a CEL policy that disallows hostPath volumes, then use a policies.kyverno.io PolicyException to exempt a specific Pod.

Policy manifest (disallow-host-path.yaml):

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: disallow-host-path
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['pods']
validations:
- expression: '!has(object.spec.volumes) || object.spec.volumes.all(volume, !has(volume.hostPath))'
message: 'HostPath volumes are forbidden. The field spec.volumes[*].hostPath must be unset.'

Next, we define a Pod that clearly violates this policy by mounting a hostPath volume. Without an exception, this Pod would be blocked.

Resource manifest (pod-with-hostpath.yaml):

apiVersion: v1
kind: Pod
metadata:
name: pod-with-hostpath
spec:
containers:
- name: nginx
image: nginx
volumes:
- name: udev
hostPath:
path: /etc/udev

Now, create a CEL PolicyException to exempt the specific Pod from the policy. The exception matches the Pod by name and references the disallow-host-path policy.

Policy Exception manifest (exception.yaml):

apiVersion: policies.kyverno.io/v1
kind: PolicyException
metadata:
name: exempt-hostpath-pod
spec:
policyRefs:
- name: disallow-host-path
kind: ValidatingPolicy
matchConditions:
- name: 'skip-pod-by-name'
expression: "object.metadata.name == 'pod-with-hostpath'"

Now, we use the kyverno apply command, providing the policy, the resource, and the exception using the --exception flag. We will also use -p to generate a detailed report.

Terminal window
kyverno apply policy.yaml --resource resource.yaml --exception exception.yaml -p

The following output will be generated:

apiVersion: openreports.io/v1alpha1
kind: ClusterReport
metadata:
creationTimestamp: null
name: merged
results:
- message: 'rule is skipped due to policy exception: exempt-hostpath-pod'
policy: disallow-host-path
properties:
exceptions: exempt-hostpath-pod
process: background scan
resources:
- apiVersion: v1
kind: Pod
name: pod-with-hostpath
namespace: default
result: skip
rule: exception
scored: true
source: KyvernoValidatingPolicy
timestamp:
nanos: 0
seconds: 1752759828
source: ''
summary:
error: 0
fail: 0
pass: 0
skip: 1
warn: 0

The output confirms that the CEL PolicyException worked as intended:

  • result: skip: The policy rule was not enforced on the resource.

  • properties.exceptions: exempt-hostpath-pod: The report explicitly names the CEL PolicyException responsible for the skip.

  • summary.skip: 1: The final count reflects that one rule was skipped.

The test command evaluates policy behavior against resource manifests and compares the results with expectations declared in a kyverno-test.yaml file. It scans a local directory or Git repository for test manifests and runs each test with its colocated policy and resource files.

Kyverno CLI tests support CEL-based Kyverno policies, including ValidatingPolicy, and Kubernetes-native admission policies including ValidatingAdmissionPolicy and MutatingAdmissionPolicy. Use --detailed-results for comprehensive output, or pass -h for the command’s flags and usage.

A test manifest identifies the policies and resources to evaluate, optional test context or CEL policy exceptions, and expected outcomes. For example:

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: kyverno-test
policies:
- policy.yaml
resources:
- resources.yaml
context: context.yaml # optional mock resources for CEL resource lookups
variables: values.yaml # optional CEL test values, such as mock namespace objects
exceptions: # optional PolicyException manifests for CEL ValidatingPolicies
- exception.yaml
apiCallResponses: # optional offline HTTP mocks for CEL http.Get()/http.Post()
- url: https://example.com/api
method: GET
response:
statusCode: 200
body:
allowed: true
globalContextEntries: # optional mock data for CEL globalContext.Get()
- name: config
data:
allowed: true
results:
- isValidatingPolicy: true # set for a Kyverno CEL ValidatingPolicy
policy: check-policy
kind: Pod
resources:
- default/example
result: pass
- isValidatingAdmissionPolicy: true # set for a Kubernetes ValidatingAdmissionPolicy
policy: check-native-policy
kind: Pod
resources:
- default/example
result: pass

For native ValidatingAdmissionPolicy tests, set isValidatingAdmissionPolicy: true on the corresponding result. For Kyverno CEL ValidatingPolicy tests, set isValidatingPolicy: true. See the examples below for complete test manifests.

Test a set of local files in the working directory.

Terminal window
kyverno test .

Test a set of local files by specifying the directory.

Terminal window
kyverno test /path/to/folderContainingTestYamls

Test an entire Git repository by specifying its URL. If a branch is not specified, main is used by default.

Terminal window
kyverno test https://github.com/owner/repository

Test a specific directory of the repository by specifying the directory within repo URL and the branch with the --git-branch or -b flag. Even if testing against main, when using a directory in the URL of the repo requires passing the --git-branch or -b flag.

Terminal window
kyverno test https://github.com/owner/repository/path/to/tests -b branch-name

Use the -f flag to set a custom file name which includes test cases. By default, test will search for a file called kyverno-test.yaml.

Testing Policies with Image Registry Access

Section titled “Testing Policies with Image Registry Access”

For CEL policies which require image registry access to set context variables, those variables may be sourced from a values file or from a live registry by passing the --registry flag.

In some cases, you may wish to test only a subset of policy and resource combinations from a test manifest. Use the --test-case-selector flag to specify the exact tests you wish to execute.

Terminal window
kyverno test . --test-case-selector "policy=check-deployment-replicas, resource=nginx-demo"

The kyverno test command can be used to test native Kubernetes policies.

To test a ValidatingAdmissionPolicy, the test manifest must include the isValidatingAdmissionPolicy: true field in each result entry.

Below is an example of testing a ValidatingAdmissionPolicy against two resources, one of which violates the policy.

Policy manifest (disallow-host-path.yaml):

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: disallow-host-path
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ['apps']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['deployments']
validations:
- expression: '!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume, !has(volume.hostPath))'
message: 'HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath must be unset.'

Resource manifest (deployments.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-pass
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-server
image: nginx
volumeMounts:
- name: temp
mountPath: /scratch
volumes:
- name: temp
emptyDir: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-fail
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-server
image: nginx
volumeMounts:
- name: udev
mountPath: /data
volumes:
- name: udev
hostPath:
path: /etc/udev

Test manifest (kyverno-test.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: disallow-host-path-test
policies:
- disallow-host-path.yaml
resources:
- deployments.yaml
results:
- policy: disallow-host-path
resources:
- deployment-pass
isValidatingAdmissionPolicy: true
kind: Deployment
result: pass
- policy: disallow-host-path
resources:
- deployment-fail
isValidatingAdmissionPolicy: true
kind: Deployment
result: fail
Terminal window
$ kyverno test .
Loading test ( kyverno-test.yaml ) ...
Loading values/variables ...
Loading policies ...
Loading resources ...
Applying 1 policy to 2 resources ...
Checking results ...
│────│────────────────────│──────│────────────────────────────│────────│────────│
│ ID │ POLICY │ RULE │ RESOURCE │ RESULT │ REASON │
│────│────────────────────│──────│────────────────────────────│────────│────────│
│ 1 │ disallow-host-path │ │ Deployment/deployment-pass │ Pass │ Ok │
│ 2 │ disallow-host-path │ │ Deployment/deployment-fail │ Pass │ Ok │
│────│────────────────────│──────│────────────────────────────│────────│────────│
Test Summary: 2 tests passed and 0 tests failed

In the below example, a ValidatingAdmissionPolicy and its corresponding ValidatingAdmissionPolicyBinding are tested against six resources. Two of these resources do not match the binding, two match the binding but violate the policy, and the remaining two match the binding and do not violate the policy.

Policy manifest (check-deployment-replicas.yaml):

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: 'check-deployment-replicas'
spec:
matchConstraints:
resourceRules:
- apiGroups:
- apps
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- deployments
validations:
- expression: object.spec.replicas <= 2
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: 'check-deployment-replicas-binding'
spec:
policyName: 'check-deployment-replicas'
validationActions: [Deny]
matchResources:
namespaceSelector:
matchExpressions:
- key: environment
operator: In
values:
- staging
- production

Resource manifest (resource.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
name: testing-deployment-1
namespace: testing
labels:
app: busybox
spec:
replicas: 4
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: testing-deployment-2
namespace: testing
labels:
app: busybox
spec:
replicas: 2
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: staging-deployment-1
namespace: staging
labels:
app: nginx
spec:
replicas: 4
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: staging-deployment-2
namespace: staging
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: production-deployment-1
namespace: production
labels:
app: nginx
spec:
replicas: 4
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: production-deployment-2
namespace: production
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest

The above resource manifest contains the following:

  1. Two Deployments named testing-deployment-1 and testing-deployment-2 in the testing namespace. The first Deployment has four replicas, while the second Deployment has two.

  2. Two Deployments named staging-deployment-1 and staging-deployment-2 in the staging namespace. The first Deployment has four replicas, while the second Deployment has two.

  3. Two Deployments named production-deployment-1 and production-deployment-2 in the production namespace. The first Deployment has four replicas, while the second Deployment has two.

Variables manifest (values.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Values
metadata:
name: values
namespaceSelector:
- name: staging
labels:
environment: staging
- name: production
labels:
environment: production
- name: testing
labels:
environment: testing

Test manifest (kyverno-test.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: kyverno-test.yaml
policies:
- policy.yaml
resources:
- resource.yaml
variables: values.yaml
results:
- kind: Deployment
policy: check-deployment-replicas
isValidatingAdmissionPolicy: true
resources:
- testing-deployment-1
- testing-deployment-2
result: skip
- kind: Deployment
policy: check-deployment-replicas
isValidatingAdmissionPolicy: true
resources:
- staging-deployment-1
- production-deployment-1
result: fail
- kind: Deployment
policy: check-deployment-replicas
isValidatingAdmissionPolicy: true
resources:
- staging-deployment-2
- production-deployment-2
result: pass
Terminal window
$ kyverno test .
Loading test ( kyverno-test.yaml ) ...
Loading values/variables ...
Loading policies ...
Loading resources ...
Loading exceptions ...
Applying 1 policy to 6 resources ...
Checking results ...
│────│───────────────────────────│──────│────────────────────────────────────│────────│──────────│
│ ID │ POLICY │ RULE │ RESOURCE │ RESULT │ REASON │
│────│───────────────────────────│──────│────────────────────────────────────│────────│──────────│
│ 1 │ check-deployment-replicas │ │ Deployment/testing-deployment-1 │ Pass │ Excluded │
│ 2 │ check-deployment-replicas │ │ Deployment/testing-deployment-2 │ Pass │ Excluded │
│ 3 │ check-deployment-replicas │ │ Deployment/staging-deployment-1 │ Pass │ Ok │
│ 4 │ check-deployment-replicas │ │ Deployment/production-deployment-1 │ Pass │ Ok │
│ 5 │ check-deployment-replicas │ │ Deployment/staging-deployment-2 │ Pass │ Ok │
│ 6 │ check-deployment-replicas │ │ Deployment/production-deployment-2 │ Pass │ Ok │
│────│───────────────────────────│──────│────────────────────────────────────│────────│──────────│
Test Summary: 6 tests passed and 0 tests failed

To test a MutatingAdmissionPolicy, the test manifest must include the isMutatingAdmissionPolicy field set to true in the test results array. In addition, the patchedResources field must be included to specify the resource that is expected to be patched by the policy.

This example tests a policy that adds a label to a ConfigMap.

Policy manifest (policy.yaml):

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: 'add-label-to-configmap'
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE']
resources: ['configmaps']
failurePolicy: Fail
reinvocationPolicy: Never
mutations:
- patchType: 'ApplyConfiguration'
applyConfiguration:
expression: >
object.metadata.?labels["lfx-mentorship"].hasValue() ?
Object{} :
Object{ metadata: Object.metadata{ labels: {"lfx-mentorship": "kyverno"}}}

Resource manifest (resource.yaml):

apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
labels:
app: game
data:
player_initial_lives: '3'

Patched resource manifest (patched-resource.yaml):

This file defines what the ConfigMap should look like after the policy is applied.

apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
labels:
app: game
lfx-mentorship: kyverno
data:
player_initial_lives: '3'

Test manifest (kyverno-test.yaml):

apiVersion: kyverno.io/v1
kind: Test
metadata:
name: test
policies:
- policy.yaml
resources:
- resource.yaml
results:
- isMutatingAdmissionPolicy: true
kind: ConfigMap
patchedResources: patched-resource.yaml
policy: add-label-to-configmap
resources:
- game-demo
result: pass
Terminal window
$ kyverno test .
Loading test ( kyverno-test.yaml ) ...
Loading values/variables ...
Loading policies ...
Loading resources ...
Loading exceptions ...
Applying 1 policy to 1 resource with 0 exceptions ...
Checking results ...
│────│────────────────────────│──────│────────────────────────────────│────────│────────│
│ ID │ POLICY │ RULE │ RESOURCE │ RESULT │ REASON │
│────│────────────────────────│──────│────────────────────────────────│────────│────────│
│ 1 │ add-label-to-configmap │ │ v1/ConfigMap/default/game-demo │ Pass │ Ok │
│────│────────────────────────│──────│────────────────────────────────│────────│────────│
Test Summary: 1 tests passed and 0 tests failed
Example 2: Mutation with a Binding and Namespace Selector
Section titled “Example 2: Mutation with a Binding and Namespace Selector”

This example tests a policy that adds a label to ConfigMaps in specific namespaces.

Policy manifest (policy.yaml):

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
name: 'add-label-to-configmap'
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE']
resources: ['configmaps']
failurePolicy: Fail
reinvocationPolicy: Never
mutations:
- patchType: 'ApplyConfiguration'
applyConfiguration:
expression: >
object.metadata.?labels["lfx-mentorship"].hasValue() ?
Object{} :
Object{ metadata: Object.metadata{ labels: {"lfx-mentorship": "kyverno"}}}
---
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicyBinding
metadata:
name: 'add-label-to-configmap-binding'
spec:
policyName: 'add-label-to-configmap'
matchResources:
namespaceSelector:
matchExpressions:
- key: environment
operator: In
values:
- staging
- production

Resource manifest (resource.yaml):

apiVersion: v1
kind: ConfigMap
metadata:
name: matched-cm-1
namespace: staging
labels:
color: red
data:
player_initial_lives: '3'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: matched-cm-2
namespace: production
labels:
color: red
data:
player_initial_lives: '3'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: unmatched-cm
namespace: testing
labels:
color: blue
data:
player_initial_lives: '3'

Patched resource manifest (patched-resource.yaml):

apiVersion: v1
kind: ConfigMap
metadata:
name: matched-cm-1
namespace: staging
labels:
color: red
lfx-mentorship: kyverno
data:
player_initial_lives: '3'
---
apiVersion: v1
kind: ConfigMap
metadata:
name: matched-cm-2
namespace: production
labels:
color: red
lfx-mentorship: kyverno
data:
player_initial_lives: '3'

Variables manifest (values.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Value
metadata:
name: values
namespaceSelector:
- labels:
environment: staging
name: staging
- labels:
environment: production
name: production
- labels:
environment: testing
name: testing

Test manifest (kyverno-test.yaml):

apiVersion: kyverno.io/v1
kind: Test
metadata:
name: test
policies:
- policy.yaml
resources:
- resource.yaml
results:
- isMutatingAdmissionPolicy: true
kind: ConfigMap
patchedResources: patched-resource.yaml
policy: add-label-to-configmap
resources:
- matched-cm-1
- matched-cm-2
result: pass
variables: values.yaml
Terminal window
$ kyverno test .
Loading test ( kyverno-test.yaml ) ...
Loading values/variables ...
Loading policies ...
Loading resources ...
Loading exceptions ...
Applying 1 policy to 3 resources with 0 exceptions ...
Checking results ...
│────│────────────────────────│──────│──────────────────────────────────────│────────│────────│
│ ID │ POLICY │ RULE │ RESOURCE │ RESULT │ REASON │
│────│────────────────────────│──────│──────────────────────────────────────│────────│────────│
│ 1 │ add-label-to-configmap │ │ v1/ConfigMap/staging/matched-cm-1 │ Pass │ Ok │
│ 2 │ add-label-to-configmap │ │ v1/ConfigMap/production/matched-cm-2 │ Pass │ Ok │
│────│────────────────────────│──────│──────────────────────────────────────│────────│────────│
Test Summary: 2 tests passed and 0 tests failed

To test a ValidatingPolicy, the test manifest must include the isValidatingPolicy field set to true in the results[] array.

Below is an example of testing a ValidatingPolicy against two resources, one of which violates the policy.

Policy manifest (check-deployment-replicas.yaml):

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-deployment-replicas
spec:
matchConstraints:
resourceRules:
- apiGroups: ['apps']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['deployments']
validations:
- expression: 'object.spec.replicas <= 2'
message: 'Deployment replicas must be less than or equal to 2'

Resource manifest (deployments.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
name: good-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bad-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest

Test manifest (kyverno-test.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: kyverno-test
policies:
- policy.yaml
resources:
- deployments.yaml
results:
- isValidatingPolicy: true
kind: Deployment
policy: check-deployment-replicas
resources:
- bad-deployment
result: fail
- isValidatingPolicy: true
kind: Deployment
policy: check-deployment-replicas
resources:
- good-deployment
result: pass
Terminal window
$ kyverno test .
Loading test ( kyverno-test.yaml ) ...
Loading values/variables ...
Loading policies ...
Loading resources ...
Loading exceptions ...
Applying 1 policy to 2 resources with 0 exceptions ...
Checking results ...
│────│───────────────────────────│──────│────────────────────────────────────────────│────────│────────│
│ ID │ POLICY │ RULE │ RESOURCE │ RESULT │ REASON │
│────│───────────────────────────│──────│────────────────────────────────────────────│────────│────────│
│ 1 │ check-deployment-replicas │ │ apps/v1/Deployment/default/bad-deployment │ Pass │ Ok │
│ 2 │ check-deployment-replicas │ │ apps/v1/Deployment/default/good-deployment │ Pass │ Ok │
│────│───────────────────────────│──────│────────────────────────────────────────────│────────│────────│
Test Summary: 2 tests passed and 0 tests failed

Some policies need to make decisions based on the properties of the namespace where a resource is being created, such as its labels or name. The namespaceObject variable in CEL provides access to the full namespace object for this purpose.

When running kyverno test, the command operates in an offline mode and does not have access to a live cluster’s namespaces. To test policies that use namespaceObject, you must provide mock namespace definitions. This is done by creating a “values file” and referencing it in your kyverno-test.yaml.

Let’s start with a policy that disallows creating certain Deployments in the default namespace. It uses namespaceObject.metadata.name to check the name of the Namespace.

Policy manifest (disallow-default-deployment.yaml):

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-deployment-namespace
spec:
matchConstraints:
resourceRules:
- apiGroups: ['apps']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['deployments']
# This policy only applies to Deployments with the label `app: nginx`
objectSelector:
matchLabels:
app: nginx
validations:
# The validation logic checks the name of the Namespace object.
- expression: "namespaceObject.metadata.name != 'default'"
message: "Using 'default' namespace is not allowed for this application."

We define several Deployments to cover all test cases:

  1. A Deployment in the default namespace (should fail).
  2. A Deployment in a different namespace, staging (should pass).
  3. Two Deployments that do not have the app: nginx label (should be skipped).

Resource manifest (deployments.yaml):

# This deployment should FAIL because it's in the 'default' namespace.
apiVersion: apps/v1
kind: Deployment
metadata:
name: bad-deployment
namespace: default
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
---
# This deployment should PASS because it's in the 'staging' namespace.
apiVersion: apps/v1
kind: Deployment
metadata:
name: good-deployment
namespace: staging
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
---
# This deployment should be SKIPPED because it lacks the 'app: nginx' label.
apiVersion: apps/v1
kind: Deployment
metadata:
name: skipped-deployment-1
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:latest
---
# This deployment should also be SKIPPED for the same reason.
apiVersion: apps/v1
kind: Deployment
metadata:
name: skipped-deployment-2
namespace: staging
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:latest

This is the crucial step. We create a values file (e.g., values.yaml) to provide the mock Namespace objects. The kyverno test command will use these objects to populate the namespaceObject variable during the test run.

Values manifest (values.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Value
metadata:
name: values
namespaces:
- apiVersion: v1
kind: namespace
metadata:
labels:
environment: staging
name: staging
- apiVersion: v1
kind: namespace
metadata:
labels:
environment: default
name: default

Finally, we create the test manifest that ties everything together.

Test manifest (kyverno-test.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: kyverno-test.yaml
policies:
- policy.yaml
resources:
- resources.yaml
results:
- isValidatingPolicy: true
kind: Deployment
policy: check-deployment-namespace
resources:
- bad-deployment
result: fail
- isValidatingPolicy: true
kind: Deployment
policy: check-deployment-namespace
resources:
- good-deployment
result: pass
- isValidatingPolicy: true
kind: Deployment
policy: check-deployment-namespace
resources:
- skipped-deployment-1
- skipped-deployment-2
result: skip
variables: values.yaml
Terminal window
$ kyverno test .
Loading test ( kyverno-test.yaml ) ...
Loading values/variables ...
Loading policies ...
Loading resources ...
Loading exceptions ...
Applying 1 policy to 4 resources with 0 exceptions ...
Checking results ...
│────│────────────────────────────│──────│────────────────────────────────────────────│────────│──────────│
│ ID │ POLICY │ RULE │ RESOURCE │ RESULT │ REASON │
│────│────────────────────────────│──────│────────────────────────────────────────────│────────│──────────│
│ 1 │ check-deployment-namespace │ │ apps/v1/Deployment/default/bad-deployment │ Pass │ Ok │
│ 2 │ check-deployment-namespace │ │ apps/v1/Deployment/staging/good-deployment │ Pass │ Ok │
│ 3 │ check-deployment-namespace │ │ apps/Deployment/v1 │ Pass │ Excluded │
│ 4 │ check-deployment-namespace │ │ apps/Deployment/v1 │ Pass │ Excluded │
│────│────────────────────────────│──────│────────────────────────────────────────────│────────│──────────│
Test Summary: 4 tests passed and 0 tests failed

Policies often need to validate resources based on external data, such as a value stored in a ConfigMap. Kyverno’s custom CEL function resource.Get() allows policies to fetch these external resources from the cluster.

When using kyverno test for offline testing, you must provide this external resource as “context” so that the resource.Get() function can resolve successfully. This can be done by referencing a context file directly from your kyverno-test.yaml.

In this example, we will test a policy that validates a Pod’s name against a value stored in a ConfigMap.

First, let’s define a policy named check-pod-name-from-configmap. This policy uses resource.Get() to fetch a ConfigMap named policy-cm and then checks if the incoming Pod’s name matches the name key in the ConfigMap’s data.

Policy manifest (check-pod-name-from-configmap.yaml):

policy.yaml
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-pod-name-from-configmap
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['pods']
variables:
# Get the ConfigMap 'policy-cm' from the Pod's namespace.
- name: cm
expression: >-
resource.Get("v1", "configmaps", object.metadata.namespace, "policy-cm")
validations:
# The Pod is valid only if its name matches the value from the ConfigMap.
- expression: >-
object.metadata.name == variables.cm.data.name

Next, we create a context file that contains the mock ConfigMap policy-cm. The kyverno test command will load this file and make its contents available to the resource.Get() function during the test.

Context manifest (context.yaml):

context.yaml
apiVersion: cli.kyverno.io/v1alpha1
kind: Context
metadata:
name: test-context
spec:
resources:
- apiVersion: v1
kind: ConfigMap
metadata:
namespace: default
name: policy-cm
data:
# The 'name' key specifies that the only valid pod name is 'good-pod'.
name: good-pod

We define two Pod manifests: good-pod, whose name matches the value in our context, and bad-pod, whose name does not.

Resource manifest (pods.yaml):

# This Pod should PASS validation.
apiVersion: v1
kind: Pod
metadata:
name: good-pod
spec:
containers:
- name: nginx
image: nginx
---
# This Pod should FAIL validation.
apiVersion: v1
kind: Pod
metadata:
name: bad-pod
spec:
containers:
- name: nginx
image: nginx

Finally, we create the kyverno-test.yaml file. The key here is the context: context.yaml field, which links our mock ConfigMap to the test.

Test manifest (kyverno-test.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: kyverno-test.yaml
policies:
- policy.yaml
resources:
- pod1.yaml
- pod2.yaml
results:
- isValidatingPolicy: true
kind: Pod
policy: disallow-host-path
resources:
- bad-pod
result: fail
- isValidatingPolicy: true
kind: Pod
policy: disallow-host-path
resources:
- good-pod
result: pass
context: context.yaml
Terminal window
$ kyverno test .
Loading test ( kyverno-test.yaml ) ...
Loading values/variables ...
Loading policies ...
Loading resources ...
Loading exceptions ...
Applying 1 policy to 2 resources with 0 exceptions ...
Checking results ...
│────│────────────────────│──────│─────────────────────────│────────│────────│
│ ID │ POLICY │ RULE │ RESOURCE │ RESULT │ REASON │
│────│────────────────────│──────│─────────────────────────│────────│────────│
│ 1 │ disallow-host-path │ │ v1/Pod/default/bad-pod │ Pass │ Ok │
│ 2 │ disallow-host-path │ │ v1/Pod/default/good-pod │ Pass │ Ok │
│────│────────────────────│──────│─────────────────────────│────────│────────│
Test Summary: 2 tests passed and 0 tests failed

Testing Policies with Offline HTTP Mocks and Global Context

Section titled “Testing Policies with Offline HTTP Mocks and Global Context”

CEL policies that call an external HTTP service via http.Get() or http.Post(), or look up a GlobalContextEntry, can be tested offline with kyverno test. Starting in Kyverno CLI v1.14, kyverno-test.yaml accepts two optional fields — apiCallResponses and globalContextEntries — so expected external data can be declared inline for local development and CI/CD pipelines.

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: my-test
policies:
- policy.yaml
resources:
- resources.yaml
# Inline mock responses for CEL http.Get() / http.Post() calls.
apiCallResponses:
- url: https://external-service.example.com/api/check # URL to match (required)
method: GET # HTTP method (optional; matches any method if omitted)
response:
statusCode: 200 # Must be a valid HTTP status code (100–599)
body: # Arbitrary JSON object
allowed: true
# Inline mock data for GlobalContextEntry references.
# Each entry requires exactly ONE of: data, resources, or resourceFiles (mutually exclusive).
globalContextEntries:
- name: gctxentry-deployments # Must match the GlobalContextEntry name used in the policy
# Option 1: arbitrary JSON root value
data:
deployments:
- name: existing-deployment-1
- name: existing-deployment-2
# Option 2: inline Kubernetes resource manifests (mimics kubernetesResource cache shape)
# resources:
# - apiVersion: apps/v1
# kind: Deployment
# metadata:
# name: existing-deployment-1
# namespace: default
# Option 3: YAML file paths relative to the test directory
# resourceFiles:
# - gce-deployments.yaml
results:
- kind: Pod
isValidatingPolicy: true
policy: my-policy
resources:
- default/my-pod
result: pass

The following example tests a ValidatingPolicy that calls an external configuration endpoint using Kyverno’s CEL http.Get() library function.

Policy manifest (policy.yaml):

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-external-config
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: [CREATE, UPDATE]
resources: [pods]
variables:
- name: config
expression: http.Get("https://example.com/config")
validations:
- expression: variables.config.allowed == true
message: 'External config check failed'

Test manifest (kyverno-test.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: cel-http-get-mock-test
policies:
- policy.yaml
resources:
- resources.yaml
apiCallResponses:
- method: GET
url: https://example.com/config
response:
statusCode: 200
body:
allowed: true
results:
- isValidatingPolicy: true
kind: Pod
policy: check-external-config
resources:
- default/test-pod
result: pass

Run the test:

Terminal window
kyverno test .

Expected output:

Loading test ( kyverno-test.yaml ) ...
Applying 1 policy to 1 resource with 0 exceptions ...
Checking results ...
│────│───────────────────────│──────│─────────────────────────────│────────│────────│
│ ID │ POLICY │ RULE │ RESOURCE │ RESULT │ REASON │
│────│───────────────────────│──────│─────────────────────────────│────────│────────│
│ 1 │ check-external-config │ │ v1/Pod/default/test-pod │ Pass │ Ok │
│────│───────────────────────│──────│─────────────────────────────│────────│────────│
Test Summary: 1 tests passed and 0 tests failed

The following example tests a ValidatingPolicy that calls an external authorization service using Kyverno’s CEL http.Post() library function. Both a pass scenario (the service allows the request) and a deny scenario (the service rejects the request) are shown.

Policy manifest (policy.yaml):

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-pod-admission
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: [CREATE, UPDATE]
resources: [pods]
validations:
- expression: >-
http.Post("https://authz.example.com/validate",
{"namespace": object.metadata.namespace, "name": object.metadata.name}).body.allowed == true
message: 'External authorization service rejected this pod'

Test manifest — pass scenario (kyverno-test.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: cel-http-post-mock
policies:
- policy.yaml
resources:
- resources.yaml
apiCallResponses:
- method: POST
url: https://authz.example.com/validate
response:
statusCode: 200
body:
allowed: true
results:
- isValidatingPolicy: true
kind: Pod
policy: check-pod-admission
resources:
- default/test-pod
result: pass

Test manifest — deny scenario (kyverno-test.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: cel-http-post-mock-deny
policies:
- policy.yaml
resources:
- resources.yaml
apiCallResponses:
- method: POST
url: https://authz.example.com/validate
response:
statusCode: 200
body:
allowed: false
results:
- isValidatingPolicy: true
kind: Pod
policy: check-pod-admission
resources:
- default/test-pod
result: fail

Run the test:

Terminal window
kyverno test .

Expected output (pass scenario):

Loading test ( kyverno-test.yaml ) ...
Applying 1 policy to 1 resource with 0 exceptions ...
Checking results ...
│────│───────────────────────│──────│─────────────────────────────│────────│────────│
│ ID │ POLICY │ RULE │ RESOURCE │ RESULT │ REASON │
│────│───────────────────────│──────│─────────────────────────────│────────│────────│
│ 1 │ check-pod-admission │ │ v1/Pod/default/test-pod │ Pass │ Ok │
│────│───────────────────────│──────│─────────────────────────────│────────│────────│
Test Summary: 1 tests passed and 0 tests failed

Mocking GlobalContextEntry data for CEL policies

Section titled “Mocking GlobalContextEntry data for CEL policies”

The globalContextEntries field supplies offline data for CEL policies that call globalContext.Get(). Declare either arbitrary JSON with data, Kubernetes manifests with resources, or fixture file paths with resourceFiles for each entry. These sources are mutually exclusive.

For example, a ValidatingPolicy can read a mocked collection of Deployments:

Policy manifest (policy.yaml):

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-deployment-count
spec:
matchConstraints:
resourceRules:
- apiGroups: [apps]
apiVersions: [v1]
operations: [CREATE, UPDATE]
resources: [deployments]
variables:
- name: deploymentData
expression: globalContext.Get("gctxentry-deployments", "")
validations:
- expression: >-
variables.deploymentData != null && size(variables.deploymentData.deployments) >= 2
message: 'At least two deployments must be present in global context'

Test manifest (kyverno-test.yaml):

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: mock-global-context
policies:
- policy.yaml
resources:
- resources.yaml
globalContextEntries:
- name: gctxentry-deployments
data:
deployments:
- name: existing-deployment-1
- name: existing-deployment-2
results:
- isValidatingPolicy: true
kind: Deployment
policy: check-deployment-count
resources:
- test-ns/test-deployment
result: pass

For larger fixtures, replace data with resourceFiles and list YAML manifests relative to the test directory, or use resources to define them inline. The policy’s CEL expression must read the shape of the data supplied by the selected source.