Aller au contenu

Exécuter une expérience ​

Pour exécuter une expérience sur un serveur depuis un script ou un pipeline et savoir comment elle s’est passée, envoyez-la à POST /api/run. Le serveur l’exécute jusqu’au bout et répond avec le résultat — ou, si vous le demandez, avec chaque étape au fur et à mesure. C’est ce qu’utilise signallab run --server.

C’est la même exécution que le Exécuter l’expérience de l’éditeur et experiment_start : une tâche que chaque page ouverte voit et peut arrêter, les mêmes événements, le même rapport dans le dossier de données.

La demande ​

http
POST /api/run
Authorization: Bearer <token>
Content-Type: application/json

{ "template": "osc-ping-reply", "overrides": { "device": "192.0.2.20:9000" }, "seed": 42, "timeout": 30 }

Le corps nomme une expérience — un document ou un template, pas les deux — et avec quoi l’exécuter :

ChampTypeDéfautSignification
documentobject—Une expérience telle que l’éditeur l’enregistre et l’exporte. Les versions plus anciennes sont migrées, comme à l’ouverture d’un fichier
templatestring—Un modèle fourni, par nom de fichier, avec ou sans .json (plus bas)
overridesobject{}Les valeurs de paramètres pour cette exécution seulement. Les valeurs peuvent être du texte, des nombres ou des booléens ; chacune doit être un paramètre de l’expérience
profilestringcelui du documentExécuter avec ce profil ; "" exécute avec les valeurs par défaut
seednumbercelui du document, sinon une nouvelle0 à 9007199254740991 ; la même graine tire les mêmes valeurs aléatoires
timeoutnumber300Secondes avant que l’exécution échoue avec run.timeout ; 1 à 300

Un champ que le serveur ne connaît pas est refusé (400, api.run_invalid). Le document lui-même est lu comme l’est un fichier ouvert, donc il fait au plus 4 Mio.

Les modèles fournis — les expériences que l’éditeur propose sous Modèles :

