193 lines
5.7 KiB
Bash
Executable File
193 lines
5.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
STACK_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
PD_CONFIG_ROOT="${PD_CONFIG_ROOT:-/mnt/tank/docker/appdata/technitium-pilot/config}"
|
|
LOG_ROOT="${LOG_ROOT:-/mnt/tank/docker/appdata/technitium-pilot/logs}"
|
|
DIG_BIN="${DIG_BIN:-/usr/bin/dig}"
|
|
RSYNC_BIN="${RSYNC_BIN:-/usr/bin/rsync}"
|
|
SSH_BIN="${SSH_BIN:-/usr/bin/ssh}"
|
|
DATE_BIN="${DATE_BIN:-/usr/bin/date}"
|
|
MKDIR_BIN="${MKDIR_BIN:-/usr/bin/mkdir}"
|
|
|
|
SSH_OPTS=(-o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new)
|
|
RSYNC_EXCLUDES=(--exclude cache.bin --exclude 'stats/' --exclude 'blocklists/')
|
|
|
|
NOMAD_HOST="${NOMAD_HOST:-fizzlepoof@10.5.30.7}"
|
|
NOMAD_KEY="${NOMAD_KEY:-/home/truenas_admin/.ssh/nomad_technitium_sync_ed25519}"
|
|
NOMAD_STACK_ROOT="${NOMAD_STACK_ROOT:-/opt/technitium-nomad}"
|
|
NOMAD_DNS_IP="${NOMAD_DNS_IP:-10.5.30.9}"
|
|
|
|
SERENITY_HOST="${SERENITY_HOST:-root@10.5.30.5}"
|
|
SERENITY_KEY="${SERENITY_KEY:-/home/truenas_admin/.ssh/serenity_backup_ed25519}"
|
|
SERENITY_STACK_ROOT="${SERENITY_STACK_ROOT:-/mnt/user/appdata/technitium-serenity}"
|
|
SERENITY_DNS_IP="${SERENITY_DNS_IP:-10.5.30.10}"
|
|
|
|
DRY_RUN=false
|
|
ONLY_TARGET="all"
|
|
SKIP_RESTART=false
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: sync_backup_nodes.sh [--dry-run] [--target nomad|serenity|all] [--skip-restart]
|
|
|
|
Push the live PD Technitium config to the Nomad and Serenity backup Technitium nodes,
|
|
then recycle their compose stacks and verify both private and public DNS answers.
|
|
EOF
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--dry-run)
|
|
DRY_RUN=true
|
|
;;
|
|
--target)
|
|
ONLY_TARGET="${2:-}"
|
|
shift
|
|
;;
|
|
--skip-restart)
|
|
SKIP_RESTART=true
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown argument: $1" >&2
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ ! -d "$PD_CONFIG_ROOT" ]]; then
|
|
echo "PD config root missing: $PD_CONFIG_ROOT" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -x "$RSYNC_BIN" ]]; then
|
|
echo "rsync missing at $RSYNC_BIN" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -x "$SSH_BIN" ]]; then
|
|
echo "ssh missing at $SSH_BIN" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -x "$DIG_BIN" ]]; then
|
|
echo "dig missing at $DIG_BIN" >&2
|
|
exit 1
|
|
fi
|
|
|
|
$MKDIR_BIN -p "$LOG_ROOT"
|
|
STAMP="$($DATE_BIN -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
|
|
log() {
|
|
printf '[%s] %s\n' "$($DATE_BIN -u +%Y-%m-%dT%H:%M:%SZ)" "$*"
|
|
}
|
|
|
|
remote_prepare_nomad() {
|
|
"$SSH_BIN" -i "$NOMAD_KEY" "${SSH_OPTS[@]}" "$NOMAD_HOST" \
|
|
"sudo -n mkdir -p '$NOMAD_STACK_ROOT/data/config' '$NOMAD_STACK_ROOT/data/logs'"
|
|
}
|
|
|
|
remote_prepare_serenity() {
|
|
"$SSH_BIN" -i "$SERENITY_KEY" "${SSH_OPTS[@]}" "$SERENITY_HOST" \
|
|
"mkdir -p '$SERENITY_STACK_ROOT/data/config' '$SERENITY_STACK_ROOT/data/logs'"
|
|
}
|
|
|
|
remote_restart_nomad() {
|
|
"$SSH_BIN" -i "$NOMAD_KEY" "${SSH_OPTS[@]}" "$NOMAD_HOST" \
|
|
"printf '%s\n' '$STAMP' | sudo -n tee '$NOMAD_STACK_ROOT/data/last_pd_sync_utc.txt' >/dev/null && \
|
|
cd '$NOMAD_STACK_ROOT' && \
|
|
sudo -n docker compose --env-file .env down && \
|
|
sudo -n docker compose --env-file .env up -d"
|
|
}
|
|
|
|
remote_restart_serenity() {
|
|
"$SSH_BIN" -i "$SERENITY_KEY" "${SSH_OPTS[@]}" "$SERENITY_HOST" \
|
|
"printf '%s\n' '$STAMP' > '$SERENITY_STACK_ROOT/data/last_pd_sync_utc.txt' && \
|
|
cd '$SERENITY_STACK_ROOT' && \
|
|
docker compose --env-file .env down && \
|
|
docker compose --env-file .env up -d"
|
|
}
|
|
|
|
verify_dns() {
|
|
local label="$1"
|
|
local dns_ip="$2"
|
|
local private_tmp="/tmp/technitium-sync-${label}-private.txt"
|
|
local public_tmp="/tmp/technitium-sync-${label}-public.txt"
|
|
local attempt
|
|
log "Verifying $label at $dns_ip"
|
|
for attempt in {1..10}; do
|
|
if "$DIG_BIN" +time=2 +tries=1 @"$dns_ip" dns.home.paccoco.com A >"$private_tmp" 2>/dev/null && \
|
|
"$DIG_BIN" +time=2 +tries=1 @"$dns_ip" cloudflare.com A >"$public_tmp" 2>/dev/null && \
|
|
grep -q 'status: NOERROR' "$private_tmp" && \
|
|
grep -q 'status: NOERROR' "$public_tmp" && \
|
|
grep -q '10.5.30.8' "$private_tmp"; then
|
|
return 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
echo "Verification failed for $label ($dns_ip) after retries" >&2
|
|
cat "$private_tmp" 2>/dev/null >&2 || true
|
|
echo '---' >&2
|
|
cat "$public_tmp" 2>/dev/null >&2 || true
|
|
return 1
|
|
}
|
|
|
|
sync_nomad() {
|
|
log "Preparing Nomad target"
|
|
remote_prepare_nomad
|
|
local rsync_cmd=("$RSYNC_BIN" -a --delete "${RSYNC_EXCLUDES[@]}" -e "$SSH_BIN -i $NOMAD_KEY ${SSH_OPTS[*]}" --rsync-path="sudo -n $RSYNC_BIN" "$PD_CONFIG_ROOT/" "$NOMAD_HOST:$NOMAD_STACK_ROOT/data/config/")
|
|
if $DRY_RUN; then
|
|
rsync_cmd=("$RSYNC_BIN" -an --delete "${RSYNC_EXCLUDES[@]}" -e "$SSH_BIN -i $NOMAD_KEY ${SSH_OPTS[*]}" --rsync-path="sudo -n $RSYNC_BIN" "$PD_CONFIG_ROOT/" "$NOMAD_HOST:$NOMAD_STACK_ROOT/data/config/")
|
|
fi
|
|
log "Syncing Nomad config"
|
|
"${rsync_cmd[@]}"
|
|
if ! $DRY_RUN && ! $SKIP_RESTART; then
|
|
log "Recycling Nomad Technitium stack"
|
|
remote_restart_nomad
|
|
verify_dns nomad "$NOMAD_DNS_IP"
|
|
fi
|
|
}
|
|
|
|
sync_serenity() {
|
|
log "Preparing Serenity target"
|
|
remote_prepare_serenity
|
|
local rsync_cmd=("$RSYNC_BIN" -a --delete "${RSYNC_EXCLUDES[@]}" -e "$SSH_BIN -i $SERENITY_KEY ${SSH_OPTS[*]}" "$PD_CONFIG_ROOT/" "$SERENITY_HOST:$SERENITY_STACK_ROOT/data/config/")
|
|
if $DRY_RUN; then
|
|
rsync_cmd=("$RSYNC_BIN" -an --delete "${RSYNC_EXCLUDES[@]}" -e "$SSH_BIN -i $SERENITY_KEY ${SSH_OPTS[*]}" "$PD_CONFIG_ROOT/" "$SERENITY_HOST:$SERENITY_STACK_ROOT/data/config/")
|
|
fi
|
|
log "Syncing Serenity config"
|
|
"${rsync_cmd[@]}"
|
|
if ! $DRY_RUN && ! $SKIP_RESTART; then
|
|
log "Recycling Serenity Technitium stack"
|
|
remote_restart_serenity
|
|
verify_dns serenity "$SERENITY_DNS_IP"
|
|
fi
|
|
}
|
|
|
|
case "$ONLY_TARGET" in
|
|
all)
|
|
sync_nomad
|
|
sync_serenity
|
|
;;
|
|
nomad)
|
|
sync_nomad
|
|
;;
|
|
serenity)
|
|
sync_serenity
|
|
;;
|
|
*)
|
|
echo "Invalid --target value: $ONLY_TARGET" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
log "Technitium backup sync finished successfully"
|