Bouw je Kubernetes operators, controllers of Helm charts, dan is Kyverno Chainsaw op dit moment de sterkste tool om end-to-end tests te schrijven als declaratieve YAML, in plaats van als Go-boilerplate of bash. Het draait je manifests tegen een echte cluster, assert op de werkelijke clusterstaat met JMESPath, en zit al in de productie-CI van de OpenTelemetry Operator, Crossplane's testtool uptest en Kyverno zelf. De docs leggen het wat nogal abstract uit; dit artikel gaat over het waarom, het hoe en de stukken die niemand op de landingspagina zet.
TL;DR
- Chainsaw is een declaratief end-to-end testframework voor Kubernetes. Je beschrijft tests als
Testcustom resources in YAML en het draait ze tegen een echte cluster (kind, k3s, wat je CI ook opspint). - De killer-feature zijn assertion trees: subset matching plus JMESPath-expressies, zodat je alleen de velden assert waar je om geeft en condities als "ready replicas gelijk aan gewenste replicas" inline kunt uitdrukken.
- Echte gebruikers zijn onder meer Kyverno, kyverno/policies, Crossplane/uptest, de OpenTelemetry Operator en de OT-CONTAINER-KIT Redis-operator.
- Het is de de-facto keuze voor declaratief operator-testen, maar het vervangt Go-gebaseerde integratietests (Ginkgo + envtest) niet, en het heeft een echte cluster nodig. Dat maakt je CI trager.
- De laatste release is v0.2.15 (6 mei 2026); het is een sub-project van Kyverno, dat in maart 2026 binnen de CNCF graduate werd.
Inhoudsopgave
- TL;DR
- Inhoudsopgave
- Wat Chainsaw is, en welk gat het vult
- Waar Chainsaw vandaan komt
- Hoe een Chainsaw-test er echt uitziet
- Assertion trees: de feature die de overstap waard maakt
- Wie Chainsaw echt in productie draait
- Chainsaw versus de alternatieven
- Wanneer Chainsaw niet de juiste tool is
Wat Chainsaw is, en welk gat het vult
Chainsaw test Kubernetes zoals je Kubernetes bedient: declaratief. Een test is een Test-resource (apiVersion: chainsaw.kyverno.io/v1alpha1) met een lijst steps. Elke step past manifests toe, wacht, en assert dat de cluster in de staat is beland die je verwachtte. Geen Go, geen testharness om te compileren, geen in-process fake API-server die net doet alsof het Kubernetes is.
Het gat dat het vult is specifiek. Schrijf je een controller, dan heb je drie slechte opties en één goede. Je schrijft honderden regels Go met Ginkgo, Gomega en envtest: krachtig, maar zwaar en vastgeketend aan een Go-toolchain. Of je gebruikt KUTTL: wél declaratief, maar beperkt in wat het kan asserten. Of je plakt bash en kubectl aan elkaar, wat werkt tot het niet meer werkt en je vervolgens failure-output krijgt die niemand kan lezen. Chainsaw is de vierde optie: declaratief als KUTTL, expressief als code, en maanden later nog leesbaar.
De officiële introductie zegt het doel rechtuit: het is "primarily developed to run end-to-end tests in Kubernetes clusters, meant to test Kubernetes operators by running a sequence of steps and asserting various conditions." Dat framing is belangrijk. Chainsaw is een end-to-end tool. Het draait de echte reconcile-loop tegen een echte API-server, dus het vangt precies de bugs die alleen opduiken als een echte controller op een echte resource reageert.
Waar Chainsaw vandaan komt
Chainsaw bestaat omdat het Kyverno-team het zat was om met z'n testtooling te vechten. Ze begonnen met KUTTL voor Kyverno's eigen e2e-tests, liepen tegen de grenzen aan en forkten het. Die fork dreef zo ver af dat, in hun eigen woorden, "the changes we were making was simply too large to have a chance to be incorporated upstream." Dus bouwden ze het in 2023 helemaal opnieuw als Chainsaw, met bewust een testbestandsformaat dat dicht bij dat van KUTTL bleef zodat bestaande suites konden migreren (introductie-docs).
De hoofdauteur is Charles-Edouard Brétéché (@eddycharly), Senior Staff Engineer bij Nirmata en de architect van het assertion-tree-model. De huidige maintainers zijn volgens de MAINTAINERS.md van de Kyverno-community Brétéché, Shubham Gupta (@shubham-cmyk) en Mariam Fahmy (@MariamFahmy98) van Cloudflare. Chainsaw is een expliciet sub-project van Kyverno onder de kyverno GitHub-organisatie, en valt daarmee onder de paraplu van een project dat op 16 maart 2026 CNCF Graduated status bereikte. Dat is het hoogste CNCF-volwassenheidsniveau, en een redelijk governance-signaal als je infratooling op de levensduur van een project baseert.
Het ship't vaak. De releases-pagina laat 87 getagde releases zien, met v0.2.15 op 6 mei 2026, inclusief support voor status subresource patches en snellere namespace-deletes. Voor een testtool zeggen frequente releases tegen echte bugmeldingen meer dan een hoog aantal sterren.
Hoe een Chainsaw-test er echt uitziet
Een testmap bevat een chainsaw-test.yaml-bestand. Chainsaw scant recursief, draait elke test standaard in z'n eigen ephemerale namespace en ruimt die daarna op. Hieronder een complete, draaibare test die een Deployment toepast en assert dat hij volledig ready wordt, met een catch-blok dat diagnostics dumpt als de assert faalt:
# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: deployment-becomes-ready
spec:
steps:
- try:
- apply:
file: deployment.yaml # je manifest under test
- assert:
resource:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
status:
(readyReplicas == replicas): true # JMESPath-expressie
catch: # draait alleen als try faalt
- describe:
apiVersion: apps/v1
kind: Deployment
- podLogs:
selector: app=nginx
De vier blokken die een step kan hebben zijn het kennen waard. try is het happy path. catch draait alleen als een try-operatie faalt, en daar zet je describe, events en podLogs in zodat een fout debugbaar wordt in plaats van cryptisch. finally draait altijd na de step. cleanup wordt uitgesteld tot de hele test klaar is (step-docs).
De set operaties is breed. De kern-operaties zijn apply, create, assert, error, delete, update, patch, script, command en sleep. Daarbovenop zitten kubectl-achtige helpers die je in elk blok kunt gebruiken: describe, events, get, podLogs, proxy en wait (operations-referentie). De error-operatie is degene waar KUTTL nooit een net antwoord op had: het assert dat een resource niet bestaat, of dat het toepassen van een manifest wordt geweigerd. Schrijf je admission policies, dan is dat hoe je bewijst dat een foute resource geblokkeerd wordt. Precies wat je wilt verifiëren op een cluster die Kyverno-policies draait.
Timeouts zijn granulair en cascaderen van global naar test naar step. Er zijn zes losse types: applyTimeout, assertTimeout, cleanupTimeout, deleteTimeout, errorTimeout en execTimeout (timeout-docs). Die splitsing is bewust. Asserten dat een Deployment uitrolt kan veel langer duren dan een ConfigMap deleten, en met één global timeout moet je voor allebei hetzelfde slechte getal kiezen.
De tooling rond het bestand is goed voor een project van deze omvang. chainsaw lint valideert testbestanden, chainsaw create scaffold't nieuwe, en chainsaw export schemas schrijft JSON-schema's die je in je editor kunt hangen. Zet een # yaml-language-server: $schema=...-comment bovenaan een test en VS Code geeft je autocompletion en validatie terwijl je typt (JSON-schema-docs). Er is ook een chainsaw migrate kuttl-commando dat bestaande KUTTL-tests en -config omzet. Dat is de oprit voor teams die KUTTL achter zich laten.
Assertion trees: de feature die de overstap waard maakt
Alles hierboven is handig. Assertion trees zijn de reden om écht over te stappen. Het model komt uit kyverno-json: bij elke node van de YAML waartegen je assert kan Chainsaw eerst een JMESPath-projectie toepassen voordat het verder afdaalt, en een leaf-node triggert een vergelijking (assertion-trees quick-start).
In de praktijk geeft je dat drie dingen die KUTTL's structurele gelijkheid niet kan.
Ten eerste subset matching standaard. Je assertion noemt alleen de velden waar je om geeft. Elk ander veld op de live resource wordt genegeerd. Je hoeft geen complete Deployment-spec na te bouwen om één status-veld te controleren.
Ten tweede expressies op elke node. Zet een key tussen haakjes en het wordt een JMESPath-expressie. Wil je asserten dat replicas binnen een range vallen in plaats van een exact getal?
spec:
(replicas > `1` && replicas < `4`): true
Ten derde filteren en itereren over arrays, en daar zit nou net de echte controller-status. Assert dat de Ready-conditie True is, zonder je druk te maken om de volgorde of de andere condities:
(conditions[?type == 'Ready']):
- status: 'True'
Of waaier een assertion uit over elk element van een array, bijvoorbeeld dat elke container een securityContext draagt:
~.(containers):
securityContext: {}
Dat laatste patroon is precies de check die je wilt als je valideert dat een mutating policy een veld over álle containers heeft geïnjecteerd, niet alleen de eerste. Met KUTTL schrijf je dan een aparte assertion per container of val je terug op een script. Met Chainsaw is het één node. De Kyverno-blogpost over assertion trees loopt het model dieper door, en die is het lezen waard voordat je iets niet-triviaals schrijft, want de syntax ((expressie), ~. voor iteratie, ->binding voor variabelen) leest de eerste keer echt niet vanzelf.
Wie Chainsaw echt in productie draait
Dit is het stuk dat de documentatie onderbelicht, en juist het stuk dat je beslissing zou moeten sturen. De 576 GitHub-sterren van Chainsaw onderschatten het bereik flink, want adoptie zit in CI-pipelines, niet in sterren.
Kyverno zelf. Chainsaw is Kyverno's primaire e2e-testtool, het canonieke dogfood-geval. In kyverno/kyverno issue #12065, in februari 2025 geopend door Brétéché, staat het er zo: "Currently Kyverno uses Chainsaw as the primary testing tool, which executes end-to-end tests on a real cluster." Diezelfde issue is eerlijk dat de suite "takes a long time to be executed," en dat is een nuttige hint over de trade-off. Daar kom ik op terug.
De Kyverno policy-catalogus. De kyverno/policies-repository gebruikt Chainsaw om de officiële policy-bibliotheek te valideren, met een umbrella-issue die de migratie van alle sample-policy-tests bijhoudt.
Crossplane en Upbound. Crossplane's end-to-end testtool, uptest, draait onder de motorkap op Chainsaw en genereert er testcases mee op basis van input. Upbound migreerde uptest van KUTTL naar Chainsaw en legde uit waarom: "while kuttl meets the basic requirements for Uptest so far, chainsaw is a more up-to-date and maintainable tool, with responsive maintenance, detailed documentation, and frequent releases." Elke Crossplane-provider die via uptest getest wordt, wordt dus op Chainsaw getest.
De OpenTelemetry Operator. Dit is het schoonste voorbeeld van productie-CI. De repo heeft een gecommitte .chainsaw.yaml, e2e-tests onder tests/e2e*/, en een GitHub Actions-workflow die chainsaw test draait over een matrix van Kubernetes-versies (van 1.25 tot 1.33) en JUnit XML-rapporten uitspuugt. Wil je een referentie-implementatie om te kopiëren, dan is dit 'm.
De OT-CONTAINER-KIT Redis-operator. Die draait alle e2e-tests via Chainsaw onder tests/e2e-chainsaw/. Het is ook de bron van het eerlijkste datapunt in dit hele artikel: een issue uit oktober 2024 merkt op dat de Chainsaw-tests "quite flakey and take a significant amount of time to run" zijn. Daarover later meer.
Er zijn ook persoonlijke verhalen. Maram El-Sayed, Quality Coach bij OutSystems, pakte Chainsaw om 15+ Flux-gebaseerde GitOps-repositories te testen die daarvoor geen geautomatiseerde tests hadden, en sprak erover in april 2025: "Chainsaw helped us catch Kubernetes issues early and saved us hours of debugging." En een Medium-stuk van Shivam Tiwarri beschrijft hoe hij 200+ regels Go-testcode voor een eigen controller verving door ongeveer vier regels Chainsaw-YAML, met flaky tests, ondoorzichtige logica en moeizame onboarding als wat hem over de streep trok.
Chainsaw versus de alternatieven
KUTTL. Nog actief onderhouden (v0.26.0 kwam uit op 11 mei 2026), en de tool waar Chainsaw oorspronkelijk van geforkt is. Wat het mist is de expressiviteit: geen JMESPath-assertions, geen error-operatie om te bewijzen dat een resource geweigerd wordt, geen catch/finally-debughooks en geen bindings. Zijn je tests simpele structurele checks, dan is KUTTL nog prima. Zodra je conditionele assertions, negatieve tests of leesbare failure-output nodig hebt, is Chainsaw de sterkere keuze, en het migratiecommando bestaat juist omdat zoveel teams die grens overgaan.
Ginkgo/Gomega met envtest. Dit is het door kubebuilder aanbevolen pad voor integratie-tests, en voor in-process controller-logica is het echt krachtiger. Maar envtest spint een API-server en etcd op zonder scheduler en zonder kubelet, dus controllers die afhangen van echt node-gedrag slagen voor de test en falen in productie. Het vraagt ook Go-vaardigheid, en dat sluit veel platformteams uit. De docs van controller-runtime waarschuwen zelf dat fake-client-tests "gradually re-implement poorly-written impressions of a real API server." Chainsaw draait tegen een echte cluster en vangt dus scheduler- en node-gedrag dat envtest structureel niet kan. De trade-off is snelheid: een echte cluster is trager dan een in-process server.
Bash plus kubectl. Prima voor een smoke test, beroerd op schaal. Geen retry of backoff op assertions, geen gestructureerde cleanup, en failure-output die langer kost om te lezen dan de code kostte om te schrijven. Elk team dat ik naar Chainsaw heb zien overstappen noemt dat ze hier juist van wegliepen.
Terratest en de scaffolding van operator-sdk. Terratest is een Go-library gericht op Terraform en cloud-resources; het kan een cluster porren maar biedt geen Kubernetes-native assertion-model. operator-sdk scaffold't standaard Ginkgo/envtest en noemt Chainsaw expliciet als een valide alternatief e2e-pad, wat een stille endorsement is.
De eerlijke samenvatting: voor declaratief, YAML-native e2e-testen van operators, controllers en charts is Chainsaw de de-facto keuze. Het heeft Ginkgo/envtest niet verdrongen in Go-centrische projecten, en dat probeert het ook niet. Ze testen verschillende lagen. Een volwassen operator-project draait vaak allebei: snelle Go-integratietests voor reconcile-logica, Chainsaw voor volledige e2e tegen een echte cluster.
Wanneer Chainsaw niet de juiste tool is
De grootste beperking is structureel: Chainsaw heeft een echte cluster nodig. Kind of k3s opspinnen kost minuten per CI-run, en die overhead is onvermijdelijk voor elke serieuze operator-test. Kyverno's eigen maintainer kaartte dit aan in issue #12065, met de opmerking dat de e2e-suite traag is en zelfs gebruikt wordt voor "very basic/simple cases" waar een snellere in-process laag beter zou zijn. v0.2.15 voegde snelle namespace-deletes toe om er wat van de scherpte af te halen, maar de cluster-eis is de prijs van echt gedrag testen. Wil je unit-tests van reconcile-logica in milliseconden, dan zit je met Chainsaw in de verkeerde laag.
Flakiness is reëel, zoals de Redis-operator-issue laat zien. Dat is niet uniek voor Chainsaw; elke cluster-gebaseerde e2e-suite is timing-gevoelig. Maar het retry- en timeout-model schermt je niet volledig af van flakes op een CI-runner met resource-druk, en je gaat tijd kwijt zijn aan het tunen van timeouts.
De JMESPath-syntax heeft een leercurve. Dezelfde expressiviteit die assertion trees zo goed maakt, maakt ze op dag één ook ondoorzichtig. Engineers die nieuw zijn in zowel Chainsaw als JMESPath krijgen een dubbele klim. Brétéché sprak zelf openlijk over de moeite om adoptie te krijgen, wat je vertelt dat de frictie erkend wordt en niet verstopt.
Het is alleen e2e, en alleen Kubernetes. Chainsaw heeft geen mening over de HTTP-responses van je applicatie, je databasestaat of message queues. Als YAML-assertions opraken, val je terug op script- en command-blokken met arbitraire bash, en dan komt precies de imperatieve fragiliteit terug die Chainsaw juist moest wegnemen. Gebruik het voor de Kubernetes-resourcegraaf; rek het niet op tot een algemene integratieharness.
Twee kleinere punten voor wie er een langetermijnweddenschap op aangaat. Test-suite-organisatie is dun: er is geen first-class concept van benoemde suites of tags voorbij regex-filtering op testnamen, met een open discussie die erom vraagt, dus grote repos leunen op mapstructuur. En de maintainerspoel is drie mensen, twee daarvan bij Nirmata, wat een normaal bus-factor-profiel is voor een CNCF-sub-project van deze omvang maar het wegen waard als Chainsaw dragend wordt voor je release-pipeline. Voor een tool die operator-testen verandert van een Go-project in een map leesbare YAML, zijn dat trade-offs die ik zou nemen.