mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 03:38:23 +00:00
The pick was made once per matrix entry, in a workflow step ahead of the drill,
and every distro of that entry then ran on it. Coverage followed: one entry
exercised one filesystem, and the swarm never reached zfs at all, because
resolve.sh's userland_for() kept it out of any entry containing arch, centos or
fedora - which is every automatic sweep. The draw now happens inside the loop in
distros.sh, once per distro, so a five-distro entry covers zfs with 87% and all
three kinds with 62% probability instead of one kind with certainty.
The workflow input changes meaning with it. It was a single kind, offered as a
choice in the manual entry; it is now a space-separated allow-list. Empty draws
from all three, and naming kinds narrows the draw to them and marks the pick
required, so a host that cannot deliver one of them fails rather than quietly
running on something else. A name that is not a filesystem this project puts a
data root on is rejected outright instead of reaching the applying step.
For the draw to mean anything the images have to carry the userlands, so
scripts/install/zfs.sh becomes scripts/install/filesystem.sh and installs ext4
and btrfs alongside zfs. That is not symmetric work: btrfs-progs does not exist
in the EL family at all, because RHEL dropped btrfs, and a plain
`dnf install btrfs-progs` there fails the whole transaction and takes e2fsprogs
with it. EPEL carries it, so the EL family goes through EPEL from the start -
split by distro rather than by a failed first attempt, since Fedora has no
epel-release to fall back to and routing a transient failure there would turn it
into a fatal one. verify() now demands all four binaries, so a packaging
regression fails the build instead of leaving a hole the drill discovers later.
Measured on all five bases: debian trixie, ubuntu 26.04, arch, fedora 44 and
centos 10 each end with mkfs.ext4, mkfs.btrfs, zfs and zpool present and ldd
fully resolved.
Two defects only became reachable once a single job could ask for different
filesystems in turn:
* docker_dataroot.sh answered "already prepared" and returned for any mount it
found, so from the second distro on, every draw would have been silently
ignored on a host that survives the sweep - the runner in compose and host
mode. It now compares what is mounted against what was asked for and
dismantles the old root when they differ. Everything docker cached there
goes with it; that is what covering more than one filesystem per entry
costs.
* reclaim_stale_pool ran only when the new pick was zfs, so a zfs to btrfs
transition left the previous distro's pool imported on the shared kernel,
pinning a loop device over an image its dead container had already unlinked.
It now runs on every pick, which needed the /dev/zfs handling lifted out of
the zfs branch into zfs_device_ready().
The restore_docker trap is armed before anything can stop docker, rather than
after, so a failure while dismantling a data root cannot leave a host without a
daemon. resolve.sh splits its allow-list into an array before testing it, so a
whitespace-only input is the empty list it looks like rather than a narrowed one
that yields an empty pick marked required. pick.sh clears both keys when it
declines to draw: they are declared in default.env, so a generated .env carries
whatever was exported when it was made, and a caller gating its apply on the
value would otherwise repoint the developer's own docker in the very case the
skip exists to prevent.
Verified by execution rather than by reading: a privileged container ran
docker_dataroot.sh with real loop devices and mkfs through ext4 -> ext4 -> btrfs
-> ext4, confirming the recognition, the replacement in both directions and that
no loop leaks across it; a second harness with a recording zpool stub confirmed
that a btrfs pick does reclaim a stale pool, that no pool means no destroy, and
that a host without /dev/zfs still completes an ext4 pick without touching zpool.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
55 lines
2.3 KiB
Bash
Executable File
55 lines
2.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Draw the docker data-root filesystem for the distro iteration in progress and
|
|
# leave the decision in the calling shell's environment.
|
|
#
|
|
# Sourced rather than executed: resolve.sh hands its answer over a file in the
|
|
# GITHUB_ENV format, and the caller needs those values in its own environment,
|
|
# so the answer is routed through a temporary file of that format and sourced
|
|
# back.
|
|
#
|
|
# Nothing is drawn outside GitHub Actions, and nothing is drawn for the runner
|
|
# scope under act, which shares the developer's docker daemon: repointing its
|
|
# data root would take their local images with it. The node scope still draws
|
|
# under act, because those containers are the lab's own and are thrown away with
|
|
# it. A caller that gets no pick finds INFINITO_DOCKER_FILESYSTEM empty and
|
|
# leaves the data root where it is. Skipping therefore clears both keys rather
|
|
# than leaving them alone: they are declared in default.env, so a generated .env
|
|
# carries whatever was exported when it was made, and a caller gating its apply
|
|
# on the value would otherwise act on a stale pick - on the developer's own
|
|
# docker, in the very case the skip exists to protect.
|
|
#
|
|
# Reads:
|
|
# INFINITO_DOCKER_FILESYSTEM_ALLOWED space-separated subset of
|
|
# 'ext4 btrfs zfs' the draw may use;
|
|
# empty draws from all three
|
|
# INFINITO_DISTRO distro under test, recorded with the pick
|
|
|
|
# Param: $1 label the pick belongs to, e.g. compose/web-app-gitea/debian
|
|
# Param: $2 scope the pick gets applied in, runner or node
|
|
filesystem_pick() {
|
|
local label="$1" scope="$2" answer resolver
|
|
|
|
if [ -z "${GITHUB_ACTIONS:-}" ] ||
|
|
{ [ -n "${ACT:-}" ] && [ "${scope}" = runner ]; }; then
|
|
INFINITO_DOCKER_FILESYSTEM=""
|
|
INFINITO_DOCKER_FILESYSTEM_REQUIRED=""
|
|
export INFINITO_DOCKER_FILESYSTEM INFINITO_DOCKER_FILESYSTEM_REQUIRED
|
|
echo "filesystem: no pick for the ${scope} here, leaving its data root alone"
|
|
return 0
|
|
fi
|
|
|
|
resolver="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/resolve.sh"
|
|
answer="$(mktemp)"
|
|
GITHUB_ENV="${answer}" bash "${resolver}" \
|
|
"${INFINITO_DOCKER_FILESYSTEM_ALLOWED:-}" \
|
|
"${label}" \
|
|
"${INFINITO_DISTRO:-}" \
|
|
"${scope}"
|
|
|
|
set -a
|
|
# shellcheck source=/dev/null
|
|
. "${answer}"
|
|
set +a
|
|
rm -f "${answer}"
|
|
}
|