Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Tester une politique Rego avec opa test

Par mis à jour le

Améliorer cette page

Un test Rego est une règle dont le nom commence par test_ : opa test les évalue toutes, et un test réussit si sa règle vaut true. Les tests sont le seul filet contre les erreurs que Rego ne signale pas, comme la faute de chemin qui laisse tout passer ou le conflit qui n'apparaît qu'avec certaines entrées. Cette leçon teste la politique kubernetes.validation des leçons précédentes : écriture des tests, cas positifs et négatifs, couverture et seuil en CI, puis les pièges d'un test qui passe sans rien prouver. Mesuré avec OPA 1.21.1.

  • Écrire des tests Rego avec des règles test_ et with input as.
  • Couvrir chaque règle par un cas accepté et un cas refusé.
  • Mesurer la couverture et imposer un seuil en CI.
  • Reconnaître un test qui passe sans rien vérifier.

Les leçons La CLI opa et Pièges de l'évaluation.

Un test vit dans un fichier _test.rego, à côté de la politique, dans un package distinct qui importe celui qu'il teste. Chaque test est une règle préfixée par test_ ; le mot-clé with input as fournit le document d'entrée, sans fichier JSON séparé. Voici la politique du fil rouge, rangée dans policy/validation.rego :

package kubernetes.validation
# Valeur par défaut : refus si aucune règle `allow` ne s'évalue à vrai
default allow := false
# Namespaces interdits pour les Deployments
namespaces_interdits := {"default", "kube-system"}
allow if {
input.kind == "Deployment"
label_app_present
not namespace_interdit
}
label_app_present if input.metadata.labels.app
namespace_interdit if input.metadata.namespace in namespaces_interdits

Et ses tests, dans policy/validation_test.rego. Une fonction de fabrique, deploiement, évite de répéter le même document à chaque cas :

package kubernetes.validation_test
import data.kubernetes.validation
deploiement(namespace, labels) := {
"kind": "Deployment",
"metadata": {"name": "api", "namespace": namespace, "labels": labels},
}
test_deploiement_conforme_accepte if {
validation.allow with input as deploiement("production", {"app": "api"})
}
test_namespace_default_refuse if {
not validation.allow with input as deploiement("default", {"app": "api"})
}
test_label_app_absent_refuse if {
not validation.allow with input as deploiement("production", {"team": "web"})
}
test_pod_refuse if {
not validation.allow with input as {"kind": "Pod", "metadata": {"namespace": "production", "labels": {"app": "api"}}}
}

Chaque condition de allow a son cas refusé, et la règle entière a son cas accepté. Le test du refus s'écrit avec not : allow vaut false grâce au default, et not false est vrai.

opa test prend un dossier, charge politiques et tests, et évalue chaque règle test_. Sans option, il n'affiche que le bilan ; -v détaille chaque test avec son fichier, sa ligne et sa durée :

Fenêtre de terminal
opa test policy/
# PASS: 4/4
opa test policy/ -v
policy/validation_test.rego:10:
data.kubernetes.validation_test.test_deploiement_conforme_accepte: PASS (357.385µs)
policy/validation_test.rego:14:
data.kubernetes.validation_test.test_namespace_default_refuse: PASS (367.279µs)
...
PASS: 4/4

L'option --run filtre les tests par expression régulière sur leur nom : opa test policy/ -v --run 'namespace' ne lance que test_namespace_default_refuse. Les appels à print dans une règle ou un test ne s'affichent qu'avec -v, sous le test concerné : c'est l'outil de mise au point, à retirer ensuite.

Pourquoi un cas accepté par règle est indispensable

Section intitulée « Pourquoi un cas accepté par règle est indispensable »

Un jeu de tests qui ne contient que des refus ne protège pas d'une faute de chemin. Mesuré en remplaçant labels.app par labels.ap dans la politique : label_app_present ne se déclenche plus jamais, allow vaut toujours false, et les trois tests de refus passent encore. Seul le test positif échoue :

policy/validation_test.rego:10:
data.kubernetes.validation_test.test_deploiement_conforme_accepte: FAIL (267.273µs)
--------------------------------------------------------------------------------
PASS: 3/4
FAIL: 1/4

opa test sort alors avec le code 2, ce qui bloque une CI. La règle à retenir : chaque décision a au moins un cas accepté et un cas refusé, et pour une condition numérique, un cas de chaque côté de la frontière, là où se cachent les conflits de règles.

with ne remplace pas seulement input : il peut substituer une règle ou une valeur de data le temps d'une évaluation. Un test peut ainsi vérifier le comportement de la politique avec une autre liste de namespaces, sans toucher au fichier :

package kubernetes.validation_data_test
import data.kubernetes.validation
test_namespace_interdit_remplace if {
validation.allow with input as {"kind": "Deployment", "metadata": {"namespace": "default", "labels": {"app": "api"}}}
with validation.namespaces_interdits as {"kube-system"}
}

Le namespace default ne figure plus dans la liste remplacée, et le Deployment est accepté : le test passe. C'est la façon de tester une politique qui lit des données externes, chargées avec -d en production, sans dépendre d'un fichier réel.

opa test --coverage rend un rapport JSON : le pourcentage global dans coverage, et pour chaque fichier les lignes non couvertes dans not_covered. Mesuré en ne gardant que le test du namespace default :

Fenêtre de terminal
opa test policy/ --coverage | jq '.coverage'
# 90
opa test policy/ --coverage --threshold 95
# Code coverage threshold not met: got 90.00 instead of 95.00 (code 2)

La ligne non couverte est la ligne 9, l'en-tête de allow : aucun test n'y entre jusqu'au bout, puisque le seul cas restant échoue avant. L'option --threshold transforme la mesure en contrôle de CI. Une couverture à 100 % ne prouve pas que les tests sont bons, seulement que chaque ligne a été évaluée au moins une fois.

Un test Rego échoue quand sa règle n'est pas vraie, y compris quand elle est undefined. Une faute dans le nom d'une règle testée en positif est donc attrapée : validation.alow vaut undefined, et le test est en échec. Mais la même faute sous not passe :

package kubernetes.not_test
import data.kubernetes.validation
# ❌ « alow » n'existe pas : not undefined est vrai, le test passe sans rien tester
test_faute_sous_not if {
not validation.alow with input as {"kind": "Deployment", "metadata": {"labels": {}}}
}

Mesuré : PASS, et ni opa check --strict ni Regal ne signalent le nom inconnu, puisque data.kubernetes.validation.alow est un chemin valide. C'est le second argument pour les cas positifs : un test de refus seul ne prouve pas que la règle existe.

Un test dont le nom commence par todo_ est ignoré et marqué SKIPPED. Attention au code de retour : mesuré, opa test sort en 2 dès qu'un test est ignoré, même si tous les autres passent. Un test laissé en todo_ bloque donc la CI, ce qui est voulu : Regal le signale aussi, par sa règle todo-test.

  • Un test est une règle test_ dans un fichier _test.rego, qui importe le package testé ; with input as fournit l'entrée.
  • opa test policy/ donne le bilan, -v le détail et les print, --run filtre par nom.
  • Chaque décision a un cas accepté et un cas refusé : des refus seuls laissent passer une faute de chemin.
  • with remplace aussi une règle ou une donnée de data.
  • --coverage --threshold impose une couverture minimale, en code 2.
  • Un not sur une règle mal nommée passe toujours ; un test todo_ fait sortir opa test en 2.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn