[infra] La chaîne n'est pas reconstructible : la configuration de l'exécuteur n'existe que sur le serveur #141

Closed
opened 2026-09-04 08:06:43 +00:00 by lenaic · 2 comments
Owner

Exigence couverte

ENF-09 — Reproductibilité : « l'hôte est décrit par un playbook Ansible rejouable sur une machine vierge. Aucun geste manuel non documenté. »

Épreuve servie

EC03 · CI/CD et qualité

Ce qu'on veut obtenir

La configuration de l'exécuteur Forgejo n'existe qu'en un seul exemplaire, sur le serveur, sous /opt/g2-forge/data/runner/config.yml.

  • infra/compose/forge/docker-compose.yml le monte depuis ./data/runner/ ;
  • data/ est exclu par .gitignore (section « données et modèles, hors dépôt ») ;
  • git ls-tree -r --name-only develop | grep runner ne renvoie rien.

C'est pourtant ce fichier qui force le réseau hôte des conteneurs de tâche. Sans lui, aucun job ne démarre sur notre LXC, où runc ne peut écrire aucun sysctl réseau. L'en-tête de .forgejo/workflows/ci.yml le dit explicitement : « les conteneurs de tâches sont forcés en réseau hôte dans la configuration du runner, il n'y a rien à déclarer ici ».

Conséquence : un clone du dépôt sur une machine vierge ne rejoue pas la chaîne. C'est une contradiction directe avec ENF-09, et le seul geste manuel non documenté qui reste sur le socle.

Voir le relevé serveur en commentaire : capacity vaut déjà 2 et n'est pas montée, la mesure ne gagnerait rien.

Hors périmètre, volontairement : l'image de chaîne pré-cuite. La #134 (mutualisation des préambules, cache pip et npm) lui a retiré l'essentiel de sa valeur ; elle part au backlog en Priority/Low.

Critères d'acceptation

  • Le config.yml de l'exécuteur est versionné dans un rôle Ansible ci_runner, à l'identique de ce qui tourne : réseau hôte forcé, force_pull: false, serveur de cache activé, capacity laissée à 2 et justifiée en commentaire.
  • Le fichier actuellement en place sur le serveur est récupéré et comparé au fichier versionné avant tout remplacement ; l'équivalence est constatée dans la demande de fusion.
  • Sur une machine vierge, ansible-playbook site.yml --tags ci suffit à obtenir un exécuteur opérationnel. Aucun geste manuel.
  • tests/ci/test-runner-reproductible.sh vérifie statiquement que le config.yml versionné porte le forçage réseau hôte et une capacity d'au moins 2, et que les libellés de l'exécuteur sont déclarés.
  • docs/runbooks/ci.md gagne une section « Reconstruire la chaîne sur une machine vierge » ; docs/runbooks/forge.md §6 renvoie vers le rôle plutôt que vers un geste manuel.
  • Les bancs d'essai existants de tests/ci/ passent sans modification.

Comment on le vérifie

tests/ci/test-runner-reproductible.sh    # nouveau
tests/ci/test-deploiement-continu.sh     # non-régression
tests/ci/test-verifier-images.sh         # non-régression
tests/ci/test-supervision.sh             # non-régression
ansible-playbook tests/ci/test-role-app.yml

Preuve à joindre : le diff entre le config.yml du serveur et le fichier versionné, et une exécution complète de la chaîne sur la demande de fusion.

Manuel d'exploitation à mettre à jour

  • docs/runbooks/ci.md — nouvelle section « Reconstruire la chaîne sur une machine vierge ».
  • docs/runbooks/forge.md — §6, réenregistrement de l'exécuteur.
  • infra/ansible/README.md — le nouveau rôle et son étiquette.

Risque et retour arrière

Risque élevé : une erreur dans config.yml arrête l'exécuteur, donc toute la chaîne, donc l'équipe entière.

Geste zéro, avant toute autre chose : récupérer /opt/g2-forge/data/runner/config.yml depuis le serveur et le mettre de côté. C'est aujourd'hui le seul exemplaire existant.

Piège connu : l'exécuteur tire l'image de tâche par défaut. Toute évolution ultérieure vers une image construite localement exigera force_pull: false — à prévoir dans la structure du fichier, pas à découvrir plus tard.

Retour arrière : restaurer le config.yml mis de côté, docker compose up -d g2-forge-runner, révoquer le commit.

Recouvrement connu

