feat: backup test script, ntfy notifications, and error categorization
extras/test_backup.sh — new unified test script (Kopia + Borg):
• Stops container, moves live data aside, restores latest backup,
compares restored vs live with diff -rq (content, not timestamps),
moves live data back and restarts container
• PASS = restore succeeded; diff output is informational (files changed
since last backup are normal)
• FAIL = restore command failed or target empty after restore
• --list flag, CLI service arg, interactive picker
• Handles both full-service dirs and sub-path sources (gaming-backup)
• Cleanup trap always restores live data even on error
• Sends ntfy notification on pass and fail
extras/backup_kopia.sh, backup_borg.sh, backup_gaming.sh:
• ntfy_send() + categorize_error() helpers added
• Each snapshot/archive failure captures stderr and categorizes:
disk full, remote unreachable, repository not found, wrong passphrase,
permission denied, unknown error
• Single ntfy notification at end: success (low priority) or failure
(urgent) with per-service failure reasons listed
• backup_borg.sh: changed 2>&1 | pipe to 2>"$_ERR" | so stdout logs
cleanly and stderr is captured for error categorization
services/backup.sh, borg-backup.sh, gaming-backup.sh:
• New ntfy prompt section in installer (URL + optional token)
• NTFY_URL / NTFY_TOKEN written to backup.conf
• test_backup.sh copied from extras/ into service dir
• Summary updated to show test_backup.sh commands and ntfy URL
https://claude.ai/code/session_019XgsQ13XKm4Zj3cNsDNwHj
This commit is contained in:
+52
-6
@@ -20,9 +20,37 @@ source "$CONF"
|
||||
ACTUAL_USER="${SUDO_USER:-${USER:-$(id -un)}}"
|
||||
ACTUAL_HOME="$(getent passwd "$ACTUAL_USER" 2>/dev/null | cut -d: -f6 || echo "/home/$ACTUAL_USER")"
|
||||
DOCKER_DIR="$ACTUAL_HOME/docker"
|
||||
HOST="$(hostname -s 2>/dev/null || hostname)"
|
||||
|
||||
log() { echo "[$(date '+%F %T')] $*"; }
|
||||
|
||||
ntfy_send() {
|
||||
local title="$1" msg="$2" priority="${3:-default}" tags="${4:-}"
|
||||
[ -z "${NTFY_URL:-}" ] && return 0
|
||||
local -a _args=(-fsS -o /dev/null)
|
||||
_args+=(-H "Title: $title" -H "Priority: $priority")
|
||||
[ -n "$tags" ] && _args+=(-H "Tags: $tags")
|
||||
[ -n "${NTFY_TOKEN:-}" ] && _args+=(-H "Authorization: Bearer $NTFY_TOKEN")
|
||||
curl "${_args[@]}" -d "$msg" "$NTFY_URL" 2>/dev/null || true
|
||||
}
|
||||
|
||||
categorize_error() {
|
||||
local txt="$1"
|
||||
if echo "$txt" | grep -qi "no space left\|disk quota exceeded"; then
|
||||
echo "disk full — backup destination is out of space"
|
||||
elif echo "$txt" | grep -qi "connection refused\|network unreachable\|no route to host\|ssh.*connect\|timed out\|host unreachable"; then
|
||||
echo "remote unreachable — check network / destination host"
|
||||
elif echo "$txt" | grep -qi "repository.*not.*exist\|not a valid kopia\|not connected"; then
|
||||
echo "repository not found — re-run the backup installer"
|
||||
elif echo "$txt" | grep -qi "passphrase\|wrong key\|cannot decrypt"; then
|
||||
echo "wrong passphrase — check backup.conf"
|
||||
elif echo "$txt" | grep -qi "permission denied\|access denied"; then
|
||||
echo "permission denied — check file permissions"
|
||||
else
|
||||
echo "error — see system logs on $HOST"
|
||||
fi
|
||||
}
|
||||
|
||||
kp_for() {
|
||||
local dest="$1"; shift
|
||||
local cfg_var="DEST_${dest}_CONFIG" pw_var="DEST_${dest}_PASSWORD"
|
||||
@@ -55,6 +83,9 @@ esac
|
||||
|
||||
log "===== Backup starting ====="
|
||||
rc=0
|
||||
declare -a FAILED_SVCS=()
|
||||
_ERR="$(mktemp)"
|
||||
trap 'rm -f "$_ERR"' EXIT
|
||||
|
||||
for svc_dir in "$DOCKER_DIR"/*/; do
|
||||
[ -f "${svc_dir}docker-compose.yml" ] || continue
|
||||
@@ -73,10 +104,13 @@ for svc_dir in "$DOCKER_DIR"/*/; do
|
||||
sleep 5
|
||||
fi
|
||||
log "Snapshotting $svc (dest: $dest)..."
|
||||
if kp_for "$dest" snapshot create --description="backup: $svc" "$svc_dir"; then
|
||||
if kp_for "$dest" snapshot create --description="backup: $svc" "$svc_dir" 2>"$_ERR"; then
|
||||
log "OK $svc (Minecraft, no downtime)"
|
||||
else
|
||||
log "WARNING: snapshot failed for $svc"; rc=1
|
||||
_reason="$(categorize_error "$(cat "$_ERR")")"
|
||||
log "WARNING: snapshot failed for $svc — $_reason"
|
||||
FAILED_SVCS+=("$svc: $_reason")
|
||||
rc=1
|
||||
fi
|
||||
else
|
||||
STOPPED=false
|
||||
@@ -89,10 +123,13 @@ for svc_dir in "$DOCKER_DIR"/*/; do
|
||||
fi
|
||||
|
||||
log "Snapshotting $svc (dest: $dest)..."
|
||||
if kp_for "$dest" snapshot create --description="backup: $svc" "$svc_dir"; then
|
||||
if kp_for "$dest" snapshot create --description="backup: $svc" "$svc_dir" 2>"$_ERR"; then
|
||||
log "OK $svc"
|
||||
else
|
||||
log "WARNING: snapshot failed for $svc"; rc=1
|
||||
_reason="$(categorize_error "$(cat "$_ERR")")"
|
||||
log "WARNING: snapshot failed for $svc — $_reason"
|
||||
FAILED_SVCS+=("$svc: $_reason")
|
||||
rc=1
|
||||
fi
|
||||
|
||||
if [ "$STOPPED" = true ]; then
|
||||
@@ -107,14 +144,23 @@ if [ "${REMOTE_TYPE:-none}" != "none" ] && [ -n "${REMOTE_TYPE:-}" ]; then
|
||||
for dest in ${DEST_NAMES:-default}; do
|
||||
log "Mirroring '$dest' offsite ($REMOTE_TYPE)..."
|
||||
# shellcheck disable=SC2086
|
||||
kp_for "$dest" repository sync-to "$REMOTE_TYPE" $REMOTE_ARGS \
|
||||
|| { log "WARNING: mirror failed for '$dest'"; rc=1; }
|
||||
if ! kp_for "$dest" repository sync-to "$REMOTE_TYPE" $REMOTE_ARGS 2>"$_ERR"; then
|
||||
_reason="$(categorize_error "$(cat "$_ERR")")"
|
||||
log "WARNING: mirror failed for '$dest' — $_reason"
|
||||
FAILED_SVCS+=("mirror[$dest]: $_reason")
|
||||
rc=1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$rc" -eq 0 ]; then
|
||||
log "===== Backup complete ====="
|
||||
ntfy_send "✓ Backup complete" "$HOST: all services backed up successfully" \
|
||||
"low" "white_check_mark"
|
||||
else
|
||||
log "===== Backup finished WITH WARNINGS (see above) ====="
|
||||
_ntfy_msg="$HOST: backup failures:"
|
||||
for _s in "${FAILED_SVCS[@]}"; do _ntfy_msg+=$'\n'"• $_s"; done
|
||||
ntfy_send "✗ Backup FAILED" "$_ntfy_msg" "urgent" "rotating_light"
|
||||
fi
|
||||
exit "$rc"
|
||||
|
||||
Reference in New Issue
Block a user