Aller au contenu

Bonnes pratiques appliquées au dépôt

Inventaire des conventions et bonnes pratiques réellement en place dans ce dépôt — code, documentation, normes, process. Chaque entrée renvoie à la décision qui la fonde (un ADR) ou à l’outil/la convention qui l’applique.

Cette page applique l’ADR 0061 (posture d’adoption des bonnes pratiques) : elle rend visible et vérifiable le corpus de pratiques que le dépôt a adoptées au fil de ses décisions. C’est une page de référence (ADR 0059) — elle liste, elle ne décide pas (le pourquoi vit dans chaque ADR cité).

94 pratiques recensées sur 6 domaines. Liste tenue à jour à la main lors d’une revue ; la conformité à ces pratiques est, elle, vérifiée automatiquement (pnpm check:gouvernance, ADR 0060).

Six domaines : Reproductibilité, validation & preuves (14) · Gouvernance documentaire (14) · Git, commits & merge (12) · CI, lint & qualité de code (20) · Sécurité & supply-chain (18) · Langage & architecture du code (16).

[[toc]]

Deux grilles orthogonales lisent le même corpus. Les sections suivantes classent les pratiques par mécanisme (ce que le script check_gouvernance vérifie). Cette section les regroupe par culture d’ingénierie — la lecture « quelles cultures le dépôt revendique » (ADR 0062). Une même pratique peut relever de plusieurs cultures.

CultureStatutCe qu’elle recouvre iciADR pivots
GitOps✅ en placeGit source de vérité, merge-commit, pas de push direct, Argo CD + Gitea, patron apply0022, 0037, 0044
DataOps✅ en placeDagster, lineage OpenLineage/Marquez, base managée CNPG, contrat d’interface, pas de PII0026, 0028, 0033, 0041, 0043
DevSecOps✅ en placedigests multi-arch, actions par SHA, Trivy IaC, secrets non versionnés, PSA, etcd chiffré, WireGuard0006, 0014, 0019, 0023
IaC✅ en placecatalogue de topologies déclaratif, idempotence Ansible prouvée, provisioning OpenTofu0023, 0032, 0033
Platform Engineering🔶 en constructionpaved roads (catalogue) + contrat plateforme↔consommateur ; IDP self-service à venir0023, 0043, 0056
MLOps🔶 à venirsocle DataOps = prérequis ; aucun composant ML déployé
SRE🔶 partieldrift detection (state.sh), fraîcheur, etcd backup/RPO, rollback ; sans SLO/error budget0042, 0014
FinOps (efficience)🔶 partielmétrologie ressources (Prometheus), capacité Ceph, sizing banc ; à formaliser0016
FinOps (coût €)❌ écartébare-metal non facturé, mono-tenant — pas de chargeback (rouvrirait avec la topo cloud)

Détail et justification : ADR 0062.

Mapping aux référentiels externes notés/normés

Section intitulée « Mapping aux référentiels externes notés/normés »

Les passages d’audit du 2026-06-16 ont confronté le dépôt à des référentiels externes (notations cyber, notations & normes externes). Plusieurs étaient revendiqués de fait mais non mappés : on les explicite ici, contrôle par contrôle, conformément à l’ADR 0080. Les référentiels déjà câblés (SemVer, Keep a Changelog, Conventional Commits, Diátaxis) sont couverts par les sections par mécanisme ci-dessous ; les référentiels écartés (DORA sans prod, ISO 27001/25010, SRE error-budget) le sont par choix tracé (cf. passages d’audit).

FAIR (Findable / Accessible / Interoperable / Reusable) — le dépôt-objet-de- recherche, pas les données (côté atlas, ADR 0041) :

FacetteStatutPreuve
Findable✅ fortDOI Zenodo concept (CITATION.cff), badge DOI au README, dépôt public
Accessible✅ fortdépôt public, LICENSE (MIT) + NOTICE, releases taguées, archive Zenodo pérenne
Interoperable🔶 moyenformats ouverts (YAML/Markdown), CITATION.cff ; pas de métadonnées schema.org
Reusable✅ fortlicence claire, CITATION.cff + provenance, ADR Nygard, manifeste