La branche lenaic/136-planifier-silver touche infra/ansible/group_vars/all/vars.yml et docs/runbooks/README.md, que ce ticket modifie aussi. Les deux changements sont additifs : conflit textuel trivial, rien de sémantique. Fusionner #136 en premier.

### Exigence couverte ENF-09 — Reproductibilité : « l'hôte est décrit par un playbook Ansible rejouable sur une machine vierge. Aucun geste manuel non documenté. » ### Épreuve servie EC03 · CI/CD et qualité ### Ce qu'on veut obtenir La configuration de l'exécuteur Forgejo n'existe qu'en un seul exemplaire, sur le serveur, sous `/opt/g2-forge/data/runner/config.yml`. - `infra/compose/forge/docker-compose.yml` le monte depuis `./data/runner/` ; - `data/` est exclu par `.gitignore` (section « données et modèles, hors dépôt ») ; - `git ls-tree -r --name-only develop | grep runner` ne renvoie rien. C'est pourtant ce fichier qui force le réseau hôte des conteneurs de tâche. Sans lui, aucun job ne démarre sur notre LXC, où runc ne peut écrire aucun sysctl réseau. L'en-tête de `.forgejo/workflows/ci.yml` le dit explicitement : « les conteneurs de tâches sont forcés en réseau hôte dans la configuration du runner, il n'y a rien à déclarer ici ». **Conséquence : un clone du dépôt sur une machine vierge ne rejoue pas la chaîne.** C'est une contradiction directe avec ENF-09, et le seul geste manuel non documenté qui reste sur le socle. Voir le relevé serveur en commentaire : `capacity` vaut déjà 2 et n'est pas montée, la mesure ne gagnerait rien. Hors périmètre, volontairement : l'image de chaîne pré-cuite. La #134 (mutualisation des préambules, cache pip et npm) lui a retiré l'essentiel de sa valeur ; elle part au backlog en `Priority/Low`. ### Critères d'acceptation - [ ] Le `config.yml` de l'exécuteur est versionné dans un rôle Ansible `ci_runner`, à l'identique de ce qui tourne : réseau hôte forcé, `force_pull: false`, serveur de cache activé, `capacity` laissée à 2 et justifiée en commentaire. - [ ] Le fichier actuellement en place sur le serveur est récupéré et comparé au fichier versionné avant tout remplacement ; l'équivalence est constatée dans la demande de fusion. - [ ] Sur une machine vierge, `ansible-playbook site.yml --tags ci` suffit à obtenir un exécuteur opérationnel. Aucun geste manuel. - [x] `tests/ci/test-runner-reproductible.sh` vérifie statiquement que le `config.yml` versionné porte le forçage réseau hôte et une `capacity` d'au moins 2, et que les libellés de l'exécuteur sont déclarés. - [x] `docs/runbooks/ci.md` gagne une section « Reconstruire la chaîne sur une machine vierge » ; `docs/runbooks/forge.md` §6 renvoie vers le rôle plutôt que vers un geste manuel. - [x] Les bancs d'essai existants de `tests/ci/` passent sans modification. ### Comment on le vérifie ``` tests/ci/test-runner-reproductible.sh # nouveau tests/ci/test-deploiement-continu.sh # non-régression tests/ci/test-verifier-images.sh # non-régression tests/ci/test-supervision.sh # non-régression ansible-playbook tests/ci/test-role-app.yml ``` Preuve à joindre : le `diff` entre le `config.yml` du serveur et le fichier versionné, et une exécution complète de la chaîne sur la demande de fusion. ### Manuel d'exploitation à mettre à jour - `docs/runbooks/ci.md` — nouvelle section « Reconstruire la chaîne sur une machine vierge ». - `docs/runbooks/forge.md` — §6, réenregistrement de l'exécuteur. - `infra/ansible/README.md` — le nouveau rôle et son étiquette. ### Risque et retour arrière Risque élevé : une erreur dans `config.yml` arrête l'exécuteur, donc toute la chaîne, donc l'équipe entière. **Geste zéro, avant toute autre chose : récupérer `/opt/g2-forge/data/runner/config.yml` depuis le serveur et le mettre de côté. C'est aujourd'hui le seul exemplaire existant.** Piège connu : l'exécuteur tire l'image de tâche par défaut. Toute évolution ultérieure vers une image construite localement exigera `force_pull: false` — à prévoir dans la structure du fichier, pas à découvrir plus tard. Retour arrière : restaurer le `config.yml` mis de côté, `docker compose up -d g2-forge-runner`, révoquer le commit. ### Recouvrement connu La branche `lenaic/136-planifier-silver` touche `infra/ansible/group_vars/all/vars.yml` et `docs/runbooks/README.md`, que ce ticket modifie aussi. Les deux changements sont additifs : conflit textuel trivial, rien de sémantique. Fusionner #136 en premier.
Author
Owner

