Use assertions¶
Chainsaw allows declaring complex assertions with a simple and no-code approach, allowing assertions based on comparisons beyond simple equality, working with arrays, and other scenarios that could not be achieved before.
Tip
Under the hood, Chainsaw uses kyverno-json assertion trees. Refer to the assertion trees documentation for more details on the supported syntax.
Basic assertion¶
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: example
spec:
steps:
- try:
- assert:
resource:
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
spec:
replicas: 2
When asking Chainsaw to execute the assertion above, it will look for a deployment named coredns
in the kube-system
namespace and will compare the existing resource with the (partial) resource definition contained in the assertion.
In this specific case, if the field spec.replicas
is set to 2 in the existing resource, the assertion will be considered valid. If it is not equal to 2 the assertion will be considered failed.
This is the most basic assertion Chainsaw can evaluate.
Slightly less basic assertion¶
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: example
spec:
steps:
- try:
- assert:
resource:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: kube-dns
namespace: kube-system
spec:
replicas: 2
This time we are not providing a resource name.
Chainsaw will look up all deployments with the k8s-app: kube-dns
label in the kube-system
namespace. The assertion will be considered valid if at least one deployment matches the (partial) resource definition contained in the assertion. If none match, the assertion will be considered failed.
Apart from the resource lookup process being a little bit more interesting, this kind of assertion is essentially the same as the previous one. Chainsaw is basically making a decision by comparing an actual and expected resource.
Beyond simple equality¶
The assertion below will check that the number of replicas for a deployment is greater than 1 AND less than 4.
Chainsaw doesn't need to know the exact expected number of replicas. The (replicas > 1 && replicas < 4)
expression will be evaluated until the result is true
or the operation timeout expires (making the assertion fail).
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: example
spec:
steps:
- try:
- assert:
resource:
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
spec:
(replicas > `1` && replicas < `4`): true
Tip
To indicate that a key or value in the YAML document is an expression, simply place the element between parentheses:
this is an expression
-> interpreted as astring
(this is an expression)
-> interpreted as a JMESPath expression
Working with arrays¶
Chainsaw query language makes it easy to assert on arrays. You can filter and transform arrays to select what you want to assert.
Filtering¶
In the example below we are creating a resource, then we assert that a condition with type == 'Ready'
exists and has a field matching status: 'True'
:
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: example
spec:
steps:
- try:
- apply:
resource:
apiVersion: tempo.grafana.com/v1alpha1
kind: TempoStack
metadata:
name: simplest
spec:
storage:
secret:
name: minio
type: s3
...
- assert:
resource:
apiVersion: tempo.grafana.com/v1alpha1
kind: TempoStack
metadata:
name: simplest
status:
# filter conditions array to keep elements where `type == 'Ready'`
# and assert there's a single element matching the filter
# and that this element status is `True`
(conditions[?type == 'Ready']):
- status: 'True'
Iterating¶
Being able to filter arrays allows selecting the elements to be processed.
On top of that, Chainsaw allows iterating over array elements to validate each item separately.
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: example
spec:
steps:
- try:
- assert:
resource:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: kube-dns
namespace: kube-system
spec:
template:
spec:
# the `~` modifier tells Chainsaw to iterate over the array elements
~.(containers):
securityContext: {}
This assertion uses the ~
modifier and Chainsaw will evaluate descendants once per element in the array.
Comprehensive reporting¶
Chainsaw offers detailed resource diffs upon assertion failures.
In the example below, the assertion failure message (metadata.annotations.foo: Invalid value: "null": Expected value: "bar"
) is augmented with a resource diff.
It provides a clear view of discrepancies between expected and actual resources and gives more context around the specific failure (we can easily identify the owner of the offending pod for example).
| 09:55:50 | deployment | step-1 | ASSERT | RUN | v1/Pod @ chainsaw-rare-liger/*
| 09:56:20 | deployment | step-1 | ASSERT | ERROR | v1/Pod @ chainsaw-rare-liger/*
=== ERROR
---------------------------------------------------
v1/Pod/chainsaw-rare-liger/example-5477b4ff8c-tnhd9
---------------------------------------------------
* metadata.annotations.foo: Invalid value: "null": Expected value: "bar"
--- expected
+++ actual
@@ -1,10 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
- annotations:
- foo: bar
labels:
app: nginx
+ name: example-5477b4ff8c-tnhd9
namespace: chainsaw-rare-liger
+ ownerReferences:
+ - apiVersion: apps/v1
+ blockOwnerDeletion: true
+ controller: true
+ kind: ReplicaSet
+ name: example-5477b4ff8c
+ uid: 118abe16-ec42-4894-83db-64479c4aac6f
spec: {}
| 09:56:20 | deployment | step-1 | TRY | DONE |
Next step¶
To continue our exploration of the main Chainsaw features, let's look at bindings and resource templating next.