0063 — ansible-runner pour la boucle « suggère → lance » (P5)
Accepted (2026-06-13)
Contexte
Section intitulée « Contexte »L’outil déclaratif (ADR 0056) avance par
paliers (plan-modele-declaratif.md). P0 à P4 sont livrés : ils
modélisent, génèrent, dérivent, exposent, filtrent et consignent — toutes
des opérations read-only ; aucune ne lance de playbook. Le palier P5
introduit la première façade qui orchestre réellement : la boucle
diff → suggère → lance que
l’ADR 0056 §2 décrit (lire l’état voulu,
calculer ce qui manque, lancer le bon play).
L’ADR 0056 a décidé la vision (outil Python, ansible-runner, trois
façades) ; il restait à acter l’ajout effectif de la dépendance, qui n’est
pas encore dans pyproject.toml (aujourd’hui : pyyaml, kubernetes,
certifi, jinja2). Le dépôt exige qu’une dépendance structurante soit
justifiée par un besoin réel (« pas de dépendance avant le besoin »,
ADR 0049 /
ADR 0061) et tracée par une
décision (ADR 0057 : l’ADR
décide, le plan met en œuvre). C’est l’objet de cet ADR.
Décision
Section intitulée « Décision »ansible-runner est adopté comme dépendance runtime ; textual est
différé.
-
ansible-runner(adopté). P5 pilote les playbooksbootstrap/*.yamldepuis Python avec des résultats structurés (rc,status, events). La lib Python officielle d’Ansible est le bon outil pour ça (ADR 0049, catégorie « orchestration ») :- elle ne contredit aucun ADR Accepted — elle applique l’ADR 0056 §2 ;
- elle a un gain net face à l’alternative (un
subprocess+ parse de la sortie deansible-playbook— précisément le motif fragile que l’ADR 0056 combat) ; - son coût de diversité est faible : écosystème Ansible déjà natif du
dépôt (
kubernetes.core,ansible.cfg, rôles), zéro 6ᵉ langage.
Épinglée dans
uv.lock(esprit ADR 0006). -
textual(différé). P5 commence par une boucle CLI simple (sous-commandenext+--apply). Le TUI riche (textual/rich) reste optionnel et hors P5 : la boucle informative ne l’exige pas (ADR 0049 « pas de dépendance avant le besoin » ; l’ADR 0056 citetextualen illustration, pas en exigence). Une décision ultérieure l’actera si un vrai besoin d’UX interactive émerge.
Garde-fous
Section intitulée « Garde-fous »- G1 — pas de réimplémentation de convergence
(ADR 0056 §7).
ansible-runnerlance ; le code Python lit les résultats exposés (rc,status, events). Il ne réimplémente jamais retry/backoff/idempotence ni un état réconcilié. Consommer un verdict exposé est permis ; le dériver (heuristique de temps, seuil maison) est interdit. - G2 — jamais d’auto-apply silencieux.
next(sans flag) suggère en texte ;--applyest une décision humaine explicite qui lance une phase. Pas d’enchaînement automatique de toute la séquence ; aucun lancement déclenché par la simple lecture. - G3 — chemin nommé codé
(ADR 0045, CLAUDE.md). P5 ne
crée pas un 2ᵉ chemin d’installation. La phase suggérée appartient à la
séquence d’un chemin nommé connu ;
--applyinvoque le même playbook avec les mêmes-edérivés (derive_run_params, P2) querun-phases.sh. L’ordre des phases est une transcription fidèle des arms derun-phases.sh, pas une réinvention. - G4 — non-régression byte-identique
(ADR 0056 §3). P5 n’ajoute que de
la convergence : elle ne touche ni la génération d’inventaire (P1), ni la
dérivation (P2), ni les épreuves/historique (P4).
validate/generate/diff/status/epreuves/runsrestent inchangés. - G5 — couche d’exécution isolée. Le seul module qui importe
ansible_runnerest l’adaptateurnestor/runner.py(frontière pur/I-O nette, ADR 0017). Cela garde la porte ouverte à un autre moteur (l’ADR 0056 §7 a écarté Terraform/Pulumi pour raison technique, pas par couplage) et rend P5 testable sans cluster (l’adaptateur est stubbé en CI ; la preuve réelle passe par un run de banc consigné, ADR 0034/0052).
Conséquences
Section intitulée « Conséquences »pyproject.tomlgagne une dépendance justifiée (ansible-runner), épinglée dansuv.lock. La libkubernetes(déjà présente) suffit pour lire l’état réel — aucune nouvelle dépendance d’état.- P5 devient actionnable : module pur
nestor/plan.py(séquence attendue, diff, suggestion) + adaptateurnestor/runner.py+ sous-commandenext. - Les tests P5 stubbent l’adaptateur (
launch_phasemonkeypatché) : aucun SSH ni cluster en CI.ansible-runnerest une dépendance runtime, pas exécutée par la suite de tests.
Alternatives écartées
Section intitulée « Alternatives écartées »subprocess+ parse deansible-playbook: fragile (sortie humaine non contractuelle), exactement le motif que l’ADR 0056 combat.textuald’emblée : scope-creep — transformerait P5 en chantier d’UI alors que la boucle informative tient en CLI.- Tout en bash : réimplémenterait l’orchestration que
run-phases.shporte déjà, à contre-courant de l’ADR 0017 (Python dès que la logique se densifie).