feat: add restore.sh scaffold with arg parsing and usage
This commit is contained in:
Executable
+129
@@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Restore tooling for the borg-backup.sh / dump_db.sh backup system.
|
||||
# Modes:
|
||||
# restore.sh full [--archive NAME] [--force] [--dry-run]
|
||||
# restore.sh db <db_name> [--archive NAME] [--yes] [--dry-run]
|
||||
# restore.sh file <path-within-target> [--archive NAME] [--dest DIR] [--dry-run]
|
||||
# restore.sh --list-archives
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
|
||||
umask 077
|
||||
|
||||
# ========================= CONFIGURATION =========================
|
||||
|
||||
NAME="borg-2025"
|
||||
REPO="/home/srv/files/backups/$NAME"
|
||||
TARGET="/home/srv/files/content"
|
||||
ARCHIVE_TARGET_PATH="${TARGET#/}"
|
||||
|
||||
DB_CONTAINER="mariadb"
|
||||
DB_START_TIMEOUT=180
|
||||
|
||||
BORG_PASSPHRASE_FILE="${BORG_PASSPHRASE_FILE:-/root/.borg-passphrase}"
|
||||
ROOT_PASSWORD_FILE="${ROOT_PASSWORD_FILE:-/root/.mariadb-root.pw}"
|
||||
DUMP_SUBDIR="mariadb/dump"
|
||||
|
||||
LOGDIR="${RESTORE_LOGDIR:-/var/log/borg}"
|
||||
mkdir -p "$LOGDIR" 2>/dev/null || LOGDIR="/tmp"
|
||||
LOGFILE="$LOGDIR/restore-$(date +%Y-%m-%d-%H%M%S).log"
|
||||
|
||||
export BORG_REPO="$REPO"
|
||||
export BORG_PASSCOMMAND="cat $BORG_PASSPHRASE_FILE"
|
||||
|
||||
# =================================================================
|
||||
|
||||
log() {
|
||||
local line
|
||||
line="[$(date '+%F %T')] $*"
|
||||
echo "$line"
|
||||
echo "$line" >> "$LOGFILE" 2>/dev/null || true
|
||||
}
|
||||
|
||||
step() { echo; echo "=== $* ==="; }
|
||||
|
||||
die() { log "ERROR: $*"; exit 1; }
|
||||
|
||||
run_cmd() {
|
||||
log "[RUN] $*"
|
||||
"$@"
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
restore.sh full [--archive NAME] [--force] [--dry-run]
|
||||
restore.sh db <db_name> [--archive NAME] [--yes] [--dry-run]
|
||||
restore.sh file <path-within-target> [--archive NAME] [--dest DIR] [--dry-run]
|
||||
restore.sh --list-archives
|
||||
restore.sh -h | --help
|
||||
EOF
|
||||
}
|
||||
|
||||
DRY_RUN=false
|
||||
FORCE=false
|
||||
YES=false
|
||||
ARCHIVE_OVERRIDE=""
|
||||
DEST=""
|
||||
|
||||
parse_common_flags() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--archive) ARCHIVE_OVERRIDE="$2"; shift 2 ;;
|
||||
--force) FORCE=true; shift ;;
|
||||
--yes) YES=true; shift ;;
|
||||
--dry-run) DRY_RUN=true; shift ;;
|
||||
--dest) DEST="$2"; shift 2 ;;
|
||||
*) die "unknown flag: $1" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
local cmd="${1:-}"
|
||||
case "$cmd" in
|
||||
"")
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--list-archives)
|
||||
shift
|
||||
parse_common_flags "$@"
|
||||
list_archives
|
||||
;;
|
||||
full)
|
||||
shift
|
||||
parse_common_flags "$@"
|
||||
cmd_full
|
||||
;;
|
||||
db)
|
||||
shift
|
||||
local dbname="${1:-}"
|
||||
[[ -n "$dbname" ]] || die "db: missing <db_name>"
|
||||
shift
|
||||
parse_common_flags "$@"
|
||||
cmd_db "$dbname"
|
||||
;;
|
||||
file)
|
||||
shift
|
||||
local relpath="${1:-}"
|
||||
[[ -n "$relpath" ]] || die "file: missing <path>"
|
||||
shift
|
||||
parse_common_flags "$@"
|
||||
cmd_file "$relpath"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
die "unknown command: $cmd"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
set -uo pipefail
|
||||
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
RESTORE="$HERE/../restore.sh"
|
||||
FAILURES=0
|
||||
|
||||
assert_eq() {
|
||||
local expected="$1" actual="$2" msg="$3"
|
||||
if [[ "$expected" != "$actual" ]]; then
|
||||
echo "FAIL: $msg (expected '$expected', got '$actual')"
|
||||
FAILURES=$((FAILURES + 1))
|
||||
else
|
||||
echo "PASS: $msg"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local haystack="$1" needle="$2" msg="$3"
|
||||
if [[ "$haystack" != *"$needle"* ]]; then
|
||||
echo "FAIL: $msg (expected to contain '$needle', got '$haystack')"
|
||||
FAILURES=$((FAILURES + 1))
|
||||
else
|
||||
echo "PASS: $msg"
|
||||
fi
|
||||
}
|
||||
|
||||
test_help_exits_zero() {
|
||||
local out rc
|
||||
out="$(bash "$RESTORE" -h)"
|
||||
rc=$?
|
||||
assert_eq "0" "$rc" "help exits 0"
|
||||
assert_contains "$out" "Usage:" "help prints usage"
|
||||
}
|
||||
|
||||
test_no_args_exits_one() {
|
||||
local rc
|
||||
bash "$RESTORE" >/dev/null 2>&1
|
||||
rc=$?
|
||||
assert_eq "1" "$rc" "no args exits 1"
|
||||
}
|
||||
|
||||
test_unknown_command_exits_one() {
|
||||
local rc
|
||||
bash "$RESTORE" bogus >/dev/null 2>&1
|
||||
rc=$?
|
||||
assert_eq "1" "$rc" "unknown command exits 1"
|
||||
}
|
||||
|
||||
test_help_exits_zero
|
||||
test_no_args_exits_one
|
||||
test_unknown_command_exits_one
|
||||
|
||||
echo "-----"
|
||||
if [[ "$FAILURES" -gt 0 ]]; then
|
||||
echo "$FAILURES failure(s)"
|
||||
exit 1
|
||||
fi
|
||||
echo "All tests passed"
|
||||
Reference in New Issue
Block a user