Files
computer-playbook/scripts/tests/deploy/swarm/routine
kevinveenbirkenbach 230d9a1e5e feat(cache): declare every cached upstream once and generate the rest
The package cache was configured in six places at once: the nginx upstream
map, the consumer compose override, pip.conf, .npmrc, the apt sources and the
twenty ensure_proxy_repo calls that bootstrap Nexus each spelled the same
hostnames, repository names and suites by hand. A new upstream meant editing
five files and hoping none was missed, and the docs told contributors to do
exactly that.

A role now declares what it needs under cache: in its meta/networks.yml, and
everything else is derived from it. svc-cache-package holds the general
upstreams; web-svc-libretranslate holds its own. All nine derived files are
generated and gitignored, so a second spelling of a hostname cannot exist.

utils.cache.hosts reads the declarations without PyYAML, because the .env
generator reaches them before any dependency is installed; it raises rather
than truncate when a comment sits at column 0 inside the block, which would
otherwise drop every declaration below it in silence. utils.cache.records
owns the Nexus record format and the Ubuntu mirror resolution, so the dev
stack and the role provision from one rendering.

A build may only resolve a cached host it can verify: the plain-HTTP hosts
are hijacked for every image, and the TLS hosts only for an image whose
Dockerfile installs the frontend CA. Hijacking all of them would fail every
pip and npm build with CERTIFICATE_VERIFY_FAILED.

svc-cache-package deploys the cache and its TLS frontend by calling the same
shell scripts the dev stack calls, rather than reimplementing them as tasks.
The role is invokable through the new svc-cache category and inert until a
host carries it.

Verified: the twenty Nexus payloads, the fifteen nginx server blocks and all
seven package-manager configs are byte-identical to the hand-written files
they replace, and the new category adds one role to the deploy order while
preserving the relative order of the other 180.

The role has not been deployed yet.
2026-09-27 16:28:48 +02:00
..

Swarm test routine 🔁

Sequenced steps of the swarm lab run. Bring-up and the chaos phase are workflow steps; the per-round loop in between is driven by utils.tests.swarm.matrix (one round per variant of the primary app). Highlighted boxes (⚡) are the moments a backup-executing systemd unit actually starts. See ../README.md for topology, naming SPOTs and helpers; the DR drill's own steps are documented in backup/README.md.

flowchart TB
    boot["Bring up the simulated cluster:<br/>build + start the 5 node containers, wait for systemd,<br/>wire lab DNS + IPs, install the project on every node"]

    subgraph matrix["Variant matrix - one round per variant of the primary app"]
        purge["Remove the prior round's stacks<br/>(rounds after the first)"]
        prov["Provision the round's inventory and deploy the app stack"]
        t_pre["⚡ Service-loader preload (MODE_BACKUP):<br/>volume + secrets units instant-start on the manager,<br/>nfs unit on the export host - pre-state snapshot<br/>before the deploy mutates anything"]
        conv["Wait until every stack service converges"]
        reach["Probe the app is reachable in-cluster"]
        bkphost["Deploy the backup host<br/>(round 1 only: pull + device roles on bkp-01)"]
        t_term["Terminator at that play's end:<br/>the lab deploys with --skip-backup, so MODE_BACKUP is false<br/>and remote-2-local stays out of the final run list -<br/>the drill is the only caller that starts the pull"]

        subgraph drill["Disaster-recovery drill (round 1 only)"]
            d1["Seed markers on the live NFS volume<br/>and in the manager secrets"]
            t_d2["⚡ Trigger the deployed backup units again:<br/>volume + secrets on the manager,<br/>nfs on the export host - markers get captured"]
            d3["Locate the backup generation holding the marker;<br/>assert the volume repo is fresh and non-empty<br/>(its NFS-backed volumes are excluded by design)"]
            t_d4["⚡ Install the ssh pull identity, then start<br/>the remote-2-local unit: bkp-01 pulls every provider"]
            t_d5["⚡ Space gate (pulled tree + watchdog floor,<br/>du breakdown on failure), then plug a LUKS loop<br/>device as simulated USB: the .mount unit fires<br/>the local-2-device unit"]
            d6["Recover device -> local backup root<br/>(full LUKS open; the pulled tree is wiped first,<br/>so the device holds the only copy)"]
            d7["Remove the stack, wipe the live NFS export<br/>(marker proven gone), recover it from the local<br/>root, restore NFS coherence"]
            d8["Delete the marker server-side, recover the docker<br/>volume through its NFS-mounted mountpoint and<br/>re-verify on the server; recover the host secrets"]
            d9["Redeploy the stack, assert the marker<br/>is back on the live volume"]
            d1 --> t_d2 --> d3 --> t_d4 --> t_d5 --> d6 --> d7 --> d8 --> d9
        end

        purge --> prov --> t_pre --> conv --> reach --> bkphost --> t_term --> drill
        drill -.->|next variant round| purge
    end

    subgraph chaos["Chaos phase (against the last round's stack)"]
        seed["Seed a marker file on the app's NFS volume"]
        drain["Drain the worker running the app,<br/>forcing a reschedule to another node"]
        assertion["Assert the marker and reachability<br/>survived the reschedule"]
        seed --> drain --> assertion
    end

    teardown["Tear down: collect diagnostics + Playwright<br/>reports on failure, kill nodes, remove the lab network"]

    boot --> matrix --> chaos --> teardown

    classDef trigger fill:#f7e28b,stroke:#b8860b,color:#000
    class t_pre,t_d2,t_d4,t_d5 trigger

The nightly timers (volume 01:00, nfs 01:30, secrets 01:45, device 02:00, remote pull 00:30) run independently of every step above and never fire during a lab run.

Scripts per step: bring-up 01_bootstrap.sh - provision 02_provision_inventory.sh - converge 03_wait_converge.sh - reachability 04_verify_reachable.sh - drill backup/base.sh - marker 05_seed_content.sh - drain 06_drain_worker.sh - assert 07_assert_state.sh - purge ../utils/clean/purge_stacks.sh.