Files
T
kevinveenbirkenbachandClaude Opus 5.5 91e1eff8a6 fix(btrfs): allocate the swapfile with fallocate instead of dd
On a btrfs root the swapfile was written with dd from /dev/zero, so a
swapfile the size of the RAM cost tens of GiB of writes and stalled
every other writer on the filesystem. A caller that bounds the run, such
as an async Ansible task, killed dd halfway, and the next run found the
wrong size and started over. btrfs accepts a preallocated NOCOW
swapfile and the swap directory already carries +C, so every filesystem
now allocates with fallocate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:36:04 +02:00

104 lines
3.8 KiB
Bash

#!/bin/sh
# An unprivileged container cannot activate swap, so the run is expected to fail
# at mkswap or swapon - which of the two depends on the host's /proc/swaps.
# Everything before it - findmnt, fallocate, chmod - runs for real.
set -eu
fail() {
echo "FAIL: $1" >&2
exit 1
}
SWAPFORGE="$(command -v swap-forge)" || fail "swap-forge is not on PATH after install"
echo "ok: swap-forge installed at ${SWAPFORGE}"
command -v swafo >/dev/null || fail "the swafo alias is not on PATH after install"
echo "ok: swafo alias installed"
case "${SWAPFORGE}" in
/usr/local/bin/*) ;;
*) fail "swap-forge resolved to ${SWAPFORGE}, outside the install prefix" ;;
esac
echo "ok: resolved from the installed package, not the source tree"
for binary in findmnt swapoff fallocate chmod mkswap swapon chattr btrfs; do
command -v "${binary}" >/dev/null || fail "${binary} is missing from the image"
done
echo "ok: every declared binary is present"
swap-forge --help | grep -q "Swapfile size" || fail "--help does not describe the tool"
echo "ok: --help"
set +e
swap-forge >/dev/null 2>&1
status=$?
set -e
[ "${status}" -eq 2 ] || fail "a missing size exited ${status}, expected 2"
echo "ok: a missing size exits 2"
set +e
output="$(swap-forge 64T 2>&1)"
status=$?
set -e
[ "${status}" -eq 1 ] || fail "an invalid size exited ${status}, expected 1"
echo "${output}" | grep -q "Invalid size format" || fail "invalid size message wrong"
if echo "${output}" | grep -q "Traceback"; then
fail "an invalid size produced a traceback"
fi
echo "ok: an invalid size exits 1 without a traceback"
printf 'UUID=deadbeef / ext4 defaults 0 1\n' >/etc/fstab
cp /etc/fstab /tmp/fstab.before
set +e
output="$(swap-forge 8M 2>&1)"
status=$?
set -e
[ "${status}" -eq 1 ] || fail "the blocked activation exited ${status}, expected 1"
echo "${output}" | grep -q "Creating 8 MiB swapfile" || fail "the creation banner is missing"
echo "${output}" | grep -qE "'(mkswap|swapon)' failed with exit code" || fail "the blocked activation was not reported cleanly"
if echo "${output}" | grep -q "Traceback"; then
fail "the blocked activation produced a traceback"
fi
echo "ok: real fallocate and chmod ran; the blocked activation exits 1 cleanly"
[ -f /swapfile ] || fail "/swapfile was not created"
size="$(stat -c %s /swapfile)"
[ "${size}" -eq 8388608 ] || fail "/swapfile is ${size} bytes, expected 8388608"
echo "ok: /swapfile exists with exactly the requested size"
cmp -s /etc/fstab /tmp/fstab.before || fail "/etc/fstab changed although activation failed"
echo "ok: /etc/fstab is untouched when activation fails"
set +e
output="$(swap-forge 8M 2>&1)"
status=$?
set -e
[ "${status}" -eq 0 ] || fail "the idempotent rerun exited ${status}, expected 0"
echo "${output}" | grep -q "already exists with correct size (8 MiB). Skipping." \
|| fail "an existing swapfile of the right size was not detected"
echo "ok: a rerun with the same size is a no-op - the idempotency the role relies on"
set +e
output="$(swap-forge 12M 2>&1)"
status=$?
set -e
echo "${output}" | grep -q "has size 8 MiB, expected 12 MiB. Recreating..." \
|| fail "a size change did not trigger a recreation"
size="$(stat -c %s /swapfile)"
[ "${size}" -eq 12582912 ] || fail "/swapfile is ${size} bytes after resize, expected 12582912"
echo "ok: a different size really removes and recreates the swapfile"
cmp -s /etc/fstab /tmp/fstab.before || fail "/etc/fstab changed during the resize cycle"
echo "ok: /etc/fstab survived the resize cycle byte for byte"
set +e
output="$(env PATH=/nonexistent-bin "${SWAPFORGE}" 8M 2>&1)"
status=$?
set -e
[ "${status}" -eq 127 ] || fail "missing findmnt exited ${status}, expected 127"
echo "${output}" | grep -q "required command 'findmnt' is not installed" || fail "missing findmnt message wrong"
echo "ok: missing findmnt exits 127 with a clean message"
echo "ALL E2E CHECKS PASSED"