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:
co-authored by
Claude Sonnet 4.6
parent
4e22f5d1f7
commit
009820e981
@@ -0,0 +1,453 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================================
|
||||
# ================================= Failover Test ==============================================
|
||||
# ==============================================================================================
|
||||
# Controlled simulation of the failover lifecycle — validates the entire failover sequence
|
||||
# without waiting for a real outage.
|
||||
#
|
||||
# ── WHAT THIS SCRIPT IS ───────────────────────────────────────────────────────────────────────
|
||||
# A test harness only — contains no failover logic.
|
||||
# All failover logic lives in fallback.sh and is exercised by this test.
|
||||
# Any changes to fallback.sh are automatically reflected here.
|
||||
#
|
||||
# ── TEST SEQUENCE ─────────────────────────────────────────────────────────────────────────────
|
||||
# Phase 1 — Pre-flight verify both servers reachable, daemons healthy,
|
||||
# version parity, fallback.sh exists, state is NORMAL
|
||||
# Phase 2 — Block Remote iptables rule drops all traffic to remote IP
|
||||
# Phase 3 — Fallback Detection wait for fallback.sh to detect outage and enter FAILOVER
|
||||
# Phase 4 — Container Start verify Tier 1 failover containers started locally
|
||||
# Phase 5 — Restore remove iptables rule, remote becomes reachable
|
||||
# Phase 6 — Handback wait for fallback.sh to complete handback to NORMAL
|
||||
# Phase 7 — Container Handback verify Tier 1 containers stopped locally after handback
|
||||
# Phase 8 — Report full pass/fail summary per phase
|
||||
#
|
||||
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
|
||||
# FALLBACK_ENABLED gate — aborts if fallback monitoring is disabled
|
||||
# iptables safety trap — rule ALWAYS removed on exit (crash, error, ctrl-c, normal)
|
||||
# remote connectivity always restored regardless of outcome
|
||||
# Version parity check — pre-flight verifies both servers on compatible unRAID versions
|
||||
# Remote Docker daemon — pre-flight verifies remote daemon is responsive
|
||||
# DOCKER_TIMEOUT — all docker calls protected against daemon hangs
|
||||
# MY_ID-based routing — tier containers selected via MY_ID not hostname comparison
|
||||
# Command validation — iptables and notify validated before use
|
||||
# Dry-run safe — full sequence walkthrough without touching iptables or containers
|
||||
#
|
||||
# ── WARNING ───────────────────────────────────────────────────────────────────────────────────
|
||||
# ⚠️ This script starts and stops REAL containers on both servers.
|
||||
# Run during a maintenance window — users will experience a brief service interruption.
|
||||
# Use --dry-run to walk through the sequence without any real changes.
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# FALLBACK_TEST_BLOCK_WAIT — seconds to wait for fallback.sh to detect outage
|
||||
# FALLBACK_TEST_HANDBACK_WAIT — seconds to wait for fallback.sh to complete handback
|
||||
# FALLBACK_CHECK_INTERVAL — check interval of the running fallback.sh (informational)
|
||||
# FALLBACK_HANDBACK_STRIKES — strikes required before handback (informational)
|
||||
# FALLBACK_STATE_FILE — state file path to read current state
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# fallback_test.sh — run full test sequence
|
||||
# fallback_test.sh --dry-run — walk through all phases without changes
|
||||
# fallback_test.sh --status — show current fallback state and test config
|
||||
# fallback_test.sh --log — verbose output
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
source "$SCRIPT_DIR/../load_config.sh"
|
||||
|
||||
parse_args "$@"
|
||||
|
||||
FALLBACK_SCRIPT="$SCRIPT_DIR/fallback.sh"
|
||||
DOCKER_TIMEOUT=15
|
||||
|
||||
# ==============================================================================================
|
||||
# ── SAFETY TRAP — always remove iptables rule on exit ─────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# Fires on normal exit, error exit, ctrl-c, and script crashes.
|
||||
# Remote connectivity is ALWAYS restored regardless of test outcome.
|
||||
|
||||
IPTABLES_RULE_ACTIVE=false
|
||||
|
||||
cleanup() {
|
||||
if [[ "$IPTABLES_RULE_ACTIVE" == true ]]; then
|
||||
echo ""
|
||||
warn "$ICON_SHIELD Cleanup — removing iptables block on $REMOTE_SERVER..."
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
iptables -D OUTPUT -d "$REMOTE_SERVER" -j DROP 2>/dev/null
|
||||
IPTABLES_RULE_ACTIVE=false
|
||||
warn "iptables rule removed — remote connectivity restored"
|
||||
else
|
||||
warn "DRY RUN — would remove iptables rule"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Setup ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━ $ICON_GEAR Setup ━━━"
|
||||
|
||||
if [[ "$EUID" -ne 0 ]]; then
|
||||
error "Must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# FALLBACK_ENABLED gate — no point testing if failover is disabled
|
||||
if [[ "${FALLBACK_ENABLED:-false}" == false ]]; then
|
||||
warn "FALLBACK_ENABLED=false — fallback test aborted"
|
||||
warn "Enable fallback in master.conf before running this test"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
acquire_lock # strict single instance — modifies iptables and containers
|
||||
|
||||
detect_hosts
|
||||
resolve_remote_ip
|
||||
|
||||
# Validate commands used by this script
|
||||
validate_unraid_cmd \
|
||||
"$(which iptables 2>/dev/null || echo /sbin/iptables)" \
|
||||
"--version" "iptables" \
|
||||
"iptables" || { error "iptables not found — required for connectivity simulation"; exit 1; }
|
||||
|
||||
validate_unraid_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
if [[ ! -f "$FALLBACK_SCRIPT" ]]; then
|
||||
error "fallback.sh not found at $FALLBACK_SCRIPT"
|
||||
exit 1
|
||||
fi
|
||||
log "fallback.sh found at $FALLBACK_SCRIPT"
|
||||
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no iptables rules or container changes will be made"
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Status ━━━
|
||||
# ==============================================================================================
|
||||
if [[ "$SHOW_STATUS" == true ]]; then
|
||||
local_ver=$(grep -oP '(?<=version=")[^"]+' /etc/unraid-version 2>/dev/null || echo "unknown")
|
||||
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
|
||||
echo "$ICON_HOST My ID: $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo "$ICON_HOST Remote ID: $REMOTE_ID ($REMOTE_SERVER_NAME — $REMOTE_SERVER)"
|
||||
echo "$ICON_GEAR unRAID ver: $local_ver"
|
||||
echo "$ICON_FALLBACK Block wait: ${FALLBACK_TEST_BLOCK_WAIT}s"
|
||||
echo "$ICON_FALLBACK Handback wait: ${FALLBACK_TEST_HANDBACK_WAIT}s"
|
||||
echo "$ICON_FALLBACK Check interval: ${FALLBACK_CHECK_INTERVAL}s"
|
||||
echo "$ICON_FALLBACK Handback strikes: ${FALLBACK_HANDBACK_STRIKES}"
|
||||
echo "$ICON_GEAR Dry Run: $DRY_RUN"
|
||||
|
||||
if [[ -f "$FALLBACK_STATE_FILE" ]]; then
|
||||
CURRENT_STATE=$(grep "^state=" "$FALLBACK_STATE_FILE" 2>/dev/null | cut -d= -f2)
|
||||
echo "$ICON_FALLBACK Current state: ${CURRENT_STATE:-unknown}"
|
||||
else
|
||||
echo "$ICON_FALLBACK Current state: no state file"
|
||||
fi
|
||||
|
||||
# Show Tier 1 containers for this host
|
||||
TIER1_VAR="FALLBACK_${MY_ID}_COVERS_${REMOTE_ID}_TIER1"
|
||||
eval "TIER1_CONTAINERS=(\"\${${TIER1_VAR}[@]:-}\")"
|
||||
echo "$ICON_CONTAINERS Tier 1 to test: ${TIER1_CONTAINERS[*]:-none configured}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ==============================================================================================
|
||||
# ── PHASE TRACKING ────────────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
PHASES_PASS=()
|
||||
PHASES_FAIL=()
|
||||
TOTAL_START=$(date +%s)
|
||||
|
||||
phase_pass() { PHASES_PASS+=("$1"); warn "$ICON_DONE Phase: $1 — PASSED ✅"; }
|
||||
phase_fail() { PHASES_FAIL+=("$1"); error "Phase: $1 — FAILED ❌"; }
|
||||
|
||||
# Get Tier 1 containers for this server's failover responsibility
|
||||
TIER1_VAR="FALLBACK_${MY_ID}_COVERS_${REMOTE_ID}_TIER1"
|
||||
eval "TIER1_CONTAINERS=(\"\${${TIER1_VAR}[@]:-}\")"
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Phase 1 — Pre-flight ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " $ICON_SHIELD FALLBACK TEST — $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo " $ICON_HOST $MY_ID ($LOCAL_SERVER_NAME) → $REMOTE_ID ($REMOTE_SERVER_NAME)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "━━━ $ICON_SHIELD Phase 1 — Pre-flight ━━━"
|
||||
|
||||
# Remote reachable
|
||||
if ping_remote; then
|
||||
log "$REMOTE_SERVER_NAME is reachable"
|
||||
else
|
||||
error "$REMOTE_SERVER_NAME is not reachable — cannot run test"
|
||||
phase_fail "Pre-flight"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Internet reachable
|
||||
if ping_internet; then
|
||||
log "Internet is reachable"
|
||||
else
|
||||
error "No internet connectivity — cannot run test"
|
||||
phase_fail "Pre-flight"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Version parity — test may produce misleading results on mismatch
|
||||
if ! check_unraid_version_parity; then
|
||||
error "unRAID version mismatch — test aborted to prevent misleading results"
|
||||
phase_fail "Pre-flight"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remote Docker daemon — must be responsive before test manipulates containers
|
||||
if ! check_remote_docker_daemon; then
|
||||
error "Remote Docker daemon not responsive — cannot run test"
|
||||
phase_fail "Pre-flight"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Fallback state must be NORMAL before test
|
||||
if [[ -f "$FALLBACK_STATE_FILE" ]]; then
|
||||
CURRENT_STATE=$(grep "^state=" "$FALLBACK_STATE_FILE" 2>/dev/null | cut -d= -f2)
|
||||
if [[ "$CURRENT_STATE" != "NORMAL" ]]; then
|
||||
error "Fallback state is $CURRENT_STATE — must be NORMAL before running test"
|
||||
phase_fail "Pre-flight"
|
||||
exit 1
|
||||
fi
|
||||
log "Fallback state is NORMAL"
|
||||
else
|
||||
warn "No state file found — assuming NORMAL (first run)"
|
||||
fi
|
||||
|
||||
# Tier 1 containers configured
|
||||
if [[ ${#TIER1_CONTAINERS[@]} -eq 0 ]]; then
|
||||
error "No Tier 1 containers configured for $MY_ID → $REMOTE_ID"
|
||||
error "Check FALLBACK_${MY_ID}_COVERS_${REMOTE_ID}_TIER1 in master_host*.conf"
|
||||
phase_fail "Pre-flight"
|
||||
exit 1
|
||||
fi
|
||||
log "Tier 1 containers: ${TIER1_CONTAINERS[*]}"
|
||||
|
||||
phase_pass "Pre-flight"
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Phase 2 — Block Remote Connectivity ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━ $ICON_PING Phase 2 — Block Remote Connectivity ━━━"
|
||||
warn "Adding iptables rule — dropping all traffic to $REMOTE_SERVER ($REMOTE_SERVER_NAME)"
|
||||
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
iptables -I OUTPUT -d "$REMOTE_SERVER" -j DROP
|
||||
IPTABLES_RULE_ACTIVE=true
|
||||
|
||||
# Verify block is working
|
||||
sleep 2
|
||||
if ! ping -c1 -W2 "$REMOTE_SERVER" &>/dev/null; then
|
||||
log "Connectivity block confirmed — ping to remote fails as expected"
|
||||
phase_pass "Block Remote"
|
||||
else
|
||||
error "iptables rule did not block connectivity — ping still succeeds"
|
||||
phase_fail "Block Remote"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
warn "DRY RUN — would block $REMOTE_SERVER with iptables DROP rule"
|
||||
phase_pass "Block Remote"
|
||||
fi
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Phase 3 — Fallback Detection ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━ $ICON_FALLBACK Phase 3 — Fallback Detection ━━━"
|
||||
warn "Waiting ${FALLBACK_TEST_BLOCK_WAIT}s for fallback.sh to detect outage..."
|
||||
log "fallback.sh check interval: ${FALLBACK_CHECK_INTERVAL}s"
|
||||
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
sleep "$FALLBACK_TEST_BLOCK_WAIT"
|
||||
|
||||
if [[ -f "$FALLBACK_STATE_FILE" ]]; then
|
||||
NEW_STATE=$(grep "^state=" "$FALLBACK_STATE_FILE" 2>/dev/null | cut -d= -f2)
|
||||
if [[ "$NEW_STATE" == "FALLBACK" ]]; then
|
||||
log "State changed to FALLBACK — outage detected correctly ✅"
|
||||
phase_pass "Fallback Detection"
|
||||
else
|
||||
error "State is $NEW_STATE — expected FALLBACK after ${FALLBACK_TEST_BLOCK_WAIT}s"
|
||||
warn "Is fallback.sh running? Check User Scripts plugin"
|
||||
phase_fail "Fallback Detection"
|
||||
fi
|
||||
else
|
||||
error "No state file found after wait — fallback.sh may not be running"
|
||||
phase_fail "Fallback Detection"
|
||||
fi
|
||||
else
|
||||
warn "DRY RUN — would wait ${FALLBACK_TEST_BLOCK_WAIT}s then check for FALLBACK state"
|
||||
phase_pass "Fallback Detection"
|
||||
fi
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Phase 4 — Container Start Verification ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━ $ICON_CONTAINERS Phase 4 — Tier 1 Containers Started Locally ━━━"
|
||||
log "Checking Tier 1 containers: ${TIER1_CONTAINERS[*]}"
|
||||
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
CONTAINERS_OK=true
|
||||
for container in "${TIER1_CONTAINERS[@]}"; do
|
||||
[[ -z "$container" ]] && continue
|
||||
STATUS=$(timeout "$DOCKER_TIMEOUT" docker inspect -f '{{.State.Running}}' \
|
||||
"$container" 2>/dev/null)
|
||||
if [[ "$STATUS" == "true" ]]; then
|
||||
log "$ICON_RUNNING $container is running locally ✅"
|
||||
else
|
||||
error "$ICON_NOT_RUNNING $container is NOT running locally"
|
||||
CONTAINERS_OK=false
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$CONTAINERS_OK" == true ]]; then
|
||||
phase_pass "Container Start"
|
||||
else
|
||||
phase_fail "Container Start"
|
||||
fi
|
||||
else
|
||||
warn "DRY RUN — would verify these Tier 1 containers started: ${TIER1_CONTAINERS[*]}"
|
||||
phase_pass "Container Start"
|
||||
fi
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Phase 5 — Restore Remote Connectivity ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━ $ICON_PING Phase 5 — Restore Remote Connectivity ━━━"
|
||||
warn "Removing iptables block — $REMOTE_SERVER_NAME becomes reachable again"
|
||||
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
iptables -D OUTPUT -d "$REMOTE_SERVER" -j DROP 2>/dev/null
|
||||
IPTABLES_RULE_ACTIVE=false
|
||||
|
||||
sleep 3
|
||||
if ping_remote; then
|
||||
log "$REMOTE_SERVER_NAME is reachable again ✅"
|
||||
phase_pass "Restore Connectivity"
|
||||
else
|
||||
error "$REMOTE_SERVER_NAME still unreachable after removing iptables rule"
|
||||
phase_fail "Restore Connectivity"
|
||||
fi
|
||||
else
|
||||
warn "DRY RUN — would remove iptables rule"
|
||||
phase_pass "Restore Connectivity"
|
||||
fi
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Phase 6 — Handback ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━ $ICON_FALLBACK Phase 6 — Handback ━━━"
|
||||
warn "Waiting ${FALLBACK_TEST_HANDBACK_WAIT}s for fallback.sh to complete handback..."
|
||||
log "Requires $FALLBACK_HANDBACK_STRIKES consecutive checks at ${FALLBACK_CHECK_INTERVAL}s"
|
||||
log "Minimum handback time: $(( FALLBACK_HANDBACK_STRIKES * FALLBACK_CHECK_INTERVAL ))s"
|
||||
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
sleep "$FALLBACK_TEST_HANDBACK_WAIT"
|
||||
|
||||
if [[ -f "$FALLBACK_STATE_FILE" ]]; then
|
||||
FINAL_STATE=$(grep "^state=" "$FALLBACK_STATE_FILE" 2>/dev/null | cut -d= -f2)
|
||||
if [[ "$FINAL_STATE" == "NORMAL" ]]; then
|
||||
log "State returned to NORMAL — handback completed ✅"
|
||||
phase_pass "Handback"
|
||||
else
|
||||
error "State is $FINAL_STATE — expected NORMAL after ${FALLBACK_TEST_HANDBACK_WAIT}s"
|
||||
warn "Handback may still be in progress — check fallback.sh output"
|
||||
phase_fail "Handback"
|
||||
fi
|
||||
else
|
||||
error "No state file found"
|
||||
phase_fail "Handback"
|
||||
fi
|
||||
else
|
||||
warn "DRY RUN — would wait ${FALLBACK_TEST_HANDBACK_WAIT}s then verify NORMAL state"
|
||||
phase_pass "Handback"
|
||||
fi
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Phase 7 — Container Handback Verification ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━ $ICON_CONTAINERS Phase 7 — Tier 1 Containers Stopped Locally ━━━"
|
||||
log "Verifying Tier 1 containers returned to $REMOTE_SERVER_NAME"
|
||||
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
HANDBACK_OK=true
|
||||
for container in "${TIER1_CONTAINERS[@]}"; do
|
||||
[[ -z "$container" ]] && continue
|
||||
STATUS=$(timeout "$DOCKER_TIMEOUT" docker inspect -f '{{.State.Running}}' \
|
||||
"$container" 2>/dev/null)
|
||||
if [[ "$STATUS" != "true" ]]; then
|
||||
log "$ICON_NOT_RUNNING $container stopped locally — handed back ✅"
|
||||
else
|
||||
error "$ICON_RUNNING $container still running locally — handback may have failed"
|
||||
HANDBACK_OK=false
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$HANDBACK_OK" == true ]]; then
|
||||
phase_pass "Container Handback"
|
||||
else
|
||||
phase_fail "Container Handback"
|
||||
fi
|
||||
else
|
||||
warn "DRY RUN — would verify Tier 1 containers stopped locally after handback"
|
||||
phase_pass "Container Handback"
|
||||
fi
|
||||
|
||||
TOTAL_END=$(date +%s)
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Test Report ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY FALLBACK TEST REPORT ━━━━━"
|
||||
echo "$ICON_HOST My ID: $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo "$ICON_HOST Remote: $REMOTE_ID ($REMOTE_SERVER_NAME)"
|
||||
echo "$ICON_TIME Duration: $(format_duration $((TOTAL_END - TOTAL_START)))"
|
||||
echo ""
|
||||
echo " Phase Results:"
|
||||
for phase in "${PHASES_PASS[@]}"; do
|
||||
echo " $ICON_SUCCESS $phase"
|
||||
done
|
||||
for phase in "${PHASES_FAIL[@]}"; do
|
||||
echo " $ICON_ERROR $phase"
|
||||
done
|
||||
echo ""
|
||||
|
||||
PASS_COUNT=${#PHASES_PASS[@]}
|
||||
FAIL_COUNT=${#PHASES_FAIL[@]}
|
||||
TOTAL_PHASES=$(( PASS_COUNT + FAIL_COUNT ))
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — no changes made"
|
||||
elif [[ "$FAIL_COUNT" -eq 0 ]]; then
|
||||
warn "$ICON_DONE ALL $TOTAL_PHASES PHASES PASSED"
|
||||
notify "Fallback test PASSED on $(hostname) — all $TOTAL_PHASES phases completed" \
|
||||
"Fallback Test" "normal"
|
||||
else
|
||||
error "$FAIL_COUNT/$TOTAL_PHASES PHASES FAILED"
|
||||
notify "Fallback test FAILED on $(hostname) — $FAIL_COUNT/$TOTAL_PHASES phases failed: ${PHASES_FAIL[*]}" \
|
||||
"Fallback Test" "warning"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
[[ "$FAIL_COUNT" -gt 0 ]] && exit 1
|
||||
exit 0
|
||||
Reference in New Issue
Block a user