refactor: rename failover/HA → fallback across entire codebase

Removes all references to "failover" and "HA" (high availability)
terminology from variable names, config keys, state values, rsync
profile names, directory paths, and user-visible strings.

Mapping:
  FAILOVER_*              → FALLBACK_*
  FAILOVER_HOST*_RUNS_FOR → FALLBACK_HOST*_COVERS
  critical-failover       → critical-fallback
  emby-failover           → emby-fallback
  appdata-Failover/       → appdata-Fallback/
  "FAILOVER" state value  → "FALLBACK"
  failover_start key      → fallback_start
  Failover/ directory     → Fallback/
  failover.sh             → fallback.sh
  failover_state.db       → fallback_state.db
  -Failover folder suffix → -Fallback

State machine: NORMAL | FALLBACK | DARK (unchanged)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gmer4Lfe
2026-05-08 19:28:21 -04:00
co-authored by Claude Sonnet 4.6
parent 4e22f5d1f7
commit 009820e981
19 changed files with 517 additions and 416 deletions
+101
View File
@@ -0,0 +1,101 @@
#!/bin/bash
# ==============================================================================================
# ================================= Claude Code Startup ========================================
# ==============================================================================================
# Restores Claude Code's persistent data after an Unraid reboot and launches Claude.
#
# Unraid's root filesystem lives in RAM — /root/.claude and /root/.local are wiped on every
# reboot. This script symlinks both directories back to persistent appdata storage before
# launching Claude, so memory, sessions, and settings survive across reboots.
#
# ── FIRST RUN ─────────────────────────────────────────────────────────────────────────────────
# If persistent storage has no data yet, migrates from the current live locations:
# /root/.claude → PERSIST_DIR/.claude (memory, sessions, settings)
# /root/.local/share/claude → PERSIST_DIR/local/share/claude (installed binaries)
# Subsequent runs skip the migration and just create the symlinks.
#
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
# claude_startup.sh — set up persistent symlinks and launch Claude
# claude_startup.sh --setup — set up only, do not launch (for array_start.sh use)
# ==============================================================================================
PERSIST_DIR="/mnt/user/appdata/claude-code"
CLAUDE_DATA="$PERSIST_DIR/.claude"
CLAUDE_BIN="$PERSIST_DIR/local/share/claude"
LAUNCH=true
[[ "$1" == "--setup" ]] && LAUNCH=false
# Standalone — no common.sh dependency
_log() { echo "$*"; }
_warn() { echo " ⚠️ $*"; }
_err() { echo "$*" >&2; }
echo ""
echo "━━━ Claude Code Startup ━━━"
echo ""
# ── Array must be mounted ─────────────────────────────────────────────────────────────────────
if ! mountpoint -q /mnt/user 2>/dev/null; then
_err "Array not mounted — /mnt/user not available"
exit 1
fi
# ── Create persistent dirs ────────────────────────────────────────────────────────────────────
mkdir -p "$CLAUDE_DATA" "$CLAUDE_BIN"
# ── Migrate .claude on first run ──────────────────────────────────────────────────────────────
if [[ ! -L /root/.claude && -d /root/.claude ]]; then
_warn "First run — migrating /root/.claude → $CLAUDE_DATA"
cp -a /root/.claude/. "$CLAUDE_DATA/"
rm -rf /root/.claude
_log "Migrated .claude (memory, sessions, settings)"
elif [[ -z "$(ls -A "$CLAUDE_DATA" 2>/dev/null)" && -d /root/.claude ]]; then
_warn "Persistent storage empty — copying current .claude data"
cp -a /root/.claude/. "$CLAUDE_DATA/"
_log "Copied .claude data to persistent storage"
fi
# ── Migrate Claude binaries on first run ──────────────────────────────────────────────────────
if [[ ! -L /root/.local/share/claude && -d /root/.local/share/claude ]]; then
_warn "First run — migrating Claude binaries → $CLAUDE_BIN"
cp -a /root/.local/share/claude/. "$CLAUDE_BIN/"
_log "Migrated Claude binaries"
fi
# ── Create symlinks ───────────────────────────────────────────────────────────────────────────
# Remove any real directories first — ln -sfn silently creates inside a dir instead of
# replacing it, which produces a circular symlink on subsequent runs after migration.
mkdir -p /root/.local/share /root/.local/bin
[[ -d /root/.claude && ! -L /root/.claude ]] && rm -rf /root/.claude
ln -sfn "$CLAUDE_DATA" /root/.claude
_log ".claude → $CLAUDE_DATA"
[[ -d /root/.local/share/claude && ! -L /root/.local/share/claude ]] && rm -rf /root/.local/share/claude
ln -sfn "$CLAUDE_BIN" /root/.local/share/claude
_log "claude binary → $CLAUDE_BIN"
# ── Point the claude binary at the latest installed version ───────────────────────────────────
LATEST=$(ls "$CLAUDE_BIN/versions/" 2>/dev/null | sort -V | tail -1)
if [[ -z "$LATEST" ]]; then
_err "No Claude versions found in $CLAUDE_BIN/versions/"
_err "Install Claude Code first: npm install -g @anthropic-ai/claude-code"
exit 1
fi
ln -sfn "$CLAUDE_BIN/versions/$LATEST" /root/.local/bin/claude
_log "claude v$LATEST ready"
echo ""
# ── Setup-only mode (used by array_start.sh or other callers) ─────────────────────────────────
if [[ "$LAUNCH" == false ]]; then
_log "Setup complete — run 'claude' to start"
echo ""
exit 0
fi
# ── Launch ────────────────────────────────────────────────────────────────────────────────────
export PATH="$HOME/.local/bin:$PATH"
exec claude
@@ -2,22 +2,22 @@
# ==============================================================================================
# ============================= Failover State Reset ===========================================
# ==============================================================================================
# Resets the failover state file to NORMAL and clears all tier flags.
# Use when the failover state file is stuck in a non-NORMAL state after:
# Resets the fallback state file to NORMAL and clears all tier flags.
# Use when the fallback state file is stuck in a non-NORMAL state after:
# - Failover testing that left state as FAILOVER
# - A failed handback that did not complete cleanly
# - Manual intervention that left state inconsistent
# - failover.sh was killed mid-cycle and state is unknown
# - fallback.sh was killed mid-cycle and state is unknown
#
# ── WHAT THIS DOES ────────────────────────────────────────────────────────────────────────────
# Writes a fresh state file with:
# state=NORMAL
# failover_start=0
# fallback_start=0
# handback_strikes=0
# tier2_started=false / tier3_started=false / tier4_started=false
#
# Does NOT start or stop any containers — state file only.
# After reset, failover.sh will resume from NORMAL on its next cycle.
# After reset, fallback.sh will resume from NORMAL on its next cycle.
#
# ── ⚠️ ONLY RUN WHEN SAFE ────────────────────────────────────────────────────────────────────
# Verify BEFORE resetting:
@@ -26,22 +26,22 @@
# ✓ No active failover actually in progress
# ✓ Both servers can see each other
#
# Resetting state while a real failover is happening causes failover.sh to stop
# Resetting state while a real fallback is happening causes fallback.sh to stop
# covering the remote server — services go offline until next detection cycle.
#
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
# failover.sh running check — warns if failover.sh is active when reset is attempted
# fallback.sh running check — warns if fallback.sh is active when reset is attempted
# acquire_lock — prevents concurrent resets
# flock on state write — prevents race with failover.sh mid-cycle read
# flock on state write — prevents race with fallback.sh mid-cycle read
# Confirmation required — interactive: type YES | non-interactive: --force flag
# validate_unraid_cmd — notify validated before use
#
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
# failover_state_reset.sh — interactive reset (prompts for YES)
# failover_state_reset.sh --dry-run — show current state, show what would be written
# failover_state_reset.sh --status — show current state file contents and exit
# failover_state_reset.sh --force — non-interactive reset (no prompt, use in scripts)
# failover_state_reset.sh --force --dry-run — dry run without prompt
# fallback_state_reset.sh — interactive reset (prompts for YES)
# fallback_state_reset.sh --dry-run — show current state, show what would be written
# fallback_state_reset.sh --status — show current state file contents and exit
# fallback_state_reset.sh --force — non-interactive reset (no prompt, use in scripts)
# fallback_state_reset.sh --force --dry-run — dry run without prompt
# ==============================================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -85,31 +85,31 @@ detect_hosts
# ━━━ Current State ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_FAILOVER Current Failover State ━━━"
echo "━━━ $ICON_FALLBACK Current Fallback State ━━━"
echo "$ICON_HOST My ID: $MY_ID ($LOCAL_SERVER_NAME)"
echo ""
if [[ ! -f "$FAILOVER_STATE_FILE" ]]; then
warn "State file not found: $FAILOVER_STATE_FILE"
if [[ ! -f "$FALLBACK_STATE_FILE" ]]; then
warn "State file not found: $FALLBACK_STATE_FILE"
warn "Will be created fresh on reset"
CURRENT_STATE="NOT FOUND"
else
log "State file: $FAILOVER_STATE_FILE"
log "State file: $FALLBACK_STATE_FILE"
echo ""
while IFS='=' read -r key value; do
[[ -z "$key" ]] && continue
echo " $ICON_INFO $key = $value"
done < "$FAILOVER_STATE_FILE"
CURRENT_STATE=$(grep "^state=" "$FAILOVER_STATE_FILE" 2>/dev/null | cut -d= -f2)
done < "$FALLBACK_STATE_FILE"
CURRENT_STATE=$(grep "^state=" "$FALLBACK_STATE_FILE" 2>/dev/null | cut -d= -f2)
fi
if [[ "$SHOW_STATUS" == true ]]; then
echo ""
# Check if failover.sh is running — informational in status mode
if pgrep -f "failover.sh" >/dev/null 2>&1; then
warn "failover.sh is currently RUNNING — any reset would race with active cycle"
# Check if fallback.sh is running — informational in status mode
if pgrep -f "fallback.sh" >/dev/null 2>&1; then
warn "fallback.sh is currently RUNNING — any reset would race with active cycle"
else
log "failover.sh is not running"
log "fallback.sh is not running"
fi
exit 0
fi
@@ -120,18 +120,18 @@ fi
echo ""
echo "━━━ $ICON_SHIELD Safety Checks ━━━"
# Check if failover.sh is actively running
FAILOVER_RUNNING=false
if pgrep -f "failover.sh" >/dev/null 2>&1; then
FAILOVER_RUNNING=true
warn "⚠️ failover.sh is currently RUNNING"
# Check if fallback.sh is actively running
FALLBACK_RUNNING=false
if pgrep -f "fallback.sh" >/dev/null 2>&1; then
FALLBACK_RUNNING=true
warn "⚠️ fallback.sh is currently RUNNING"
warn "Resetting state mid-cycle may cause incorrect decisions on the next iteration"
warn "Consider stopping failover.sh first (click Abort in User Scripts)"
warn "Then reset state, then restart failover.sh"
warn "Consider stopping fallback.sh first (click Abort in User Scripts)"
warn "Then reset state, then restart fallback.sh"
echo ""
warn "If you are sure you want to proceed anyway, confirm below"
else
log "failover.sh is not running — safe to reset ✅"
log "fallback.sh is not running — safe to reset ✅"
fi
# Check current state — if already NORMAL warn user
@@ -144,11 +144,11 @@ fi
# ━━━ Confirmation ━━━
# ==============================================================================================
echo ""
warn "This will reset failover state to NORMAL on $MY_ID ($LOCAL_SERVER_NAME)"
warn "This will reset fallback state to NORMAL on $MY_ID ($LOCAL_SERVER_NAME)"
warn "Verify before proceeding:"
warn " ✓ Right containers running on the right server"
warn " ✓ DDNS pointing at correct server"
warn " ✓ No real failover actually in progress"
warn " ✓ No real fallback actually in progress"
warn " ✓ Both servers can reach each other"
echo ""
@@ -165,7 +165,7 @@ if [[ "$DRY_RUN" == false ]]; then
else
# Non-interactive — no terminal, cannot prompt
error "Non-interactive mode — use --force flag to skip confirmation"
error "Usage: failover_state_reset.sh --force"
error "Usage: fallback_state_reset.sh --force"
exit 1
fi
fi
@@ -174,10 +174,10 @@ fi
# ━━━ Reset State File ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_FAILOVER Resetting State File ━━━"
echo "━━━ $ICON_FALLBACK Resetting State File ━━━"
NEW_STATE_CONTENT="state=NORMAL
failover_start=0
fallback_start=0
handback_strikes=0
tier2_started=false
tier3_started=false
@@ -186,31 +186,31 @@ last_reset=$(date '+%Y-%m-%d %H:%M:%S')
reset_by=$MY_ID"
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would write to $FAILOVER_STATE_FILE:"
warn "DRY RUN — would write to $FALLBACK_STATE_FILE:"
echo ""
echo "$NEW_STATE_CONTENT" | while IFS= read -r line; do
echo " $line"
done
else
mkdir -p "$(dirname "$FAILOVER_STATE_FILE")"
mkdir -p "$(dirname "$FALLBACK_STATE_FILE")"
# flock prevents race with failover.sh mid-cycle read/write
# flock prevents race with fallback.sh mid-cycle read/write
(
flock -x 200
echo "$NEW_STATE_CONTENT" > "$FAILOVER_STATE_FILE"
) 200>"${FAILOVER_STATE_FILE}.lock"
echo "$NEW_STATE_CONTENT" > "$FALLBACK_STATE_FILE"
) 200>"${FALLBACK_STATE_FILE}.lock"
warn "State file reset to NORMAL ✅"
log "Written to: $FAILOVER_STATE_FILE"
log "Written to: $FALLBACK_STATE_FILE"
fi
# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY FAILOVER STATE RESET SUMMARY ━━━━━"
echo "━━━━━ $ICON_SUMMARY FALLBACK STATE RESET SUMMARY ━━━━━"
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_FAILOVER File: $FAILOVER_STATE_FILE"
echo "$ICON_FALLBACK File: $FALLBACK_STATE_FILE"
echo "$ICON_TIME Reset at: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
@@ -218,12 +218,12 @@ if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — no changes made"
else
warn "$ICON_DONE State reset to NORMAL"
log "failover.sh will resume from NORMAL on next cycle"
log "fallback.sh will resume from NORMAL on next cycle"
log "No containers were started or stopped"
echo ""
[[ "$FAILOVER_RUNNING" == true ]] && \
warn "⚠️ failover.sh was running during reset — monitor next cycle carefully"
notify "Failover state manually reset to NORMAL on $(hostname) ($MY_ID)" \
"Failover State Reset" "warning"
[[ "$FALLBACK_RUNNING" == true ]] && \
warn "⚠️ fallback.sh was running during reset — monitor next cycle carefully"
notify "Fallback state manually reset to NORMAL on $(hostname) ($MY_ID)" \
"Fallback State Reset" "warning"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"