feat: import native backup/<dbname>.conf+.sql pairs directly into alt-mariadb

This commit is contained in:
Marek Miklewicz
2026-07-05 16:53:31 +02:00
parent d5040037d6
commit 6b7ec39025
2 changed files with 320 additions and 1 deletions
+141
View File
@@ -0,0 +1,141 @@
#!/bin/bash
set -euo pipefail
PLUGIN_DIR="$(cd "$(dirname "$0")/.." && pwd)"
SCRIPT="$PLUGIN_DIR/scripts/da-integration/alt_mysql_native_backup_restore.sh"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
fail() {
echo "FAIL: $*" >&2
exit 1
}
# --- Fake alt-mariadb client, tracks statements + a simple existing-db state file ---
mkdir -p "$TMP_DIR/mariadb/bin"
cat > "$TMP_DIR/mariadb/bin/mariadb" <<'STUB'
#!/bin/bash
STATE_FILE="${FAKE_DB_STATE_FILE:?FAKE_DB_STATE_FILE must be set}"
CALL_LOG="${FAKE_CALL_LOG:?FAKE_CALL_LOG must be set}"
sql=""
mode=""
for arg in "$@"; do
case "$arg" in
-e) mode="sql" ;;
-Nse) mode="sql" ;;
--database=*|--user=*|--socket=*|--host=*|--port=*|--protocol=*) ;;
*)
if [ "$mode" = "sql" ]; then
sql="$arg"
mode=""
fi
;;
esac
done
if [ -n "$sql" ]; then
printf '%s\n' "$sql" >> "$CALL_LOG"
case "$sql" in
*"SELECT 1 FROM information_schema.SCHEMATA"*)
name="$(printf '%s' "$sql" | sed -n "s/.*SCHEMA_NAME='\([^']*\)'.*/\1/p")"
grep -qx "$name" "$STATE_FILE" 2>/dev/null && echo 1
;;
*"DROP DATABASE"*)
name="$(printf '%s' "$sql" | sed -n 's/.*DATABASE[^`]*`\([A-Za-z0-9_]*\)`.*/\1/p')"
[ -n "$name" ] && { grep -vx "$name" "$STATE_FILE" > "$STATE_FILE.tmp" 2>/dev/null || true; mv -f "$STATE_FILE.tmp" "$STATE_FILE"; }
;;
*"CREATE DATABASE"*)
name="$(printf '%s' "$sql" | sed -n 's/.*DATABASE[^`]*`\([A-Za-z0-9_]*\)`.*/\1/p')"
[ -n "$name" ] && echo "$name" >> "$STATE_FILE"
;;
esac
exit 0
fi
cat >> "$CALL_LOG"
printf 'IMPORT\n' >> "$CALL_LOG"
if [ "${FAKE_IMPORT_FAIL:-0}" = "1" ]; then
exit 1
fi
exit 0
STUB
chmod 755 "$TMP_DIR/mariadb/bin/mariadb"
cat > "$TMP_DIR/mariadb/bin/mariadb-dump" <<'STUB'
#!/bin/bash
echo "-- fake rollback dump"
exit 0
STUB
chmod 755 "$TMP_DIR/mariadb/bin/mariadb-dump"
cat > "$TMP_DIR/alt-mysql.conf" <<EOF
host=
passwd=secret
port=4407
socket=$TMP_DIR/data/mariadb.sock
user=da_admin
EOF
cat > "$TMP_DIR/settings.conf" <<EOF
MYSQL_PLUGIN_CREDENTIALS_FILE=$TMP_DIR/alt-mysql.conf
MYSQL_PLUGIN_CLIENT_BIN_DIR=$TMP_DIR/mariadb/bin
EOF
# run_import NAME_PREFIX -- ARGS...
# Runs the script under test with fresh, isolated state/log/call-log files
# for this scenario, writes its stdout to "$TMP_DIR/<prefix>.stdout" and
# writes every mariadb-stub SQL statement to "$TMP_DIR/<prefix>.calls",
# then prints nothing (the two files are read directly by the caller).
run_import() {
local prefix="$1"
shift
local db_state="$TMP_DIR/${prefix}.dbstate"
local call_log="$TMP_DIR/${prefix}.calls"
local stdout_file="$TMP_DIR/${prefix}.stdout"
local restore_log="$TMP_DIR/${prefix}.restore.log"
: > "$db_state"
: > "$call_log"
DA_ALT_MYSQL_SETTINGS_FILE="$TMP_DIR/settings.conf" \
DA_ALT_MYSQL_RESTORE_LOG="$restore_log" \
FAKE_DB_STATE_FILE="$db_state" \
FAKE_CALL_LOG="$call_log" \
bash "$SCRIPT" "$@" > "$stdout_file"
}
# --- Build a fake backup directory with one valid database pair ---
BACKUP_DIR="$TMP_DIR/backup"
mkdir -p "$BACKUP_DIR"
cat > "$BACKUP_DIR/demo_app.conf" <<'EOF'
accesshosts=0=localhost
db_collation=DEFAULT_CHARACTER_SET_NAME=utf8mb4&DEFAULT_COLLATION_NAME=utf8mb4_unicode_ci&SCHEMA_NAME=demo_app
demo_app=accesshosts=localhost&select_priv=Y&insert_priv=Y&passwd=%2Aabc123&plugin=mysql_native_password
EOF
cat > "$BACKUP_DIR/demo_app.sql" <<'EOF'
CREATE TABLE t (id INT);
EOF
# --- Scenario 1: happy path, single valid database ---
run_import scenario1 --user demo --root "$BACKUP_DIR" --overwrite allow
grep -q "IMPORTED demo_app" "$TMP_DIR/scenario1.stdout" \
|| fail "expected 'IMPORTED demo_app' in output, got: $(cat "$TMP_DIR/scenario1.stdout")"
grep -q "CREATE USER 'demo_app'@'localhost'" "$TMP_DIR/scenario1.calls" \
|| fail "expected CREATE USER statement for demo_app@localhost"
grep -q "GRANT SELECT,INSERT ON \`demo_app\`.\* TO 'demo_app'@'localhost'" "$TMP_DIR/scenario1.calls" \
|| fail "expected GRANT SELECT,INSERT for demo_app@localhost"
grep -q "IMPORT" "$TMP_DIR/scenario1.calls" || fail "expected the .sql dump to be piped in for import"
# --- Scenario 2: a database with no matching .conf must be skipped, not abort the run ---
mkdir -p "$TMP_DIR/backup_missing_conf"
cat > "$TMP_DIR/backup_missing_conf/orphan_db.sql" <<'EOF'
CREATE TABLE t (id INT);
EOF
run_import scenario2 --user orphan --root "$TMP_DIR/backup_missing_conf" --overwrite allow
grep -q "SKIPPED orphan_db" "$TMP_DIR/scenario2.stdout" \
|| fail "expected orphan_db (no .conf) to be reported as SKIPPED, got: $(cat "$TMP_DIR/scenario2.stdout")"
echo "alt_mysql_native_backup_restore_import_test: OK"