From 98b9436cd1e84c45f07cae1ff4f03d5c169bcb5c Mon Sep 17 00:00:00 2001 From: Marek Miklewicz Date: Sun, 5 Jul 2026 17:04:05 +0200 Subject: [PATCH] feat: restore native backup/.conf pairs before falling back to native staging --- ...alt_mysql_user_restore_post_pre_cleanup.sh | 38 +++++++++++-- .../da_restore_native_staging_to_alt_mysql.sh | 12 ++++- ...ser_restore_post_pre_cleanup_tiers_test.sh | 54 +++++++++++++++++++ 3 files changed, 99 insertions(+), 5 deletions(-) create mode 100755 tests/alt_mysql_user_restore_post_pre_cleanup_tiers_test.sh diff --git a/scripts/da-integration/alt_mysql_user_restore_post_pre_cleanup.sh b/scripts/da-integration/alt_mysql_user_restore_post_pre_cleanup.sh index 6636209..9f9918d 100755 --- a/scripts/da-integration/alt_mysql_user_restore_post_pre_cleanup.sh +++ b/scripts/da-integration/alt_mysql_user_restore_post_pre_cleanup.sh @@ -7,6 +7,8 @@ SETTINGS_FILE="$PLUGIN_DIR/plugin-settings.conf" LOG_FILE="${DA_ALT_MYSQL_RESTORE_LOG:-$PLUGIN_DIR/data/logs/da-restore.log}" RESTORE_PAYLOAD_SCRIPT="$SCRIPT_DIR/alt_mysql_restore_payload.sh" NATIVE_MIGRATE_SCRIPT="$SCRIPT_DIR/da_restore_native_staging_to_alt_mysql.sh" +NATIVE_BACKUP_RESTORE_SCRIPT="${DA_ALT_MYSQL_NATIVE_BACKUP_RESTORE_BIN:-$SCRIPT_DIR/alt_mysql_native_backup_restore.sh}" +NATIVE_MIGRATE_SCRIPT="${DA_ALT_MYSQL_NATIVE_STAGING_BIN:-$NATIVE_MIGRATE_SCRIPT}" mkdir -p "$(dirname "$LOG_FILE")" @@ -140,7 +142,7 @@ detect_payload_dir() { } main() { - local da_user enabled overwrite payload native_mode + local da_user enabled overwrite payload native_mode restore_root da_user="$(detect_da_user "${1:-}")" enabled="$(read_plugin_setting DA_ALT_MYSQL_RESTORE_ENABLED true)" native_mode="$(read_plugin_setting DA_ALT_MYSQL_NATIVE_STAGING_MIGRATE true)" @@ -161,14 +163,42 @@ main() { return 0 fi - log "plugin payload not found; considering native staging migration" + log "plugin payload not found; trying native backup/.conf restore" + + restore_root="$(candidate_dir_if_valid "${restore_path:-}")" \ + || restore_root="$(candidate_dir_if_valid "${restore_dir:-}")" \ + || restore_root="$(candidate_dir_if_valid "${tmpdir:-}")" \ + || restore_root="$(candidate_dir_if_valid "${backup_path:-}")" \ + || restore_root="$(pwd -P)" + + local -a imported=() + local -a skip_args=() + if [ -x "$NATIVE_BACKUP_RESTORE_SCRIPT" ] && [ -d "$restore_root" ]; then + while IFS= read -r line; do + [ -n "$line" ] || continue + log "native backup restore: $line" + case "$line" in + IMPORTED\ *) + imported+=("${line#IMPORTED }") + ;; + esac + done < <("$NATIVE_BACKUP_RESTORE_SCRIPT" --user "$da_user" --root "$restore_root" --overwrite "$overwrite") + else + log "native backup restore script unavailable or restore root not found: $NATIVE_BACKUP_RESTORE_SCRIPT / $restore_root" + fi + + for db in "${imported[@]}"; do + skip_args+=("--skip" "$db") + done + + log "native backup restore imported: ${imported[*]:-}; considering native staging migration for the rest" case "$native_mode" in true|yes|on|1) [ -x "$NATIVE_MIGRATE_SCRIPT" ] || die "native staging migration script is not executable: $NATIVE_MIGRATE_SCRIPT" - "$NATIVE_MIGRATE_SCRIPT" --user "$da_user" + "$NATIVE_MIGRATE_SCRIPT" --user "$da_user" "${skip_args[@]}" ;; *) - log "native staging migration disabled; account restore continues without alt-mysql DB migration" + log "native staging migration disabled; account restore continues without further alt-mysql DB migration" ;; esac } diff --git a/scripts/da-integration/da_restore_native_staging_to_alt_mysql.sh b/scripts/da-integration/da_restore_native_staging_to_alt_mysql.sh index 3dc54a7..c1b3b69 100644 --- a/scripts/da-integration/da_restore_native_staging_to_alt_mysql.sh +++ b/scripts/da-integration/da_restore_native_staging_to_alt_mysql.sh @@ -10,6 +10,7 @@ DA_USER="" NATIVE_MY_CNF="${NATIVE_MY_CNF:-/usr/local/directadmin/conf/my.cnf}" OVERWRITE_POLICY="" CLEANUP_POLICY="" +declare -a SKIP_DATABASES=() mkdir -p "$(dirname "$LOG_FILE")" @@ -36,6 +37,7 @@ while [ "$#" -gt 0 ]; do --native-my-cnf) NATIVE_MY_CNF="${2:-}"; shift 2 ;; --overwrite) OVERWRITE_POLICY="${2:-}"; shift 2 ;; --cleanup) CLEANUP_POLICY="${2:-}"; shift 2 ;; + --skip) SKIP_DATABASES+=("${2:-}"); shift 2 ;; *) usage ;; esac done @@ -87,12 +89,20 @@ safe_identifier() { } list_native_databases() { + local db native_query information_schema " SELECT SCHEMA_NAME FROM SCHEMATA WHERE SCHEMA_NAME LIKE '$(mysql_escape_literal "$DA_USER")\\_%' ESCAPE '\\' ORDER BY SCHEMA_NAME - " | grep -Ev '^(information_schema|performance_schema|mysql|sys)$' || true + " | grep -Ev '^(information_schema|performance_schema|mysql|sys)$' | while IFS= read -r db; do + [ -n "$db" ] || continue + local skipped=0 candidate + for candidate in "${SKIP_DATABASES[@]}"; do + [ "$candidate" = "$db" ] && { skipped=1; break; } + done + [ "$skipped" -eq 1 ] || printf '%s\n' "$db" + done || true } list_native_user_hosts() { diff --git a/tests/alt_mysql_user_restore_post_pre_cleanup_tiers_test.sh b/tests/alt_mysql_user_restore_post_pre_cleanup_tiers_test.sh new file mode 100755 index 0000000..2847b59 --- /dev/null +++ b/tests/alt_mysql_user_restore_post_pre_cleanup_tiers_test.sh @@ -0,0 +1,54 @@ +#!/bin/bash +set -euo pipefail + +PLUGIN_DIR="$(cd "$(dirname "$0")/.." && pwd)" +SCRIPT="$PLUGIN_DIR/scripts/da-integration/alt_mysql_user_restore_post_pre_cleanup.sh" +TMP_DIR="$(mktemp -d)" +trap 'rm -rf "$TMP_DIR"' EXIT + +fail() { + echo "FAIL: $*" >&2 + exit 1 +} + +mkdir -p "$TMP_DIR/fake_bin" + +# Fake native-backup-restore tier: reports one imported, one skipped database. +cat > "$TMP_DIR/fake_bin/alt_mysql_native_backup_restore.sh" <<'EOF' +#!/bin/bash +echo "CALLED native_backup_restore $*" >> "$FAKE_CALL_LOG" +echo "IMPORTED demo_ok" +echo "SKIPPED demo_bad import-failed" +EOF +chmod 755 "$TMP_DIR/fake_bin/alt_mysql_native_backup_restore.sh" + +# Fake native-staging-migrate tier: just logs that it ran, and with which user. +cat > "$TMP_DIR/fake_bin/da_restore_native_staging_to_alt_mysql.sh" <<'EOF' +#!/bin/bash +echo "CALLED native_staging_migrate $*" >> "$FAKE_CALL_LOG" +EOF +chmod 755 "$TMP_DIR/fake_bin/da_restore_native_staging_to_alt_mysql.sh" + +# No plugin payload present anywhere reachable, so the hook falls past tier 1. +mkdir -p "$TMP_DIR/empty_restore_root" + +CALL_LOG="$TMP_DIR/calls.log" +: > "$CALL_LOG" + +DA_ALT_MYSQL_RESTORE_LOG="$TMP_DIR/restore.log" \ +DA_ALT_MYSQL_NATIVE_BACKUP_RESTORE_BIN="$TMP_DIR/fake_bin/alt_mysql_native_backup_restore.sh" \ +DA_ALT_MYSQL_NATIVE_STAGING_BIN="$TMP_DIR/fake_bin/da_restore_native_staging_to_alt_mysql.sh" \ +DA_ALT_MYSQL_SETTINGS_FILE="$TMP_DIR/missing-settings.conf" \ +FAKE_CALL_LOG="$CALL_LOG" \ +restore_path="$TMP_DIR/empty_restore_root" \ +username="demo" \ +bash "$SCRIPT" demo + +grep -q "CALLED native_backup_restore" "$CALL_LOG" || fail "expected the native-backup-restore tier to be invoked" +grep -q "CALLED native_staging_migrate" "$CALL_LOG" || fail "expected the native-staging-migrate fallback to still run for skipped databases" + +# The staging-migrate fallback must be told which databases were already +# imported, so it never touches demo_ok again. +grep -q "\-\-skip demo_ok" "$CALL_LOG" || fail "expected native-staging-migrate to be told to skip demo_ok, log: $(cat "$CALL_LOG")" + +echo "alt_mysql_user_restore_post_pre_cleanup_tiers_test: OK"