diff --git a/CHANGELOG.md b/CHANGELOG.md index 3944085..fbf34c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # CHANGELOG — Set-OPS +## 2026-10-04 (76) — Validation avant release : un test cassé par (74), trois défauts d'outillage + +**Contexte** : avant d'étiqueter, `make verifier` et `make ci`. La validation a trouvé une +régression **introduite par (74)**, que sa propre validation n'avait pas vue : +`--syntax-check` et `ansible-lint` seulement, pas `make test`. + +**Corrigé** : +- `scripts/tests/test_restauration.py` : le test rendait `restaurer.sh.j2` sans + `client_backup_attente_verrou` (ajoutée en (74)) et échouait au rendu. Il la fournit + désormais, et son faux `restic` **exige** `--retry-lock` avant la sous-commande : l'attente + du verrou est couverte, plus seulement tolérée. +- `scripts/tests/test_frontiere_refus.py` (depuis le 2026-10-01) : il lisait le WAN sous + `interfaces.wan`, une clé que le devis ne produit pas (c'est `if_wan`). Il retombait sur + « wan », juste au site par chance. +- `scripts/prouver.py` : en mode `--verifier`, le harnais comptait les échecs (« 6 echec ») + sans les nommer. Il nomme maintenant chaque preuve en échec, avec son détail. +- `Makefile` : sur un clone sans aucune clé de voûte (la CI de la forge), l'export posait + `ANSIBLE_VAULT_IDENTITY_LIST=` vide ; Ansible le lisait comme un fichier de mot de passe + et refusait (« can not be a directory »). `make ci` mourait à sa première commande. Vide, + la variable n'est plus exportée. Sur le poste, la valeur exportée est **identique** + (mêmes 5 clés, comparée avant et après). + +**Pas corrigé ici, et nommé** : `make ci` reste non conforme (77 OK, 6 échecs : P02, P20, +P74, P79, P81, P82). Le modèle public `exemples/modeles/socle` date d'avant la séparation +site / locataire et ne satisfait pas les gardes ajoutées depuis août. Décision de +l'exploitant : le modèle aura son propre dépôt, `OPS-Modele`, comme les locataires. Aucune +preuve n'a été assouplie pour le vieux modèle. + +**Validation** : `make verifier` conforme (83/83), `ansible-lint` (0 échec). La release +attend une reconstruction qui prouve (73), (74) et ces corrections. + ## 2026-10-04 (75) — Exporter les voûtes une seconde fois ne bute plus sur la première **Signalé par l'exploitant** : `make voutes-exporter VERS=` a refusé (« existe deja. diff --git a/Makefile b/Makefile index 50dc276..a0fa89c 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,16 @@ export SETOPS_INSTANCE ?= instance # # `?=` : une valeur deja posee dans l'environnement gagne — la GUI et les runners peuvent # nommer leurs propres cles sans que ce fichier ait a les connaitre. -export ANSIBLE_VAULT_IDENTITY_LIST ?= $(shell python3 scripts/voutes.py identites 2>/dev/null) +# +# VIDE, ON N'EXPORTE RIEN (2026-10-04). Sur un clone sans aucune cle (la CI de la forge), +# `voutes.py` ne rend rien, et l'export posait quand meme `ANSIBLE_VAULT_IDENTITY_LIST=`. +# Ansible lit cette chaine vide comme un FICHIER de mot de passe, la resout en repertoire +# courant et refuse (« can not be a directory ») : `make ci` mourait a sa premiere commande +# sur un clone nu. Le poste a toujours des cles, il ne pouvait pas le voir. +SETOPS_VOUTES_IDENTITES := $(shell python3 scripts/voutes.py identites 2>/dev/null) +ifneq ($(strip $(SETOPS_VOUTES_IDENTITES)),) +export ANSIBLE_VAULT_IDENTITY_LIST ?= $(SETOPS_VOUTES_IDENTITES) +endif # Inventaire de l'instance : un seul par instance dans le modèle « séparation par # instance ». Détection rétro-compatible : principal > production > lab. # Surchargeable : make … SETOPS_INVENTAIRE=chemin/hosts.yml diff --git a/scripts/prouver.py b/scripts/prouver.py index ecaa738..e69027f 100644 --- a/scripts/prouver.py +++ b/scripts/prouver.py @@ -4972,6 +4972,12 @@ def main(argv: list[str] | None = None) -> int: print(f"{'CONFORME' if conforme else 'NON CONFORME'} : " f"{n_ok} OK, {n_echec} echec, {n_saute} saute.") if not ecrire_rapport: + # NOMMER CE QUI ECHOUE (2026-10-04) : le verdict seul (« 6 echec ») obligeait a + # reconstituer le contexte et relire le code pour savoir lesquelles. + for p, s, d in resultats: + if s == "ECHEC": + print(f" ECHEC {p['id']} — {p.get('titre') or p.get('nom') or ''} : " + f"{' '.join(str(d).split())[:300]}") return 0 if conforme else 1 icone = {"OK": "✅", "ECHEC": "❌", "SAUTE": "⚪"} diff --git a/scripts/tests/test_frontiere_refus.py b/scripts/tests/test_frontiere_refus.py index 85374de..13da5c1 100644 --- a/scripts/tests/test_frontiere_refus.py +++ b/scripts/tests/test_frontiere_refus.py @@ -28,8 +28,11 @@ def main() -> int: if r.returncode != 0 or not r.stdout.strip().startswith("{"): print("SAUTE le devis de la frontiere ne se genere pas ici (site invisible)") return 0 - regles = json.loads(r.stdout).get("regles") or [] - wan = str((json.loads(r.stdout).get("interfaces") or {}).get("wan") or "wan") + devis = json.loads(r.stdout) + regles = devis.get("regles") or [] + # `if_wan`, la cle que le devis produit. Le test lisait `interfaces.wan`, qui n'existe + # pas : il retombait sur « wan », juste au site par chance, faux sur le modele public. + wan = str(devis.get("if_wan") or "wan") echecs = [] refus = [x for x in regles if x.get("action") == "reject"] if any(x.get("interface") == wan for x in refus): diff --git a/scripts/tests/test_restauration.py b/scripts/tests/test_restauration.py index 82e31e5..fd363fc 100644 --- a/scripts/tests/test_restauration.py +++ b/scripts/tests/test_restauration.py @@ -130,6 +130,9 @@ def outil() -> None: # SETOPS_TEST_DEPOT_ABSENT=1 imite restic 0.17+ face a un depot inexistant (code 10). executable(bin_ / "restic", f"""#!/bin/bash if [[ -n "${{SETOPS_TEST_DEPOT_ABSENT:-}}" ]]; then exit 10; fi +# L'outil passe `--retry-lock ` avant la sous-commande (74) : on l'exige, puis on le saute. +[[ "$1" == --retry-lock && -n "$2" ]] || {{ echo "restic appele sans --retry-lock : $*" >&2; exit 99; }} +shift 2 case "$1" in snapshots) cat "{d}/snapshots.json" ;; restore) @@ -164,6 +167,7 @@ if (( ! avec_c )); then cat > "{log}/section.sql"; fi client_backup_restauration_mise_de_cote=str(d / "mis-de-cote"), client_backup_staging=str(staging), client_backup_jobs=[{"nom": "courriel"}, {"nom": "postgresql"}], + client_backup_attente_verrou="30m", ).replace("/etc/ssh/ssh_host_ed25519_key.pub", str(cle)) \ .replace("/etc/setops/restic.pass", str(d / "restic.pass")) script = d / "setops-restaurer"