Automate reference host data restore
This commit is contained in:
+119
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
# Restore persistent UI data and the approved secret subset from a reference
|
||||
# host backup without reviving its obsolete containers or configuration files.
|
||||
set -Eeuo pipefail
|
||||
umask 077
|
||||
|
||||
BACKUP=${1:-}
|
||||
STACK_DIR=${STACK_DIR:-/opt/mike-ai/stack}
|
||||
SECRETS_DIR=${SECRETS_DIR:-/etc/mike-ai}
|
||||
OPENWEBUI_VOLUME=${OPENWEBUI_VOLUME:-mike-ai_open-webui-data}
|
||||
|
||||
die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; }
|
||||
log() { printf '\n==> %s\n' "$*"; }
|
||||
|
||||
[[ $EUID -eq 0 ]] || die "Bitte als root ausführen."
|
||||
[[ -n $BACKUP && -d $BACKUP ]] || \
|
||||
die "Aufruf: $0 /pfad/zum/ki-migration-backup"
|
||||
[[ -f $BACKUP/SHA256SUMS ]] || die "SHA256SUMS fehlt."
|
||||
[[ -f $SECRETS_DIR/stack.env ]] || die "$SECRETS_DIR/stack.env fehlt."
|
||||
|
||||
ui_archive=$BACKUP/archives/open-webui-migrated.tar.gz
|
||||
mcp_archive=$BACKUP/archives/mcp-and-search-config.tar.gz
|
||||
for archive in "$ui_archive" "$mcp_archive"; do
|
||||
[[ -f $archive ]] || die "Archiv fehlt: $archive"
|
||||
expected=$(awk -v suffix="/${archive#"$BACKUP"/}" \
|
||||
'$2 ~ suffix "$" {print $1; exit}' "$BACKUP/SHA256SUMS")
|
||||
[[ -n $expected ]] || die "Keine Prüfsumme für $archive gefunden."
|
||||
actual=$(sha256sum "$archive" | awk '{print $1}')
|
||||
[[ $actual == "$expected" ]] || die "Prüfsumme falsch: $archive"
|
||||
done
|
||||
|
||||
stage=$(mktemp -d /data/mike-ai-restore.XXXXXX)
|
||||
trap 'rm -rf "$stage"' EXIT
|
||||
|
||||
log "OpenWebUI-Sicherung entpacken und prüfen"
|
||||
tar -xzf "$ui_archive" -C "$stage"
|
||||
ui_source=$stage/open-webui-migrated
|
||||
[[ -s $ui_source/data/webui.db ]] || die "OpenWebUI-Datenbank fehlt oder ist leer."
|
||||
[[ -f $ui_source/openwebui.env ]] || die "OpenWebUI-Umgebung fehlt."
|
||||
|
||||
old_webui_key=$(sed -n 's/^WEBUI_SECRET_KEY=//p' \
|
||||
"$ui_source/openwebui.env" | head -n 1)
|
||||
[[ -n $old_webui_key ]] || die "WEBUI_SECRET_KEY fehlt in der Sicherung."
|
||||
|
||||
log "Freigegebene MCP-Secrets isoliert entpacken"
|
||||
mkdir -p "$stage/approved"
|
||||
tar -xzf "$mcp_archive" -C "$stage/approved" \
|
||||
etc/mike-ai/router-api-key \
|
||||
etc/mike-ai/homeassistant-admin-mcp.env \
|
||||
etc/mike-ai/arr-mcp.env \
|
||||
etc/mike-ai/runraid/.env \
|
||||
etc/mike-ai/runraid/homeserver-cert.pem \
|
||||
etc/mike-ai/keys/unraid_root \
|
||||
etc/mike-ai/ssh/known_hosts_unraid_ai \
|
||||
usr/local/bin/runraid
|
||||
|
||||
old_router_key=$(<"$stage/approved/etc/mike-ai/router-api-key")
|
||||
[[ -n $old_router_key ]] || die "Router-Key in der Sicherung ist leer."
|
||||
|
||||
set_env_value() {
|
||||
local file=$1 key=$2 value=$3 tmp
|
||||
tmp=$(mktemp "${file}.XXXXXX")
|
||||
awk -v key="$key" -v value="$value" '
|
||||
BEGIN { done=0 }
|
||||
$0 ~ "^" key "=" { print key "=" value; done=1; next }
|
||||
{ print }
|
||||
END { if (!done) print key "=" value }
|
||||
' "$file" >"$tmp"
|
||||
install -m 0600 "$tmp" "$file"
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
log "Aktuellen leeren UI-Stand als Rückfall sichern"
|
||||
volume_path=$(docker volume inspect -f '{{.Mountpoint}}' "$OPENWEBUI_VOLUME")
|
||||
fallback=/data/open-webui-before-restore-$(date +%Y%m%d-%H%M%S).tar.gz
|
||||
tar -czf "$fallback" -C "$volume_path" .
|
||||
|
||||
log "OpenWebUI-Daten atomar ersetzen"
|
||||
docker stop mike-ai-open-webui >/dev/null 2>&1 || true
|
||||
rsync -a --delete "$ui_source/data/" "$volume_path/"
|
||||
|
||||
log "Schlüssel und aktuelle Tool-Secrets wiederherstellen"
|
||||
set_env_value "$SECRETS_DIR/stack.env" WEBUI_SECRET_KEY "$old_webui_key"
|
||||
set_env_value "$SECRETS_DIR/stack.env" ROUTER_API_KEY "$old_router_key"
|
||||
|
||||
install -d -m 0700 "$SECRETS_DIR/runraid" "$SECRETS_DIR/keys" "$SECRETS_DIR/ssh"
|
||||
install -m 0600 "$stage/approved/etc/mike-ai/homeassistant-admin-mcp.env" \
|
||||
"$SECRETS_DIR/homeassistant-admin-mcp.env"
|
||||
install -m 0600 "$stage/approved/etc/mike-ai/arr-mcp.env" \
|
||||
"$SECRETS_DIR/arr-mcp.env"
|
||||
install -m 0600 "$stage/approved/etc/mike-ai/runraid/.env" \
|
||||
"$SECRETS_DIR/runraid/.env"
|
||||
install -m 0600 "$stage/approved/etc/mike-ai/runraid/homeserver-cert.pem" \
|
||||
"$SECRETS_DIR/runraid/homeserver-cert.pem"
|
||||
install -m 0600 "$stage/approved/etc/mike-ai/keys/unraid_root" \
|
||||
"$SECRETS_DIR/keys/unraid_root"
|
||||
install -m 0600 "$stage/approved/etc/mike-ai/ssh/known_hosts_unraid_ai" \
|
||||
"$SECRETS_DIR/ssh/known_hosts_unraid_ai"
|
||||
install -m 0755 "$stage/approved/usr/local/bin/runraid" /usr/local/bin/runraid
|
||||
|
||||
log "Tool-Container mit der neuen Stackdefinition aktivieren"
|
||||
"$STACK_DIR/platform/mcp/install-tools.sh"
|
||||
|
||||
log "Router und OpenWebUI mit den wiederhergestellten Schlüsseln neu erstellen"
|
||||
cd "$STACK_DIR"
|
||||
docker compose --env-file "$SECRETS_DIR/stack.env" up -d --force-recreate \
|
||||
router open-webui
|
||||
|
||||
deadline=$((SECONDS + 180))
|
||||
for container in mike-ai-router mike-ai-open-webui; do
|
||||
until [[ $(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' \
|
||||
"$container" 2>/dev/null || true) == healthy ]]; do
|
||||
(( SECONDS < deadline )) || die "$container wurde nicht rechtzeitig gesund."
|
||||
sleep 2
|
||||
done
|
||||
done
|
||||
|
||||
log "Wiederherstellung abgeschlossen"
|
||||
printf 'Rückfallsicherung des vorherigen UI-Stands: %s\n' "$fallback"
|
||||
Reference in New Issue
Block a user