feat: import native backup/<dbname>.conf+.sql pairs directly into alt-mariadb
This commit is contained in:
+141
@@ -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"
|
||||
Reference in New Issue
Block a user