ci/forge/kvm-gate.sh
#!/bin/sh
# kvm-gate.sh — take dwarf's KVM gate lock the way the CI forge expects (lane CI-RED, SC-GATE-STARVE).
#
# kvm-gate.sh <TAG> "<what, ~how long>" <command...>
#
# Runs <command> holding ~/locks/kvm-gate.lock (KVM_GATE_LOCK), and writes
# "<TAG>-START <time> (<what>)" and "<TAG>-DONE <time> rc=<rc>" into kvm-gate.turn.
# The CI turn: while the dwarves forge has a CI run waiting it asks for a turn
# (kvm-gate.ci-turn, touched every few seconds). A fresh ask goes first: this helper
# waits while it stands, and if it got the lock while an ask stood it gives the lock
# back at once and waits again. The forge then holds the lock for ONE CI run, at most
# 30 minutes, and lets a waiting gate go first afterwards. A stale ask (no touch for
# 120 s: the forge is gone) is ignored. A bare `flock -o kvm-gate.lock` still works,
# but it does not let the forge's ask go first.
set -u
LOCK=${KVM_GATE_LOCK:-$HOME/locks/kvm-gate.lock}
BASE=${LOCK%.lock}
TURN=$BASE.turn
ASK=$BASE.ci-turn
POLL=${KVM_GATE_POLL:-5}
FRESH=${KVM_GATE_ASK_FRESH:-120}
now() { date -u +%Y-%m-%dT%H:%M:%SZ; }
fresh() { [ -f "$ASK" ] && [ $(( $(date +%s) - $(stat -c %Y "$ASK" 2>/dev/null || echo 0) )) -lt "$FRESH" ]; }
if [ "${1:-}" = "--inside" ]; then
# under the lock: give it back at once if the forge asked meanwhile
shift; MARK=$1; TAG=$2; WHAT=$3; shift 3
if fresh; then : > "$MARK"; exit 0; fi
echo "$TAG-START $(now) ($WHAT)" >> "$TURN"
"$@"; rc=$?
echo "$TAG-DONE $(now) rc=$rc" >> "$TURN"
exit $rc
fi
[ $# -ge 3 ] || { echo "usage: kvm-gate.sh <TAG> \"<what>\" <command...>" >&2; exit 2; }
TAG=$1; WHAT=$2; shift 2
mkdir -p "$(dirname "$LOCK")"
MARK=$(mktemp "${TMPDIR:-/tmp}/kvm-gate.XXXXXX"); rm -f "$MARK"
said=
while :; do
while fresh; do
[ -n "$said" ] || { echo "kvm-gate: the CI forge asked for a turn; $TAG waits" >&2; said=1; }
sleep "$POLL"
done
flock -o "$LOCK" sh "$0" --inside "$MARK" "$TAG" "$WHAT" "$@"; rc=$?
if [ -e "$MARK" ]; then rm -f "$MARK"; sleep "$POLL"; continue; fi
exit $rc
done