112 lines
4.3 KiB
Bash
Executable File
112 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# FED R7 / #1802. Scratch acceptance with labelled ancestry stand-in (sage-3625).
|
|
# No systemd units. All children run in owned process groups, removed on exit.
|
|
set -euo pipefail
|
|
R7_INTERRUPT=0
|
|
case "${1:-}" in
|
|
'') ;;
|
|
--kill-after-start) R7_INTERRUPT=1 ;;
|
|
*) echo 'usage: scripts/test-federation-acceptance.sh [--kill-after-start]' >&2; exit 2 ;;
|
|
esac
|
|
[ "$#" -le 1 ] || exit 2
|
|
for R7_TOOL in ss rg timeout node ps awk; do
|
|
command -v "$R7_TOOL" >/dev/null || { echo "R7 requires $R7_TOOL" >&2; exit 1; }
|
|
done
|
|
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
|
R7_ROOT="$(mktemp -d /mnt/storage/scratch/tmp/r7.XXXXXXXX)"
|
|
mkdir -p "$R7_ROOT/home" "$R7_ROOT/tmp"
|
|
touch "$R7_ROOT/groups" "$R7_ROOT/ports" "$R7_ROOT/sockets"
|
|
|
|
cleanup() {
|
|
local result=$? pid start raw port socket listeners cleanup_failed=0 attempt
|
|
local -a fields
|
|
trap - EXIT INT TERM
|
|
if [ -n "${R7_DRIVER:-}" ]; then
|
|
kill -TERM "$R7_DRIVER" 2>/dev/null || true
|
|
wait "$R7_DRIVER" 2>/dev/null || true
|
|
fi
|
|
# A detached child is its group's leader. Confirm pid/start time/group before
|
|
# signalling only groups in this run's ledger; never a live stack process.
|
|
while read -r pid start; do
|
|
[ -n "$pid" ] || continue
|
|
if [ -r "/proc/$pid/stat" ]; then
|
|
raw="$(cat "/proc/$pid/stat")"
|
|
read -r -a fields <<< "${raw##*) }"
|
|
if [ "${fields[19]}" != "$start" ] || [ "${fields[2]}" != "$pid" ]; then
|
|
echo "R7 cleanup failed: group identity changed: $pid" >&2
|
|
result=1; cleanup_failed=1
|
|
continue
|
|
fi
|
|
kill -TERM -- "-$pid" 2>/dev/null || true
|
|
for ((attempt=0; attempt<800; attempt++)); do
|
|
ps -eo pgid=,stat= | awk -v g="$pid" '$1==g && $2 !~ /^Z/ {found=1} END {exit !found}' || break
|
|
sleep 0.05
|
|
done
|
|
kill -KILL -- "-$pid" 2>/dev/null || true
|
|
for ((attempt=0; attempt<40; attempt++)); do
|
|
ps -eo pgid=,stat= | awk -v g="$pid" '$1==g && $2 !~ /^Z/ {found=1} END {exit !found}' || break
|
|
sleep 0.05
|
|
done
|
|
fi
|
|
# Zombies hold no tools/listeners and are reaped by their parent. The test
|
|
# requires no live member of an owned group, including orphan descendants.
|
|
if ps -eo pgid=,stat= | awk -v g="$pid" '$1==g && $2 !~ /^Z/ {found=1} END {exit !found}'; then
|
|
echo "R7 cleanup failed: group $pid still has a live process" >&2
|
|
result=1; cleanup_failed=1
|
|
fi
|
|
done < "$R7_ROOT/groups"
|
|
while IFS= read -r port; do
|
|
[ -n "$port" ] || continue
|
|
if ! listeners="$(ss -H -ltn "sport = :$port")"; then
|
|
echo "R7 cleanup failed: cannot inspect port $port" >&2
|
|
result=1; cleanup_failed=1
|
|
elif [ -n "$listeners" ]; then
|
|
echo "R7 cleanup failed: port $port still listens" >&2
|
|
result=1; cleanup_failed=1
|
|
fi
|
|
done < "$R7_ROOT/ports"
|
|
while IFS= read -r socket; do
|
|
[ -n "$socket" ] || continue
|
|
if ! listeners="$(ss -H -lx)"; then
|
|
echo "R7 cleanup failed: cannot inspect socket $socket" >&2
|
|
result=1; cleanup_failed=1
|
|
elif printf '%s\n' "$listeners" | rg -F -- "$socket" >/dev/null; then
|
|
echo "R7 cleanup failed: socket $socket still listens" >&2
|
|
result=1; cleanup_failed=1
|
|
fi
|
|
done < "$R7_ROOT/sockets"
|
|
if listeners="$(ss -H -lx)"; then
|
|
if printf '%s\n' "$listeners" | rg -F -- "$R7_ROOT/" >/dev/null; then
|
|
echo 'R7 cleanup failed: another scratch Unix listener remains' >&2
|
|
result=1; cleanup_failed=1
|
|
fi
|
|
else
|
|
result=1; cleanup_failed=1
|
|
fi
|
|
if [ "$result" -ne 0 ]; then
|
|
for raw in "$R7_ROOT"/*.log; do
|
|
[ -f "$raw" ] || continue
|
|
echo "R7 diagnostic: ${raw##*/}" >&2
|
|
tail -40 "$raw" >&2
|
|
done
|
|
fi
|
|
rm -rf -- "$R7_ROOT"
|
|
if [ -e "$R7_ROOT" ]; then
|
|
echo 'R7 cleanup failed: scratch directory remains' >&2
|
|
result=1; cleanup_failed=1
|
|
fi
|
|
if [ "$cleanup_failed" -eq 0 ]; then echo 'R7 cleanup: no child groups, ports or sockets remain; no units created'; fi
|
|
exit "$result"
|
|
}
|
|
trap cleanup EXIT
|
|
trap 'exit 130' INT
|
|
trap 'exit 143' TERM
|
|
# No host configuration, preload hooks, credentials or agent env in fixtures.
|
|
# The ancestry stand-in lives only in federation-host.mjs, per sage-3625.
|
|
timeout -k 10 210 env -i PATH="$PATH" HOME="$R7_ROOT/home" TMPDIR="$R7_ROOT/tmp" \
|
|
MOSAIC_R7_ROOT="$R7_ROOT" MOSAIC_R7_INTERRUPT="$R7_INTERRUPT" \
|
|
node --test --test-concurrency=1 packages/cli/tests/federation-acceptance.test.mjs &
|
|
R7_DRIVER=$!
|
|
wait "$R7_DRIVER"
|
|
R7_DRIVER=
|