OpenGitOps (CNCF, 4 principes) — le dépôt revendique « GitOps » sans citer le référentiel ; il le satisfait sur la couche applicative :

PrincipeStatutPreuve
Déclaratif✅ PASSmanifestes K8s, Argo CD Applications
Versionné & immuable✅ PASSGit source de vérité, merge-commit, images par digest (ADR 0006)
Tiré automatiquement✅ PASS (applicatif)Argo CD + Gitea (pull) ; bootstrap impératif assumé
Réconcilié en continu✅ PASS (applicatif)Argo CD self-heal ; bootstrap/state.sh (drift 7 couches) côté infra
PratiqueSource
La preuve de validation d’une brique d’infra est un run e2e from-scratch sur banc (depuis down, d’une traite, sans intervention manuelle entre les phases) — jamais le passage au vert du lint, qui reste obligatoire mais seulement comme filet bon marché.ADR 0034, ADR 0052
On corrige le CODE d’installation (manifeste, rôle Ansible, harnais), jamais l’état du cluster : kubectl patch/apply/create manuel est réservé au diagnostic, et tout correctif repart dans le code versionné puis est re-prouvé par un run.ADR 0046, ADR 0052
Le banc se monte par des CHEMINS d’installation nommés (socle, atlas, storage-real, cluster-dataops) à intention de preuve distincte — l’ancien agrégat ambigu all est supprimé ; l’ordre des couches et les dépendances inter-phases sont une propriété du code, pas de l’opérateur.ADR 0045
L’idempotence d’un rôle/playbook est prouvée par REJEU : chaque phase Ansible est jouée deux fois et le 2e passage doit donner changed=0 ; un changed_when:true fautif est attrapé là (gate, pas Molecule).ADR 0052, ADR 0051
Un chemin de reprise (rescue: d’un rôle à effet de bord non idempotent : kubeadm init/join) ne compte comme prouvé que s’il a été exercé par une vraie FAUTE INJECTÉE, dans l’ordre opposable : 1er run échoue → compensation tracée (kubeadm reset) → re-jeu du même chemin réussit.ADR 0052, ADR 0050
Tout drift révélé par un run e2e (écart que le lint ne voit pas) est tracé et catégorisé dans un registre indexé unique (symptôme, cause-racine, correctif, statut), réutilisé comme savoir transverse.ADR 0034, ADR 0023, ADR 0046
Un garde-fou CI vérifie la FRAÎCHEUR des preuves de banc PAR CHEMIN (atlas 7 j et storage-real 30 j obligatoires, cluster-dataops 90 j warn-only) et alerte — sans bloquer les PR — quand le dernier run consigné d’un chemin dépasse sa cadence ; un atlas frais ne masque plus un storage-real périmé.ADR 0042, ADR 0045
La source de fraîcheur est un champ daté machine-lisible versionné (pas le mtime Git, non préservé par le checkout CI) : chaque run from-scratch complété appende une entrée datée à l’historique des runs (id, date ISO 8601 UTC, branche, commit, profil, topologie, phases…).ADR 0042
Le déterminisme des inputs est imposé : images/versions épinglées par digest d’index multi-arch, et valeurs de déploiement dérivées du terrain ou surchargées (jamais codées en dur), pour que le résultat soit reproductible.ADR 0052, ADR 0006, ADR 0023
Traçabilité commit ↔ run : tout résultat consigné porte le commit exact qui l’a produit, sinon il n’est pas reproductible (on ne sait pas quel code relancer).ADR 0052
Chaque couche/phase déclare ce qui prouve sa réussite via trois niveaux : assertion pure unitaire (bats hors cluster), gate de phase bloquant sur cluster réel (exit≠0 sinon), et scénario e2e ; toute nouvelle couche ajoute son gate et un chemin n’est validé que si tous les gates de ses couches passent.ADR 0045, ADR 0017
La logique de décision non triviale est extraite en fonctions PURES (ni ansible, ni ssh, ni réseau ; verdict STATUS|message sur stdout) testables hors banc — bash orchestre, les fonctions pures sont testées par bats, le code complexe migre vers Python (uv/pytest).ADR 0017
Le banc ne pouvant pas tourner en CI (nested virt, arm64, ~30 min), un check STATIQUE garde-fou vérifie en revue que le contrat d’interface cluster→atlas reste aligné sur le socle réel (platform/), attrapant une dérive (rename de Service, endpoint déplacé) qui ne se verrait sinon qu’au run.ADR 0043
Un run aidé à la main (état complété hors code) est une preuve INVALIDE : il doit être consigné comme tel avec réserve d’honnêteté et marqué « à re-prouver from-scratch » ; on ne réécrit jamais un résultat passé pour le rendre propre.ADR 0052, ADR 0023
PratiqueSource
Toute décision structurante est tracée par un ADR au format Nygard léger (Contexte / Décision / Statut / Conséquences), numéroté et immuable — jamais en bullets dans un TODO.ADR 0057 · CLAUDE.md
Un ADR est immuable : un choix qui change n’est pas réécrit mais marqué ‘Superseded by NNNN’ (ou Deprecated) ; le statut Proposed/Accepted/Superseded est porté dans l’index.ADR 0057
Frontière de gouvernance par temporalité : un ADR DÉCIDE (immuable), un plan MET EN ŒUVRE (vivant), une issue EXÉCUTE (fermable), une PR LIVRE — rôles non chevauchants ; interdiction de mettre paliers/checklist/TODO dans un ADR.ADR 0057
Un ADR avec mise en œuvre échelonnée a un plan dédié, THÉMATIQUE et vivant (plan-<thème>.md, non daté), qui référence en en-tête l’ADR fondateur et porte une section « Suivi » obligatoire (paliers cochables, issues créées #NNN, état d’achèvement, renvoi aux runs de preuve).ADR 0057
L’audit qualité est séparé en une GRILLE permanente (dimensions + critères + méthode, qui ne périme pas) et des PASSAGES datés append-only (notes /5 à une date) ; un passage renvoie aux ADR pour les pourquoi au lieu de les paraphraser, et ses manques deviennent des issues.ADR 0058
Honnêteté de l’historique : on ne réécrit pas un résultat ou un audit passé pour le rendre ‘propre’ ; les passages d’audit et les RESULTS de banc sont empilés (append-only), datés, et un passage périmé est explicitement marqué plutôt qu’écrasé.ADR 0058, ADR 0052, ADR 0023
Toute page Markdown versionnée est ‘atteignable’ depuis la documentation (entrée sidebar VitePress ou cible d’un lien depuis une page atteignable) — pas de page orpheline.ADR 0029
Les liens et ancres internes de la documentation doivent être valides : le build VitePress échoue sur lien/ancre mort, et lychee vérifie les liens internes vers fichiers/ancres.ADR 0029, ADR 0059 · VitePress
La documentation suit la typologie Diátaxis : une page = un mode dominant (Tutorial / How-to / Reference / Explanation) ; les modes se relient par câblage Markdown inline standard dirigé par l’intention, jamais par recopie ni bandeau ‘voir aussi’.ADR 0059
Valeurs génériques dans TOUT le contenu versionné, y compris la prose (docs, ADR, RUNBOOK) : le dépôt est un catalogue de topologies, pas l’infra réelle d’un auteur ; on génère des valeurs-exemple concrètes (IP 10.0.0.0/22, nœuds cp1/node1…, « l’organisation »), on garde les briques portant une décision (Ceph, Cilium…), et les spécificités réelles vivent dans une config locale gitignorée doublée d’un .example versionné.ADR 0023
Un écart révélé par un run e2e (drift) que le lint ne voyait pas est consigné dans un registre indexé unique (id Lnn stable, portée, symptôme, cause, correctif, statut corrige/caduc/ouvert) ; les pages de synthèse et journaux y réfèrent par id sans dupliquer le détail, et un drift non résolu reste explicitement ‘ouvert’.ADR 0023, ADR 0058, ADR 0046
Posture d’adoption bornée : biais ADOPTIF par défaut, mais aucune bonne pratique n’est adoptée contre un ADR Accepted (il faut d’abord superseder), ni sans la tracer par un ADR si elle est structurante — le principe incline la réponse, il ne dispense pas de la délibération ADR.ADR 0061
Reproductibilité comme principe-chapeau de la documentation des preuves : tout résultat consigné porte le commit exact qui l’a produit, un état complété à la main est marqué ‘à re-prouver’, et la fraîcheur des preuves est surveillée.ADR 0052, ADR 0042
Le numéro d’ADR et l’index ADR sont une ressource partagée : en cas de collision entre branches parallèles, on renumérote au rebase et on corrige l’index + toutes les références (les ajouts au même endroit de l’index collisionnent au merge).CONTRIBUTING.md
PratiqueSource
Tout message de commit doit suivre la convention Conventional Commits (type(scope?): sujet), avec types validés (feat, fix, docs, ci, chore, perf, refactor, etc.).commitlint
Le sujet du commit doit être en minuscules (interdiction de sentence-case, start-case, pascal-case, upper-case) et l’en-tête limité à 100 colonnes, le corps à 100 colonnes par ligne.commitlint
Le message de commit ne doit contenir aucune adresse email — ce qui interdit de facto les trailers Co-Authored-By: (qui portent un email).lefthook
Les hooks lefthook ne doivent jamais être contournés (—no-verify, LEFTHOOK=0 interdits) ; la CI rejoue les mêmes contrôles pour qu’un bypass local ne passe pas.CLAUDE.md
Les PR sont fusionnées en merge commit (et non en squash ni rebase), pour préserver l’historique fin de chaque commit dans main.ADR 0037
Chaque commit d’une PR (pas seulement le titre) doit être propre et conforme, car il atterrit tel quel dans main et alimente le changelog.ADR 0037 · CONTRIBUTING.md
Le push direct sur la branche main est interdit ; tout changement passe par une branche et une PR.lefthook
Le merge d’une PR sur main est conditionné à la réussite de 13 checks CI requis (branche à jour avec main, strict: true) et à la résolution des conversations.Convention propre au dépôt : branch protection Git
Le versionnement et la publication des releases sont automatisés via release-please : les Conventional Commits déterminent le bump semver, une PR de release est ouverte puis auto-mergée (une release par jour, la nuit, en merge commit).ADR 0006, ADR 0037 · release-please
Les actions GitHub référencées dans les workflows sont épinglées par SHA de commit immuable (jamais @vX ni @branch), avec le tag de référence en commentaire.ADR 0006
Les branches suivent la convention <type>/<courte-description> et les issues sont fermées via « Closes #N » placé dans la description de la PR.CONTRIBUTING.md
Une PR = un type/scope cohérent (deux capacités indépendantes => deux PR), pour garder un historique et un changelog lisibles.ADR 0037 · CONTRIBUTING.md
PratiqueSource
Une cible agrégée pnpm lint enchaîne tous les linters/tests en une commande unique (format prettier, yamllint, shellcheck, kubeconform, ansible-lint, jscpd, ruff, garde-fous Python, bats, unittest), reproductible en local avant de pousser.CLAUDE.md
Formatage automatique des YAML/JSON/Markdown via Prettier, vérifié (—check) avant commit, avant push et en CI ; config dédiée (2 espaces, single-quote, proseWrap=always et printWidth 80 pour le Markdown).ADR 0049 · prettier
Lint YAML par yamllint avec règles personnalisées (line-length 160 en warning, indentation 2, truthy désactivé pour les clés K8s), appliqué partout sauf manifestes vendored.yamllint
Lint shell par shellcheck à zéro warning sur tous les *.sh, avec couverture des fichiers échappant à l’extension : template bash etcd-snapshot.sh.j2 (forcé —shell=bash) et scripts Perl (perl -c).ADR 0017, ADR 0049 · shellcheck
Validation des manifestes Kubernetes par kubeconform en mode strict, avec catalogue de schémas CRD (datreeio), à partir des seuls fichiers versionnés (git ls-files) pour exclure les YAML gitignorés générés par le banc.kubeconform
Lint Ansible par ansible-lint en profil production, plus une vérification de syntaxe (—syntax-check) de chaque playbook qui complète le lint en garantissant que tout playbook se parse (includes, rôles, templating), en utilisant l’inventaire générique versionné hosts.example.yaml.ADR 0023 · ansible-lint
Détection de duplication de code (copier-coller) par jscpd avec un seuil de 5% maximum (minTokens 70) couvrant markdown, yaml, shell et json ; les bundles vendored sont exclus.jscpd
Lint et formatage Python par ruff (version épinglée 0.15.16), règles E/F/I/UP/B/SIM, line-length 100, exécuté via uv pour un environnement reproductible (uv.lock committé).ADR 0017, ADR 0006 · ruff
Lint Markdown structurel par markdownlint-cli2, config tolérante ciblant les vrais défauts de structure (pas le style typographique déjà géré par prettier : MD013/MD033/MD041/MD036/MD034 désactivées, MD024 siblings_only).markdownlint
Scan de posture sécurité IaC (manifestes K8s, config) par Trivy en mode config, échec sur HIGH/CRITICAL, avec allowlist ciblée PAR CHEMIN et justifiée par finding (jamais d’ignore global sans justification écrite).ADR 0010 · CLAUDE.md
Vérification des liens Markdown : VitePress échoue sur lien mort interne au build de docs, complété par lychee en mode hors-ligne (valide liens internes/fichiers, ignore les externes flaky).ADR 0029 · VitePress
Garde-fou « toute page Markdown versionnée est atteignable depuis la documentation » contrôlé automatiquement par un script Python (détecte les pages orphelines, ce que VitePress ne fait pas).ADR 0029
Garde-fou « contrat d’interface cluster→atlas aligné sur les manifestes platform/ » contrôlé automatiquement par un script Python (le contrat machine-lisible ne diverge pas du code réel).ADR 0043
Conventional Commits forcés sur TOUTE la plage de commits d’une PR (pas seulement le titre), sujet en minuscules, longueurs contrôlées ; les commits historiques pré-CC sont ignorés via un filtre regex plutôt qu’une liste fragile.ADR 0037 · commitlint
Interdiction des adresses e-mail dans les messages de commit (et donc pas de Co-Authored-By), refusée dès le commit.lefthook
Interdiction du push direct sur main (impose branche + PR), bloqué au pre-push ; les hooks lefthook ne doivent jamais être bypassés (—no-verify / LEFTHOOK=0 interdits).lefthook
Factorisation de la logique en libs bash partagées et testées : fonctions PURES de classification (state-classify.sh, health-classify.sh) et helpers SSH (ssh-report.sh) sourcées par les frontaux, testées sans cluster par bats ; helpers de scénarios de banc factorisés dans lib.sh mais seulement quand strictement identiques (pas de factorisation des fonctions qui divergent).ADR 0053
Tests unitaires des fonctions bash pures par bats et des scripts Python par unittest, exécutés en local et en CI.ADR 0017
Manifestes upstream vendored (bundles volumineux Ceph, cert-manager, argocd, monitoring, CloudNativePG, Dagster, Marquez) exclus de façon cohérente de prettier, yamllint, jscpd et kubeconform, et RBAC inhérent allowlisté par chemin dans .trivyignore.yaml.ADR 0006 · CLAUDE.md
Actions GitHub épinglées par SHA de commit (avec commentaire de version) et installations reproductibles (pnpm —frozen-lockfile, uv sync sur uv.lock committé) dans tous les jobs CI.ADR 0006
PratiqueSource
Toute image de conteneur est épinglée par digest sha256, et ce digest doit pointer un INDEX multi-arch (image.index / manifest.list), jamais un manifeste single-arch (sinon exec format error sur arm64).ADR 0006
Les GitHub Actions (uses:) sont épinglées par SHA de commit 40 caractères (jamais @vX ni @branch, mutables), avec en commentaire le tag de référence (# v4) pour la lisibilité.ADR 0006
Les versions des composants (K8s patch figé, Cilium, Rook, Ceph, containerd, charts, outils dev) sont gelées dans une matrice unique, sans bump silencieux : chaque montée passe par une PR dédiée après vérification de la compat croisée.ADR 0006
Veille de dépendances automatisée (Renovate) en PR dédiées, jamais d’auto-merge, rythme mensuel groupé, et alertes de vulnérabilité traitées à tout moment.ADR 0006 · Renovate
Reproductibilité de l’environnement Python/CI par lockfile committé : uv.lock fige l’arbre exact des dépendances (esprit du pinning par digest).ADR 0006
Scan de posture sécurité IaC (Trivy config) sur manifestes K8s et Dockerfiles, qui échoue la CI sur tout HIGH/CRITICAL non explicitement allowlisté.ADR 0010 · Trivy
Secrets/mots de passe non versionnés : génération robuste côté Ansible, stockage local gitignoré, et fichiers modèles *.example versionnés à la place des valeurs réelles.ADR 0023
Chiffrement at-rest des Secrets dans etcd (provider secretbox), clé générée sur le nœud au bootstrap (0600 root), une seule fois et jamais commitée.ADR 0014
Audit-policy API server activée (niveau Metadata, exclusions du bruit) pour journaliser qui/quoi/quand sur le cluster, avec rotation des logs.ADR 0014
Pod Security Admission (baseline en enforce, restricted en warn) sur les namespaces applicatifs maison pour bloquer les pods privileged/hostPath/hostNetwork.ADR 0014
Chiffrement transparent du trafic pod-to-pod inter-nœuds par WireGuard, avec gestion des clés par Cilium (pas de KMS), appliqué à l’install ET à l’upgrade, et échec visible si le datapath n’est pas réellement chiffré.ADR 0019
Observabilité réseau Hubble (relay + CLI hubble observe) activée SANS UI, pour éviter d’exposer une surface web supplémentaire.ADR 0019
Pare-feu hôte UFW : politique entrante deny par défaut, tout l’inter-nœuds autorisé sur le CIDR cluster, SSH limité (anti-brute-force) à la plage d’administration, NodePort restreint au réseau cluster, logging activé.ADR 0003, ADR 0023
Durcissement SSH des nœuds : authentification par clé uniquement, root désactivé, AllowUsers restreint, MaxAuthTries et timeouts d’inactivité, posé de façon idempotente via un drop-in sshd.Convention bootstrap (responsabilité unique de fir
Durcissement OS opt-in : mises à jour de sécurité automatiques (unattended-upgrades), détection d’intrusion fail2ban, journalisation auditd, surveillance disque smartd — chaque couche activée explicitement par tag.Convention bootstrap/security (toutes couches OPT-
Snapshots etcd protégés par permissions strictes (répertoire 0700, fichiers 0600 root) et copiés hors-nœud sur poste de confiance, et jamais versionnés (ils contiennent tous les Secrets).ADR 0014
Manifestes upstream vendored (bundles volumineux) appliqués tels quels et exclus des linters de style, mais leur RBAC inhérent (large par fonction) est allowlisté Trivy par chemin avec justification.ADR 0021 · CLAUDE.md
Les messages de commit ne doivent contenir aucune adresse e-mail ni Co-Authored-By, et les hooks lefthook ne sont jamais contournés (—no-verify / LEFTHOOK=0 interdits).CLAUDE.md
PratiqueSource
Le choix du langage se fait par catégorie d’action (doctrine outil-par-action pondérée par le coût de diversité) et non par optimalité locale de chaque script : bash orchestre les CLIs, Ansible converge l’état Kubernetes, jq parse, Python porte la logique non triviale, bats/pytest testent.ADR 0049, ADR 0017, ADR 0033
L’orchestration de CLIs externes (kubectl/ceph/ssh/vagrant, helpers de banc, hooks git) reste en bash robuste : set -euo pipefail et shellcheck à 0 warning ; on ne réécrit pas par opportunisme le bash qui marche (pas de portage gratuit vers Go ou Python).ADR 0017, ADR 0049
Le parsing de sorties de CLIs se fait sur du JSON via jq (kubectl -o json/-o jsonpath, ceph -f json), jamais awk/grep/cut sur des sorties humaines.ADR 0017, ADR 0049
La logique non triviale (parcours de graphe, validation structurée au-delà de jq, calculs, traitement texte) va en Python (uv + ruff, tests obligatoires), pas en bash contorsionné ; Python est un second langage de plein droit.ADR 0017, ADR 0049
Node.js n’est pas un langage de script applicatif du dépôt : c’est uniquement le runtime d’outils (VitePress, prettier, commitlint, jscpd, lefthook, bats). Un nouveau besoin de logique va en Python.ADR 0017, ADR 0049
La convergence d’état sur les nœuds et l’apply Kubernetes idempotent de la couche plateforme (addons, opérateurs, CRDs, manifestes figés) sont faits en Ansible (kubernetes.core.k8s/k8s_info), pas en shell impératif : Ansible est le moteur de convergence idempotent.ADR 0033, ADR 0049
Le bootstrap NU (kubeadm, CRI, init/join/rollback, avant que le cluster existe) reste en bash + kubectl sans collection (zéro dépendance kubernetes.core), distinct de la couche plateforme.ADR 0033, ADR 0049
Les gates de convergence s’appuient sur l’état réel du cluster via k8s_info + retries/until (et non sur un sleep ou un k8s_exec WebSocket fragile) ; on gate sur le .status du CR.ADR 0033, ADR 0049
On exploite les directives natives d’Ansible plutôt que de réimplémenter en command/shell : changed_when reflète le vrai changement (jamais true constant), check_mode: false sur tout command dont le stdout pilote une logique aval, server_side_apply via le module pour CRDs/gros bundles, pré-check idempotent qui skip avant un effet de bord, redémarrage de service toujours par handler notify.ADR 0051, ADR 0049
Aucune constante dépendant du déploiement (IP, devices disque, tailles, comptes, seuils) n’est codée en dur dans une tâche : elle vit dans defaults/main.yaml (défaut = valeur d’exemple générique = prod) surchargeable par group_vars non versionné ; les constantes intrinsèques (chemins/ports/versions épinglées) restent inline.ADR 0051, ADR 0023, ADR 0033
Modèle de reprise/transactionnalité d’un rôle Ansible : tout step non-idempotent est gardé par un marqueur creates: (checkpoint), écrit dans une tâche séparée APRÈS succès (pas via redirection >> qui créerait le marqueur même en échec) ; le rescue compense les étapes à effet de bord (kubeadm reset —force, retrait du demi-état du seul step) puis re-fail, et reste diagnostique pour les étapes déclaratives-idempotentes.ADR 0050
Les preuves de compensation (rescue) sont injectées CÔTÉ HARNAIS (kill du process kubeadm, suppression du marqueur entre deux runs), jamais via une variable inject_fault lue par le rôle de production : un rôle prod n’embarque aucun hook de test.ADR 0050, ADR 0046
La logique de décision isolable (classification, comptage, parsing) est extraite en fonctions pures dans des libs bash sourçables et testée au comportement par bats (shellcheck valide la syntaxe, bats le comportement) ; un script de logique sans test n’est pas mergeable.ADR 0017, ADR 0049
Tout code Python de logique est couvert par des tests (unittest stdlib / pytest), non mergeable sans test ; le code jetable de validation e2e est explicitement exclu du lint (pas du code de production).ADR 0017, ADR 0049
La surcharge d’un manifeste figé (helm template vendored) se fait par patch strategic-merge kubernetes.core.k8s ciblé sur le champ concerné, jamais en emballant le rendu dans un template Jinja (.j2) qui divergerait du rendu upstream.ADR 0033, ADR 0049
Exceptions délibérément maintenues en bash et tracées : cni.sh (arme les features Cilium — kube-proxy replacement, LB-IPAM, Gateway API — valeurs dérivées du terrain au bootstrap, doit tourner dans la VM sans repo ; les UI sont exposées en L4 hors Gateway depuis l’ADR 0092, cilium-expo retiré), state.sh/report.sh (diagnostic lecture seule multi-couches — Ansible = convergence, pas reporting), cleanup.sh, first-access.sh, gitea-init.sh, harnais de banc et émetteur OpenLineage jetable, Mailpit (puits SMTP de test).ADR 0049, ADR 0092