Set-OPS-Public/playbooks/maintenance/emancipation-prouver.yml
Daniel Allaire aab1bcc83a doc : rangee par lecteur, trois portes d'entree (architecture, setup, usage) et un index
README reduit au role du projet et au demarrage rapide. docs/architecture.md (schemas
Mermaid), docs/setup.md, docs/usage.md (154 cibles en 13 familles), docs/README.md (index).
Documents ranges en conception/, exploitation/, hebergement/, projet/ ; 598 chemins suivis.
QUICKSTART.md, SOLUTION.md, architecture-set-ops.md et la carte fondus puis retires.

Preuves : P48 lit l'index et conception/mecanismes.md ; les gardes parcourent docs/ sous-
dossiers compris (_documents) ; usage.md suit le Makefile dans les deux sens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:01:54 -04:00

160 lines
8 KiB
YAML

---
# PROUVER QU'UN LIEN EST COUPÉ — l'instrument de la quatrième ligne.
#
# `docs/conception/filiation-emancipation.md` décrit quatre temps, et n'en outillait que trois :
#
# le lien de filiation déclaré au plan le moteur
# le constat d'aptitude « tu pourrais » la machine INSTRUIT
# l'acte d'émancipation retirer le lien l'HUMAIN, CONFIRMER=true
# la preuve que c'est coupé ← CE PLAYBOOK la machine, APRÈS l'acte
#
# « Une émancipation non prouvée est une émancipation non faite. »
#
# ─── POURQUOI SONDER NE SUFFIT PAS ───────────────────────────────────────────
#
# Vérifier que le service local répond ne prouve RIEN. Le dépôt le dit déjà, à propos du
# cache : « tant qu'internet répond, un `apt update` qui réussit ne dit pas d'où vient
# l'octet ». Un écosystème peut avoir monté son propre service ET continuer à tirer de
# l'amont sans que personne ne le voie.
#
# On COUPE donc, et on refait marcher la chose. C'est le seul geste qui distingue
# « j'ai un service local » de « je n'ai plus besoin du tien ».
#
# ─── LE MÊME ESSAI REND LES DEUX VERDICTS ────────────────────────────────────
#
# Le verdict vient de la MESURE, jamais d'une déclaration :
#
# coupé, la fonction marche -> ÉMANCIPÉ, et c'est prouvé
# coupé, la fonction casse -> PAS ÉMANCIPÉ — et la dépendance est prouvée REELLE
#
# Le second n'est pas un échec de l'outil : c'est son contrôle négatif, rendu par la même
# commande. Une preuve d'émancipation qui ne saurait pas montrer la dépendance qu'elle
# mesure ne prouverait rien le jour où elle passerait au vert.
#
# ─── LA COUPURE EST GARANTIE RÉVERSIBLE ──────────────────────────────────────
#
# Une TABLE nftables dédiée, jamais une règle glissée dans une table existante : elle se
# retire d'un seul geste, et ne peut pas laisser d'état partiel si l'essai s'interrompt.
# Le `always` la retire quoi qu'il arrive — y compris si la mesure échoue.
#
# Usage :
# make emancipation-prouver SERVICE=artefacts CONFIRMER=true
# make emancipation-prouver SERVICE=genome CONFIRMER=true
# make emancipation-prouver SERVICE=resolveur CONFIRMER=true
- name: Prouver qu'un lien de filiation est coupé
hosts: "{{ emancipation_hotes | default('hotes_actifs') }}"
gather_facts: false
become: true
vars:
# CE QU'ON COUPE, ET CE QU'ON REFAIT MARCHER. Chaque service nomme son amont (lu au
# plan, jamais écrit ici) et l'épreuve qui dit s'il tient sans lui.
emancipation_services:
artefacts:
amont: "{{ serveur_artefacts_amont | default(artefacts_amorcage | default('')) }}"
epreuve: "apt-get -qq update"
dit: "installer des paquets"
genome:
amont: "{{ serveur_ops_forge_amont | default('') }}"
epreuve: "git ls-remote {{ serveur_ops_forge_amont | default('') }}/genome/set-ops-public.git HEAD"
dit: "cloner le génome"
resolveur:
amont: "{{ dns_amorcage | default('') }}"
epreuve: "getent hosts deb.debian.org"
dit: "résoudre un nom public"
emancipation_ce_service: "{{ emancipation_services[emancipation_service] }}"
# L'ADRESSE SEULE, sans schéma ni port : c'est elle qu'on bloque.
emancipation_adresse: >-
{{ (emancipation_ce_service.amont | regex_replace('^\w+://', '')
| regex_replace('[:/].*$', '')) }}
tasks:
- name: Exiger un service connu et un amont déclaré
ansible.builtin.assert:
that:
- emancipation_service is defined
- emancipation_service in emancipation_services
- emancipation_adresse | length > 0
fail_msg: >-
SERVICE doit valoir l'un de {{ emancipation_services.keys() | join(', ') }},
et cet écosystème doit DÉCLARER l'amont correspondant. Un emprunt qu'aucune
variable ne nomme ne peut pas être prouvé coupé — c'est la forme normée de
`docs/conception/filiation-emancipation.md` qui rend cette preuve possible.
# PAS de `run_once` : l'amont se lit dans les variables de CHAQUE hote. Un
# ecosysteme peut tres bien emprunter sur une machine et pas sur une autre — c'est
# meme le cas ici, ou seul le porteur du cache chaine vers celui du site. Verifier
# une fois pour toutes lirait le premier hote et parlerait au nom des autres.
- name: Prouver
block:
# ── L'ÉTAT AVANT : la fonction marche-t-elle seulement ? ────────────
#
# Sans ce témoin, une fonction déjà cassée AVANT la coupure se lirait comme une
# dépendance prouvée. On mesure donc le point de départ.
- name: Témoin — la fonction marche-t-elle AVANT toute coupure ?
ansible.builtin.command:
cmd: "{{ emancipation_ce_service.epreuve }}"
register: emancipation_temoin
failed_when: false
changed_when: false
- name: Refuser de mesurer une fonction déjà cassée
ansible.builtin.assert:
that:
- emancipation_temoin.rc == 0
fail_msg: >-
« {{ emancipation_ce_service.dit }} » ne marche DÉJÀ PAS sur cet hôte, avant
qu'on ait rien coupé. Une coupure ne prouverait rien ici : on lirait une
panne préexistante comme une dépendance. Réparer d'abord.
# ── LA COUPURE ─────────────────────────────────────────────────────
- name: Couper le lien vers l'amont
ansible.builtin.command:
argv:
- nft
- -f
- "-"
stdin: |
table inet setops_emancipation {
chain output {
type filter hook output priority -10; policy accept;
ip daddr {{ emancipation_adresse }} reject with icmpx type admin-prohibited
}
}
changed_when: true
# ── L'ÉPREUVE ──────────────────────────────────────────────────────
- name: L'épreuve, l'amont coupé
ansible.builtin.command:
cmd: "{{ emancipation_ce_service.epreuve }}"
register: emancipation_epreuve
failed_when: false
changed_when: false
always:
# RETIRÉE QUOI QU'IL ARRIVE — y compris si l'épreuve échoue, si le play est
# interrompu, ou si la mesure lève. Une coupure qui survit à son essai serait une
# panne qu'on aurait fabriquée soi-même.
- name: Rétablir le lien
ansible.builtin.command:
argv: ["nft", "delete", "table", "inet", "setops_emancipation"]
failed_when: false
changed_when: true
- name: Vérifier que le lien est bien rétabli
ansible.builtin.command:
argv: ["nft", "list", "table", "inet", "setops_emancipation"]
register: emancipation_reste
failed_when: emancipation_reste.rc == 0
changed_when: false
- name: "✔ Émancipation — {{ emancipation_service }}"
ansible.builtin.debug:
msg: >-
{{ (emancipation_epreuve.rc == 0) | ternary(
'ÉMANCIPÉ (prouvé) — « ' ~ emancipation_ce_service.dit ~ ' » fonctionne
l amont ' ~ emancipation_adresse ~ ' COUPÉ. Le lien peut être retiré du
plan : il ne sert plus.',
'PAS ÉMANCIPÉ — « ' ~ emancipation_ce_service.dit ~ ' » casse dès que
l amont ' ~ emancipation_adresse ~ ' est coupé. La dépendance est REELLE,
et cette mesure est le contrôle négatif de la preuve future.') }}