Files
computer-playbook/utils/tests/swarm
kevinveenbirkenbachandClaude Opus 5 ec5bce2eb3 test(swarm): print the disk figure the heartbeat already computes
The watchdog reads free space every 30s but surfaces it only in the abort
banner, so a job that dies without tripping the floor leaves no disk series at
all - which is exactly the silent-ENOSPC shape the function's own docstring
names. Reuse the reading the guard already takes and put it in the heartbeat,
so the next occurrence is decidable from the job log alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 16:29:55 +02:00
..

Swarm test inventory extension

extend_inventory.py expands a provisioned swarm-test inventory so the static validator and the deploy see every group the run will use. It reads APP_ID, INV_PATH and SWARM_NAME from the environment, merges the computed groups into INV_PATH, and writes a sibling backup.yml for the second (backup-host) play.

Flow

flowchart TD
    ENV["env: SWARM_NAME, APP_ID, INV_PATH"] --> MAIN["main()"]

    MAIN --> TOPO["_host_topology(app_id)"]
    MAIN --> DEP["_placement_dep_groups(app_id)"]
    MAIN --> BKP{"backup_repos.repo_placements:\nvolume / secrets role in derive_includes(app_id)?"}
    MAIN --> NBKP{"backup_repos.repo_placements:\nsvc-bkp-nfs-2-local in derive_includes(svc-storage-nfs-server)?"}

    TOPO --> T1["app_id → manager (+ workers unless manager-placed)"]
    TOPO --> T2["svc-swarm-node → manager + workers"]
    TOPO --> T3["svc-swarm-manager → manager"]
    TOPO --> T4["svc-storage-nfs-client → manager + workers"]
    TOPO --> T5["svc-storage-nfs-server → nfs-server"]

    DEP --> D1["derive_includes(app_id)\nrole with placement=manager → manager"]

    BKP -- yes --> B1["svc-bkp-volume-2-local,\nsvc-bkp-secrets-2-local → manager"]
    BKP -- no --> SKIP["skip app-induced backup legs"]
    NBKP -- yes --> NFS["svc-bkp-nfs-2-local → nfs-server"]
    NBKP -- no --> NSKIP["skip nfs-backup leg"]

    T1 & T2 & T3 & T4 & T5 & D1 & B1 & NFS --> MERGE["merge groups into all.children"]

    MERGE --> OUT1["write INV_PATH (devices.yml)"]
    MAIN --> OUT2["write backup.yml:\nsvc-bkp-remote-2-local,\nsvc-bkp-local-2-device → backup host"]

Groups

Group Host Condition
APP_ID manager, workers workers dropped when the role is placement: manager
svc-swarm-node manager, workers always
svc-swarm-manager manager always
svc-storage-nfs-client manager, workers always
svc-storage-nfs-server nfs-server always
dep roles from derive_includes manager role declares placement: manager
svc-bkp-volume-2-local manager derive_includes(APP_ID) pulls it in via the container_backup consumer
svc-bkp-secrets-2-local manager derive_includes(APP_ID) pulls it in
svc-bkp-nfs-2-local nfs-server derive_includes(svc-storage-nfs-server) pulls it in via the nfs_backup consumer
svc-bkp-remote-2-local, svc-bkp-local-2-device backup host always, written to backup.yml

Node names come from default.env (INFINITO_SWARM_*_NAME) with the SWARM_NAME prefix. INFINITO_APP_VARIANTS feeds derive_includes to select the active variant; in-process callers pass the map as derive_includes(app_id, variants=...).

The three svc-bkp-* repository rows come from backup_repos.repo_placements. utils/tests/swarm/matrix.py calls backup_repos.backup_provider_ips, which resolves the same two closures and returns the nodes as IPs, to build svc-bkp-remote-2-local.services.remote-2-local.backup_providers.