From 94bfa224ff1e8246c833a1184ddee255b709fd51 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Sun, 13 Sep 2026 17:54:56 -0400 Subject: [PATCH] gabarit dore : une seule declaration, et les deux souches se rejoignent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le plan du site disait 9006 (modeleSetOPS-minimal), l underlay 99998 — que le plan nomme lui-meme `precedent`. Le Makefile derive VMID_MODELE du PLAN : les locataires clonaient 9006. `site_machines` lisait l autre : les machines du SITE clonaient 99998. POURQUOI CA NE S EST JAMAIS VU. Les deux VMID designent un gabarit valide. Les deux clonent, les deux demarrent, rien n echoue. La flotte etait issue de deux souches, et aucun message ne pouvait le dire puisqu aucune operation n avait echoue. `site_machines` lit desormais underlay.gabarit(), la meme source que tout le monde. Le motif de la seconde declaration est preserve : cette source lit le PLAN DU SITE, jamais celui du tenant actif, donc elle ne depend d aucun symlink `instance`. materialisation.vmid_modele retire des deux cartes. `precedent:` reste au plan — il dit d ou l on vient et n est jamais clone. P74 refuse toute redeclaration. Eprouvee dans les deux sens. SITE-Technolibre visait 99998, repris de la carte de Chezlepro au lieu de son plan. Corrige avant son premier clonage. make prouver : 73 OK, 0 echec, 1 saute. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q --- AGENTS.md | 2 +- CHANGELOG.md | 31 +++++++++++++++++++++++ docs/audit/preuve-2026-09-13.md | 5 ++-- docs/devis-services.md | 2 +- scripts/prouver.py | 45 +++++++++++++++++++++++++++++++++ scripts/site_machines.py | 31 +++++++++++++++++------ 6 files changed, 104 insertions(+), 12 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index adb91a9..1d650e5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -188,7 +188,7 @@ Si `ansible-lint` n’est pas disponible, le signaler clairement. Ne pas invente ## Écrire, puis relire (D-68) `--syntax-check` et `ansible-lint` prouvent que le dépôt est cohérent **avec lui-même**. -C'est aussi ce que font les 73 preuves de `make prouver` : elles lisent le dépôt, sans le +C'est aussi ce que font les 74 preuves de `make prouver` : elles lisent le dépôt, sans le moindre appel réseau. **Aucune ne demande au système déployé s'il ressemble à ce que le dépôt annonce.** diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a7eb2e..c64a4b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # CHANGELOG — Set-OPS +## 2026-09-13 (5) — Le gabarit dore etait declare deux fois, et les deux clonaient + +`plan/10-intrants.yml` disait **9006** (`modeleSetOPS-minimal`). `underlay.yml` disait +**99998** — que le plan nomme lui-meme `precedent`. + +Le Makefile derive `VMID_MODELE` de `underlay.gabarit()`, donc du PLAN : **les locataires +clonaient 9006**. `site_machines` lisait l'autre : **les machines du site clonaient 99998**. + +### Pourquoi ca ne s'est jamais vu + +Les deux VMID designent un gabarit valide. Les deux clonent, les deux demarrent, rien +n'echoue. La flotte etait simplement issue de **deux souches** — et aucun message ne +pouvait le dire, puisqu'aucune operation n'avait echoue. + +### Ce qui a ete fait + +`site_machines` lit desormais `underlay.gabarit()`, la meme source que tout le monde. Le +motif d'origine de la seconde declaration est preserve : cette source lit le PLAN DU SITE, +jamais celui du tenant actif — elle ne depend d'aucun symlink `instance`. + +`materialisation.vmid_modele` est retire des deux cartes. `precedent:` reste au plan : il +dit d'ou l'on vient, et il n'est jamais clone. + +**`P74`** refuse toute redeclaration. Eprouvee dans les deux sens. + +### Au passage + +`SITE-Technolibre` visait 99998 — l'ancien — parce que sa valeur avait ete reprise de la +carte de Chezlepro plutot que de son plan. Corrige avant son premier clonage. + + ## 2026-09-13 (4) — La cle USB ne savait pas relire ce qu'on venait d'y ecrire `exporter_cles --support-chiffre` ecrit un TAR CLAIR quand le support est deja chiffre au diff --git a/docs/audit/preuve-2026-09-13.md b/docs/audit/preuve-2026-09-13.md index 5951ed4..3c5e843 100644 --- a/docs/audit/preuve-2026-09-13.md +++ b/docs/audit/preuve-2026-09-13.md @@ -7,7 +7,7 @@ > [`docs/audit/affirmations.md`](affirmations.md). - **Instance** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml` -- **Verdict** : ✅ CONFORME (72 OK · 0 echec · 1 saute) +- **Verdict** : ✅ CONFORME (73 OK · 0 echec · 1 saute) ## Preuves @@ -69,7 +69,7 @@ | P54 | L'insemination ne reclame aucun secret du tenant | — | ✅ OK | 2 couche(s) d'insemination (serveur_debian, serveur_ops), 9 role(s) applique(s), aucun secret de tenant reclame. | | P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. | | P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. | -| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (73 preuves, 68 roles, 41 groupes). | +| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (74 preuves, 68 roles, 41 groupes). | | P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. | | P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). | | P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `904ece3` (publie le 2026-09-12). | @@ -86,6 +86,7 @@ | P71 | Pool du site : le genome ne nait pas chez un tenant | — | ✅ OK | `site-creer` nomme `--pool-site` ; le pool du genome ne peut plus etre celui d'un tenant. | | P72 | Annuaire : aucun service ne se lie avec le compte du maitre | — | ✅ OK | 5 role(s) consultent l'annuaire, chacun avec SON compte de service ; seul `amorcage_acces` garde celui d'administration, et il provisionne au lieu de consommer. | | P73 | Le locataire designe les services de son site REEL | — | ✅ OK | CONFORME : 5 intrant(s) du locataire concordent avec ce que le site expose. | +| P74 | Gabarit dore : une seule declaration, au plan du site | — | ✅ OK | Gabarit declare une seule fois : VMID 9006 « modeleSetOPS-minimal », precedent 99998. | ## Couverture des affirmations ✅ du registre diff --git a/docs/devis-services.md b/docs/devis-services.md index 698e994..dfc7706 100644 --- a/docs/devis-services.md +++ b/docs/devis-services.md @@ -30,7 +30,7 @@ make placement-plan # chaque VM est-elle là où le plan la met ## Le trou qu'il comble -`scripts/prouver.py` porte 73 preuves (dont une conditionnelle, sautée sans la clé de la voûte). Elles sont toutes **statiques** : elles lisent le +`scripts/prouver.py` porte 74 preuves (dont une conditionnelle, sautée sans la clé de la voûte). Elles sont toutes **statiques** : elles lisent le dépôt. Zéro appel réseau, zéro SSH, zéro `ansible`. Elles établissent que le dépôt est cohérent **avec lui-même** — que les handlers existent, que les intrants ont un propriétaire, que rien n'est codé en dur. diff --git a/scripts/prouver.py b/scripts/prouver.py index e7e159c..8bf5cce 100644 --- a/scripts/prouver.py +++ b/scripts/prouver.py @@ -3314,6 +3314,49 @@ def preuve_pool_du_site_nest_pas_celui_du_tenant() -> tuple[bool, str]: + +def preuve_gabarit_une_seule_declaration() -> tuple[bool, str]: + """Le gabarit dore est-il declare a UN SEUL endroit ? + + C'est de lui que descend chaque VM de chaque ecosysteme. Il vivait a deux endroits : + `plan/10-intrants.yml` (`gabarit:`) et `underlay.yml` + (`materialisation.vmid_modele`). Les deux ont diverge — le plan disait 9006, l'underlay + 99998, que le plan nomme justement `precedent`. + + CE QUE LA DIVERGENCE PRODUISAIT, et pourquoi elle etait invisible : les deux VMID + designent un gabarit VALIDE, donc les deux clonent. Les machines du SITE naissaient de + l'ancien, celles des LOCATAIRES du nouveau — le Makefile derivait deja du plan. Aucune + erreur, aucun message : deux flottes issues de deux souches. + + Le champ `precedent:` est ADMIS et meme utile — il dit d'ou l'on vient. Ce qui est + refuse, c'est une seconde declaration de ce qu'il faut CLONER. + """ + import yaml as _y + try: + sys.path.insert(0, str(RACINE / "scripts")) + import underlay as _u + c = _u.chemin() + if not c: + return True, "Aucun site monte : aucun gabarit a declarer." + carte = _y.safe_load(c.read_text(encoding="utf-8")) or {} + except Exception as e: + return True, f"Carte illisible ({type(e).__name__}) : rien a comparer." + + mat = ((carte.get("underlay") or {}).get("materialisation") or {}) + if "vmid_modele" in mat: + return False, ("`underlay.yml` redeclare `materialisation.vmid_modele` — le gabarit " + "se declare au PLAN du site (`gabarit.vmid`), et nulle part ailleurs. " + "Deux declarations divergent, et les deux clonent : la flotte se " + "retrouve issue de deux souches sans qu'aucun message ne le dise.") + g = _u.gabarit() + if not g.get("vmid"): + return False, ("Aucun `gabarit.vmid` au plan du site — chaque VM en descend, et il " + "ne peut pas etre decide par un tenant.") + p = f", precedent {g['precedent']}" if g.get("precedent") else "" + return True, (f"Gabarit declare une seule fois : VMID {g['vmid']} " + f"« {g.get('nom', '?')} »{p}.") + + def preuve_locataire_suit_son_site() -> tuple[bool, str]: """Le locataire monte designe-t-il encore les services de son site REEL ? @@ -3685,6 +3728,8 @@ PREUVES: list[dict] = [ "refs": [], "func": preuve_annuaire_sans_compte_maitre}, {"id": "P73", "titre": "Le locataire designe les services de son site REEL", "refs": [], "func": preuve_locataire_suit_son_site}, + {"id": "P74", "titre": "Gabarit dore : une seule declaration, au plan du site", + "refs": [], "func": preuve_gabarit_une_seule_declaration}, ] diff --git a/scripts/site_machines.py b/scripts/site_machines.py index 8aa5b07..57e4dbe 100755 --- a/scripts/site_machines.py +++ b/scripts/site_machines.py @@ -47,17 +47,32 @@ def _taille(v) -> str: def materialisation() -> dict: - """Gabarit, stockage et mode de clonage — DECLARES par le site. + """Gabarit, stockage et mode de clonage — le gabarit vient du PLAN du site. - Sans ce bloc, le playbook de clonage prenait ces valeurs dans les group_vars du - TENANT ACTIF : `make site-creer` aurait clone depuis un gabarit different selon le - symlink `instance`, sans rien dire. Un accident silencieux vaut moins qu'un refus. + Sans declaration du site, le playbook de clonage prenait ces valeurs dans les + group_vars du TENANT ACTIF : `make site-creer` aurait clone depuis un gabarit + different selon le symlink `instance`, sans rien dire. + + LE REMEDE AVAIT CREE UNE SECONDE DECLARATION (corrige le 2026-09-13). Le VMID vivait + a la fois dans `plan/10-intrants.yml` (`gabarit:`) et dans `underlay.yml` + (`materialisation.vmid_modele`). Les deux ont diverge : le plan disait 9006, l'underlay + 99998 — que le plan nomme justement `precedent`. + + Consequence, invisible parce que les deux gabarits clonent : les MACHINES DU SITE + naissaient de l'ancien, les machines des LOCATAIRES du nouveau. Le Makefile, lui, + derivait deja `VMID_MODELE` de `underlay.gabarit()`, c'est-a-dire du plan. + + On lit donc la meme source que tout le monde. Le motif d'origine est preserve : + `underlay.gabarit()` lit le plan DU SITE, jamais celui du tenant actif — il ne depend + d'aucun symlink `instance`. """ - m = (U.charger() or {}).get("materialisation") or {} - if not m.get("vmid_modele"): + m = dict((U.charger() or {}).get("materialisation") or {}) + g = U.gabarit() + if not g.get("vmid"): raise SystemExit( - "underlay.yml : `materialisation.vmid_modele` est requis — sans lui, le site " - "clonerait le gabarit du tenant actif, ce qui depend du symlink `instance`.") + "plan/10-intrants.yml : `gabarit.vmid` est requis — c'est de lui que descend " + "chaque VM du site, et il ne peut pas etre decide par un tenant.") + m["vmid_modele"] = g["vmid"] return m