Relevé sur le serveur avant d'écrire le rôle

Le config.yml en service a été récupéré (/opt/g2-forge/data/runner/config.yml, exécuteur v6.4.0). Il corrige trois hypothèses de la rédaction initiale :

Hypothèse au dépôt du ticket Réalité
capacity: 1, quatre tâches à la queue leu leu capacity: 2
force_pull à prévoir force_pull: false déjà posé
Serveur de cache de l'exécuteur déjà activé (cache.enabled: true), c'est lui qui fait marcher les actions/cache@v4 de la #134

capacity reste donc à 2, et le ticket ne la monte plus. Le serveur a 4 cœurs, 8 Gio, 13 conteneurs déjà en service et une charge moyenne de 1,88. Surtout : depuis la #134 la chaîne compte quatre tâches dont une, python, domine largement le chemin critique (outillage, Ruff, mypy, pytest, audits, deux inventaires). Monter la capacité à 3 ne retire aucune vague — on passerait de « python ‖ node, puis images ‖ secrets » à « python ‖ node ‖ images, puis secrets », la durée totale restant celle de python. On achèterait de la contention sur la forge de toute l'équipe sans gagner une seconde.

Effet de bord utile

Le fichier .runner porte les libellés de l'exécuteur, et ils répondent à une question restée ouverte dans l'en-tête de .forgejo/workflows/ci.yml (« on ne sait pas non plus si cette image a npm — jamais exercé ») :

labels: ['docker:docker://node:22-bookworm', 'ubuntu-24.04:docker://ubuntu:24.04']

L'image par défaut du libellé docker est node:22-bookworm. Elle a donc node et npm, et n'a pas pip — ce qui explique enfin les exécutions #14 et #15 en échec silencieux. Le commentaire d'en-tête sera corrigé dans la même demande de fusion : ce n'est plus une inconnue.

Ces libellés vivent dans .runner, aux côtés du jeton d'enregistrement. Ce fichier ne peut donc pas être versionné : le rôle les déclare à l'enregistrement, et le jeton passe par le coffre (vault_forgejo_runner_token).

### Relevé sur le serveur avant d'écrire le rôle Le `config.yml` en service a été récupéré (`/opt/g2-forge/data/runner/config.yml`, exécuteur `v6.4.0`). Il corrige trois hypothèses de la rédaction initiale : | Hypothèse au dépôt du ticket | Réalité | |---|---| | `capacity: 1`, quatre tâches à la queue leu leu | **`capacity: 2`** | | `force_pull` à prévoir | **`force_pull: false` déjà posé** | | — | Serveur de cache de l'exécuteur **déjà activé** (`cache.enabled: true`), c'est lui qui fait marcher les `actions/cache@v4` de la #134 | **`capacity` reste donc à 2, et le ticket ne la monte plus.** Le serveur a 4 cœurs, 8 Gio, 13 conteneurs déjà en service et une charge moyenne de 1,88. Surtout : depuis la #134 la chaîne compte quatre tâches dont une, `python`, domine largement le chemin critique (outillage, Ruff, mypy, pytest, audits, deux inventaires). Monter la capacité à 3 ne retire aucune vague — on passerait de « python ‖ node, puis images ‖ secrets » à « python ‖ node ‖ images, puis secrets », la durée totale restant celle de `python`. On achèterait de la contention sur la forge de toute l'équipe sans gagner une seconde. ### Effet de bord utile Le fichier `.runner` porte les libellés de l'exécuteur, et ils répondent à une question restée ouverte dans l'en-tête de `.forgejo/workflows/ci.yml` (« on ne sait pas non plus si cette image a npm — jamais exercé ») : ``` labels: ['docker:docker://node:22-bookworm', 'ubuntu-24.04:docker://ubuntu:24.04'] ``` L'image par défaut du libellé `docker` est **`node:22-bookworm`**. Elle a donc node et npm, et n'a pas `pip` — ce qui explique enfin les exécutions #14 et #15 en échec silencieux. Le commentaire d'en-tête sera corrigé dans la même demande de fusion : ce n'est plus une inconnue. Ces libellés vivent dans `.runner`, aux côtés du jeton d'enregistrement. Ce fichier ne peut donc pas être versionné : le rôle les déclare à l'enregistrement, et le jeton passe par le coffre (`vault_forgejo_runner_token`).
gabriel added this to the EnerVision project 2026-09-04 12:28:58 +00:00
Author
Owner

