diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a9ae84..44e8943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,12 @@ Le nom de l'écosystème s'écrit en toutes lettres (`INSTANCE=`), comme pour `r n'a pas d'autre condition. Arrêt à la première étape en échec, avec la commande de reprise (`DEPUIS=<étape>`) ; journal complet dans `/logs/`. +**Le runner du site se met à jour avant sa première étape** (quelle que soit la reprise) : +il tire le moteur, le plan du locataire et SON dépôt de site — déduit du lien `underlay.yml`, +pas nommé —, en avance rapide seulement, et refuse devant une modification locale (éprouvé : +une ligne ajoutée au README du site → REFUS, rc=1 ; fichier remis). Avant, seul `raser` +tirait, deux dépôts sur trois, et une reprise `DEPUIS=creer` ne tirait rien. + **Éprouvé sur Technolibre de `armer` à `bilan`** (les étapes non destructives) : 26 min, 0 échec — armement, flotte montée et recettée, pare-feu 13/13 sans flux perdu, bilan. Un défaut de forme corrigé : chaque relevé de suivi recopiait tout le journal distant ; seul diff --git a/scripts/reconstruire_locataire.py b/scripts/reconstruire_locataire.py index f5a357e..1fcd818 100644 --- a/scripts/reconstruire_locataire.py +++ b/scripts/reconstruire_locataire.py @@ -83,6 +83,32 @@ def sur_site(args, commande: str, filtre: tuple[str, ...] = ()) -> int: "sudo -u setops bash -lc " + shlex.quote(distante)], filtre=filtre) +def site_a_jour(args, tenant: str) -> int: + """Le runner du SITE tire les trois depots qu'il va utiliser, AVANT sa premiere etape. + + Le moteur (Set-OPS-public), le plan du locataire, et le depot du SITE — deduit du lien + `underlay.yml`, pas nomme : un autre site s'appelle autrement. Jusqu'au 2026-09-30 seul + `raser` tirait, et seulement les deux premiers : une reprise `DEPUIS=creer` partait de + ce que le runner avait sous la main, et `flotte-creer` lisait un plan du site jamais tire. + + AVANCE RAPIDE SEULEMENT, et refus devant une modification locale : un runner n'edite + pas ce qu'il tire. S'il porte un ecart, c'est a comprendre, pas a ecraser. + """ + r = args.racine_runner + commande = ( + 'site=$(dirname "$(readlink -f underlay.yml)"); ' + f'for d in {r}/Set-OPS-public {r}/{tenant} "$site"; do ' + ' git -C "$d" rev-parse --git-dir >/dev/null 2>&1 ' + ' || { echo "REFUS : $d n est pas un depot git"; exit 1; }; ' + ' if [ -n "$(git -C "$d" status --porcelain --untracked-files=no)" ]; then ' + ' echo "REFUS : modification locale dans $d"; exit 1; fi; ' + ' git -C "$d" pull -q --ff-only ' + ' || { echo "REFUS : $d ne se met pas a jour en avance rapide"; exit 1; }; ' + ' echo "a jour : $(basename "$d") $(git -C "$d" rev-parse --short HEAD)"; ' + 'done') + return sur_site(args, commande, filtre=("a jour :", "REFUS")) + + def sur_locataire(ip: str, commande: str, journaliser: bool = True) -> tuple[int, str]: distante = "sudo -u setops bash -lc " + shlex.quote(commande) r = subprocess.run(SSH_NEUF + [f"ansible@{ip}", distante], capture_output=True, text=True, @@ -149,15 +175,20 @@ def main() -> int: f"INSTANCE={args.instance} CONFIRMER=true DEPUIS={nom}") return rc or 1 + site_tire = False for nom in a_faire: etape(nom) + if nom in ("raser", "creer", "inseminer") and not site_tire: + rc = site_a_jour(args, T) + if rc != 0: + return arret(nom, rc, "le runner du site n'a pas pu se mettre a jour") + site_tire = True if nom == "sauvegarder": rc = lancer(["ansible-playbook", "-i", str(inv), "playbooks/backup/restauration.yml", "-e", "restauration_action=sauvegarder"], env=env, filtre=("fatal:", "PLAY RECAP", "failed=")) elif nom == "raser": - rc = sur_site(args, f"git pull -q --ff-only && git -C {args.racine_runner}/{T} pull -q --ff-only " - f"&& SETOPS_INSTANCE={args.racine_runner}/{T} make raser CONFIRMER=true " + rc = sur_site(args, f"SETOPS_INSTANCE={args.racine_runner}/{T} make raser CONFIRMER=true " f"INSTANCE={shlex.quote(args.instance)}", filtre=("detruite", "VM detruites", "Refus", "REFUS", "ERREUR")) elif nom == "creer":