matrix
matrix
¶
Multi-distro test-matrix engine shared by the terok-* siblings.
Historically each sibling carried its own ~500-line run-matrix.sh
plus a set of Containerfiles, ~90% identical across repos and already
drifting. This package is the single engine; a consuming repo keeps only
a declarative tests/containers/matrix.yml and (optionally) per-slot
Containerfile fragments.
Collaborators, in reading order:
catalog— facts about the shared slot images (expected podman versions, non-systemd slots, slot kinds);config— thematrix.ymlschema and loader (MatrixConfig);contract— the consumer conftest's half of theTEROK_EXPECTcapability contract (check_capability_contract);inner— generates the scripts that run inside a slot's test container;runner— host-side build/run/prune;cli— theterok-matrixentry point (uv run terok-matrixfrom a consuming repo's root).
External tooling (the superbuild TUI, CI matrix generation) should not
shell-parse anything: load the same matrix.yml via
load_config or ask the CLI
(terok-matrix --slots-json / --image-prefix).
SLOTS = {'debian12': SlotSpec(expected_podman='4.3.1'), 'ubuntu2404': SlotSpec(expected_podman='4.9.3'), 'ubuntu2604': SlotSpec(expected_podman='5.7.0'), 'debian13': SlotSpec(expected_podman='5.4.2'), 'fedora43': SlotSpec(expected_podman='5.8.4'), 'fedora44': SlotSpec(expected_podman='5.8.4'), 'podman': SlotSpec(expected_podman='5.8.4', user='podman'), 'alpine': SlotSpec(expected_podman='5.3.2', non_systemd=True, pasta_symlink=True), 'void': SlotSpec(expected_podman='5.8.3', non_systemd=True, pasta_symlink=True), 'mageia': SlotSpec(expected_podman='4.9.5'), 'manjaro': SlotSpec(expected_podman='6.1.0', pasta_symlink=True), 'nix': SlotSpec(kind=SlotKind.NIX), 'nixos': SlotSpec(expected_podman='5.8.7', requires_boot=True, boot_init='/init', bash_path='/run/current-system/sw/bin/bash')}
module-attribute
¶
__all__ = ['SLOTS', 'MatrixConfig', 'MatrixConfigError', 'SlotKind', 'SlotSpec', 'binary_on_path', 'check_capability_contract', 'load_config', 'tcp_reachable']
module-attribute
¶
SlotKind
¶
SlotSpec(expected_podman='latest', non_systemd=False, user='testrunner', kind=SlotKind.CONTAINER, pasta_symlink=False, requires_boot=False, boot_init=SYSTEMD_INIT, bash_path='/bin/bash')
dataclass
¶
Facts about one matrix slot's base image.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expected_podman
|
str
|
Distro-shipped podman version the slot is pinned
to; |
'latest'
|
non_systemd
|
bool
|
The runner hard-fails the slot if systemd is present — these slots exist to prove the systemd-free floor. |
False
|
user
|
str
|
Non-root user baked into the image (uid 1000). |
'testrunner'
|
kind
|
SlotKind
|
Driving mode, see |
CONTAINER
|
pasta_symlink
|
bool
|
The distro ships |
False
|
requires_boot
|
bool
|
The image needs its normal boot to provision runtime
state (NixOS security wrappers); skip it outside |
False
|
boot_init
|
str
|
Image entry point for a booted slot. |
SYSTEMD_INIT
|
bash_path
|
str
|
Bash path for scripts and systemd units in this image. |
'/bin/bash'
|
expected_podman = 'latest'
class-attribute
instance-attribute
¶
non_systemd = False
class-attribute
instance-attribute
¶
user = 'testrunner'
class-attribute
instance-attribute
¶
kind = SlotKind.CONTAINER
class-attribute
instance-attribute
¶
pasta_symlink = False
class-attribute
instance-attribute
¶
requires_boot = False
class-attribute
instance-attribute
¶
boot_init = SYSTEMD_INIT
class-attribute
instance-attribute
¶
bash_path = '/bin/bash'
class-attribute
instance-attribute
¶
runs_nested_podman(flavor)
¶
Whether this slot runs nested rootless podman under flavor.
True only for a container-kind slot on the podman flavor: the nix
slot has no podman inside, and the dbus flavor runs none. This gates
the resolv.conf fix and, under krun, the device setup and store binds.
Source code in src/terok_util/matrix/catalog.py
may_boot_systemd(flavor, krun)
¶
Whether this slot boots systemd as PID 1, where its image ships one.
Only under krun: the microVM owns its kernel and cgroup tree, so the
image's systemd can be PID 1 and give the tests a real per-user
manager. Under a shared kernel the slot stays a plain --init
container, and the systemd-free floor slots stay systemd-free
everywhere. NixOS requires boot even on the dbus flavor. Otherwise
the runner checks for the image's own systemd and system bus.
Source code in src/terok_util/matrix/catalog.py
MatrixConfig(image_prefix, flavor, groups, expect, slots, phases, containers_dir, repo_root, krun=False, host_confines_pasta=False)
dataclass
¶
A repo's whole matrix declaration, resolved and validated.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image_prefix
|
str
|
Image/container name prefix and prune-label value. |
required |
flavor
|
str
|
Shared Containerfile family ( |
required |
groups
|
tuple[str, ...]
|
Dependency groups synced before testing. |
required |
expect
|
tuple[str, ...]
|
|
required |
slots
|
dict[str, SlotConfig]
|
Selected slots in declaration order. |
required |
phases
|
tuple[Phase, ...]
|
Repo-level test flow. |
required |
containers_dir
|
Path
|
Directory of the |
required |
repo_root
|
Path
|
Build context and bind-mounted source tree. |
required |
host_confines_pasta
|
bool
|
The host's AppArmor |
False
|
krun
|
bool
|
Run each slot as a libkrun microVM (its own kernel) instead of a
shared-kernel container. A run-mode set from |
False
|
image_prefix
instance-attribute
¶
flavor
instance-attribute
¶
groups
instance-attribute
¶
expect
instance-attribute
¶
slots
instance-attribute
¶
phases
instance-attribute
¶
containers_dir
instance-attribute
¶
repo_root
instance-attribute
¶
krun = False
class-attribute
instance-attribute
¶
host_confines_pasta = False
class-attribute
instance-attribute
¶
slot_groups(name)
¶
slot_expect(name)
¶
Capability contract effective for slot name.
slot_phases(name)
¶
MatrixConfigError
¶
Bases: ValueError
A matrix.yml failed validation.
load_config(path)
¶
Load and validate a matrix.yml.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The config file; its grandparent directory is taken as the
repo root ( |
required |
Raises:
| Type | Description |
|---|---|
MatrixConfigError
|
On unknown keys, unknown slots, or a phase that is neither a command phase nor a pytest phase. |
Source code in src/terok_util/matrix/config.py
binary_on_path(name)
¶
check_capability_contract(probes)
¶
Verify TEROK_EXPECT against probes; return the failure, if any.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
probes
|
Mapping[str, Callable[[], bool]]
|
Capability name to presence probe, the repo-specific half of the contract. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
A human-readable failure message when the contract names unknown |
str | None
|
capabilities or a declared capability is missing; |
str | None
|
the contract holds (or none is declared — dev-machine runs). |
Source code in src/terok_util/matrix/contract.py
tcp_reachable(ip, port, timeout=5.0)
¶
Whether a TCP connection to ip:port succeeds within timeout.