Ticket fermé sans qu'aucun critère soit coché. J'ai vérifié les six plutôt que
de tous les cocher : trois sont atteints, trois ne le sont pas encore.

Atteints

Le banc tests/ci/test-runner-reproductible.sh est sur develop et joué par la
chaîne, qui en câble neuf au total. docs/runbooks/ci.md porte bien la section
« Reconstruire la chaîne sur une machine vierge », et forge.md renvoie vers le
rôle. Les bancs existants passent, develop est vert sur ses cinq contrôles.

Ce qui n'est pas atteint, et pourquoi

Le rôle n'a jamais été appliqué. Comparaison entre la configuration qui
tourne dans g2-forge-runner et celle du dépôt :

network      serveur host    dépôt host    identiques
force_pull   serveur false   dépôt false   identiques
enabled      serveur true    dépôt true    identiques
capacity     serveur 3       dépôt 2       DIVERGENT

Trois clés sur quatre coïncident. La quatrième diverge, et c'est délibéré : le
dépôt fixe capacity: 2 avec sa justification mesurée, le serveur tourne encore
sur la configuration posée à la main, à 3.

C'est cohérent avec la conception du ticket, qui exclut volontairement
l'étiquette ci du déploiement continu pour qu'une fusion ne redémarre pas
l'exécuteur qui l'exécute. Le rôle doit donc être joué à la main, et il ne l'a
pas été.

Conséquence à connaître avant de le jouer : l'appliquer fera passer la
capacité de 3 à 2. Les tâches se sérialiseront davantage. La justification est
dans runner-config.yml, mais autant le savoir avant plutôt que de le constater
sur une file d'attente.

Donc :

  • critère 1, « à l'identique de ce qui tourne » : trois clés sur quatre
  • critère 2, l'équivalence constatée : idem, la divergence sur la capacité étant
    un choix et non un oubli
  • critère 3, « sur une machine vierge, --tags ci suffit » : jamais éprouvé, et
    il faudrait une machine vierge pour le faire

Rien de tout ça ne remet en cause le travail : la configuration est versionnée,
le banc la surveille, et c'était l'objet. Il reste à la poser, et à décider si
la capacité passe vraiment à 2.

Ticket fermé sans qu'aucun critère soit coché. J'ai vérifié les six plutôt que de tous les cocher : **trois sont atteints, trois ne le sont pas encore.** ### Atteints Le banc `tests/ci/test-runner-reproductible.sh` est sur `develop` et joué par la chaîne, qui en câble neuf au total. `docs/runbooks/ci.md` porte bien la section « Reconstruire la chaîne sur une machine vierge », et `forge.md` renvoie vers le rôle. Les bancs existants passent, `develop` est vert sur ses cinq contrôles. ### Ce qui n'est pas atteint, et pourquoi **Le rôle n'a jamais été appliqué.** Comparaison entre la configuration qui tourne dans `g2-forge-runner` et celle du dépôt : ``` network serveur host dépôt host identiques force_pull serveur false dépôt false identiques enabled serveur true dépôt true identiques capacity serveur 3 dépôt 2 DIVERGENT ``` Trois clés sur quatre coïncident. La quatrième diverge, et c'est délibéré : le dépôt fixe `capacity: 2` avec sa justification mesurée, le serveur tourne encore sur la configuration posée à la main, à 3. C'est cohérent avec la conception du ticket, qui exclut volontairement l'étiquette `ci` du déploiement continu pour qu'une fusion ne redémarre pas l'exécuteur qui l'exécute. Le rôle doit donc être joué à la main, et il ne l'a pas été. **Conséquence à connaître avant de le jouer** : l'appliquer fera passer la capacité de 3 à 2. Les tâches se sérialiseront davantage. La justification est dans `runner-config.yml`, mais autant le savoir avant plutôt que de le constater sur une file d'attente. Donc : - critère 1, « à l'identique de ce qui tourne » : trois clés sur quatre - critère 2, l'équivalence constatée : idem, la divergence sur la capacité étant un choix et non un oubli - critère 3, « sur une machine vierge, `--tags ci` suffit » : jamais éprouvé, et il faudrait une machine vierge pour le faire Rien de tout ça ne remet en cause le travail : la configuration est versionnée, le banc la surveille, et c'était l'objet. Il reste à la **poser**, et à décider si la capacité passe vraiment à 2.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
g2/enervision#141
No description provided.