105 lines
3.1 KiB
Bash
Executable File
105 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
MODE="dry-run"
|
|
CUTOFF_DATE="2026-06-02"
|
|
|
|
if [[ "${1:-}" == "--execute" ]]; then
|
|
MODE="execute"
|
|
elif [[ "${1:-}" != "" && "${1:-}" != "--dry-run" ]]; then
|
|
echo "Usage: $0 [--dry-run|--execute]" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ "$(id -u)" -ne 0 ]]; then
|
|
echo "Must run as root on the Unraid host." >&2
|
|
exit 1
|
|
fi
|
|
|
|
today="$(date +%F)"
|
|
if [[ "$MODE" == "execute" && "$today" < "$CUTOFF_DATE" ]]; then
|
|
echo "Refusing: cutoff is $CUTOFF_DATE, today is $today." >&2
|
|
exit 1
|
|
fi
|
|
|
|
declare -a CANDIDATES=(
|
|
"/mnt/user/appdata/postgresql17|/mnt/user/appdata/postgresql18|shared PostgreSQL 17 rollback"
|
|
"/mnt/user/appdata/mealie/postgres|/mnt/user/appdata/mealie/postgres18|Mealie PostgreSQL 17 rollback"
|
|
"/mnt/user/appdata/nextcloud/postgres|/mnt/user/appdata/nextcloud/postgres18|Nextcloud PostgreSQL 17 rollback"
|
|
"/mnt/user/appdata/immich_postgres|/mnt/user/appdata/immich_postgres_vectorchord|Immich pgvecto.rs rollback"
|
|
)
|
|
|
|
require_container_healthy() {
|
|
local name="$1"
|
|
local state
|
|
local health
|
|
|
|
state="$(docker inspect "$name" --format '{{.State.Status}}' 2>/dev/null || true)"
|
|
health="$(docker inspect "$name" --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' 2>/dev/null || true)"
|
|
|
|
if [[ "$state" != "running" ]]; then
|
|
echo "Container $name is not running (state=$state)." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$health" != "healthy" && "$health" != "none" ]]; then
|
|
echo "Container $name is not healthy (health=$health)." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
echo "Alt-volume release check"
|
|
echo "Mode: $MODE"
|
|
echo "Date: $today"
|
|
echo
|
|
|
|
require_container_healthy postgresql17
|
|
require_container_healthy mealie-postgres
|
|
require_container_healthy nextcloud-postgres
|
|
require_container_healthy immich_postgres
|
|
|
|
if docker ps --filter health=unhealthy --format '{{.Names}}' | grep -q .; then
|
|
echo "Refusing: unhealthy containers exist." >&2
|
|
docker ps --filter health=unhealthy --format ' {{.Names}} {{.Status}}' >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -x /mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh ]]; then
|
|
/mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh freshness
|
|
fi
|
|
|
|
mapfile -t active_mounts < <(docker inspect $(docker ps -q) --format '{{range .Mounts}}{{println .Source}}{{end}}' 2>/dev/null || true)
|
|
|
|
for entry in "${CANDIDATES[@]}"; do
|
|
IFS='|' read -r old_path active_path label <<< "$entry"
|
|
|
|
if [[ ! -d "$active_path" ]]; then
|
|
echo "Missing active path for $label: $active_path" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -d "$old_path" ]]; then
|
|
echo "Already absent: $old_path ($label)"
|
|
continue
|
|
fi
|
|
|
|
if printf '%s\n' "${active_mounts[@]}" | grep -Fxq "$old_path"; then
|
|
echo "Refusing: old path is still mounted by a running container: $old_path" >&2
|
|
exit 1
|
|
fi
|
|
|
|
size="$(du -sh "$old_path" 2>/dev/null | awk '{print $1}')"
|
|
echo "Candidate: $old_path ($label, $size)"
|
|
echo "Active: $active_path"
|
|
|
|
if [[ "$MODE" == "execute" ]]; then
|
|
rm -rf --one-file-system "$old_path"
|
|
echo "Removed: $old_path"
|
|
else
|
|
echo "Dry-run: would remove $old_path"
|
|
fi
|
|
echo
|
|
done
|
|
|
|
echo "Alt-volume release check completed."
|