65 lines
3.4 KiB
Bash
65 lines
3.4 KiB
Bash
#!/usr/bin/env bash
|
|
# Root-only installer preparation. No production SQL writes or credentials in Docker.
|
|
set -Eeuo pipefail
|
|
LIB_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
# shellcheck source=lib/common.sh
|
|
source "$LIB_DIR/common.sh"
|
|
CONFIG_FILE=${1:?Configuration required}
|
|
load_config
|
|
[[ $ENABLE_RESTORE_TEST == true ]] || exit 0
|
|
guard_workspaces
|
|
# shellcheck source=lib/locking.sh
|
|
source "$LIB_DIR/locking.sh"
|
|
acquire_backup_lock 1
|
|
export APP_BACKUP_INHERIT_LOCK=1
|
|
build_plan
|
|
(( ${#DB_NAMES[@]} )) || exit 0
|
|
need docker; need timeout
|
|
docker_cmd=(docker --host=unix:///var/run/docker.sock)
|
|
if ! "${docker_cmd[@]}" info --format '{{.DockerRootDir}}' >/dev/null 2>&1; then
|
|
systemctl start docker.service
|
|
fi
|
|
docker_root=$("${docker_cmd[@]}" info --format '{{.DockerRootDir}}')
|
|
[[ $docker_root == /* ]] || die 'Cannot determine local Docker storage'
|
|
version=''
|
|
for i in "${!DB_NAMES[@]}"; do
|
|
secure_file "${DB_CNFS[i]}"
|
|
server=$("$DB_CLIENT_BIN" --defaults-extra-file="${DB_CNFS[i]}" --database="${DB_NAMES[i]}" --batch --skip-column-names --execute='SELECT VERSION()')
|
|
[[ $server =~ ^([0-9]+\.[0-9]+)\.[0-9]+.*MariaDB ]] || die 'Automatic restore-test preparation requires MariaDB; no production server changes made'
|
|
found=${BASH_REMATCH[1]}
|
|
[[ -z $version || $version == "$found" ]] || die 'Different production MariaDB series require separate restore-test configurations'
|
|
version=$found
|
|
done
|
|
image=${RESTORE_TEST_DB_IMAGE:-mariadb:$version}
|
|
[[ $image =~ ^[a-zA-Z0-9][a-zA-Z0-9._/:@-]*$ ]] || die 'Invalid restore-test image reference'
|
|
if ! "${docker_cmd[@]}" image inspect "$image" >/dev/null 2>&1; then
|
|
# Image layers are stored on Docker's filesystem, not necessarily WORKDIR.
|
|
[[ -d $docker_root ]] || die 'Docker storage is not accessible for the space check'
|
|
# shellcheck source=lib/space.sh
|
|
source "$LIB_DIR/space.sh"
|
|
ensure_space "$docker_root" 2147483648
|
|
echo "-- Preparing isolated MariaDB image: $image"
|
|
timeout --kill-after=30s 1800 "${docker_cmd[@]}" pull "$image"
|
|
fi
|
|
# Pin the probe to an immutable local image ID; never pull during monthly jobs.
|
|
image_id=$("${docker_cmd[@]}" image inspect --format '{{.Id}}' "$image")
|
|
"${docker_cmd[@]}" image inspect "$image_id" | python3 -c 'import json,sys; info=json.load(sys.stdin)[0]; assert not set(info.get("Config",{}).get("Volumes") or {}) - {"/var/lib/mysql"}, "Unexpected image volumes"'
|
|
probe=$(timeout --kill-after=10s 60 "${docker_cmd[@]}" run --rm --pull=never --network=none --read-only \
|
|
--cap-drop=ALL --security-opt=no-new-privileges --memory=128m --pids-limit=64 \
|
|
--entrypoint=mariadbd "$image_id" --no-defaults --version)
|
|
[[ $probe == *" $version."* && $probe == *MariaDB* ]] || die 'Restore-test image does not match production MariaDB series'
|
|
echo "-- MariaDB $version image verified; testing isolated initialization/import/cleanup"
|
|
# Use a private temporary config; publish the image selection only after success.
|
|
prepared=$(mktemp "${CONFIG_FILE}.prepare-XXXXXX")
|
|
trap 'rm -f -- "$prepared"' EXIT
|
|
cp -- "$CONFIG_FILE" "$prepared"
|
|
printf '\nRESTORE_TEST_DB_IMAGE=%q\n' "$image" >> "$prepared"
|
|
script="$LIB_DIR/../usr-local-sbin/app-restore-test.sh"
|
|
[[ -f $script ]] || script=/usr/local/sbin/app-restore-test.sh
|
|
bash "$script" --config "$prepared" --prepare-db
|
|
if [[ $RESTORE_TEST_DB_IMAGE != "$image" ]]; then
|
|
chmod 600 "$prepared"
|
|
mv -fT -- "$prepared" "$CONFIG_FILE"
|
|
fi
|
|
echo '-- MariaDB restore-test preparation passed; temporary database removed'
|