Skip to content

fix(runtimes/services): un conteneur arrêté se dit, au lieu d'accuser la commande (0.1.66) - #158

Merged
stephrobert merged 2 commits into
mainfrom
fix/tests-services-instables
Aug 24, 2026
Merged

fix(runtimes/services): un conteneur arrêté se dit, au lieu d'accuser la commande (0.1.66)#158
stephrobert merged 2 commits into
mainfrom
fix/tests-services-instables

Conversation

@stephrobert

Copy link
Copy Markdown
Owner

L'issue #155 demandait de mesurer avant de corriger, et de ne pas allonger un
délai, qui est « la correction la plus tentante et la moins informative ». Cette
PR suit cet ordre, et le résultat de la mesure n'est pas celui qu'on attendait.

Ce que la mesure a réfuté

Hypothèse Verdict
Fenêtre entre docker run -d et docker exec Réfutée : conteneur déjà running, premier exec réussi en 0,08 s au repos et 0,23 s à load 15,8, sur 12 tours chacun
Image tirée du réseau au premier test Réfutée : nginx:alpine et hello-world en cache local
Ordre des tests aléatoire Réfutée : le dépôt n'a ni pytest-randomly ni xdist
Collision de conteneur ou de réseau Réfutée : réseaux distincts (dsoxlab-test / dsoxlab-test-net), aucun parallélisme

L'occurrence exacte de l'issue n'a pas été reproduite : trois exécutions de la
suite complète à load 46 sont restées vertes. D'où Refs #155 et non Closes :
seules plusieurs semaines de pre-push diront si l'intermittence a disparu.

Ce qu'elle a trouvé, et qui ne demande aucune charge

Quand post_start vise un conteneur qui n'est plus debout, Docker répond
container <64 caractères hexadécimaux> is not running. Cette phrase était
reprise telle quelle :

Le service « x » … a échoué sur « sh -c echo x » :
Error response from daemon: container 9dad8320afa7… is not running

Deux erreurs dans une seule ligne : elle accuse une commande qui n'a jamais été
jouée
, et elle désigne le conteneur par un identifiant que l'utilisateur n'a
jamais vu. Désormais :

Le service « x » ne tourne plus, son initialisation ne peut pas être jouée.
Conteneur dsoxlab-diagnostic-local-diag-mortne, sorti avec le code 0.
Dernières lignes : …

Le contrôle n'a lieu qu'après l'échec d'un docker exec : le chemin nominal
ne paie aucun appel supplémentaire, et un service sain n'est jamais interrogé
pour rien. Une première version vérifiait avant, en réutilisant _is_running :
elle était fausse, parce que cette fonction répond à une autre question (« y
a-t-il un conteneur à réutiliser ? », avant le run), et elle a cassé un test
mocké. La mesure l'a attrapée.

Les deux tests de l'issue

Ils déclarent enfin la sonde ready_exec que le contrat recommande, au lieu
d'enchaîner un docker exec que rien n'autorisait : l'attente implicite tenait à
la charge de la machine. Aucun délai n'a été allongé. Leurs messages portent
maintenant l'état du conteneur, son code de sortie et ses logs, parce qu'un échec
intermittent en intégration continue ne laisse aucune autre trace, et qu'un
assert x.ok nu ne laissait rien à diagnostiquer.

Un défaut trouvé en chemin, qui empêchait la mesure

Deux tests de test_documentation_synchrone.py étaient rouges chez le
contributeur et verts en intégration continue
: le contrôle lisait tous les
Markdown de la racine, y compris ceux que git ne suit pas. Un CLAUDE.md citant
~/.config/dsoxlab/config.yaml pour dire que ce chemin n'existe pas encore
suffisait à le faire échouer. Il ne retient plus que les fichiers versionnés, et
retombe sur tout ce qu'il trouve hors dépôt git, pour qu'une archive extraite ne
le rende pas vert en le vidant.

Contrôles joués

