diff --git a/dump_db.sh b/dump_db.sh index 8cd6f82..5c8140d 100755 --- a/dump_db.sh +++ b/dump_db.sh @@ -18,7 +18,7 @@ umask 077 CONTAINER="${DOCKER_CONTAINER_NAME:-mariadb}" PASSWORD_FILE="${PASSWORD_FILE:-/root/.mariadb-backup.pw}" -DB_USER="${DB_USER:-backup}" +DB_USER="${DB_USER:-root}" SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" DUMP_DIR="${DUMP_DIR:-dump}" @@ -31,7 +31,10 @@ EXCLUDED_SCHEMAS="'information_schema','mysql','performance_schema','sys'" # ================================================================= -die() { echo "ERROR: $*" >&2; exit 1; } +die() { + echo "ERROR: $*" >&2 + exit 1 +} warn() { echo "WARNING: $*" >&2; } cleanup() { rm -rf "$STAGING"; } @@ -41,39 +44,41 @@ trap cleanup EXIT command -v docker >/dev/null 2>&1 || die "docker not found in PATH" -[[ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER" 2>/dev/null || echo false)" == "true" ]] \ - || die "container '$CONTAINER' is not running" +[[ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER" 2>/dev/null || echo false)" == "true" ]] || + die "container '$CONTAINER' is not running" if [[ -n "${MYSQL_ROOT_PASSWORD:-}" ]]; then - DB_PASS="$MYSQL_ROOT_PASSWORD" + DB_PASS="$MYSQL_ROOT_PASSWORD" elif [[ -r "$PASSWORD_FILE" ]]; then - DB_PASS="$(< "$PASSWORD_FILE")" + DB_PASS="$(<"$PASSWORD_FILE")" else - die "no password: set MYSQL_ROOT_PASSWORD or create $PASSWORD_FILE (chmod 600)" + die "no password: set MYSQL_ROOT_PASSWORD or create $PASSWORD_FILE (chmod 600)" fi # Detect client binaries (MariaDB 11+ renamed them; MySQL keeps mysql/*). if docker exec "$CONTAINER" sh -c 'command -v mariadb-dump' >/dev/null 2>&1; then - CLIENT=mariadb; DUMPER=mariadb-dump - CLIENT_BIN="mariadb" + CLIENT=mariadb + DUMPER=mariadb-dump + CLIENT_BIN="mariadb" else - CLIENT=mysql; DUMPER=mysqldump - CLIENT_BIN="mysql" + CLIENT=mysql + DUMPER=mysqldump + CLIENT_BIN="mysql" fi # MYSQL_PWD keeps the password out of the process list. db_exec() { docker exec -e MYSQL_PWD="$DB_PASS" "$CONTAINER" "$@"; } -sql() { db_exec "$CLIENT_BIN" -u "$DB_USER" -N -B -e "$1"; } +sql() { db_exec "$CLIENT_BIN" -u "$DB_USER" -N -B -e "$1"; } DUMP_OPTS=( - -u "$DB_USER" - --single-transaction - --quick - --routines --triggers --events - --hex-blob - --max-allowed-packet=1G - --default-character-set=utf8mb4 - --skip-dump-date + -u "$DB_USER" + --single-transaction + --quick + --routines --triggers --events + --hex-blob + --max-allowed-packet=1G + --default-character-set=utf8mb4 + --skip-dump-date ) [[ "$DEDUP_FRIENDLY" == "1" ]] && DUMP_OPTS+=(--skip-extended-insert) @@ -87,9 +92,9 @@ NON_TXN="$(sql " AND table_schema NOT IN ($EXCLUDED_SCHEMAS);" || true)" if [[ -n "$NON_TXN" ]]; then - warn "non-transactional tables found - NOT covered by --single-transaction:" - echo "$NON_TXN" | sed 's/^/ /' >&2 - warn "convert them: ALTER TABLE ENGINE=InnoDB;" + warn "non-transactional tables found - NOT covered by --single-transaction:" + echo "$NON_TXN" | sed 's/^/ /' >&2 + warn "convert them: ALTER TABLE ENGINE=InnoDB;" fi # --- Database list ------------------------------------------------------ @@ -99,15 +104,15 @@ mapfile -t DATABASES < <(sql " WHERE schema_name NOT IN ($EXCLUDED_SCHEMAS) ORDER BY schema_name;" | tr -d '\r') -(( ${#DATABASES[@]} > 0 )) || die "no databases returned - check credentials" +((${#DATABASES[@]} > 0)) || die "no databases returned - check credentials" echo "Found ${#DATABASES[@]} database(s) to dump" # --- Free space sanity check -------------------------------------------- if [[ -d "$DUMP_DIR" ]]; then - need_kb=$(( $(du -sk "$DUMP_DIR" | cut -f1) * 3 / 2 )) - free_kb=$(df -Pk "$(dirname "$DUMP_DIR")" | awk 'NR==2{print $4}') - (( free_kb > need_kb )) || die "not enough free space (~${need_kb}K needed, ${free_kb}K free)" + need_kb=$(($(du -sk "$DUMP_DIR" | cut -f1) * 3 / 2)) + free_kb=$(df -Pk "$(dirname "$DUMP_DIR")" | awk 'NR==2{print $4}') + ((free_kb > need_kb)) || die "not enough free space (~${need_kb}K needed, ${free_kb}K free)" fi # --- Detect user-dump method -------------------------------------------- @@ -116,25 +121,25 @@ fi # fallback : dump full mysql schema (larger, includes grant tables) dump_users_and_grants() { - local out="$1" - if "$DUMPER" --help 2>&1 | grep -q -- ' --users '; then - # MySQL 8.0.31+: --users dumps CREATE USER + GRANT statements only - # (when no --databases / --all-databases is given) - log "[dump] using mysqldump --users (MySQL 8.0.31+ style)" - db_exec "$DUMPER" -u "$DB_USER" --users \ - > "$out" 2>/dev/null - elif docker exec "$CONTAINER" sh -c "command -v mariadb-dump" >/dev/null 2>&1 \ - && docker exec "$CONTAINER" mariadb-dump --help 2>&1 | grep -q -- ' --system '; then - # MariaDB: --system=users - log "[dump] using mariadb-dump --system=users" - db_exec "$DUMPER" -u "$DB_USER" --system=users \ - > "$out" 2>/dev/null - else - # Fallback: full mysql schema dump - log "[dump] falling back to full mysql schema dump (older server)" - db_exec "$DUMPER" "${DUMP_OPTS[@]}" --databases mysql \ - > "$out" 2>/dev/null - fi + local out="$1" + if "$DUMPER" --help 2>&1 | grep -q -- ' --users '; then + # MySQL 8.0.31+: --users dumps CREATE USER + GRANT statements only + # (when no --databases / --all-databases is given) + log "[dump] using mysqldump --users (MySQL 8.0.31+ style)" + db_exec "$DUMPER" -u "$DB_USER" --users \ + >"$out" 2>/dev/null + elif docker exec "$CONTAINER" sh -c "command -v mariadb-dump" >/dev/null 2>&1 && + docker exec "$CONTAINER" mariadb-dump --help 2>&1 | grep -q -- ' --system '; then + # MariaDB: --system=users + log "[dump] using mariadb-dump --system=users" + db_exec "$DUMPER" -u "$DB_USER" --system=users \ + >"$out" 2>/dev/null + else + # Fallback: full mysql schema dump + log "[dump] falling back to full mysql schema dump (older server)" + db_exec "$DUMPER" "${DUMP_OPTS[@]}" --databases mysql \ + >"$out" 2>/dev/null + fi } # --- Dump into staging, verify each file -------------------------------- @@ -145,48 +150,51 @@ mkdir -p "$STAGING" failed=() verify_dump() { - local f="$1" - [[ -s "$f" ]] || { warn "$(basename "$f"): empty"; return 1; } - # mysqldump ends with "-- Dump completed on ..."; mysqlpump ends similarly. - # For the mysql-schema fallback, just check non-empty (it has no marker). - if grep -q '^-- Dump completed' "$f" 2>/dev/null; then - return 0 - fi - # Fallback files (full mysql dump) won't have the marker — accept non-empty. - if [[ "$(basename "$f")" == 00-mysql-schema.sql ]]; then - return 0 - fi - warn "$(basename "$f"): no completion marker" + local f="$1" + [[ -s "$f" ]] || { + warn "$(basename "$f"): empty" return 1 + } + # mysqldump ends with "-- Dump completed on ..."; mysqlpump ends similarly. + # For the mysql-schema fallback, just check non-empty (it has no marker). + if grep -q '^-- Dump completed' "$f" 2>/dev/null; then + return 0 + fi + # Fallback files (full mysql dump) won't have the marker — accept non-empty. + if [[ "$(basename "$f")" == 00-mysql-schema.sql ]]; then + return 0 + fi + warn "$(basename "$f"): no completion marker" + return 1 } # Users and grants first USERS_FILE="$STAGING/00-users-and-grants.sql" echo "Dumping users and grants" if dump_users_and_grants "$USERS_FILE" && verify_dump "$USERS_FILE"; then - echo " -> ok ($(du -h "$USERS_FILE" | cut -f1))" + echo " -> ok ($(du -h "$USERS_FILE" | cut -f1))" else - warn "users-and-grants dump failed or empty" - failed+=("users-and-grants") - rm -f "$USERS_FILE" + warn "users-and-grants dump failed or empty" + failed+=("users-and-grants") + rm -f "$USERS_FILE" fi for db in "${DATABASES[@]}"; do - out="$STAGING/${db}.sql" - printf 'Dumping %-32s ' "$db" - if db_exec "$DUMPER" "${DUMP_OPTS[@]}" -- "$db" > "$out" && verify_dump "$out"; then - printf 'ok (%s)\n' "$(du -h "$out" | cut -f1)" - else - printf 'FAILED\n' - failed+=("$db") - rm -f "$out" - fi + out="$STAGING/${db}.sql" + printf 'Dumping %-32s ' "$db" + if db_exec "$DUMPER" "${DUMP_OPTS[@]}" -- "$db" >"$out" && verify_dump "$out"; then + printf 'ok (%s)\n' "$(du -h "$out" | cut -f1)" + else + printf 'FAILED\n' + failed+=("$db") + rm -f "$out" + fi done # --- Abort before touching the good copy -------------------------------- -if (( ${#failed[@]} > 0 )); then - die "${#failed[@]} dump(s) failed: ${failed[*]} - previous dumps left intact" +if ((${#failed[@]} > 0)); then + die "${#failed[@]} dump(s) failed: ${failed[*]} - previous dumps left intact" fi # --- Atomic swap --------------------------------------------------------