"""L'ecriture du plan est ATOMIQUE : une panne en cours d'ecriture ne tronque rien. POURQUOI CE TEST EXISTE. Jusqu'au 2026-09-08, treize sites du moteur ecrivaient les registres par `path.open("w")` — une forme qui TRONQUE le fichier avant d'ecrire. Entre les deux, il est vide. Une exception dans `yaml.safe_dump`, un disque plein, un Ctrl-C, et `instance/plan/serveurs.yml` reste tronque. L'asymetrie fait la gravite : `hosts.yml` se regenere d'un `make instancier-appliquer`, le PLAN ne se regenere de rien. C'est la source unique de verite. CE TEST PORTE SON PROPRE CONTROLE NEGATIF, et c'est le point. Il rejoue d'abord l'ANCIENNE forme pour montrer qu'elle DETRUIT — sans quoi le succes de la nouvelle ne prouverait rien. Une garantie qu'on n'a jamais vue echouer n'est pas une garantie, c'est une habitude. Aucun fichier reel n'est touche : tout se passe dans un dossier temporaire. """ import pathlib import sys import tempfile sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1])) from inventory_rules import ecriture_atomique # noqa: E402 CONTENU = "serveurs:\n infra-pki-01:\n fonction: infra-pki\n" def _neuf() -> pathlib.Path: d = pathlib.Path(tempfile.mkdtemp(prefix="setops-atomique-")) f = d / "serveurs.yml" f.write_text(CONTENU, encoding="utf-8") return f # --- CONTROLE NEGATIF : l'ancienne forme detruit-elle vraiment ? ----------------------- f = _neuf() try: with f.open("w", encoding="utf-8") as fichier: fichier.write("moitie...") raise RuntimeError("panne simulee au milieu de l'ecriture") except RuntimeError: pass perdu = f.read_text(encoding="utf-8") print(f">>> ancienne forme, apres panne : {perdu!r}") assert perdu != CONTENU, "LE CONTROLE NE CONTROLE RIEN : l'ancienne forme n'a rien casse" print(">>> le controle mord : `open(\"w\")` laisse bien un fichier mutile") # --- LA GARANTIE : la nouvelle forme survit-elle a la meme panne ? --------------------- f = _neuf() try: with ecriture_atomique(f) as fichier: fichier.write("moitie...") raise RuntimeError("panne simulee au milieu de l'ecriture") except RuntimeError: pass assert f.read_text(encoding="utf-8") == CONTENU, "LE FICHIER A ETE TRONQUE" print(">>> apres la meme panne, le fichier est INTACT") # Aucun residu : un `.serveurs.yml.xxxx.tmp` oublie finirait par etre committe. restes = [p.name for p in f.parent.iterdir() if p.name != f.name] assert not restes, f"RESIDU LAISSE DERRIERE : {restes}" print(">>> aucun temporaire laisse dans le dossier") # --- L'ecriture normale remplace bien le contenu --------------------------------------- f = _neuf() with ecriture_atomique(f) as fichier: fichier.write("serveurs: {}\n") assert f.read_text(encoding="utf-8") == "serveurs: {}\n", "L'ECRITURE NORMALE A ECHOUE" print(">>> l'ecriture normale remplace le contenu") # --- LES DROITS SURVIVENT --------------------------------------------------------------- # `mkstemp` cree en 0600 ; le plan est en 0664, lisible par le groupe sur les runners. # Sans report explicite des droits, le correctif rendait le plan illisible pour eux. f = _neuf() f.chmod(0o664) with ecriture_atomique(f) as fichier: fichier.write(CONTENU) mode = f.stat().st_mode & 0o777 assert mode == 0o664, f"DROITS PERDUS : {oct(mode)} au lieu de 0o664" print(">>> les droits du fichier sont conserves (0664)") print("\n>>> l'ecriture du plan est atomique, eprouvee contre son propre controle.")