diff --git a/scripts/systemd/openfut-rollback-detached.sh b/scripts/systemd/openfut-rollback-detached.sh new file mode 100755 index 0000000..30a374b --- /dev/null +++ b/scripts/systemd/openfut-rollback-detached.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# Roll the production supervision promotion back to the incumbent detached pair. +# +# This restores the process-management arrangement that ran production before +# the supervision cutover: two nohup'd binaries entered into the anchor +# container's network namespace via nsenter, parented to init. +# +# It performs NO database or schema change, because the promotion it reverses +# performs none either. The economy is never at risk here. +# +# Run with --dry-run first; that prints the exact commands and touches nothing. +# +# usage: openfut-rollback-detached.sh [--dry-run] +set -eu + +DRY=0 +[ "${1:-}" = "--dry-run" ] && DRY=1 + +ANCHOR=openfut-fut-backend +ART=/home/alex/openfut-migration/promote-contract-20260822-184409/artifacts +ENVJSON=/home/alex/openfut-migration/promote-contract-20260822-184409/env.json +LOGDIR=/home/alex/openfut-promotion/logs + +run() { + if [ "$DRY" -eq 1 ]; then printf ' DRY %s\n' "$*"; else printf ' RUN %s\n' "$*"; sh -c "$*"; fi +} + +echo "== 1. stop and remove supervision (units only; binaries, env and DB untouched) ==" +run "systemctl disable --now openfut-host.service openfut-core.service openfut-netns.service openfut-netns-reconcile.timer || true" +run "/home/alex/OpenFUT/scripts/systemd/openfut-supervision-install.sh uninstall production" + +echo "== 2. resolve the anchor's CURRENT namespace (never a remembered pid) ==" +if [ "$DRY" -eq 1 ]; then + APID="$(docker inspect -f '{{.State.Pid}}' "$ANCHOR" 2>/dev/null || echo '')" + echo " DRY anchor $ANCHOR pid=$APID ns=$(readlink "/proc/$APID/ns/net" 2>/dev/null || echo '')" +else + APID="$(docker inspect -f '{{.State.Pid}}' "$ANCHOR")" + [ -n "$APID" ] && [ "$APID" != "0" ] || { echo "anchor $ANCHOR is not running — cannot roll back into its namespace" >&2; exit 1; } + echo " anchor $ANCHOR pid=$APID ns=$(readlink "/proc/$APID/ns/net")" +fi + +# The environment is replayed from the captured live process environment, never +# retyped. jq renders it as KEY=VALUE pairs for env(1). +CORE_ENV="$(jq -r '.core | to_entries[] | "\(.key)=\(.value)"' "$ENVJSON" | tr '\n' ' ')" +HOST_ENV="$(jq -r '.host | to_entries[] | "\(.key)=\(.value)"' "$ENVJSON" | tr '\n' ' ')" + +echo "== 3. relaunch incumbent Core (the writer first) ==" +run "mkdir -p $LOGDIR" +run "nsenter --net=/proc/$APID/ns/net env $CORE_ENV nohup $ART/openfut-core >> $LOGDIR/prod-core.log 2>&1 &" +run "/home/alex/OpenFUT/scripts/systemd/openfut-wait-tcp.sh 127.0.0.1 18080 30" + +echo "== 4. relaunch incumbent host ==" +run "nsenter --net=/proc/$APID/ns/net env $HOST_ENV nohup $ART/openfut-utas-host >> $LOGDIR/prod-host.log 2>&1 &" +run "/home/alex/OpenFUT/scripts/systemd/openfut-wait-tcp.sh 127.0.0.1 8099 30" + +echo "== 5. verify ==" +run "pgrep -af 'openfut-core|openfut-utas-host'" +echo "Rollback complete. No database or schema change was made." diff --git a/scripts/systemd/openfut-supervision-install.sh b/scripts/systemd/openfut-supervision-install.sh index c491c8b..99b6734 100755 --- a/scripts/systemd/openfut-supervision-install.sh +++ b/scripts/systemd/openfut-supervision-install.sh @@ -78,7 +78,11 @@ case "$ACTION" in match="MISMATCH"; [ "$ns" = "$want" ] && match="ok" printf ' %-40s pid=%-8s ns=%-18s %s\n' "$u" "$mp" "$ns" "$match" done - echo " netns mounts on /run/netns/$NSNAME: $(grep -c "run/netns/$NSNAME" /proc/mounts || true) (1 = healthy, >1 = leaked stack)" + # Exact mount-point match. A substring grep for "openfut" also counts + # "openfut-staging" and reports a phantom leaked stack on production — + # a monitoring lie of exactly the kind this tooling exists to remove. + mounts="$(awk -v t="/run/netns/$NSNAME" '$2==t {n++} END {print n+0}' /proc/mounts)" + echo " netns mounts on /run/netns/$NSNAME: $mounts (1 = healthy, >1 = leaked stack)" ;; uninstall)