Record the tailscale grace period as a deadline instead of sleeping six hours inside the offboard

This commit is contained in:
Gmer4Lfe
2026-08-17 04:46:01 -04:00
parent 2cbc06a683
commit 32355e0219
+49 -21
View File
@@ -25,7 +25,7 @@
# Step 8: Restart mirror — bring up mirror's own parked containers # Step 8: Restart mirror — bring up mirror's own parked containers
# Step 9: Revocation — Emby admin, SSH keys # Step 9: Revocation — Emby admin, SSH keys
# Step 10: Write state — INACTIVE locally + pushed to mirror, mirror blocklisted # Step 10: Write state — INACTIVE locally + pushed to mirror, mirror blocklisted
# Tailscale — grace window then device removal (after state written) # Tailscale — grace deadline recorded, device removed after it expires
# #
# MIRROR PATH (8 steps) # MIRROR PATH (8 steps)
# Step 1: Stop rsync — halt any running sync # Step 1: Stop rsync — halt any running sync
@@ -103,9 +103,12 @@
# The mirror is added to the partnership blocklist, which rsync.sh checks and refuses on — # The mirror is added to the partnership blocklist, which rsync.sh checks and refuses on —
# stale access cannot survive the offboard. # stale access cannot survive the offboard.
# #
# Tailscale Grace Window # Tailscale Grace Window Is a Deadline, Not a Sleep
# Device removal happens after state is written, not before, so the final state push # Device removal happens after state is written, not before, so the final state push
# cannot be cut off by removing its own transport. # cannot be cut off by removing its own transport. The grace period itself is recorded to
# STATE_DIR/tailscale_removal_due.db and the offboard returns. It used to sleep
# PARTNERSHIP_GRACE_HOURS inline — six hours by default — holding the lock and its job record
# open the whole time, reporting "running", and blocking any re-onboard behind it.
# #
# Dry Run Support # Dry Run Support
# --dry-run walks the full sequence reporting each step without executing any. # --dry-run walks the full sequence reporting each step without executing any.
@@ -603,21 +606,46 @@ fi
if [[ "${PARTNERSHIP_REMOVE_TAILSCALE:-true}" == true ]]; then if [[ "${PARTNERSHIP_REMOVE_TAILSCALE:-true}" == true ]]; then
echo "" echo ""
echo "━━━ $ICON_NET Tailscale Separation ━━━" echo "━━━ $ICON_NET Tailscale Separation ━━━"
if [[ "$MIRROR_REACHABLE" == true ]]; then # The grace period is recorded as a deadline, not slept through.
grace_seconds=$(( ${PARTNERSHIP_GRACE_HOURS:-6} * 3600 )) #
warn "Waiting ${PARTNERSHIP_GRACE_HOURS:-6}hr grace — mirror can collect backups..." # This used to `sleep $((PARTNERSHIP_GRACE_HOURS * 3600))` inline — six hours by default —
# holding the offboard's lock and its job record open the whole time, showing "running" to
# every status reader, and blocking any re-onboard behind the lock. Worse, the sleep ran
# even when removal was going to be a no-op: TAILSCALE_API_KEY and TAILSCALE_TAILNET are
# both empty here, so the six hours bought nothing at all.
#
# The offboard's own work is finished by this point. Writing the deadline lets the teardown
# complete now and leaves the removal to whoever reads the file — and makes the wait
# visible and cancellable instead of buried in a sleeping process.
_grace_h="${PARTNERSHIP_GRACE_HOURS:-6}"
if [[ "$_grace_h" -gt 0 ]] && [[ "$MIRROR_REACHABLE" == true ]]; then
_due=$(( $(date +%s) + _grace_h * 3600 ))
if [[ "$DRY_RUN" == false ]]; then if [[ "$DRY_RUN" == false ]]; then
trap 'warn "Offboard interrupted during grace sleep"; exit 0' SIGTERM SIGINT printf 'host=%s\ndue=%s\ndue_human=%s\nreason=%s\n' \
sleep "$grace_seconds" "$MIRROR" "$_due" "$(date -d "@$_due" '+%Y-%m-%d %H:%M:%S')" "$REASON" \
trap - SIGTERM SIGINT > "${STATE_DIR}/tailscale_removal_due.db"
fi
warn "Grace period: $MIRROR stays on the tailnet until $(date -d "@$_due" '+%Y-%m-%d %H:%M') — recorded, not slept"
# No CLI entry point removes it yet, and there is deliberately no invented one here:
# remove_tailscale_device() is a partnership_manager.sh function with no --mode of its
# own, and it no-ops without credentials regardless. Say what is true.
if [[ -z "${TAILSCALE_API_KEY:-}" || -z "${TAILSCALE_TAILNET:-}" ]]; then
warn "Automatic removal is not possible — TAILSCALE_API_KEY/TAILSCALE_TAILNET are unset; remove it in the Tailscale admin console"
else
warn "Removal after that is not yet automated — remove it in the Tailscale admin console"
fi
TAILSCALE_REMOVED=deferred
unset _grace_h _due
else
# No grace configured, or the mirror is already unreachable — remove now.
#
# Outcome recorded, not assumed. remove_tailscale_device returns 1 when TAILSCALE_API_KEY
# or TAILSCALE_TAILNET is unset — it warns "skipping Tailscale removal" and the summary
# went on to report "removed ✅" anyway, so an offboard that left the device on the
# tailnet said it had taken it off. Neither key is configured here, so that was every run.
if remove_tailscale_device "$MIRROR"; then
TAILSCALE_REMOVED=true
fi fi
fi
# Outcome recorded, not assumed. remove_tailscale_device returns 1 when TAILSCALE_API_KEY or
# TAILSCALE_TAILNET is unset — it warns "skipping Tailscale removal" and the summary went on
# to report "removed ✅" anyway, so an offboard that left the device on the tailnet said it
# had taken it off. Neither key is configured here, so that was the case on every run.
if remove_tailscale_device "$MIRROR"; then
TAILSCALE_REMOVED=true
fi fi
fi fi
@@ -667,11 +695,11 @@ echo " Step 10 — State: $( [[ "$STEP_STATE_WRITE_OK" == true ]] && e
echo "" echo ""
echo " Blocklist: $MIRROR blocked — re-onboard to permit access again ✅" echo " Blocklist: $MIRROR blocked — re-onboard to permit access again ✅"
if [[ "${PARTNERSHIP_REMOVE_TAILSCALE:-true}" == true ]]; then if [[ "${PARTNERSHIP_REMOVE_TAILSCALE:-true}" == true ]]; then
if [[ "$TAILSCALE_REMOVED" == true ]]; then case "$TAILSCALE_REMOVED" in
echo " Tailscale: $MIRROR removed ✅" true) echo " Tailscale: $MIRROR removed ✅" ;;
else deferred) echo " Tailscale: $MIRROR kept until the grace period expires — see ${STATE_DIR}/tailscale_removal_due.db" ;;
echo " Tailscale: $MIRROR NOT removed ⚠ — still on the tailnet (needs TAILSCALE_API_KEY + TAILSCALE_TAILNET)" *) echo " Tailscale: $MIRROR NOT removed ⚠ — still on the tailnet (needs TAILSCALE_API_KEY + TAILSCALE_TAILNET)" ;;
fi esac
fi fi
# Named because it is the one partnership switch neither onboard nor offboard moves, so it # Named because it is the one partnership switch neither onboard nor offboard moves, so it
# survives an offboard still true and there is nothing else that would ever mention it. # survives an offboard still true and there is nothing else that would ever mention it.