Kyverno n'est certainement pas la première solution qui vient à l'esprit du DevOps averti lorsqu'il est question de tests unitaires et de charts Helm.
Certains outils comme helm-unittest ou conftest (si vous aimez le Rego 😄) sont souvent choisis d'office pour répondre à ce besoin. Quelle que soit leur degré de popularité, tous deux imposent de passer par une une courbe d'apprentissage afin d'être maîtrisés.
Bien que la découverte de nouvelles technologies fasse partie intégrante de notre métier, il peut parfois être intéressant de faire preuve de pragmatisme pour gagner en productivité ⚡. Ainsi, réutiliser une solution déjà en place représente souvent un gain de temps appréciable et facilite l'adoption au sein des équipes.
Le principe
Pour réaliser notre premier test, il faut commencer par construire le chart cible. Cela peut être fait avec les valeurs par défaut du chart ou bien en utilisant des valeurs spécifiques à un scénario souhaité.
helm template charts/my-awesome-chart --values=policies/my-awesome-chart/basic/values.yaml
Il est ensuite possible de stocker ou de rediriger le yaml résultant de la commande précédente pour le rediriger vers une commande kyverno. Cette dernière évaluera la conformité de notre résultat vis-à-vis d'une politique choisie :
helm template charts/my-awesome-chart --values=policies/my-awesome-chart/basic/values.yaml | kyverno apply --resource - policies/my-awesome-chart/basic/policy.yaml
En fonction du contenu de la politique ainsi que du template, un résultat de ce type est obtenu :
Applying 3 policy rules to 4 resources...
pass: 2, fail: 0, warn: 0, error: 0, skip: 10

Comment rédiger une politique orientée test unitaire ?
Au risque de décevoir, la réponse à cette question est simple : de la même manière que n'importe quelle autre politique !
Et c'est bien là que réside tout l'intérêt de réutiliser Kyverno dans ce contexte.
Pour donner quelques pistes, voici deux exemples de tests qui peuvent être transformés pour s'adapter à vos besoins.
Test n°1
Ce premier test valide la présence des trois IPs listées dans une ressource de type Middleware (Traefik) nommée ipwhitelist.
Sont ici combinés :
- Une règle de validation de type
deny🚫 - L'opérateur
anyentraînant le déclenchement de la règledenydès qu'une condition est vérifiée - Trois conditions recherchant la présence d'une IP dans une liste
Si jamais une ou plusieurs des trois IPs s'avèrent absentes, alors la condition any est validée et le test échoue ❌.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: validate-ipwhitelist-middleware
annotations:
policies.kyverno.io/title: Validate IP Whitelist Middleware
policies.kyverno.io/category: Security
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Middleware
policies.kyverno.io/description: >-
Validates that IP whitelist middleware contains the correct IP ranges
spec:
validationFailureAction: audit
background: true
rules:
- name: validate-ipwhitelist
match:
resources:
kinds:
- Middleware
validate:
message: "IP whitelist middleware must contain the correct IP ranges"
deny:
conditions:
any:
- key: "{{ contains(request.object.spec.ipWhiteList.sourceRange, '10.10.0.0/16') }}"
operator: Equals
value: false
- key: "{{ contains(request.object.spec.ipWhiteList.sourceRange, '192.168.0.0/24') }}"
operator: Equals
value: false
- key: "{{ contains(request.object.spec.ipWhiteList.sourceRange, '8.8.8.8/32') }}"Test n°2
Plus simple, ce second test se compose de :
- Une règle de validation de type
pattern - Un opérateur de négation
X()vérifiant qu'un champ n'est pas défini
Tant que le champ secretObjects reste absent de .spec.SecretProviderClass, la condition de négation est respectée et le test passe ✅.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: validate-standard-app
annotations:
policies.kyverno.io/title: Standard SecretProviderClass
policies.kyverno.io/category: Best Practices
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: SecretProviderClass
policies.kyverno.io/description: >-
SecretProviderClass objects must have specific attributes.
spec:
validationFailureAction: audit
background: true
rules:
- name: check-for-null-secretobjects
match:
resources:
kinds:
- SecretProviderClass
name: "myapp"
validate:
message: "The SecretProviderClass secretObjects should not be defined (empty secrets list)."
pattern:
spec:
X(secretObjects): "null"Et l'automisation dans tout ça ?
Évidemment, tout cela n'a que peu d'intérêt sans un processus d'intégration continue pour automatiser l'exécution de ces tests ⚙️.
Cette proposition de CI GitLab s'occupe de parcourir l'arborescence de fichiers présentée précédemment et d'exécuter tous les tests associés dès qu'un chart est modifié.
⚠️ Attention : il est nécessaire de disposer d'une image Docker qui inclut les binaires kyverno-cli et helm.
stages:
- kyverno
.charts:
parallel:
matrix:
- CHART:
- my-awesome-chart
- my-second-awesome-chart
.rule_chart_modified:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
changes:
- charts/$CHART/**/*
- policies/$CHART/**/*
when: on_success
kyverno:
tags:
- my-runner
stage: kyverno
image:
name: helm-kyverno:latest # /!\ Necessite une image embarquant helm et kyverno-cli
script:
- |
echo ##### "Processing chart: ${CHART}" #####
# Mise a jour des dependances Helm
cd charts/${CHART}
helm dependency update 2>/dev/null || helm dependency build
# Traitement de chaque fichier values.yaml
find ../../policies/${CHART} -name values.yaml | while read -r values_file; do
policy_dir=$(dirname "$values_file")
policy_name=$(basename "$policy_dir")
echo "## Processing policy: ${policy_name} ##"
# Generation du manifest et lancement du test kyverno
helm template . --values="$values_file" | kyverno apply --resource - "$policy_dir"
echo -e "########\n\n"
done
extends:
- .charts
- .rule_chart_modifiedProposition de CI pour l'automatisation des tests Kyverno
A vous de jouer !
L'avantage principal de cette approche est maintenant clair : un seul outil, moins de complexité, plus d'efficacité.
Vous disposez désormais d'une bonne base pour l'implémenter dans votre processus de développement de charts et si vous utilisez déjà Kyverno en production, vous constaterez rapidement que certaines de vos politiques existantes peuvent être réutilisées sans modification pour vos tests ! 🔄

