138 lines
4.1 KiB
Bash
Executable File
138 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
CONTAINER_NAME=${STAGING_MYSQL_CONTAINER:-peipei-mysql-staging}
|
|
VOLUME_NAME=${STAGING_MYSQL_VOLUME:-peipei-mysql-staging-data}
|
|
HOST_PORT=${STAGING_MYSQL_PORT:-3307}
|
|
DB_NAME=${STAGING_MYSQL_DB:-play-with}
|
|
ROOT_PASSWORD=${STAGING_MYSQL_ROOT_PASSWORD:-root}
|
|
MYSQL_IMAGE=${STAGING_MYSQL_IMAGE:-mysql:8.0.32}
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: $0 <path-to-backup.sql[.gz|.xz]>
|
|
|
|
Recreates the local staging MySQL container (${CONTAINER_NAME}) and imports the given SQL dump.
|
|
Environment overrides: STAGING_MYSQL_CONTAINER, STAGING_MYSQL_VOLUME, STAGING_MYSQL_PORT,
|
|
STAGING_MYSQL_DB, STAGING_MYSQL_ROOT_PASSWORD, STAGING_MYSQL_IMAGE.
|
|
USAGE
|
|
exit 1
|
|
}
|
|
|
|
if [[ $# -ne 1 ]]; then
|
|
usage
|
|
fi
|
|
|
|
backup_path=$1
|
|
if [[ ! -f "$backup_path" ]]; then
|
|
echo "[ERROR] Backup file not found: $backup_path" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
echo "[ERROR] docker command not found" >&2
|
|
exit 1
|
|
fi
|
|
|
|
port_in_use() {
|
|
if command -v lsof >/dev/null 2>&1; then
|
|
lsof -iTCP:"$HOST_PORT" -sTCP:LISTEN -Pn >/dev/null 2>&1 && return 0
|
|
fi
|
|
if command -v netstat >/dev/null 2>&1; then
|
|
netstat -an | grep -E "\.$HOST_PORT .*LISTEN" >/dev/null 2>&1 && return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
confirm_remove() {
|
|
local prompt="$1"
|
|
read -r -p "$prompt (yes/no): " reply
|
|
if [[ "$reply" != "yes" ]]; then
|
|
echo "操作已取消"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
existing_container_id=$(docker ps -a --filter "name=^${CONTAINER_NAME}$" -q)
|
|
if [[ -n "$existing_container_id" ]]; then
|
|
echo "[WARN] Container ${CONTAINER_NAME} already exists."
|
|
confirm_remove "Remove the existing staging container and recreate it?"
|
|
docker rm -f "$CONTAINER_NAME" >/dev/null
|
|
fi
|
|
|
|
echo "[INFO] Removing existing volume (if any): ${VOLUME_NAME}"
|
|
if docker volume ls -q --filter "name=^${VOLUME_NAME}$" | grep -Fxq "$VOLUME_NAME"; then
|
|
docker volume rm "$VOLUME_NAME" >/dev/null
|
|
fi
|
|
|
|
if port_in_use; then
|
|
containers_on_port=$(docker ps --filter "publish=${HOST_PORT}" --format '{{.ID}}\t{{.Names}}')
|
|
if [[ -n "$containers_on_port" ]]; then
|
|
echo "[WARN] Host port ${HOST_PORT} is currently used by the following container(s):"
|
|
while IFS=$'\t' read -r cid cname; do
|
|
[[ -z "$cid" ]] && continue
|
|
echo " - ${cname} (${cid})"
|
|
done <<<"$containers_on_port"
|
|
confirm_remove "Stop and remove these container(s)?"
|
|
while IFS=$'\t' read -r cid cname; do
|
|
[[ -z "$cid" ]] && continue
|
|
docker stop "$cid" >/dev/null
|
|
docker rm "$cid" >/dev/null
|
|
done <<<"$containers_on_port"
|
|
else
|
|
echo "[ERROR] Host port ${HOST_PORT} is already in use. Set STAGING_MYSQL_PORT to an unused port or stop the process using it." >&2
|
|
exit 1
|
|
fi
|
|
if port_in_use; then
|
|
echo "[ERROR] Host port ${HOST_PORT} is still in use after stopping containers. Please free the port manually or change STAGING_MYSQL_PORT." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
echo "[INFO] Starting fresh MySQL container ${CONTAINER_NAME}"
|
|
docker run -d \
|
|
--name "$CONTAINER_NAME" \
|
|
-p "${HOST_PORT}:3306" \
|
|
-e MYSQL_ROOT_PASSWORD="$ROOT_PASSWORD" \
|
|
-e MYSQL_DATABASE="$DB_NAME" \
|
|
-v "$VOLUME_NAME:/var/lib/mysql" \
|
|
"$MYSQL_IMAGE" \
|
|
--lower_case_table_names=1 \
|
|
--explicit_defaults_for_timestamp=1 \
|
|
--character-set-server=utf8mb4 \
|
|
--collation-server=utf8mb4_unicode_ci >/dev/null
|
|
|
|
echo -n "[INFO] Waiting for MySQL to accept connections"
|
|
for attempt in {1..60}; do
|
|
if docker exec "$CONTAINER_NAME" mysqladmin ping -h127.0.0.1 -uroot -p"$ROOT_PASSWORD" --silent >/dev/null 2>&1; then
|
|
echo " - ready"
|
|
break
|
|
fi
|
|
sleep 2
|
|
echo -n "."
|
|
if [[ $attempt -eq 60 ]]; then
|
|
echo "\n[ERROR] MySQL did not become ready in time" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
case "$backup_path" in
|
|
*.gz)
|
|
reader=(gzip -dc -- "$backup_path")
|
|
;;
|
|
*.xz)
|
|
reader=(xz -dc -- "$backup_path")
|
|
;;
|
|
*.zip)
|
|
reader=(unzip -p -- "$backup_path")
|
|
;;
|
|
*)
|
|
reader=(cat -- "$backup_path")
|
|
;;
|
|
esac
|
|
|
|
echo "[INFO] Importing backup $backup_path into ${DB_NAME}"
|
|
"${reader[@]}" | docker exec -i "$CONTAINER_NAME" mysql -uroot -p"$ROOT_PASSWORD" --default-character-set=utf8mb4 "$DB_NAME"
|
|
|
|
echo "[INFO] Staging database restored. Container ${CONTAINER_NAME} is listening on port ${HOST_PORT}."
|