2
0

The common mysql_restoredb script now works with dockerized engine as well.

This commit is contained in:
2025-03-05 19:29:27 +01:00
parent 39c0f07fca
commit de8bfe5137
2 changed files with 133 additions and 64 deletions

BIN
.metadata

Binary file not shown.

View File

@ -21,9 +21,13 @@
# [-C container] [-d database] [-f dumpfile ] # [-C container] [-d database] [-f dumpfile ]
# [database (if not in -d)] [dumpfile (if not in -f)] # [database (if not in -d)] [dumpfile (if not in -f)]
# #
# Author: Kovács Zoltán <kovacs.zoltan@smartfront.hu> # Author: Kovács Zoltán <kovacsz@marcusconsulting.hu>
# 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) # 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 # 2023-06-18 v1.0
# new: forked from the "SMARTERP_skeleton" repository. # new: forked from the "SMARTERP_skeleton" repository.
# 2022-04-07 v0.4 # 2022-04-07 v0.4
@ -46,14 +50,10 @@ MYDBAUSER=${MYDBAUSER:-""} # Database admin superuser
MYDBAPASSWORD=${MYDBAPASSWORD:-""} # Credential for the DBA user MYDBAPASSWORD=${MYDBAPASSWORD:-""} # Credential for the DBA user
MYDUMP=${MYDUMP-""} # Dump file pathname MYDUMP=${MYDUMP-""} # Dump file pathname
MYHOST=${MYHOST:-"localhost"} # Connection parameter MYHOST=${MYHOST:-"localhost"} # Connection parameter
MYOPTIONS=${MYOPTIONS-""} # Options to pass to pg_dump
MYPASSWORD=${MYPASSWORD-""} # Credential for the DB owner MYPASSWORD=${MYPASSWORD-""} # Credential for the DB owner
MYPORT=${MYPORT:-"3306"} # Connection parameter MYPORT=${MYPORT:-"3306"} # Connection parameter
MYUSER=${MYUSER:-"root"} # Owner of the restored DB MYUSER=${MYUSER:-"root"} # Owner of the restored DB
### Temporailly ignored! Need to sanitize.
MYOPTIONS=""
# Basic environment settings. # Basic environment settings.
# #
LANG=C LANG=C
@ -133,14 +133,21 @@ do
;; ;;
esac esac
done; shift $((OPTIND -1)) done; shift $((OPTIND -1))
#
# All options have been processed. # All options have been processed.
# Checks the dependencies. # Checks the dependencies.
# #
# Conditional dependencies (according to native or dockerized environment). # Conditional dependencies (according to native or dockerized environment).
[[ -z "$MYCONTAINER" ]] \ if [ -n "$MYCONTAINER" ]; then
&& additem="mysql" \ # Dockerized
|| additem="docker" additem="docker"
else
# Native - MySQL or MariaDB CLI?
if [ -n "$(which mysql)" ]
then additem="mysql"
else additem="mariadb"; fi
fi
# Common dependencies. # Common dependencies.
TR=$(which tr 2>/dev/null) TR=$(which tr 2>/dev/null)
if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi 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) then export $(echo $item | "$TR" '[:lower:]' '[:upper:]')=$(which $item)
else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi
done 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. # All dependencies are available via "$THECOMMAND" (upper case) call.
# Sanitizing the parameters. # Sanitizing the parameters.
@ -184,6 +197,7 @@ done
# #
[[ -n "$MYUSER" ]] && [[ ! "$MYUSER" =~ ^([[:alnum:]]|[.-_\\+])*$ ]] \ [[ -n "$MYUSER" ]] && [[ ! "$MYUSER" =~ ^([[:alnum:]]|[.-_\\+])*$ ]] \
&& echo -e "$MSG_BADPARAM $MYUSER\n$MSG_USAGE" >&2 && exit 1 && echo -e "$MSG_BADPARAM $MYUSER\n$MSG_USAGE" >&2 && exit 1
#
# We've at least a minimally checked parameters. # We've at least a minimally checked parameters.
# Need to be root or a Docker manager user if the DB runs in a container. # 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 do
[[ "$MYDATABASE" = "$veto" ]] && exit 0 [[ "$MYDATABASE" = "$veto" ]] && exit 0
done done
#
# We've a database name to restore. # We've a database name to restore.
# Determines the dumpfile. # Determines the dumpfile.
@ -234,6 +249,7 @@ fi
# Let's check it! # Let's check it!
if [ ! -r "$MYDUMPFILE" -o ! -f "$MYDUMPFILE" ] if [ ! -r "$MYDUMPFILE" -o ! -f "$MYDUMPFILE" ]
then echo -e "$MSG_BADDUMP $MYDUMPFILE"; exit 1; fi then echo -e "$MSG_BADDUMP $MYDUMPFILE"; exit 1; fi
#
# We've an existing dumpfile. # We've an existing dumpfile.
# Tries to get the locale settings (actually CHARACTER SET) of this dump. # 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). # Trims the character set's name itself (the first word after the equal sign).
[[ -n "$MYCHARSET" ]] && MYCHARSET=$(echo -e "$MYCHARSET" | "$SED" 's/^.*= \(.*\) .*$/\1/') #' [[ -n "$MYCHARSET" ]] && MYCHARSET=$(echo -e "$MYCHARSET" | "$SED" 's/^.*= \(.*\) .*$/\1/') #'
fi fi
#
# We've a raw guess about the character sets used. # We've a raw guess about the character sets used.
# Finds the LOGFILE to use. # Finds the LOGFILE to use.
@ -262,6 +279,7 @@ fi
&& LOGFILE="${MYDUMPFILE%.gz}" \ && LOGFILE="${MYDUMPFILE%.gz}" \
&& LOGFILE="${LOGFILE%.*}.log" \ && LOGFILE="${LOGFILE%.*}.log" \
|| LOGFILE="/dev/null" || LOGFILE="/dev/null"
#
# We've a suitable logfile. # We've a suitable logfile.
# Opens the log and takes care to close it when finish. # Opens the log and takes care to close it when finish.
@ -274,35 +292,64 @@ function close_log() {
"$TEE" -a "$LOGFILE" "$TEE" -a "$LOGFILE"
} }
trap -- 'close_log' EXIT trap -- 'close_log' EXIT
#
# We started logging. # 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 # This skeleton makes the SQL calls independent to the environment
# (native or dockerized) and credentials. We need only actualize the # (native or dockerized) and credentials. We need only actualize the
# CONNECT, DATABASE and SQLVERB clauses then eval $DO_SQLVERB. # CONNECT, DATABASE and SQLVERB clauses then eval $DO_SQLSTREAM or
# Warning: the parameters must had been sanitized! # $DO_SQLVERB. Warning: the parameters must be sanitized!
DO_SQLVERB="" #
DO_SQLVERB+="export MYSQL_PWD; " if [ -n "$MYCONTAINER" ]; then
DO_SQLVERB+="\"\$MYSQL\" \$CONNECT -N \$DATABASE " # 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_SQLVERB+="-e \"\$SQLVERB\""
# We've a suitable SQL macro. 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;" SQLVERB="SELECT 1;"
result=""
# Sets the default DBA username for dockerized and native RDBMS as well. # Sets the default DBA username for dockerized and native RDBMS as well.
if [ -z "$MYDBAUSER" ]; then if [ -z "$MYDBAUSER" ]; then
[[ -n "$MYCONTAINER" ]] \ [[ -n "$MYCONTAINER" ]] \
&& MYDBAUSER="root" \ && MYDBAUSER="root" \
|| MYDBAUSER="$USER" || MYDBAUSER="root"
fi fi
# # In a native environment we'll try the local connection
# We'll try the local connection (Unix-domain socket) first. # (Unix-domain socket) first.
if [ -z "$MYCONTAINER" ]; then
CONNECT="" CONNECT=""
DATABASE="" DATABASE=""
#result=$(eval "$DO_SQLVERB" 2>/dev/null); excode=$? result=$(eval "$DO_SQLVERB" 2>/dev/null); excode=$?
result="${result//[[:space:]]/}" result="${result//[[:space:]]/}"
fi
if [ "$result" != "1" ]; then if [ "$result" != "1" ]; then
# #
# On failure we'll try the TCP connection. # On failure we'll try the TCP connection.
@ -327,10 +374,12 @@ if [ "$result" != "1" ]; then
fi fi
fi fi
fi fi
#
# We've valid MYSQL_PWD and CONNECT clauses. # We've valid MYSQL_PWD and CONNECT clauses.
# Checks the superuser privilege. # Checks the superuser privilege.
# Better check: TODO! # Better check: TODO!
#
ISDBA=false ISDBA=false
DATABASE="" DATABASE=""
SQLVERB="SHOW GRANTS;" SQLVERB="SHOW GRANTS;"
@ -343,10 +392,12 @@ else
echo -e "$MSG_NONSUPER" | "$TEE" -a "$LOGFILE" >&2 echo -e "$MSG_NONSUPER" | "$TEE" -a "$LOGFILE" >&2
echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2 echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2
fi fi
#
# We know we're a DB superuser or not. # We know we're a DB superuser or not.
# Following steps need the superuser privileges. # Following steps need the superuser privileges.
# Lack of this we'll skip them. # Lack of this we'll skip them.
#
if $ISDBA; then if $ISDBA; then
DATABASE="mysql" DATABASE="mysql"
@ -379,32 +430,21 @@ if $ISDBA; then
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1 && exit 1
fi fi
#
# RDBMS version is proper. # RDBMS version is proper.
# Creates the database user (owner) if it doesn't exist. # Database user revision.
# #
echo -e "CREATE USER" | "$TEE" -a "$LOGFILE" # If '$MYUSER'@'$MYHOST' exists, it will provide the necessaty privileges.
SQLVERB=" CREATE USER '$MYUSER'@'$MYHOST'; " # 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=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
result="${result//[[:space:]]/}" result="${result//[[:space:]]/}"
if [[ $excode -ne 0 ]]; then if [[ $excode -eq 0 && $result -eq 1 ]]; then
# Already exists (or something went wrong). # It exists, let's give it privileges.
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'; "
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_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1
fi
#
# Grants all privileges on the database to the user.
#
echo -e "GRANT" | "$TEE" -a "$LOGFILE" echo -e "GRANT" | "$TEE" -a "$LOGFILE"
SQLVERB="GRANT ALL PRIVILEGES ON $MYDATABASE.* TO '$MYUSER'@'$MYHOST'; " SQLVERB="GRANT ALL PRIVILEGES ON $MYDATABASE.* TO '$MYUSER'@'$MYHOST'; "
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
@ -413,7 +453,34 @@ if $ISDBA; then
&& echo -e "$MSG_FAILGRANT $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_FAILGRANT $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 \
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1 && exit 1
# We've the database user with the proper password. fi
# 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@%" | "$TEE" -a "$LOGFILE" >&2 \
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1
#
# We've the database user(s) with the proper grants.
# Drops all existing connections to the database being restored. # Drops all existing connections to the database being restored.
# #
@ -438,8 +505,10 @@ if $ISDBA; then
fi fi
done done
fi fi
#
# Connections have eliminated (we hope). # Connections have eliminated (we hope).
fi fi
#
# Done with the superuser part. # Done with the superuser part.
# Drops the database. # Drops the database.
@ -452,7 +521,7 @@ result="${result//[[:space:]]/}"
[[ $excode -ne 0 ]] \ [[ $excode -ne 0 ]] \
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1 && exit 1
#
# Recreates the database. # Recreates the database.
# #
echo -e "CREATE DATABASE" | "$TEE" -a "$LOGFILE" echo -e "CREATE DATABASE" | "$TEE" -a "$LOGFILE"
@ -463,6 +532,7 @@ result="${result//[[:space:]]/}"
[[ $excode -ne 0 ]] \ [[ $excode -ne 0 ]] \
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1 && exit 1
#
# We've an empty database. # We've an empty database.
# Sets the default character set. # Sets the default character set.
@ -485,13 +555,15 @@ if [ -n "$MYCHARSET" ]; then
&& exit 1 && exit 1
fi fi
fi fi
#
# We've the character set adjusted. # We've the character set adjusted.
# Restores the database from the dump. # 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 # We'll use the database user's credentials, not the superuser's
# to mitigate the effect of an unsanitized dump. # to mitigate the effect of an unsanitized dump.
#
echo -e "RESTORE" | "$TEE" -a "$LOGFILE" echo -e "RESTORE" | "$TEE" -a "$LOGFILE"
# Let's identify the file is gzipped or not. # Let's identify the file is gzipped or not.
UNPACKER=$("$FILE" --mime-type "$MYDUMPFILE") UNPACKER=$("$FILE" --mime-type "$MYDUMPFILE")
@ -501,24 +573,21 @@ UNPACKER=${UNPACKER##* } # The last word is the MIME-type.
&& UNPACKER="$GUNZIP" \ && UNPACKER="$GUNZIP" \
|| UNPACKER="$CAT" || UNPACKER="$CAT"
# This is a sed expression to modify the security definers within the dump. # This is a sed expression to modify the security definers within the dump.
MOD_DEFINER="s/DEFINER=.*@[^ ]*/DEFINER=\`$MYUSER\`@\`$MYHOST\`/" MOD_DEFINER="s/DEFINER=.*@[^ ]*/DEFINER=CURRENT_USER/"
# Considers the RDBMS environment. #
if [ -n "$MYCONTAINER" ]; then # We'll read the dump, on the fly unpack it and modify the security definer,
# Dockerized RDBMS. # then we'll pass the data stream to the MySQL client.
echo "MySQL dockerized - TODO!" | "$TEE" -a "$LOGFILE" >&2 #
else DATABASE="$MYDATABASE"
# Native RDBMS. SQLVERB=""
# Reads the dump, on the fly unpacks it and modifies the scurity definer, (eval "$DO_SQLSTREAM") \
# then passes the data stream to the MySQL client. < <("$CAT" "$MYDUMPFILE" | "$UNPACKER" | "$SED" "$MOD_DEFINER") \
"$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=$? >/dev/null 2> >("$TEE" -a "$LOGFILE" >&2); excode=$?
# Unfortunately the result code doesn't differentiate the # Unfortunately the result code doesn't differentiate the
# blocking and non-blocking states. # blocking and non-blocking states.
[[ $excode -ne 0 ]] \ [[ $excode -ne 0 ]] \
&& echo -e "$MSG_NONZERO: $excode" | "$TEE" -a "$LOGFILE" >&2 && echo -e "$MSG_NONZERO: $excode" | "$TEE" -a "$LOGFILE" >&2
fi #
# We had a try to restore the database - the result isn't properly defined. # We had a try to restore the database - the result isn't properly defined.
# Closing log entry will be handled via EXIT trap. # Closing log entry will be handled via EXIT trap.