Always

  • uv run ruff check src/dsoxlab tests tests_e2e fuzz scripts : All checks passed
  • uv run mypy src/dsoxlab : no issues found in 60 source files
  • uv run pytest : 633 passed (629 avant, +4)
  • uv run pytest tests_e2e : 16 passed sur la roue construite et installée
  • Les quinze hooks pre-commit passent, y compris le contrôle de documentation
  • Le moteur reste neutre vis-à-vis du domaine : le diff n'ajoute aucun nom de
    produit ni aucune catégorie de labs
  • Aucun chemin personnel, aucun hôte en dur
  • Testé sur deux dépôts fournisseurs, en visant le chemin modifié :
    - ansible-training, lab vault-integration-hashicorp (ready_exec: vault status et post_start) : service démarré, et le secret déposé
    par post_start relu dans le conteneur pour prouver l'effet, pas la forme
    - terraform-training (aucun bloc infra:), lab
    aws-provider-aws-first-ec2 (ready_exec sans post_start) :
    service démarré, chemin sans initialisation inchangé
  • Les deux corrections sont éprouvées par leur échec : chacune neutralisée
    tour à tour pour vérifier que le test nouveau rougit, puis restaurée
  • 8 exécutions sur 8 de la suite complète sous charge (load jusqu'à
    50,7, 32 processus de calcul et 6 boucles d'appels Docker) sans un seul
    échec

When behavior changes

  • CHANGELOG EN et FR ; version 0.1.66 ; uv.lock aligné

When a command or option is added, removed or changed — N/A

Aucune commande ni option n'est touchée. La clé i18n nouvelle
(err_service_container_stopped) est posée dans en.py et fr.py, et le
message a été lu sous DSOXLAB_LANG=en comme sous DSOXLAB_LANG=fr.

When .github/workflows/ is touched — N/A

When the declarative contract changes — N/A

Ni meta.yml ni lab.yaml ne gagnent de champ. ready_exec existait déjà, et
la documentation le décrivait déjà comme « le seul signal de disponibilité
fiable » : ce sont les tests du dépôt qui ne suivaient pas leur propre contrat.

Note de version

0.1.66 et non 0.1.65, ce numéro appartenant à la PR #157, ouverte et
mergeable. Si #157 part la première, l'enchaînement est cohérent.

Related issues

Refs #155

🤖 Generated with Claude Code

stephrobert and others added 2 commits August 24, 2026 10:01
… la commande (0.1.66)

L'issue #155 demandait de mesurer avant de corriger. La mesure a réfuté
l'hypothèse la plus tentante : entre docker run -d et le premier docker exec il
n'y a aucune fenêtre, 0,08 s au repos et 0,23 s à load 15,8, conteneur déjà
running. Ni le tirage d'image, ni l'ordre des tests, ni une collision de nom ne
tiennent non plus.

Ce qu'elle a trouvé ne demande aucune charge. Quand post_start vise un conteneur
qui n'est plus debout, Docker répond « container <64 caractères hexadécimaux> is
not running », et cette phrase était reprise dans « l'initialisation du service
a échoué sur telle commande ». Elle accuse une commande qui n'a jamais été
jouée, et nomme le conteneur par un identifiant que personne n'a vu. Le message
donne maintenant l'arrêt, le code de sortie et les dix dernières lignes des
logs. Le contrôle n'a lieu qu'après l'échec d'un docker exec : le chemin nominal
ne paie aucun appel de plus.

Les deux tests instables déclarent enfin la sonde ready_exec que le contrat
recommande, au lieu d'enchaîner un docker exec que rien n'autorisait. Aucun
délai n'a été allongé, ce que l'issue déconseillait. Leurs messages portent
l'état du conteneur, son code de sortie et ses logs : un échec intermittent en
intégration continue ne laisse aucune autre trace.

Au passage, deux tests de documentation étaient rouges chez le contributeur et
verts en intégration continue : le contrôle lisait tous les Markdown de la
racine, y compris ceux que git ne suit pas. Il ne retient plus que les fichiers
versionnés, et retombe sur tout hors dépôt git pour ne pas se vider.

Les deux corrections sont éprouvées par leur échec, chacune neutralisée tour à
tour pour voir le test rougir. L'issue reste ouverte : son occurrence exacte n'a
pas été reproduite, seules plusieurs semaines de pre-push le diront.

Refs #155

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le merge de #157 a posé 0.1.65 sur main. Les seuls conflits sont la ligne de
version et l'ordre des deux sections du CHANGELOG, résolus en gardant 0.1.66
au-dessus de 0.1.65. Le contenu obtenu est identique à celui d'un rebase joué
séparément et validé sur 650 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@stephrobert
stephrobert merged commit 3fc87d1 into main Aug 24, 2026
18 checks passed
@stephrobert
stephrobert deleted the fix/tests-services-instables branch August 24, 2026 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant