0076 — cluster refresh : matérialiser une évolution VOULUE du réel dans la déclaration
Accepted (2026-06-16)
Calque pulumi refresh (rapatrier le réel), mais borné par la doctrine
ADR 0046 (le code est la source de
vérité). Étend preview/classify_refresh (qui CONSTATE déjà le drift) et
réutilise les sondes de
cluster discover (qui
RECONSTRUIT depuis zéro) ; frontière bash/Python de
l’ADR 0049.
Contexte
Section intitulée « Contexte »topology.yaml est la source de vérité unique
(ADR 0056) : on déclare, l’outil
dérive et converge. Deux directions existent déjà :
| Sens | Commande | Ce qu’elle fait |
|---|---|---|
| déclaration → infra | up / next | monte ce qui est déclaré (ADR 0056) |
| réel → déclaration (neuve) | discover | RECONSTRUIT un topology.yaml de zéro (ADR 0074) |
| réel ↔ déclaration (vue) | preview | CONSTATE le drift, sans l’écrire (classify_refresh) |
Il manque le 3ᵉ geste : quand on fait évoluer le cluster délibérément
(ajouter un nœud, monter une couche de plus), topology.yaml reste en
retard sur cette intention réalisée. On doit aujourd’hui éditer le fichier à
la main pour le réaligner — fastidieux et source d’erreur. discover ne
convient pas : il repart de zéro (perd les commentaires, le status, les
choix non détectables comme max_replicas), il ne fusionne pas dans la topo
active.
La tension à trancher (ADR 0046)
Section intitulée « La tension à trancher (ADR 0046) »Rapatrier le réel dans la déclaration est dangereux par construction : c’est
exactement ce que pulumi refresh fait, et c’est aussi le geste que
l’ADR 0046 proscrit quand il s’agit
d’une dérive : « le code d’installation est la seule source de vérité ; tout
drift révélé → corrigé dans le code, pas entériné dans l’état ». Un
refresh naïf (réel→fichier en silence) blanchirait une dérive accidentelle
en la gravant dans la déclaration — l’inverse de la doctrine.
Le besoin est pourtant réel. La ligne de partage est l’intention :
- Évolution VOULUE (ajouter
node3, montermonitoringen plus) : le réel est en AVANCE sur la déclaration, qu’on n’a pas encore mise à jour. La matérialiser danstopology.yamlest correct — c’est faire évoluer la déclaration, pas trahir le code. - Dérive ACCIDENTELLE (un
kubectl patchmanuel, un composant posé hors modèle) : le réel CONTREDIT la déclaration. La rapatrier serait blanchir la dérive — interdit (ADR 0046 : corriger le code, re-prouver par un run).
L’outil ne peut pas LIRE l’intention. Donc il ne doit jamais décider seul :
il PROPOSE le diff, l’humain TRANCHE (comme pulumi refresh --diff + up).
Décision
Section intitulée « Décision »Ajouter cluster refresh : il calcule le diff réel ↔ topology.yaml actif,
l’AFFICHE, demande CONFIRMATION, puis FUSIONNE le réel dans la déclaration
existante (n’écrit jamais en silence, ne repart pas de zéro). Cinq points.
1. refresh PROPOSE, il n’IMPOSE pas (garde-fou ADR 0046)
Section intitulée « 1. refresh PROPOSE, il n’IMPOSE pas (garde-fou ADR 0046) »refresh ne réécrit JAMAIS topology.yaml sans confirmation. Il imprime le
diff ligne à ligne (ce qui serait ajouté / changé), puis demande [o/N] (défaut
NON ; --yes pour la CI hors-TTY). C’est le pendant déclaratif du principe
« patch manuel = diagnostic seulement » (ADR 0046) : on regarde avant d’écrire.
Le --diff-only (refresh --dry-run) affiche sans rien modifier — équivalent à
preview ciblé sur la déclaration.
2. Ce que refresh rapatrie : les dimensions OBSERVABLES et DÉCLARATIVES
Section intitulée « 2. Ce que refresh rapatrie : les dimensions OBSERVABLES et DÉCLARATIVES »Uniquement ce qui est à la fois lisible du réel ET une dimension de
topology.yaml (ADR 0056 §4) :
- nœuds & rôles : un nœud Ready présent mais absent de la déclaration →
proposé à l’ajout (
nodes[]), rôle dérivé des labels (réutilisediscover, ADR 0074) ; - couches montées : une couche dont le signal d’infra est présent
(
_LAYER_SIGNAL) mais horslayers:→ proposée à l’ajout ; - backend de stockage : StorageClass réelle ≠
storage.backenddéclaré → signalé.
Hors périmètre — JAMAIS rapatrié : le scale (replicas) — c’est une capacité runtime dérivée du nombre de nœuds (ADR 0072), pas une dimension déclarative. Le nombre de replicas réel ne « met pas à jour » la topologie : il n’y figure pas. De même, tout état applicatif interne (données, secrets) est hors modèle.
3. SUPPRESSION proposée, jamais automatique
Section intitulée « 3. SUPPRESSION proposée, jamais automatique »Un élément DÉCLARÉ mais ABSENT du réel (un nœud détruit, une couche défaite) est
le cas le plus ambigu : retrait volontaire, ou panne ? refresh le signale
(« déclaré mais absent ») et propose le retrait séparément, avec un défaut
encore plus prudent (jamais retiré sans --prune explicite). On n’efface pas
une intention déclarée sur la foi d’une absence (qui peut être une panne à
réparer, ADR 0046).
4. FUSION, pas réécriture — préserver la déclaration
Section intitulée « 4. FUSION, pas réécriture — préserver la déclaration »refresh édite topology.yaml en place (ajoute/modifie les clés
concernées) au lieu de le régénérer. Il préserve : commentaires,
catalog.status, valeurs non détectables du réel (max_replicas, choix
d’exposition implicites), et l’ordre des clés. C’est la différence nette avec
discover -o (qui produit un fichier neuf, pour un cluster non encore
déclaré). Règle : discover = adopter un cluster sans topo ; refresh =
réaligner une topo existante.
5. Honnêteté : le drift entériné est TRACÉ
Section intitulée « 5. Honnêteté : le drift entériné est TRACÉ »Tout refresh appliqué laisse une trace (le diff appliqué, horodaté), au même
titre qu’un drift corrigé dans le code est consigné (registre-drifts.yaml,
ADR 0042/0046). On distingue dans le registre
« drift corrigé dans le code » (le cas normal) de « évolution matérialisée par
refresh » (le cas de cet ADR) — pour qu’une revue voie pourquoi la déclaration
a changé sans run de montage.
Conséquences
Section intitulée « Conséquences »- L’évolution délibérée d’un cluster (nœud/couche ajoutés) se matérialise
dans
topology.yamlsans édition manuelle ni perte de commentaires — puisup/previewrepartent de la déclaration à jour. - La doctrine ADR 0046 est préservée :
refreshne blanchit pas une dérive en silence (diff + confirmation + trace) ; une dérive accidentelle reste à corriger dans le code. Le scale (runtime, ADR 0072) n’est jamais rapatrié. - Trois gestes réel↔déclaration cohérents :
preview(constater),discover(adopter de zéro),refresh(réaligner l’existant). - Façade fine (ADR 0049/0074 §6) :
refreshréutilise les sondes dediscoveret la réconciliation depreview(classify_refresh), n’invente aucune détection. - Preuve (ADR 0034/0052) : sur le banc,
ajouter un nœud →
refreshpropose son ajout ; après confirmation,topology.yamlle déclare etpreviewne montre plus de drift sur ce nœud.
À revoir si
Section intitulée « À revoir si »refreshest utilisé pour blanchir des dérives à répétition (au lieu de corriger le code) → c’est un signal d’usage à contre-doctrine ; renforcer la trace (registre) ou restreindre les dimensions rapatriables.- Le scale devient un jour DÉCLARATIF (replicas dans
topology.yaml) → revoir le point 2 (le scale rejoindrait alors les dimensions rapatriables). Aujourd’hui il est runtime (ADR 0072), donc exclu.
Alternatives écartées
Section intitulée « Alternatives écartées »- Réécrire
topology.yamlen silence (pulumi refresh nu) : viole l’ADR 0046 (blanchit une dérive). Rejeté — le diff + la confirmation sont non négociables. - Étendre
discover --mergeau lieu d’une commande dédiée :discoverreconstruit de zéro (perd commentaires/status/valeurs non détectables) ; le fusionner dans l’existant dénaturerait sa sémantique « adopter un cluster non déclaré ». Deux gestes distincts (adopter vs réaligner) = deux commandes claires. - Éditer
topology.yamlà la main (statu quo) : fastidieux, source d’erreur, pas de diff vérifié contre le réel. C’est ce querefreshremplace. - Rapatrier le scale : le scale n’est pas déclaratif (ADR 0072) ; le mettre dans la topo changerait sa nature (runtime → déclaré) — autre décision.