utils.tests.swarm package

Subpackages

Submodules

utils.tests.swarm.backup_repos module

Which test-cluster node holds a backup repository the DR drill can pull.

Single source for the two consumers that must agree: extend_inventory turns the placements into inventory groups, and matrix turns the same placements into remote-2-local.backup_providers. Exception: the group membership is also what runs user-backup on that node, so a node named as a provider without its group has no authorized pull key, and roles/svc-bkp-remote-2-local/templates/script.sh.j2 counts one error per failing provider and exits 1 for the whole unit.

utils.tests.swarm.backup_repos.backup_provider_ips(*, app_id: str, variants: dict[str, int], manager: str, nfs_server: str) → list[str]

Nodes the DR drill’s remote-2-local unit pulls from.

Parameters:
  • app_id – primary application of the round.

  • variants – the round’s {app_id: variant_index} map.

  • manager – manager node IP.

  • nfs_server – export node IP.

Returns:

provider IPs in placement order, deduplicated.

utils.tests.swarm.backup_repos.repo_placements(*, app_closure: list[str], nfs_closure: list[str], manager: str, nfs_server: str) → list[tuple[str, str]]

Pair every backup repository role with the node that will run it.

Parameters:
  • app_closure – include closure of the round’s primary app; induces the manager repositories.

  • nfs_closure – include closure of svc-storage-nfs-server; induces the export-host repository.

  • manager – manager node address (inventory host name or IP).

  • nfs_server – export node address (inventory host name or IP).

Returns:

(role_id, node) pairs, manager repositories first.

utils.tests.swarm.derive_includes module

Print, one per line, the role IDs the provision CLI should --include so the resulting inventory has every group APP_ID’s transitive deps will need at deploy time.

svc-swarm-manager (subset marker), svc-storage-nfs-client (no application_id), svc-registry-docker (consumed by the swarm handler that pushes locally-built images, but not declared as a meta-dep so it stays out of compose-mode deploys) are added explicitly because the dep-walker only knows roles that are in the applications dict. The DR-drill backup roles arrive implicitly: the apps’ container_backup service declaration pulls svc-bkp-volume-2-local into the closure, and svc-bkp-nfs-2-local deploys via its group membership on the NFS server host (extend_inventory) through the group-driven role includes.

Input (env): APP_ID. Optional INFINITO_APP_VARIANTS (JSON {app_id: variant_index}, set per round by the matrix orchestrator) selects the active variant so a variant that pins services.* flags to false prunes those providers from the closure instead of the provision step pulling them in from the variant-free base config. In-process callers that already hold the round map pass it as derive_includes(app_id, variants=...); the env var is read only when that argument is omitted.

DB-provider services are exempt from that pruning: the dep walk applies the same force-shared view as utils.tests.swarm.force_shared_db.

utils.tests.swarm.derive_includes.applications_for_round(variants: dict[str, int] | None = None) → dict[str, Any]

Return the round’s application configs with its variants applied.

Parameters:

variants – active {app_id: variant_index} map; None falls back to INFINITO_APP_VARIANTS.

Returns:

Application id to its config for this round.

utils.tests.swarm.derive_includes.derive_includes(app_id: str, *, variants: dict[str, int] | None = None) → list[str]

Resolve APP_ID’s transitive include set under the active variants.

The service registry (service key -> provider role) stays built from the full rendered base set so provider discovery is unaffected by the active variants; only the dep-walk sees the variant configs (with the swarm force-shared DB view applied on top).

Parameters:
  • app_id – role whose closure is resolved.

  • variants – active {app_id: variant_index} map; None falls back to INFINITO_APP_VARIANTS, which only the subprocess callers have in their environment.

utils.tests.swarm.derive_includes.main() → int
utils.tests.swarm.derive_includes.variant_scope(app_id: str, *, variants: dict[str, int] | None = None) → list[str]

Deployed roles for APP_ID that must carry their own variant config.

Parameters:
  • app_id – role whose closure is resolved.

  • variants – active {app_id: variant_index} map, as derive_includes.

utils.tests.swarm.extend_inventory module

Extend the provisioned test inventory with host-topology placements plus the dep-walked placement: manager set, so the static validator (running before ansible) finds every group the swarm deploy will use.

The dep walk delegates to derive_includes so the group set matches the provisioned include/credential closure.

Node base names are the SPOT in default.env (shared with scripts/tests/deploy/swarm/utils/topology/base.sh); only the SWARM_NAME prefix is applied here so host names match the containers. The env get() keeps SWARM_NAME import-safe; main() enforces it at run time.

