The common mysql_restoredb script now works with dockerized engine as well.
This commit is contained in:
@ -21,9 +21,13 @@
|
||||
# [-C container] [-d database] [-f dumpfile ]
|
||||
# [database (if not in -d)] [dumpfile (if not in -f)]
|
||||
#
|
||||
# Author: Kovács Zoltán <kovacs.zoltan@smartfront.hu>
|
||||
# Kovács Zoltán <kovacsz@marcusconsulting.hu>
|
||||
# Author: Kovács Zoltán <kovacsz@marcusconsulting.hu>
|
||||
# Kovács Zoltán <kovacs.zoltan@smartfront.hu>
|
||||
# License: GNU/GPL v3+ (https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
# 2025-03-04 v1.1
|
||||
# new: Works with dockerized databases but hasn't yet been tested with natives.
|
||||
# mod: Database user creation and grants rewritten. Now create user @'%'
|
||||
# (instead of @'myhost') if it doesn't already exist.
|
||||
# 2023-06-18 v1.0
|
||||
# new: forked from the "SMARTERP_skeleton" repository.
|
||||
# 2022-04-07 v0.4
|
||||
@ -46,14 +50,10 @@ MYDBAUSER=${MYDBAUSER:-""} # Database admin superuser
|
||||
MYDBAPASSWORD=${MYDBAPASSWORD:-""} # Credential for the DBA user
|
||||
MYDUMP=${MYDUMP-""} # Dump file pathname
|
||||
MYHOST=${MYHOST:-"localhost"} # Connection parameter
|
||||
MYOPTIONS=${MYOPTIONS-""} # Options to pass to pg_dump
|
||||
MYPASSWORD=${MYPASSWORD-""} # Credential for the DB owner
|
||||
MYPORT=${MYPORT:-"3306"} # Connection parameter
|
||||
MYUSER=${MYUSER:-"root"} # Owner of the restored DB
|
||||
|
||||
### Temporailly ignored! Need to sanitize.
|
||||
MYOPTIONS=""
|
||||
|
||||
# Basic environment settings.
|
||||
#
|
||||
LANG=C
|
||||
@ -133,14 +133,21 @@ do
|
||||
;;
|
||||
esac
|
||||
done; shift $((OPTIND -1))
|
||||
#
|
||||
# All options have been processed.
|
||||
|
||||
# Checks the dependencies.
|
||||
#
|
||||
# Conditional dependencies (according to native or dockerized environment).
|
||||
[[ -z "$MYCONTAINER" ]] \
|
||||
&& additem="mysql" \
|
||||
|| additem="docker"
|
||||
if [ -n "$MYCONTAINER" ]; then
|
||||
# Dockerized
|
||||
additem="docker"
|
||||
else
|
||||
# Native - MySQL or MariaDB CLI?
|
||||
if [ -n "$(which mysql)" ]
|
||||
then additem="mysql"
|
||||
else additem="mariadb"; fi
|
||||
fi
|
||||
# Common dependencies.
|
||||
TR=$(which tr 2>/dev/null)
|
||||
if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi
|
||||
@ -151,6 +158,12 @@ do
|
||||
then export $(echo $item | "$TR" '[:lower:]' '[:upper:]')=$(which $item)
|
||||
else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi
|
||||
done
|
||||
#
|
||||
# Unifies the call of the clients in a native environment.
|
||||
if [ -z "$MYCONTAINER" ]; then
|
||||
if [ -z "$MYSQL" -a -n "$MARIADB" ]; then MYSQL="$MARIADB"; fi
|
||||
fi
|
||||
#
|
||||
# All dependencies are available via "$THECOMMAND" (upper case) call.
|
||||
|
||||
# Sanitizing the parameters.
|
||||
@ -184,6 +197,7 @@ done
|
||||
#
|
||||
[[ -n "$MYUSER" ]] && [[ ! "$MYUSER" =~ ^([[:alnum:]]|[.-_\\+])*$ ]] \
|
||||
&& echo -e "$MSG_BADPARAM $MYUSER\n$MSG_USAGE" >&2 && exit 1
|
||||
#
|
||||
# We've at least a minimally checked parameters.
|
||||
|
||||
# Need to be root or a Docker manager user if the DB runs in a container.
|
||||
@ -213,6 +227,7 @@ for veto in $vetodatabases ""
|
||||
do
|
||||
[[ "$MYDATABASE" = "$veto" ]] && exit 0
|
||||
done
|
||||
#
|
||||
# We've a database name to restore.
|
||||
|
||||
# Determines the dumpfile.
|
||||
@ -234,6 +249,7 @@ fi
|
||||
# Let's check it!
|
||||
if [ ! -r "$MYDUMPFILE" -o ! -f "$MYDUMPFILE" ]
|
||||
then echo -e "$MSG_BADDUMP $MYDUMPFILE"; exit 1; fi
|
||||
#
|
||||
# We've an existing dumpfile.
|
||||
|
||||
# Tries to get the locale settings (actually CHARACTER SET) of this dump.
|
||||
@ -252,6 +268,7 @@ if [ -z "$MYCHARSET" ]; then
|
||||
# Trims the character set's name itself (the first word after the equal sign).
|
||||
[[ -n "$MYCHARSET" ]] && MYCHARSET=$(echo -e "$MYCHARSET" | "$SED" 's/^.*= \(.*\) .*$/\1/') #'
|
||||
fi
|
||||
#
|
||||
# We've a raw guess about the character sets used.
|
||||
|
||||
# Finds the LOGFILE to use.
|
||||
@ -262,6 +279,7 @@ fi
|
||||
&& LOGFILE="${MYDUMPFILE%.gz}" \
|
||||
&& LOGFILE="${LOGFILE%.*}.log" \
|
||||
|| LOGFILE="/dev/null"
|
||||
#
|
||||
# We've a suitable logfile.
|
||||
|
||||
# Opens the log and takes care to close it when finish.
|
||||
@ -274,35 +292,64 @@ function close_log() {
|
||||
"$TEE" -a "$LOGFILE"
|
||||
}
|
||||
trap -- 'close_log' EXIT
|
||||
#
|
||||
# We started logging.
|
||||
|
||||
# Prepopulates the SQL command skeleton (macro).
|
||||
# Prepopulates two SQL command skeletons (macros).
|
||||
#
|
||||
# This skeleton makes the SQL calls independent to the environment
|
||||
# (native or dockerized) and credentials. We need only actualize the
|
||||
# CONNECT, DATABASE and SQLVERB clauses then eval $DO_SQLVERB.
|
||||
# Warning: the parameters must had been sanitized!
|
||||
DO_SQLVERB=""
|
||||
DO_SQLVERB+="export MYSQL_PWD; "
|
||||
DO_SQLVERB+="\"\$MYSQL\" \$CONNECT -N \$DATABASE "
|
||||
DO_SQLVERB+="-e \"\$SQLVERB\""
|
||||
# We've a suitable SQL macro.
|
||||
# CONNECT, DATABASE and SQLVERB clauses then eval $DO_SQLSTREAM or
|
||||
# $DO_SQLVERB. Warning: the parameters must be sanitized!
|
||||
#
|
||||
if [ -n "$MYCONTAINER" ]; then
|
||||
# When MySQL runs in the container.
|
||||
#
|
||||
if [ -n "$("$DOCKER" exec $MYCONTAINER which mysql)" ]; then
|
||||
DO_SQLVERB="\"\$DOCKER\" exec -e MYSQL_PWD=\"\$MYSQL_PWD\" \$MYCONTAINER mysql "
|
||||
# When MariaDB runs in the container.
|
||||
elif [ -n "$("$DOCKER" exec $MYCONTAINER which mariadb)" ]; then
|
||||
DO_SQLVERB="\"\$DOCKER\" exec -e MYSQL_PWD=\"\$MYSQL_PWD\" \$MYCONTAINER mariadb "
|
||||
DO_SQLSTREAM="\"\$DOCKER\" exec -i -e MYSQL_PWD=\"\$MYSQL_PWD\" \$MYCONTAINER /bin/bash -c \"mariadb "
|
||||
# Otherwise gives it up here.
|
||||
else
|
||||
echo -e "$MSG_BADDBTYPE in $MYCONTAINER." | "$TEE" -a "$LOGFILE" >&2
|
||||
echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Common parameters.
|
||||
DO_SQLVERB+="\$CONNECT -sN --ssl-verify-server-cert=false \$DATABASE "
|
||||
DO_SQLVERB+="-e \"\$SQLVERB\""
|
||||
DO_SQLSTREAM+="\$CONNECT -sN --ssl-verify-server-cert=false \$DATABASE \""
|
||||
else
|
||||
# Native environment.
|
||||
#
|
||||
DO_SQLVERB="export \"MYSQL_PWD\"; \"\$MYSQL\" "
|
||||
DO_SQLVERB+="\$CONNECT -sN --ssl-verify-server-cert=false \$DATABASE "
|
||||
DO_SQLVERB+="-e \"\$SQLVERB\""
|
||||
SO_SQLSTREAM="$DO_SQLVERB"
|
||||
fi
|
||||
#
|
||||
# We've two suitable SQL macros.
|
||||
|
||||
# Do we connect the database as a DBA?
|
||||
# Are we able to connect to the database, preferably as a DBA?
|
||||
#
|
||||
SQLVERB="SELECT 1;"
|
||||
result=""
|
||||
# Sets the default DBA username for dockerized and native RDBMS as well.
|
||||
if [ -z "$MYDBAUSER" ]; then
|
||||
[[ -n "$MYCONTAINER" ]] \
|
||||
&& MYDBAUSER="root" \
|
||||
|| MYDBAUSER="$USER"
|
||||
|| MYDBAUSER="root"
|
||||
fi
|
||||
# In a native environment we'll try the local connection
|
||||
# (Unix-domain socket) first.
|
||||
if [ -z "$MYCONTAINER" ]; then
|
||||
CONNECT=""
|
||||
DATABASE=""
|
||||
result=$(eval "$DO_SQLVERB" 2>/dev/null); excode=$?
|
||||
result="${result//[[:space:]]/}"
|
||||
fi
|
||||
#
|
||||
# We'll try the local connection (Unix-domain socket) first.
|
||||
CONNECT=""
|
||||
DATABASE=""
|
||||
#result=$(eval "$DO_SQLVERB" 2>/dev/null); excode=$?
|
||||
result="${result//[[:space:]]/}"
|
||||
if [ "$result" != "1" ]; then
|
||||
#
|
||||
# On failure we'll try the TCP connection.
|
||||
@ -327,10 +374,12 @@ if [ "$result" != "1" ]; then
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
#
|
||||
# We've valid MYSQL_PWD and CONNECT clauses.
|
||||
|
||||
# Checks the superuser privilege.
|
||||
# Better check: TODO!
|
||||
#
|
||||
ISDBA=false
|
||||
DATABASE=""
|
||||
SQLVERB="SHOW GRANTS;"
|
||||
@ -343,10 +392,12 @@ else
|
||||
echo -e "$MSG_NONSUPER" | "$TEE" -a "$LOGFILE" >&2
|
||||
echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2
|
||||
fi
|
||||
#
|
||||
# We know we're a DB superuser or not.
|
||||
|
||||
# Following steps need the superuser privileges.
|
||||
# Lack of this we'll skip them.
|
||||
#
|
||||
if $ISDBA; then
|
||||
DATABASE="mysql"
|
||||
|
||||
@ -379,41 +430,57 @@ if $ISDBA; then
|
||||
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& exit 1
|
||||
fi
|
||||
#
|
||||
# RDBMS version is proper.
|
||||
|
||||
# Creates the database user (owner) if it doesn't exist.
|
||||
# Database user revision.
|
||||
#
|
||||
echo -e "CREATE USER" | "$TEE" -a "$LOGFILE"
|
||||
SQLVERB=" CREATE USER '$MYUSER'@'$MYHOST'; "
|
||||
# If '$MYUSER'@'$MYHOST' exists, it will provide the necessaty privileges.
|
||||
# If '$MYUSER'@'%' doesn't exist, it will create it then provide the
|
||||
# necessary privileges.
|
||||
#
|
||||
# Checks '$MYUSER'@'$MYHOST'
|
||||
SQLVERB="SELECT COUNT(1) FROM mysql.user WHERE user = '$MYUSER' AND host = '$MYHOST'; "
|
||||
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
|
||||
result="${result//[[:space:]]/}"
|
||||
if [[ $excode -ne 0 ]]; then
|
||||
# Already exists (or something went wrong).
|
||||
echo -e "$MSG_FAILUSER $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2
|
||||
echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2
|
||||
else
|
||||
# Sets the password only if the user has just created.
|
||||
echo -e "SET PASSWORD" | "$TEE" -a "$LOGFILE"
|
||||
SQLVERB="SET PASSWORD FOR '$MYUSER'@'$MYHOST' = '$MYPASSWORD'; "
|
||||
if [[ $excode -eq 0 && $result -eq 1 ]]; then
|
||||
# It exists, let's give it privileges.
|
||||
echo -e "GRANT" | "$TEE" -a "$LOGFILE"
|
||||
SQLVERB="GRANT ALL PRIVILEGES ON $MYDATABASE.* TO '$MYUSER'@'$MYHOST'; "
|
||||
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
|
||||
result="${result//[[:space:]]/}"
|
||||
[[ $excode -ne 0 ]] \
|
||||
&& echo -e "$MSG_FAILPASS $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& echo -e "$MSG_FAILGRANT $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& exit 1
|
||||
fi
|
||||
#
|
||||
# Grants all privileges on the database to the user.
|
||||
#
|
||||
echo -e "GRANT" | "$TEE" -a "$LOGFILE"
|
||||
SQLVERB="GRANT ALL PRIVILEGES ON $MYDATABASE.* TO '$MYUSER'@'$MYHOST'; "
|
||||
# Checks '$MYUSER'@'%' as well.
|
||||
SQLVERB="SELECT COUNT(1) FROM mysql.user WHERE user = '$MYUSER' AND host = '%'; "
|
||||
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
|
||||
result="${result//[[:space:]]/}"
|
||||
if [[ $excode -eq 0 && $result -ne 1 ]]; then
|
||||
# Creates if it doesn't exist yet.
|
||||
echo -e "CREATE USER %" | "$TEE" -a "$LOGFILE"
|
||||
SQLVERB="CREATE USER '$MYUSER'@'%' IDENTIFIED BY '$MYPASSWORD'; "
|
||||
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
|
||||
result="${result//[[:space:]]/}"
|
||||
# Gives it up here if something went wrong.
|
||||
[[ $excode -ne 0 ]] \
|
||||
&& echo -e "$MSG_FAILUSER $MYUSER@%" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& exit 1
|
||||
fi
|
||||
# Let's give it privileges.
|
||||
echo -e "GRANT %" | "$TEE" -a "$LOGFILE"
|
||||
SQLVERB="GRANT ALL PRIVILEGES ON $MYDATABASE.* TO '$MYUSER'@'%'; "
|
||||
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
|
||||
result="${result//[[:space:]]/}"
|
||||
[[ $excode -ne 0 ]] \
|
||||
&& echo -e "$MSG_FAILGRANT $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& echo -e "$MSG_FAILGRANT $MYUSER@%" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& exit 1
|
||||
# We've the database user with the proper password.
|
||||
#
|
||||
# We've the database user(s) with the proper grants.
|
||||
|
||||
# Drops all existing connections to the database being restored.
|
||||
#
|
||||
@ -438,8 +505,10 @@ if $ISDBA; then
|
||||
fi
|
||||
done
|
||||
fi
|
||||
#
|
||||
# Connections have eliminated (we hope).
|
||||
fi
|
||||
#
|
||||
# Done with the superuser part.
|
||||
|
||||
# Drops the database.
|
||||
@ -452,7 +521,7 @@ result="${result//[[:space:]]/}"
|
||||
[[ $excode -ne 0 ]] \
|
||||
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& exit 1
|
||||
|
||||
#
|
||||
# Recreates the database.
|
||||
#
|
||||
echo -e "CREATE DATABASE" | "$TEE" -a "$LOGFILE"
|
||||
@ -463,6 +532,7 @@ result="${result//[[:space:]]/}"
|
||||
[[ $excode -ne 0 ]] \
|
||||
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
|
||||
&& exit 1
|
||||
#
|
||||
# We've an empty database.
|
||||
|
||||
# Sets the default character set.
|
||||
@ -485,13 +555,15 @@ if [ -n "$MYCHARSET" ]; then
|
||||
&& exit 1
|
||||
fi
|
||||
fi
|
||||
#
|
||||
# We've the character set adjusted.
|
||||
|
||||
# Restores the database from the dump.
|
||||
#
|
||||
# This isn't so straightforward as in PostgreSQL.
|
||||
# This isn't so straightforward as in e.g PostgreSQL.
|
||||
# We'll use the database user's credentials, not the superuser's
|
||||
# to mitigate the effect of an unsanitized dump.
|
||||
#
|
||||
echo -e "RESTORE" | "$TEE" -a "$LOGFILE"
|
||||
# Let's identify the file is gzipped or not.
|
||||
UNPACKER=$("$FILE" --mime-type "$MYDUMPFILE")
|
||||
@ -501,24 +573,21 @@ UNPACKER=${UNPACKER##* } # The last word is the MIME-type.
|
||||
&& UNPACKER="$GUNZIP" \
|
||||
|| UNPACKER="$CAT"
|
||||
# This is a sed expression to modify the security definers within the dump.
|
||||
MOD_DEFINER="s/DEFINER=.*@[^ ]*/DEFINER=\`$MYUSER\`@\`$MYHOST\`/"
|
||||
# Considers the RDBMS environment.
|
||||
if [ -n "$MYCONTAINER" ]; then
|
||||
# Dockerized RDBMS.
|
||||
echo "MySQL dockerized - TODO!" | "$TEE" -a "$LOGFILE" >&2
|
||||
else
|
||||
# Native RDBMS.
|
||||
# Reads the dump, on the fly unpacks it and modifies the scurity definer,
|
||||
# then passes the data stream to the MySQL client.
|
||||
"$CAT" "$MYDUMPFILE" | "$UNPACKER" | "$SED" "$MOD_DEFINER" | \
|
||||
"$MYSQL" -u "$MYUSER" -p$MYPASSWORD -h "$MYHOST" -P "$MYPORT" \
|
||||
-f -D "$MYDATABASE" \
|
||||
>/dev/null 2> >("$TEE" -a "$LOGFILE" >&2); excode=$?
|
||||
# Unfortunately the result code doesn't differentiate the
|
||||
# blocking and non-blocking states.
|
||||
[[ $excode -ne 0 ]] \
|
||||
&& echo -e "$MSG_NONZERO: $excode" | "$TEE" -a "$LOGFILE" >&2
|
||||
fi
|
||||
MOD_DEFINER="s/DEFINER=.*@[^ ]*/DEFINER=CURRENT_USER/"
|
||||
#
|
||||
# We'll read the dump, on the fly unpack it and modify the security definer,
|
||||
# then we'll pass the data stream to the MySQL client.
|
||||
#
|
||||
DATABASE="$MYDATABASE"
|
||||
SQLVERB=""
|
||||
(eval "$DO_SQLSTREAM") \
|
||||
< <("$CAT" "$MYDUMPFILE" | "$UNPACKER" | "$SED" "$MOD_DEFINER") \
|
||||
>/dev/null 2> >("$TEE" -a "$LOGFILE" >&2); excode=$?
|
||||
# Unfortunately the result code doesn't differentiate the
|
||||
# blocking and non-blocking states.
|
||||
[[ $excode -ne 0 ]] \
|
||||
&& echo -e "$MSG_NONZERO: $excode" | "$TEE" -a "$LOGFILE" >&2
|
||||
#
|
||||
# We had a try to restore the database - the result isn't properly defined.
|
||||
|
||||
# Closing log entry will be handled via EXIT trap.
|
||||
|
Reference in New Issue
Block a user