collections : epingler les versions — le runner echouait la ou le poste reussissait
Premiere materialisation de VM depuis le runner du SITE :
ERROR! couldn't resolve module/action 'community.general.proxmox_pool'
Meme depot, meme playbook, meme plan que chez le mainteneur. La difference tenait
a une seule chose que le moteur ne disait pas : la VERSION de ses collections.
Le poste porte `community.general` 10.3.0. Le runner, monte un jour plus tard, a
recu la 13.3.0 — et la version 11 a RETIRE les modules Proxmox de cette collection
(ils vivent desormais dans `community.proxmox`). `requirements.yml` nommait ses
collections sans dire lesquelles : chaque machine installait donc ce qui etait
courant le jour de son montage. Une dependance non epinglee n'est pas une
dependance, c'est un pari sur l'etat d'Internet a la date du deploiement.
TROIS CORRECTIONS.
`requirements.yml` epingle les trois collections aux versions eprouvees.
`serveur_ops` installe avec `--force`. Sans lui, ansible-galaxy laisse en place une
version SUPERIEURE a celle demandee : il ne retrograde pas. Un poste peut etre en
avance, pas seulement en retard, et le depot doit faire autorite dans les deux sens.
P51 garde les deux faiblesses de ce fichier — celle du 2026-08-24, une collection
utilisee sans etre declaree (`ansible.posix`), et celle d'aujourd'hui, declaree sans
version. La preuve ne lit que les fichiers de TACHES : un `defaults/main.yml` porte
`net.ipv4.ip_forward`, qu'un motif trop large prend pour un module.
MIGRATION CONNUE, PAS FAITE : passer les quatre modules Proxmox a
`community.proxmox` permettra de suivre `community.general` au-dela de la 11.
make prouver : CONFORME, 51 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
3b65384b0f
commit
3fa6e4c3e1
4 changed files with 114 additions and 2 deletions
|
|
@ -7,7 +7,7 @@
|
|||
> [`docs/audit/affirmations.md`](affirmations.md).
|
||||
|
||||
- **Instance** : `instance` — inventaire `instance/inventories/production/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (50 OK · 0 echec · 0 saute)
|
||||
- **Verdict** : ✅ CONFORME (51 OK · 0 echec · 0 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
|
|
@ -63,6 +63,7 @@
|
|||
| P48 | La carte d'orientation designe ce qui existe, et compte juste | — | ✅ OK | La carte designe 84 chemin(s) qui existent, et ses 7 chiffres correspondent a la mesure. |
|
||||
| P49 | Registre des flux : la matrice d'audit est a jour | — | ✅ OK | Le registre des flux reproduit exactement ce que les `meta/flux.yml` declarent (110 lignes). |
|
||||
| P50 | Silences : un refus muet est declare, place en dernier, et motive | — | ✅ OK | 2 silence(s) declare(s), tous en sequence > 1 (la plus haute des 121 regles `pass`), tous non consignes et tous motives. |
|
||||
| P51 | Collections : toutes declarees, toutes epinglees | — | ✅ OK | 3 collection(s) appelee(s) par le moteur, toutes declarees et epinglees : ansible.posix==1.6.2, community.general==10.3.0, community.postgresql==3.10.2 |
|
||||
|
||||
## Couverture des affirmations ✅ du registre
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,36 @@
|
|||
# poste du mainteneur. Une collection utilisee par un role et absente d'ici marche chez
|
||||
# le mainteneur et echoue partout ailleurs : c'est le genre d'ecart qu'on ne voit qu'en
|
||||
# portant le moteur sur une autre machine.
|
||||
# LES VERSIONS SONT EPINGLEES (2026-08-27). Ce fichier nommait ses collections sans dire
|
||||
# LESQUELLES : chaque machine installait donc ce qui etait courant le jour de son montage.
|
||||
#
|
||||
# Ce que ca a coute. Le runner du SITE, monte le 2026-08-26, a recu `community.general`
|
||||
# 13.3.0 quand le poste du mainteneur porte la 10.3.0. Or la version 11 a RETIRE les
|
||||
# modules Proxmox de cette collection — ils vivent desormais dans `community.proxmox`. Le
|
||||
# runner a donc echoue a la premiere materialisation de VM, sur un
|
||||
# « couldn't resolve module/action 'community.general.proxmox_pool' », alors que le meme
|
||||
# depot, le meme playbook et le meme plan fonctionnaient chez le mainteneur.
|
||||
#
|
||||
# Un moteur qui ne dit pas de QUELLES versions il depend n'est pas portable : il est
|
||||
# seulement chanceux. Nommer une collection sans l'epingler, c'est declarer une dependance
|
||||
# sur « l'etat d'Internet a la date du deploiement ».
|
||||
#
|
||||
# MIGRATION CONNUE, PAS ENCORE FAITE : passer les quatre modules Proxmox
|
||||
# (`proxmox_kvm`, `proxmox_pool`, `proxmox_nic`, `proxmox_disk`) a `community.proxmox`
|
||||
# permettra de suivre `community.general` au-dela de la 11. Tant que ce n'est pas fait,
|
||||
# l'epingle ci-dessous est ce qui tient. Les modules LDAP, eux, restent dans
|
||||
# `community.general` — la migration ne les concerne pas.
|
||||
collections:
|
||||
- name: community.postgresql
|
||||
version: 3.10.2
|
||||
# `<11.0.0` N'EST PAS UN CAPRICE : la 11 a sorti les modules Proxmox de cette collection.
|
||||
# Les remonter demanderait de reecrire `playbooks/proxmox/*` — voir la note ci-dessus.
|
||||
- name: community.general
|
||||
version: 10.3.0
|
||||
# `serveur_backup` : ansible.posix.authorized_key. Utilisee depuis longtemps, jamais
|
||||
# declaree — donc absente du runner, qui n'aurait pas pu deployer les sauvegardes.
|
||||
- name: ansible.posix
|
||||
version: 1.6.2
|
||||
# `community.docker` a ete RETIREE le 2026-08-24 : `serveur_collabora` etait la seule
|
||||
# exception conteneurisee de la flotte, et il tourne desormais en natif (paquet coolwsd
|
||||
# du depot amont Collabora). Plus aucun role n'a besoin de Docker.
|
||||
|
|
|
|||
|
|
@ -237,9 +237,18 @@
|
|||
ansible.builtin.command:
|
||||
cmd: >-
|
||||
{{ serveur_ops_venv }}/bin/ansible-galaxy collection install
|
||||
-r requirements.yml
|
||||
-r requirements.yml --force
|
||||
-p {{ serveur_ops_racine }}/.ansible/collections
|
||||
chdir: "{{ serveur_ops_racine }}/.collections-hors-ligne"
|
||||
# `--force` PARCE QU'UN POSTE PEUT ETRE EN AVANCE, PAS SEULEMENT EN RETARD (2026-08-27).
|
||||
# Sans lui, `ansible-galaxy` laisse en place une collection deja installee dans une
|
||||
# version SUPERIEURE a celle demandee : il ne retrograde pas. Le runner du SITE, monte
|
||||
# avant que `requirements.yml` n'epingle ses versions, portait `community.general` 13.3.0
|
||||
# — une version d'ou les modules Proxmox ont ete RETIRES. La premiere materialisation de
|
||||
# VM depuis le runner a echoue la-dessus. Le depot doit faire autorite dans les DEUX
|
||||
# sens : ce que `requirements.yml` declare est ce qui est installe, ni plus haut ni plus
|
||||
# bas. Voir la preuve P51.
|
||||
#
|
||||
# LE GARDE-FOU SUIVAIT LA MAUVAISE CHOSE (2026-08-24). Il etait `creates: …/community/general` :
|
||||
# une collection AJOUTEE a requirements.yml n'aurait jamais ete installee, puisque le
|
||||
# repertoire temoin existait deja. Le runner serait reste en retard sur le moteur, en
|
||||
|
|
|
|||
|
|
@ -1396,6 +1396,82 @@ def preuve_carte_dit_vrai() -> tuple[bool, str]:
|
|||
f"{len(_carte_chiffres_mesures())} chiffres correspondent a la mesure.")
|
||||
|
||||
|
||||
def preuve_collections_declarees_et_epinglees() -> tuple[bool, str]:
|
||||
"""Toute collection appelee par le moteur est DECLAREE, et a une version EPINGLEE.
|
||||
|
||||
DEUX PANNES, LA MEME CAUSE — le moteur ne disait pas de quoi il depend.
|
||||
|
||||
Le 2026-08-24, `ansible.posix` etait utilisee depuis longtemps sans figurer dans
|
||||
`requirements.yml` : elle trainait sur le poste du mainteneur, et manquait partout
|
||||
ailleurs. Le runner n'aurait pas pu deployer les sauvegardes.
|
||||
|
||||
Le 2026-08-27, la meme faiblesse a frappe d'un autre cote. Les collections etaient
|
||||
nommees mais SANS VERSION : le runner du site, monte un jour plus tard, a recu
|
||||
`community.general` 13.3.0 quand le poste porte la 10.3.0. La version 11 ayant sorti
|
||||
les modules Proxmox de cette collection, la premiere materialisation de VM depuis le
|
||||
runner a echoue sur `couldn't resolve module/action community.general.proxmox_pool`
|
||||
— avec le meme depot, le meme playbook et le meme plan que chez le mainteneur.
|
||||
|
||||
Une dependance non epinglee n'est pas une dependance : c'est un pari sur l'etat
|
||||
d'Internet a la date du deploiement. Un moteur qui se veut portable, et exploitable
|
||||
par un sysadmin sans IA, doit pouvoir se reconstruire a l'identique dans six mois.
|
||||
|
||||
On lit les MODULES REELLEMENT APPELES dans les fichiers YAML — la clef d'une tache,
|
||||
pas une chaine dans du Python : `ansible.parsing.vault` est un import, pas une
|
||||
collection, et l'y confondre ferait crier la preuve pour rien.
|
||||
"""
|
||||
req = RACINE / "requirements.yml"
|
||||
if not req.is_file():
|
||||
return False, "requirements.yml est introuvable — le moteur ne declare aucune dependance."
|
||||
try:
|
||||
import yaml as _y
|
||||
decl = (_y.safe_load(req.read_text(encoding="utf-8")) or {}).get("collections") or []
|
||||
except Exception as e: # noqa: BLE001
|
||||
return False, f"requirements.yml est illisible : {e}"
|
||||
|
||||
declarees = {}
|
||||
for c in decl:
|
||||
if isinstance(c, dict) and c.get("name"):
|
||||
declarees[str(c["name"])] = str(c.get("version") or "").strip()
|
||||
elif isinstance(c, str):
|
||||
declarees[c] = ""
|
||||
|
||||
# Une clef de tache YAML : ` - community.general.ldap_entry:` ou ` module.x.y:`
|
||||
motif = re.compile(r"^\s*(?:-\s+)?([a-z0-9_]+\.[a-z0-9_]+)\.[a-z0-9_]+\s*:", re.M)
|
||||
# ON NE LIT QUE LES FICHIERS DE TACHES. Un `defaults/main.yml` peut contenir
|
||||
# `net.ipv4.ip_forward: 1` — une clef de sysctl que le motif prendrait pour un module,
|
||||
# et la preuve crierait a une collection manquante nommee `net.ipv4`. Un module ne
|
||||
# peut apparaitre que la ou il y a des taches.
|
||||
fichiers = list((RACINE / "playbooks").rglob("*.yml"))
|
||||
for sous in ("tasks", "handlers"):
|
||||
fichiers += list((RACINE / "roles").rglob(f"{sous}/*.yml"))
|
||||
utilisees: dict[str, str] = {}
|
||||
for f in fichiers:
|
||||
try:
|
||||
txt = f.read_text(encoding="utf-8")
|
||||
except Exception: # noqa: BLE001
|
||||
continue
|
||||
for coll in motif.findall(txt):
|
||||
if coll == "ansible.builtin":
|
||||
continue
|
||||
utilisees.setdefault(coll, str(f.relative_to(RACINE)))
|
||||
|
||||
manquantes = [f"{c} (ex. {utilisees[c]})" for c in sorted(utilisees) if c not in declarees]
|
||||
sans_epingle = [c for c in sorted(declarees) if not declarees[c]]
|
||||
fautes = []
|
||||
if manquantes:
|
||||
fautes.append("appelee(s) mais NON DECLAREE(S) dans requirements.yml : "
|
||||
+ ", ".join(manquantes))
|
||||
if sans_epingle:
|
||||
fautes.append("declaree(s) SANS VERSION — le deploiement prendra ce qui passe : "
|
||||
+ ", ".join(sans_epingle))
|
||||
if fautes:
|
||||
return False, " | ".join(fautes)
|
||||
return True, (f"{len(utilisees)} collection(s) appelee(s) par le moteur, toutes "
|
||||
f"declarees et epinglees : "
|
||||
+ ", ".join(f"{c}=={declarees[c]}" for c in sorted(utilisees)))
|
||||
|
||||
|
||||
def preuve_silences_declares() -> tuple[bool, str]:
|
||||
"""Un `block` muet doit etre DECLARE, place APRES les `pass`, et porter son motif.
|
||||
|
||||
|
|
@ -1854,6 +1930,8 @@ PREUVES: list[dict] = [
|
|||
"refs": [], "func": preuve_registre_flux_a_jour},
|
||||
{"id": "P50", "titre": "Silences : un refus muet est declare, place en dernier, et motive",
|
||||
"refs": [], "func": preuve_silences_declares},
|
||||
{"id": "P51", "titre": "Collections : toutes declarees, toutes epinglees",
|
||||
"refs": [], "func": preuve_collections_declarees_et_epinglees},
|
||||
{"id": "P43", "titre": "Frontiere : le devis voit les machines du site", "refs": [],
|
||||
"func": preuve_devis_frontiere_du_site},
|
||||
{"id": "P33", "titre": "Aucune collision de port entre roles co-localises", "refs": [],
|
||||
|
|
|
|||
Loading…
Reference in a new issue