genome : la forge du SITE fait autorite (D-81), et make genome-etat le verifie
Some checks are pending
verifier / verifier (push) Waiting to run
Some checks are pending
verifier / verifier (push) Waiting to run
Decision de l'exploitant : la forge du SITE fait autorite pour le genome. Toute autre copie — y compris celle d'ou le moteur a ete pousse jusqu'ici — est un MIROIR. Un ecosysteme se reproduit depuis la forge de son site : c'est de la qu'il clone son moteur, ses plans, ses modeles. Si l'autorite est ailleurs, cette forge devient un cache qu'on croit a jour — et le 2026-08-26 elle etait quatre commits en arriere sans que rien ne le signale, dont le correctif qui desarme le pare-feu Proxmox. UNE AUTORITE QU'ON NE VERIFIE PAS EST UNE AUTORITE QU'ON SUPPOSE. `make genome-etat` confronte, depot par depot, ce que le poste porte a ce que la forge porte. Il REFUSE en cas d'ecart plutot que de le signaler : un ecart connu et tolere redevient un ecart oublie, et la commande qui le corrige tient en trois mots. Il dit aussi quand la copie locale n'est pas propre — des commits pas encore faits sont une autre forme de retard. Mesure au passage, et traitee plutot qu'ignoree : le premier contact avec la forge echoue une fois sur six — poignee TLS expiree, puis cinq reponses de suite. Ce n'est pas le chemin, qui est prouve ; c'est l'acceptation TLS apres un temps d'inactivite. Les deux cibles reessaient. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
6a5a49f924
commit
87716cefc7
7 changed files with 180 additions and 2 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
|
@ -59,6 +59,23 @@ qu'elle porte, et on refuse si ça diffère de ce que porte le runner.
|
|||
sans que rien ne le signale — dont le correctif qui désarme le pare-feu Proxmox. Un
|
||||
écosystème qui se reproduit depuis une forge en retard reproduit ses défauts.*
|
||||
|
||||
### D-81 — la forge du site fait autorité, et `make genome-etat` le vérifie
|
||||
|
||||
Décision de l'exploitant : **la forge du SITE fait autorité pour le génome.** Toute autre
|
||||
copie — y compris celle d'où le moteur a été poussé jusqu'ici — est un **miroir**.
|
||||
|
||||
Une autorité qu'on ne vérifie pas est une autorité qu'on suppose. `make genome-etat`
|
||||
confronte donc, dépôt par dépôt, ce que le poste porte à ce que la forge porte — et
|
||||
**refuse** en cas d'écart plutôt que de le signaler. *Un écart connu et toléré redevient un
|
||||
écart oublié, et la commande qui le corrige tient en trois mots.* Il dit aussi quand la
|
||||
copie locale n'est pas propre : des commits pas encore faits sont une autre forme de
|
||||
retard.
|
||||
|
||||
Mesuré au passage, et traité plutôt qu'ignoré : **le premier contact avec la forge échoue
|
||||
une fois sur six** — poignée TLS expirée, puis cinq réponses de suite. Ce n'est pas le
|
||||
chemin, qui est prouvé ; c'est l'acceptation TLS après un temps d'inactivité. Les deux
|
||||
cibles réessaient.
|
||||
|
||||
## 2026-08-25 — Quatre zones inverses, et rien de plus
|
||||
|
||||
**47 preuves.** Le site ne servait aucun PTR. `serveur_powerdns_zone_inverse` dérivait
|
||||
|
|
|
|||
8
Makefile
8
Makefile
|
|
@ -766,6 +766,14 @@ site-appliquer: ## Applique un role aux machines du site — GROUPE=<serveur_ops
|
|||
#
|
||||
# CE QUI EST POUSSE SE DERIVE de `serveur_ops_depots` (au plan du site) : on pousse ceux
|
||||
# dont une copie locale existe a cote du moteur, et rien d'autre.
|
||||
.PHONY: genome-etat
|
||||
genome-etat: ## Ce que la forge du site porte, face au poste — ne corrige rien, regarde
|
||||
@set -e; \
|
||||
voute="$$(dirname "$$(readlink -f underlay.yml)")/underlay.vault.yml"; \
|
||||
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
||||
ansible-playbook -i scripts/site_inventaire.py \
|
||||
playbooks/maintenance/genome_etat.yml "$${supp[@]}" $(ARGS)
|
||||
|
||||
.PHONY: genome-pousser
|
||||
genome-pousser: ## Le runner du site pousse le genome sur la forge du site — DEPOT=<nom> pour un seul
|
||||
@set -e; \
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@
|
|||
| P28 | Pools Proxmox : un par tenant, sans collision | AFF-110 | ✅ OK | CONFORME : 3 pool(s) Proxmox, 35 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 28 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 17, socle-identite 2, web-sso 5) ; 2 lacune(s) nommee(s) : serveur_loki, serv |
|
||||
| P30 | SDN EVPN : zones, VNets et sous-reseaux derives | AFF-112 | ✅ OK | CONFORME : SDN EVPN, 3 zone(s), 15 VNet(s), 15 sous-reseau(x), aucune collision. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 50 scripts expliques et atteignables, 103 cibles make documentees, 60 roles avec README. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 50 scripts expliques et atteignables, 104 cibles make documentees, 60 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 14 exigence(s) de role, toutes satisfaites (39 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 33 revendication(s) de port, aucune collision entre roles co-localises (16 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (23 genere(s) exempte(s)). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (24 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 0 application(s) exigeant une base l'ont toutes (0 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 2 hote(s) detiennent de l'etat, tous porteurs de `client_backup` (9 groupe(s) au catalogue). |
|
||||
| P37 | Le placement du tenant existe chez son hebergeur | — | ✅ OK | placement confronte a l'hebergeur monte (SITE-Chezlepro) : noeud, stockage, pont — tous offerts. |
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ sont les seules vérifiables.
|
|||
|
||||
| # | Décision | Pourquoi | Détail | Garde |
|
||||
|---|---|---|---|---|
|
||||
| **D-81** | **La forge du SITE fait autorité pour le génome.** Toute autre copie — y compris celle d'où le moteur a été poussé jusqu'ici — est un **miroir**. Le poste de l'exploitant ne route pas jusqu'à elle : c'est le **runner du site** qui publie, par `make genome-pousser` | un écosystème se reproduit depuis la forge de son site : c'est de là qu'il clone son moteur, ses plans, ses modèles. Si l'autorité est ailleurs, cette forge devient un cache qu'on croit à jour — et le 2026-08-26 elle était **quatre commits en arrière** sans que rien ne le signale, dont le correctif qui désarme le pare-feu Proxmox. **Un écosystème qui se reproduit depuis une forge en retard reproduit ses défauts.** Le poste n'a de patte que sur l'administration, et on ne perce pas de chemin pour lui : le runner existe pour ce travail | `playbooks/maintenance/genome_pousser.yml`, `scripts/genome_colis.py`, `Makefile` §genome-pousser | — |
|
||||
| **D-13** | Un **hébergeur** sert plusieurs **tenants** et a son tenant par défaut | Chezlepro est les deux à la fois, ce qui masquait la distinction | `frontiere-opnsense.md` §2 | — |
|
||||
| **D-14** | `underlay.yml` appartient à l'**hébergeur**, monté par symlink | ce sont ses commutateurs, ses câbles ; le moteur est générique, un tenant n'en possède pas | `sdn-evpn.md`, `underlay.yml.example` | — |
|
||||
| **D-15** | Ce symlink **ne suit pas** `make instance-utiliser` | basculer le tenant actif ne change pas la fabric | `frontiere-opnsense.md` §2 | — |
|
||||
|
|
|
|||
117
playbooks/maintenance/genome_etat.yml
Normal file
117
playbooks/maintenance/genome_etat.yml
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
---
|
||||
# CE QUE LA FORGE DU SITE PORTE, FACE A CE QUE LE POSTE PORTE.
|
||||
#
|
||||
# La forge du SITE fait autorite pour le genome (D-81). Toute autre copie est un miroir.
|
||||
# Encore faut-il que cette autorite soit A JOUR — et la derive ne se voit pas toute seule.
|
||||
#
|
||||
# Mesure du 2026-08-26 : la forge etait restee quatre commits derriere le poste sans que
|
||||
# rien ne le signale. Le runner clonait donc un moteur perime, dont le correctif qui
|
||||
# desarme le pare-feu Proxmox. Un ecosysteme qui se reproduit depuis une forge en retard
|
||||
# reproduit ses defauts.
|
||||
#
|
||||
# Cette cible ne corrige rien : elle REGARDE. `make genome-pousser` corrige.
|
||||
#
|
||||
# make genome-etat
|
||||
|
||||
- name: Ce que la forge du site porte, face au poste
|
||||
hosts: serveur_ops_site
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
genome_racine: "{{ serveur_ops_racine | default('/opt/setops') }}"
|
||||
genome_freres: "{{ playbook_dir | dirname | dirname | dirname }}"
|
||||
genome_moteur: "{{ (serveur_ops_depots | selectattr('role', 'eq', 'moteur') | first).dest }}"
|
||||
|
||||
tasks:
|
||||
- name: Voir lesquels de ces dépôts existent sur le contrôleur
|
||||
ansible.builtin.stat:
|
||||
path: "{{ genome_freres }}/{{ item.dest }}/.git"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
loop: "{{ serveur_ops_depots | default([]) }}"
|
||||
loop_control:
|
||||
label: "{{ item.dest }}"
|
||||
register: genome_locaux
|
||||
|
||||
- name: Retenir ceux qu'on peut comparer
|
||||
ansible.builtin.set_fact:
|
||||
genome_compares: >-
|
||||
{{ genome_locaux.results | selectattr('stat.exists')
|
||||
| map(attribute='item') | list }}
|
||||
|
||||
- name: Ce que le poste porte
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- python3
|
||||
- "{{ genome_freres }}/{{ genome_moteur }}/scripts/genome_colis.py"
|
||||
- etat
|
||||
- "{{ genome_freres }}/{{ item.dest }}"
|
||||
- --branche
|
||||
- "{{ item.branche | default('main') }}"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
loop: "{{ genome_compares }}"
|
||||
loop_control:
|
||||
label: "{{ item.dest }}"
|
||||
register: genome_ici
|
||||
changed_when: false
|
||||
|
||||
- name: Ce que la forge porte
|
||||
ansible.builtin.uri:
|
||||
url: >-
|
||||
https://forge.{{ domaine_interne }}/api/v1/repos/{{ serveur_ops_forge_organisation
|
||||
| default('genome') }}/{{ item.depot }}/branches/{{ item.branche | default('main') }}
|
||||
validate_certs: false
|
||||
status_code: [200, 404]
|
||||
loop: "{{ genome_compares }}"
|
||||
loop_control:
|
||||
label: "{{ item.depot }}"
|
||||
register: genome_la_bas
|
||||
# LE PREMIER CONTACT AVEC LA FORGE RATE PARFOIS (mesure du 2026-08-26 : une fois sur
|
||||
# six, poignee TLS expiree, puis cinq reponses de suite). Ce n'est pas le chemin —
|
||||
# il est prouve — c'est l'acceptation TLS apres un temps d'inactivite. On reessaie
|
||||
# plutot que de rendre un verdict sur un alea.
|
||||
retries: 4
|
||||
delay: 3
|
||||
until: genome_la_bas is succeeded
|
||||
changed_when: false
|
||||
|
||||
- name: L'état, dépôt par dépôt
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ item.depot }} :
|
||||
poste {{ (genome_ici.results[idx].stdout | from_json).courte }}
|
||||
{{ '=' if genome_la_bas.results[idx].status == 200
|
||||
and genome_la_bas.results[idx].json.commit.id
|
||||
== (genome_ici.results[idx].stdout | from_json).tete
|
||||
else '≠' }}
|
||||
forge {{ genome_la_bas.results[idx].json.commit.id[:7]
|
||||
if genome_la_bas.results[idx].status == 200 else '(absente)' }}
|
||||
{{ '— copie locale NON PROPRE, des commits ne sont pas faits'
|
||||
if not (genome_ici.results[idx].stdout | from_json).propre else '' }}
|
||||
loop: "{{ genome_compares }}"
|
||||
loop_control:
|
||||
label: "{{ item.depot }}"
|
||||
index_var: idx
|
||||
|
||||
# ON REFUSE PLUTOT QUE DE SIGNALER. Un ecart connu et tolere redevient un ecart oublie ;
|
||||
# et la commande qui le corrige tient en trois mots.
|
||||
- name: La forge fait-elle autorité sur une copie à jour ?
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- genome_la_bas.results[idx].status == 200
|
||||
- genome_la_bas.results[idx].json.commit.id
|
||||
== (genome_ici.results[idx].stdout | from_json).tete
|
||||
fail_msg: >-
|
||||
{{ item.depot }} : la forge du site porte
|
||||
{{ genome_la_bas.results[idx].json.commit.id[:7]
|
||||
if genome_la_bas.results[idx].status == 200 else '(rien — dépôt absent)' }},
|
||||
le poste porte {{ (genome_ici.results[idx].stdout | from_json).courte }}.
|
||||
La forge du site FAIT AUTORITÉ (D-81) : tant qu'elle est en retard, tout
|
||||
écosystème qui s'y reproduit reproduit un moteur périmé.
|
||||
Corriger avec `make genome-pousser`.
|
||||
success_msg: "{{ item.depot }} : la forge est à jour."
|
||||
loop: "{{ genome_compares }}"
|
||||
loop_control:
|
||||
label: "{{ item.depot }}"
|
||||
index_var: idx
|
||||
|
|
@ -226,6 +226,13 @@
|
|||
loop_control:
|
||||
label: "{{ item.depot }}"
|
||||
register: genome_forge
|
||||
# LE PREMIER CONTACT AVEC LA FORGE RATE PARFOIS (mesure du 2026-08-26 : une fois sur
|
||||
# six, poignee TLS expiree, puis cinq reponses de suite). Ce n'est pas le chemin —
|
||||
# il est prouve — c'est l'acceptation TLS apres un temps d'inactivite. On reessaie
|
||||
# plutot que de rendre un verdict sur un alea.
|
||||
retries: 4
|
||||
delay: 3
|
||||
until: genome_forge is succeeded
|
||||
changed_when: false
|
||||
|
||||
- name: La forge porte-t-elle exactement ce que le runner porte ?
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ DEUX GESTES, DEUX COTES :
|
|||
Sur le RUNNER. Verifie le colis, avance `main` EN AVANCE RAPIDE SEULEMENT,
|
||||
puis pousse. Rend du JSON : {avant, apres, pousse}.
|
||||
|
||||
genome_colis.py etat <depot>
|
||||
Des deux cotes. Ce que cette copie porte, pour le confronter a la forge.
|
||||
|
||||
`--ff-only` est le garde-fou. Si les deux histoires ont diverge, on s'arrete plutot que de
|
||||
fabriquer une fusion que personne n'a decidee — et le message de git nomme alors exactement
|
||||
ce qu'il faut reconcilier.
|
||||
|
|
@ -62,6 +65,25 @@ def emballer(depot: Path, colis: Path, branche: str) -> int:
|
|||
return 0
|
||||
|
||||
|
||||
def etat(depot: Path, branche: str) -> int:
|
||||
"""Ce que cette copie porte — pour le confronter a ce que la forge porte.
|
||||
|
||||
LA DERIVE NE SE VOIT PAS TOUTE SEULE. Le 2026-08-26, la forge du site etait restee
|
||||
quatre commits derriere le poste sans que rien ne le signale ; le runner clonait donc
|
||||
un moteur perime, dont le correctif qui desarme le pare-feu Proxmox. On rend donc
|
||||
l'ecart LISIBLE, plutot que d'esperer s'en souvenir.
|
||||
"""
|
||||
if not (depot / ".git").exists():
|
||||
raise SystemExit(f"{depot} n'est pas un depot git.")
|
||||
print(json.dumps({
|
||||
"tete": _git(depot, "log", "-1", "--format=%H", branche),
|
||||
"courte": _git(depot, "log", "-1", "--format=%h", branche),
|
||||
"sujet": _git(depot, "log", "-1", "--format=%s", branche)[:80],
|
||||
"propre": _git(depot, "status", "--porcelain") == "",
|
||||
}))
|
||||
return 0
|
||||
|
||||
|
||||
def recevoir(depot: Path, colis: Path, pushurl: str, cle: str | None,
|
||||
branche: str) -> int:
|
||||
if not (depot / ".git").exists():
|
||||
|
|
@ -119,7 +141,13 @@ def main(argv: list[str]) -> int:
|
|||
r.add_argument("--cle", default=None, help="cle SSH du runner")
|
||||
r.add_argument("--branche", default="main")
|
||||
|
||||
d = sous.add_parser("etat", help="ce que cette copie porte")
|
||||
d.add_argument("depot")
|
||||
d.add_argument("--branche", default="main")
|
||||
|
||||
a = p.parse_args(argv)
|
||||
if a.geste == "etat":
|
||||
return etat(Path(a.depot), a.branche)
|
||||
if a.geste == "emballer":
|
||||
return emballer(Path(a.depot), Path(a.colis), a.branche)
|
||||
return recevoir(Path(a.depot), Path(a.colis), a.pushurl, a.cle, a.branche)
|
||||
|
|
|
|||
Loading…
Reference in a new issue