The backup host is NOT appended to the cluster inventory: it lives in a sibling backup.yml next to INV_PATH (same host_vars dir), deployed as a second play after the cluster one. Without a svc-swarm-node group that play resolves DEPLOYMENT_MODE=compose and IS_STACK_HOST=true on the backup host, so the backup roles run without cluster-membership special cases.

Inputs (env): APP_ID, INV_PATH (default /tmp/inv/devices.yml). Optional INFINITO_APP_VARIANTS (consumed by derive_includes).

utils.tests.swarm.extend_inventory.main() → int
utils.tests.swarm.extend_inventory.mesh_enabled() → bool

utils.tests.swarm.force_shared_db module

Force shared: true on every svc-db-* provider service in the swarm-test inventory host_vars.

Swarm-test only (called by the swarm matrix after provision). In swarm an embedded per-app database (shared: false) resolves to a per-app mariadb whose volume is NFS-backed with no placement pin — InnoDB on NFS plus a free-floating task is unsupported (locking, split-brain). Embedded DBs are a compose-only pattern. Flipping every consumer’s DB service to shared: true routes it to the central svc-db-* provider (as the baseline variant does), so no per-app DB is scheduled in swarm.

A manager-pinned role keeps its volumes node-local (swarm_nfs_backed) and its DB sidecar carries the matching placement constraint, so a literal shared: false there is honoured instead of overridden — Magento hard-fails setup:install on the central MariaDB major.

Inputs (env): INV_DIR — inventory dir holding host_vars/*.yml (default /tmp/inv).

utils.tests.swarm.force_shared_db.db_provider_service_keys(roles_dir: Path) → set[str]

Top-level provider service keys declared by every svc-db-* role.

utils.tests.swarm.force_shared_db.force_shared_true(host_vars_file: Path, db_keys: set[str]) → bool

Set shared: true on any DB-provider service already present under applications.<app>.services.<key> in host_vars_file. Only existing entries are touched. Returns True if the file changed.

utils.tests.swarm.force_shared_db.main() → int
utils.tests.swarm.force_shared_db.pinned_local_db(app_id: str, svc_name: str) → bool

True when the role owns this DB on purpose and swarm can host it.

A literal shared: false (not a Jinja expression) is the role author pinning an engine the central provider cannot serve. Honouring it is only safe on a manager-pinned role, where the volume stays node-local and the sidecar carries the matching placement constraint.

Parameters:
  • app_id – consumer role name, e.g. web-app-magento.

  • svc_name – DB service key under the role’s meta/services.yml.

utils.tests.swarm.matrix module

Swarm variant-matrix round orchestrator.

utils.tests.swarm.matrix.main(argv: list[str] | None = None) → int

utils.tests.swarm.mesh module

utils.tests.swarm.mesh.bootstrap_mesh(*, inv_dir: str) → int
utils.tests.swarm.mesh.converge_mesh(*, inv_dir: str) → int
utils.tests.swarm.mesh.lab_sudo_without_prompt(*, inv_dir: str) → int

Take the sudo prompt off the lab’s administrator before the switch.

Parameters:

inv_dir – inventory directory of the round.

utils.tests.swarm.mesh.layout() → Path
utils.tests.swarm.mesh.layout_applications() → dict
utils.tests.swarm.mesh.mesh_controller(*, inv_dir: str) → int
utils.tests.swarm.mesh.stale_controller_interfaces() → list[str]
utils.tests.swarm.mesh.switch_to_mesh_transport(*, inv_dir: str) → int
utils.tests.swarm.mesh.transport_mesh() → str
utils.tests.swarm.mesh.write_mesh(*, inv_dir: str, rotate: bool = False) → int

Write the WireGuard meshes over both inventories of the round.

Parameters:
  • inv_dir – inventory directory of the round.

  • rotate – mint a fresh keypair for every member.

utils.tests.swarm.run module

utils.tests.swarm.run.lowest_free_mb() → tuple[int, str]

Measure the storage root closest to running full.

Returns:

Free MB on the fullest existing root in STORAGE_ROOTS, and that root.

utils.tests.swarm.run.run_step(cmd: list[str], *, env: dict[str, str], label: str) → int

Announce a matrix step, run it, and report what it cost.

Parameters:
  • cmd – argv of the step.

  • env – environment the step runs with.

  • label – phase name for the banners.

Returns:

The step’s exit code.

utils.tests.swarm.run.run_watched(cmd: list[str], *, env: dict[str, str]) → int

Run a step, aborting visibly before the runner disk or RAM fills.

The Actions Worker dies silently on ENOSPC while writing its own logs, so a full disk truncates the job without diagnostics; terminating the step at DISK_FLOOR_MB keeps enough room for rescue artifacts and the log upload.

Parameters:
  • cmd – argv of the step.

  • env – environment the step runs with.

Returns:

The step’s exit code, or the abort code when a floor was hit.

Module contents