templateCe que c’est
emptyDébut et Fin
http-checkUn GET de http://127.0.0.1:8080/, puis une vérification du statut 200
status-branchUn GET de http://127.0.0.1:8080/ ; sur 200 un message OSC, sinon un délai de 500 ms
parallel-flowsUn Branche parallèle vers un GET de http://127.0.0.1:8080/ et une entrée de journal, côte à côte, puis Jonction de branches
osc-ping-replyUn /ping OSC vers le paramètre device (127.0.0.1:9000), puis une attente de 2 s de /pong sur 127.0.0.1:9001
poll-until-readyUne Boucle qui demande /status à device en OSC jusqu’à ce qu’il réponde ready, au plus 10 fois
flaky-apiUne API émulée (paramètre api) qui échoue avant de fonctionner, interrogée dans une Boucle jusqu’à ce qu’elle réponde 200
fault-phasesUn appareil UDP derrière un relais de dégradation, auquel on envoie pendant 8 s pendant que le relais devient propre, avec pertes, hors ligne et de nouveau propre
dependency-outageUne API émulée (paramètre api) mise en panne pendant 2 s pendant qu’une Boucle l’interroge jusqu’à ce qu’elle réponde de nouveau 200
websocket-echoUne connexion au paramètre service (ws://127.0.0.1:9001/echo), un message, une attente de son écho, une vérification, une fermeture

Ouvrez-en un sous Modèles pour voir ses nœuds et paramètres ; voir expériences.

Le résultat ​

Par défaut la réponse est 200 avec Content-Type: application/json, envoyée quand l’exécution est terminée : un objet JSON, le résultat de l’exécution.

json
{
  "job_id": 12,
  "experiment": "OSC ping → reply",
  "outcome": "passed",
  "seed": 42,
  "profile": null,
  "overridden": true,
  "params": { "device": "192.0.2.20:9000" },
  "started_ms": 1759600000000,
  "ended_ms": 1759600000310,
  "steps": [ { "job_id": 12, "ts": 1759600000001, "node_id": "start", "state": "running", "detail": "", "message_key": null, "message_params": null }, … ],
  "report_path": "/data/runs/run-1759600000000-12.json"
}
ChampTypeSignification
job_idnumberLa tâche de l’exécution
experimentstringLe nom de l’expérience
outcomestringpassed, failed ou stopped
seednumberLa graine avec laquelle elle a tourné : rendez-la comme seed pour tirer les mêmes valeurs
profilestring ou nullLe profil avec lequel elle a tourné
overriddenbooleanCertaines valeurs venaient d’overrides
paramsobjectChaque valeur de paramètre utilisée par l’exécution
started_ms, ended_msnumberMillisecondes depuis 1970
errorEngineErrorPourquoi elle a échoué : son premier échec. Omis quand elle a réussi
stepsobject[]Chaque étape dans l’ordre où elle s’est produite, comme experiment://step
emulatorsobject[]Ce que chaque nœud Émulateur a reçu et répondu : node, name, protocol, local, counts. Omis quand il n’y en a aucun
impairmentsobject[]Ce qu’a fait le relais de chaque nœud Dégradation, phase par phase. Omis quand il n’y en a aucun
report_pathstringLe rapport de l’exécution sur le serveur ; téléchargez-le avec /api/files. Omis quand aucun n’a été écrit
report_errorEngineErrorPourquoi le rapport n’a pas pu être écrit. Omis sinon

Les valeurs de secrets sont masquées dans tout cela. Le fichier de rapport contient les mêmes étapes ; voir exécutions et rapports.

Pendant que l’exécution se déroule, le serveur envoie un espace toutes les 15 s. JSON ignore les espaces avant une valeur, le résultat s’analyse donc toujours, et un proxy ne prend pas une exécution longue et silencieuse pour une connexion morte.

Suivre les étapes ​

Pour voir les étapes au fur et à mesure, demandez du NDJSON :

http
Accept: application/x-ndjson

La réponse est 200 avec Content-Type: application/x-ndjson : un objet JSON par ligne, chacun avec un type.

typeQuandLe reste de la ligne
startedEn premier, une foisjob_id, experiment, seed, profile, overridden, started_ms
stepChaque étapeL’étape, comme experiment://step
heartbeatToutes les 15 sRien
endedEn dernier, une foisLe résultat, comme plus haut
text
{"job_id":12,"experiment":"OSC ping → reply","seed":42,"profile":null,"overridden":true,"started_ms":1759600000000,"type":"started"}
{"job_id":12,"ts":1759600000001,"node_id":"start","state":"running","detail":"","message_key":null,"message_params":null,"type":"step"}
…
{"job_id":12,"experiment":"OSC ping → reply","outcome":"passed",…,"type":"ended"}

Lisez les lignes jusqu’à ended ; ignorez un type que vous ne connaissez pas. Le serveur envoie X-Accel-Buffering: no, pour qu’un proxy nginx transmette chaque ligne aussitôt.

Statut et résultat ​

Un statut d’erreur HTTP signifie qu’aucune exécution n’a démarré ; le corps est un EngineError :

StatutCodePourquoi
400api.run_invalidLe corps n’est pas une demande d’exécution : pas du JSON, un champ inconnu, un override qui n’est pas du texte, un nombre ou un booléen
400api.run_sourceNi document ni template, ou les deux
415command.json_requiredPas Content-Type: application/json
422api.template_unknownAucun modèle fourni de ce nom
422file.json_invalid, file.too_large, doc.*Le document ne peut pas être lu
422n’importe quel code de validation, run.override_unknown, profile.active_missing, run.limit_range, seed.range, secret.missing, transport.address_in_use…L’expérience ne peut pas démarrer : elle ne valide pas, une valeur est hors limites, un secret n’est pas stocké, un port qu’elle écoute est pris

Une fois l’exécution démarrée, le statut est 200, quoi qu’il arrive : lisez outcome dans le résultat.

outcomeSignification
passedChaque étape a réussi et Fin a été atteint
failedUne étape a échoué, ou l’exécution a duré plus que son timeout (run.timeout) ; error dit laquelle et pourquoi
stoppedElle a été arrêtée avant de se terminer : par job_stop, Tout arrêter, ou l’arrêt du serveur. steps contient les étapes qu’elle a atteintes ; aucun rapport n’est enregistré

Un client qui s’en va ​

Fermer la connexion n’arrête pas l’exécution. C’est une tâche sur le serveur : elle tourne jusqu’au bout et enregistre son rapport, comme le fait une exécution démarrée dans un navigateur quand l’onglet est fermé. Retrouvez-la avec jobs_list, arrêtez-la avec job_stop, et lisez son rapport ensuite avec experiment_runs. Quand le serveur s’arrête, l’exécution est arrêtée et un client encore connecté reçoit "outcome": "stopped".

Exemples ​

Exécutez un modèle fourni et attendez le résultat :

bash
SERVER=http://127.0.0.1:1430
TOKEN=$(cat token.txt)
curl -sS -X POST "$SERVER/api/run" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"template":"osc-ping-reply","overrides":{"device":"192.0.2.20:9000"},"timeout":30}' \
  | jq -r .outcome

Envoyez votre propre expérience avec un paramètre modifié, et affichez chaque étape au fur et à mesure :

bash
jq '{document: ., overrides: {api: "http://192.0.2.10:8080"}, profile: ""}' smoke.json |
  curl -sSN -X POST "$SERVER/api/run" \
    -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
    -H "Accept: application/x-ndjson" --data @- |
  jq -r 'select(.type == "step") | "\(.node_id)  \(.state)  \(.detail)"'

-N empêche curl de retenir les lignes. Pour faire échouer un pipeline sur une exécution en échec, vérifiez outcome :

bash
outcome=$(curl -sS -X POST "$SERVER/api/run" -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"template":"http-check"}' | jq -r .outcome)
[ "$outcome" = "passed" ]

Depuis la ligne de commande ​

signallab run avec --server <url> exécute sur un serveur via ce point de terminaison : il envoie l’expérience qu’il a lue, avec overrides, seed et timeout, demande du NDJSON, et affiche chaque étape à l’arrivée de sa ligne. Le jeton vient de --token-file, sinon SIGNALLAB_TOKEN. Avec --report il télécharge le rapport via /api/files. Un serveur qui n’envoie rien pendant 60 s — même pas un battement — est considéré comme parti.