From f16150ea70bdae87b9fcc64e01441086749be244 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Mon, 28 May 2018 09:06:19 +0200 Subject: [PATCH 01/42] Unify entrypoint scripts for 5.7 and 8.0 There were only two differences between the scripts: * 8.0 had a fix for datadir settings with spaces in them * 8.0 used alter user to set the root password, which 5.7 should also use --- 5.7/docker-entrypoint.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/5.7/docker-entrypoint.sh b/5.7/docker-entrypoint.sh index b38c11287..720ef6d65 100755 --- a/5.7/docker-entrypoint.sh +++ b/5.7/docker-entrypoint.sh @@ -77,7 +77,9 @@ _check_config() { # latter only show values present in config files, and not server defaults _get_config() { local conf="$1"; shift - "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null | awk '$1 == "'"$conf"'" { print $2; exit }' + "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \ + | awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' + # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" } # allow the container to be started with `--user` @@ -161,7 +163,7 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ; + ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; ${rootCreate} DROP DATABASE IF EXISTS test ; From 5a727ad690eb683549ab1165e5970d90d9619ba2 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Mon, 28 May 2018 09:24:16 +0200 Subject: [PATCH 02/42] Make template for 5.7+ entrypoint script 5.7 and 8.0 can use the exact same entrypoint script, so simply have the update script copy it from a template location. --- template.Debian/docker-entrypoint.sh | 216 +++++++++++++++++++++++++++ update.sh | 7 + 2 files changed, 223 insertions(+) create mode 100755 template.Debian/docker-entrypoint.sh diff --git a/template.Debian/docker-entrypoint.sh b/template.Debian/docker-entrypoint.sh new file mode 100755 index 000000000..5ed7cd25c --- /dev/null +++ b/template.Debian/docker-entrypoint.sh @@ -0,0 +1,216 @@ +#!/bin/bash +set -eo pipefail +shopt -s nullglob + +# if command starts with an option, prepend mysqld +if [ "${1:0:1}" = '-' ]; then + set -- mysqld "$@" +fi + +# skip setup if they want an option that stops mysqld +wantHelp= +for arg; do + case "$arg" in + -'?'|--help|--print-defaults|-V|--version) + wantHelp=1 + break + ;; + esac +done + +# usage: file_env VAR [DEFAULT] +# ie: file_env 'XYZ_DB_PASSWORD' 'example' +# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of +# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + local val="$def" + if [ "${!var:-}" ]; then + val="${!var}" + elif [ "${!fileVar:-}" ]; then + val="$(< "${!fileVar}")" + fi + export "$var"="$val" + unset "$fileVar" +} + +# usage: process_init_file FILENAME MYSQLCOMMAND... +# ie: process_init_file foo.sh mysql -uroot +# (process a single initializer file, based on its extension. we define this +# function here, so that initializer scripts (*.sh) can use the same logic, +# potentially recursively, or override the logic used in subsequent calls) +process_init_file() { + local f="$1"; shift + local mysql=( "$@" ) + + case "$f" in + *.sh) echo "$0: running $f"; . "$f" ;; + *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; + *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; + *) echo "$0: ignoring $f" ;; + esac + echo +} + +_check_config() { + toRun=( "$@" --verbose --help ) + if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then + cat >&2 <<-EOM + + ERROR: mysqld failed while attempting to check config + command was: "${toRun[*]}" + + $errors + EOM + exit 1 + fi +} + +# Fetch value from server config +# We use mysqld --verbose --help instead of my_print_defaults because the +# latter only show values present in config files, and not server defaults +_get_config() { + local conf="$1"; shift + "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \ + | awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' + # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" +} + +# allow the container to be started with `--user` +if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then + _check_config "$@" + DATADIR="$(_get_config 'datadir' "$@")" + mkdir -p "$DATADIR" + chown -R mysql:mysql "$DATADIR" + exec gosu mysql "$BASH_SOURCE" "$@" +fi + +if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then + # still need to check config, container may have started with --user + _check_config "$@" + # Get config + DATADIR="$(_get_config 'datadir' "$@")" + + if [ ! -d "$DATADIR/mysql" ]; then + file_env 'MYSQL_ROOT_PASSWORD' + if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + echo >&2 'error: database is uninitialized and password option is not specified ' + echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' + exit 1 + fi + + mkdir -p "$DATADIR" + + echo 'Initializing database' + "$@" --initialize-insecure + echo 'Database initialized' + + if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then + # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 + echo 'Initializing certificates' + mysql_ssl_rsa_setup --datadir="$DATADIR" + echo 'Certificates initialized' + fi + + SOCKET="$(_get_config 'socket' "$@")" + "$@" --skip-networking --socket="${SOCKET}" & + pid="$!" + + mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) + + for i in {30..0}; do + if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then + break + fi + echo 'MySQL init process in progress...' + sleep 1 + done + if [ "$i" = 0 ]; then + echo >&2 'MySQL init process failed.' + exit 1 + fi + + if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then + # sed is for https://bugs.mysql.com/bug.php?id=20545 + mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql + fi + + if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" + echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" + fi + + rootCreate= + # default root to listen for connections from anywhere + file_env 'MYSQL_ROOT_HOST' '%' + if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then + # no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + read -r -d '' rootCreate <<-EOSQL || true + CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; + GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ; + EOSQL + fi + + "${mysql[@]}" <<-EOSQL + -- What's done in this file shouldn't be replicated + -- or products like mysql-fabric won't work + SET @@SESSION.SQL_LOG_BIN=0; + + ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; + GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; + ${rootCreate} + DROP DATABASE IF EXISTS test ; + FLUSH PRIVILEGES ; + EOSQL + + if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then + mysql+=( -p"${MYSQL_ROOT_PASSWORD}" ) + fi + + file_env 'MYSQL_DATABASE' + if [ "$MYSQL_DATABASE" ]; then + echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" + mysql+=( "$MYSQL_DATABASE" ) + fi + + file_env 'MYSQL_USER' + file_env 'MYSQL_PASSWORD' + if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then + echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}" + + if [ "$MYSQL_DATABASE" ]; then + echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}" + fi + + echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}" + fi + + echo + for f in /docker-entrypoint-initdb.d/*; do + process_init_file "$f" "${mysql[@]}" + done + + if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then + "${mysql[@]}" <<-EOSQL + ALTER USER 'root'@'%' PASSWORD EXPIRE; + EOSQL + fi + if ! kill -s TERM "$pid" || ! wait "$pid"; then + echo >&2 'MySQL init process failed.' + exit 1 + fi + + echo + echo 'MySQL init process done. Ready for start up.' + echo + fi +fi + +exec "$@" diff --git a/update.sh b/update.sh index db67d2158..f28ad3f14 100755 --- a/update.sh +++ b/update.sh @@ -14,7 +14,14 @@ declare -A debianVariants=( #[5.5]='jessie' ) +# Copy entrypoint template +templateVersions=( "5.7 8.0" ) +for version in ${templateVersions}; do + cp "template.Debian/docker-entrypoint.sh" "${version}/" +done + for version in "${versions[@]}"; do + if [ "${version}" = "template.Debian" ]; then continue; fi # If update.sh is run without arguments, the template directory is included in the list debianVariant="${debianVariants[$version]:-$defaultDebianVariant}" debianSuite="${debianVariant%%-*}" # "stretch", etc From afceb7f478620cd7f2825bcbaad159314dd59840 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Mon, 6 Aug 2018 10:13:25 +0200 Subject: [PATCH 03/42] Template: Create functions for starting and stopping server during init Move the logic for doing temporary startup of the server during init to functions. Also add tags to echo commands to make it clear they are coming from the entrypoint script. --- template.Debian/docker-entrypoint.sh | 88 +++++++++++++++++----------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/template.Debian/docker-entrypoint.sh b/template.Debian/docker-entrypoint.sh index 5ed7cd25c..86e0398cc 100755 --- a/template.Debian/docker-entrypoint.sh +++ b/template.Debian/docker-entrypoint.sh @@ -27,7 +27,7 @@ file_env() { local fileVar="${var}_FILE" local def="${2:-}" if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then - echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + echo >&2 "$(date --rfc-3339=seconds) [ERROR] [Entrypoint]: Both $var and $fileVar are set (but are exclusive)" exit 1 fi local val="$def" @@ -50,10 +50,10 @@ process_init_file() { local mysql=( "$@" ) case "$f" in - *.sh) echo "$0: running $f"; . "$f" ;; - *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; - *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; - *) echo "$0: ignoring $f" ;; + *.sh) echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: $0: running $f"; . "$f" ;; + *.sql) echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: $0: running $f"; "${mysql[@]}" < "$f"; echo ;; + *.sql.gz) echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: $0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; + *) echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: $0: ignoring $f" ;; esac echo } @@ -61,9 +61,10 @@ process_init_file() { _check_config() { toRun=( "$@" --verbose --help ) if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then + datestring=$(date --rfc-3339=seconds) cat >&2 <<-EOM - ERROR: mysqld failed while attempting to check config + $datestring [ERROR] [Entrypoint]: mysqld failed while attempting to check config command was: "${toRun[*]}" $errors @@ -82,6 +83,40 @@ _get_config() { # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" } +_start_server() { + local socket=$1; shift + "$@" --skip-networking --socket="${socket}" & + local pid="$!" + + mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${socket}" ) + + for i in {30..0}; do + if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then + break + fi + sleep 1 + done + if [ "$i" = 0 ]; then + echo >&2 "$(date --rfc-3339=seconds) [ERROR] [Entrypoint]: Unable to start server." + exit 1 + fi + return $pid +} + +_stop_server() { + local server_pid="$1" + kill "$server_pid" + for i in $(seq 1 60); do + sleep 1 + if ! $(pidof /usr/sbin/mysqld >/dev/null 2>&1); then + return 0 + fi + done + # The server hasn't shut down in a timely manner + echo "$(date --rfc-3339=seconds) [ERROR] [Entrypoint]: Unable to shut down server with process id $server_pid" >&2 + return 1 + +} # allow the container to be started with `--user` if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then _check_config "$@" @@ -100,42 +135,31 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then if [ ! -d "$DATADIR/mysql" ]; then file_env 'MYSQL_ROOT_PASSWORD' if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - echo >&2 'error: database is uninitialized and password option is not specified ' - echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' + echo >&2 "$(date --rfc-3339=seconds) [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified " + echo >&2 "$(date --rfc-3339=seconds) [ERROR] [Entrypoint]: You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" exit 1 fi mkdir -p "$DATADIR" - echo 'Initializing database' + echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Initializing database" "$@" --initialize-insecure - echo 'Database initialized' + echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Database initialized" if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 - echo 'Initializing certificates' + echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Initializing certificates" mysql_ssl_rsa_setup --datadir="$DATADIR" - echo 'Certificates initialized' + echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Certificates initialized" fi SOCKET="$(_get_config 'socket' "$@")" - "$@" --skip-networking --socket="${SOCKET}" & - pid="$!" + echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Starting server" + _start_server "${SOCKET}" "$@" || pid=$? + echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Server started with pid $pid" mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) - for i in {30..0}; do - if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then - break - fi - echo 'MySQL init process in progress...' - sleep 1 - done - if [ "$i" = 0 ]; then - echo >&2 'MySQL init process failed.' - exit 1 - fi - if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then # sed is for https://bugs.mysql.com/bug.php?id=20545 mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql @@ -143,7 +167,7 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" - echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" + echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" fi rootCreate= @@ -202,13 +226,11 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then ALTER USER 'root'@'%' PASSWORD EXPIRE; EOSQL fi - if ! kill -s TERM "$pid" || ! wait "$pid"; then - echo >&2 'MySQL init process failed.' - exit 1 - fi - + echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Stopping server" + _stop_server $pid + echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Server stopped" echo - echo 'MySQL init process done. Ready for start up.' + echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: MySQL init process done. Ready for start up." echo fi fi From 34f3ef2bb66b0525fb6bac8bd87d3a6a2655b1b1 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Wed, 22 Aug 2018 08:42:40 +0200 Subject: [PATCH 04/42] Template: Rename template directory to be hidden Renamed from template.Debian to .template.Debian so the update script won't see it as a build directory. --- {template.Debian => .template.Debian}/docker-entrypoint.sh | 0 update.sh | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) rename {template.Debian => .template.Debian}/docker-entrypoint.sh (100%) diff --git a/template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh similarity index 100% rename from template.Debian/docker-entrypoint.sh rename to .template.Debian/docker-entrypoint.sh diff --git a/update.sh b/update.sh index f28ad3f14..41dfec7cb 100755 --- a/update.sh +++ b/update.sh @@ -17,11 +17,10 @@ declare -A debianVariants=( # Copy entrypoint template templateVersions=( "5.7 8.0" ) for version in ${templateVersions}; do - cp "template.Debian/docker-entrypoint.sh" "${version}/" + cp ".template.Debian/docker-entrypoint.sh" "${version}/" done for version in "${versions[@]}"; do - if [ "${version}" = "template.Debian" ]; then continue; fi # If update.sh is run without arguments, the template directory is included in the list debianVariant="${debianVariants[$version]:-$defaultDebianVariant}" debianSuite="${debianVariant%%-*}" # "stretch", etc From a8aa1cfed6d5058f64a01ca06cccb1ee3522cd51 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Wed, 22 Aug 2018 09:53:37 +0200 Subject: [PATCH 05/42] Template: Create logging functions Logging is done by calling _note, _warn or _error with the message string. _warn and _error print to stderr, and _error causes the script to exit with return code 1 --- .template.Debian/docker-entrypoint.sh | 67 ++++++++++++++------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 86e0398cc..7792f4e7f 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -2,6 +2,22 @@ set -eo pipefail shopt -s nullglob +# logging functions +_log() { + local type=$1;shift + printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" +} +_note() { + _log Note "$@" +} +_warn() { + _log Warn "$@" >&2 +} +_error() { + _log ERROR "$@" >&2 + exit 1 +} + # if command starts with an option, prepend mysqld if [ "${1:0:1}" = '-' ]; then set -- mysqld "$@" @@ -27,8 +43,7 @@ file_env() { local fileVar="${var}_FILE" local def="${2:-}" if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then - echo >&2 "$(date --rfc-3339=seconds) [ERROR] [Entrypoint]: Both $var and $fileVar are set (but are exclusive)" - exit 1 + _error "Both $var and $fileVar are set (but are exclusive)" fi local val="$def" if [ "${!var:-}" ]; then @@ -50,10 +65,10 @@ process_init_file() { local mysql=( "$@" ) case "$f" in - *.sh) echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: $0: running $f"; . "$f" ;; - *.sql) echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: $0: running $f"; "${mysql[@]}" < "$f"; echo ;; - *.sql.gz) echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: $0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; - *) echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: $0: ignoring $f" ;; + *.sh) _note "$0: running $f"; . "$f" ;; + *.sql) _note "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; + *.sql.gz) _note "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; + *) _warn "$0: ignoring $f" ;; esac echo } @@ -61,15 +76,7 @@ process_init_file() { _check_config() { toRun=( "$@" --verbose --help ) if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - datestring=$(date --rfc-3339=seconds) - cat >&2 <<-EOM - - $datestring [ERROR] [Entrypoint]: mysqld failed while attempting to check config - command was: "${toRun[*]}" - - $errors - EOM - exit 1 + _error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" fi } @@ -97,8 +104,7 @@ _start_server() { sleep 1 done if [ "$i" = 0 ]; then - echo >&2 "$(date --rfc-3339=seconds) [ERROR] [Entrypoint]: Unable to start server." - exit 1 + _error "Unable to start server." fi return $pid } @@ -113,8 +119,7 @@ _stop_server() { fi done # The server hasn't shut down in a timely manner - echo "$(date --rfc-3339=seconds) [ERROR] [Entrypoint]: Unable to shut down server with process id $server_pid" >&2 - return 1 + _error "Unable to shut down server with process id $server_pid" } # allow the container to be started with `--user` @@ -135,28 +140,26 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then if [ ! -d "$DATADIR/mysql" ]; then file_env 'MYSQL_ROOT_PASSWORD' if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - echo >&2 "$(date --rfc-3339=seconds) [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified " - echo >&2 "$(date --rfc-3339=seconds) [ERROR] [Entrypoint]: You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" - exit 1 + _error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" fi mkdir -p "$DATADIR" - echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Initializing database" + _note "Initializing database" "$@" --initialize-insecure - echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Database initialized" + _note "Database initialized" if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 - echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Initializing certificates" + _note "Initializing certificates" mysql_ssl_rsa_setup --datadir="$DATADIR" - echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Certificates initialized" + _note "Certificates initialized" fi SOCKET="$(_get_config 'socket' "$@")" - echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Starting server" + _note "Starting server" _start_server "${SOCKET}" "$@" || pid=$? - echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Server started with pid $pid" + _note "Server started with pid $pid" mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) @@ -167,7 +170,7 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" - echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" + _note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" fi rootCreate= @@ -226,11 +229,11 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then ALTER USER 'root'@'%' PASSWORD EXPIRE; EOSQL fi - echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Stopping server" + _note "Stopping server" _stop_server $pid - echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: Server stopped" + _note "Server stopped" echo - echo "$(date --rfc-3339=seconds) [Note] [Entrypoint]: MySQL init process done. Ready for start up." + _note "MySQL init process done. Ready for start up." echo fi fi From ea73775e6aa87c7f5dbf0b35a539a245e189c644 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 6 Sep 2018 10:27:49 +0200 Subject: [PATCH 06/42] Template: Store root password in file Storing the password in a file lets us use defaults-extra-file option so we don't put it on the command line --- .template.Debian/docker-entrypoint.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 7792f4e7f..78458ba20 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -197,8 +197,16 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then FLUSH PRIVILEGES ; EOSQL + # Store the password in a file so we don't use it on the command line + install -d -m0700 /tmp/mysql-files + PASSFILE=$(mktemp /tmp/mysql-files/XXXXXXXXXX) + install /dev/null -m0600 "${PASSFILE}" if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then - mysql+=( -p"${MYSQL_ROOT_PASSWORD}" ) + cat >"${PASSFILE}" < Date: Thu, 6 Sep 2018 11:07:45 +0200 Subject: [PATCH 07/42] Template: Use mysqladmin to stop temporary server Using a local socket means we don't need to know the pid of the server, and mysqladmin shutdown will block until complete when using a local socket. --- .template.Debian/docker-entrypoint.sh | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 78458ba20..e0e0112cb 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -110,17 +110,13 @@ _start_server() { } _stop_server() { - local server_pid="$1" - kill "$server_pid" - for i in $(seq 1 60); do - sleep 1 - if ! $(pidof /usr/sbin/mysqld >/dev/null 2>&1); then - return 0 - fi - done - # The server hasn't shut down in a timely manner - _error "Unable to shut down server with process id $server_pid" - + local passfile=$1 + local socket=$2 + result=0 + mysqladmin --defaults-extra-file="${passfile}" shutdown -uroot --socket="${socket}" || result=$? + if [ ! "$result" = "0" ]; then + _error "Unable to shut down server. Status code $result." + fi } # allow the container to be started with `--user` if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then @@ -238,7 +234,7 @@ EOF EOSQL fi _note "Stopping server" - _stop_server $pid + _stop_server "${PASSFILE}" "${SOCKET}" _note "Server stopped" rm -f "${PASSFILE}" unset PASSFILE From a9e85760c7b18590f5044061cd7da90061c96533 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 6 Sep 2018 11:55:16 +0200 Subject: [PATCH 08/42] Template: Use --daemonize for temporary server startup. When using --daemonize, the server process will fork and exit when the server is ready for use, removing the need for a wait loop. Note: This is only supported for 5.7+ --- .template.Debian/docker-entrypoint.sh | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index e0e0112cb..1fe2d4ef3 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -92,21 +92,11 @@ _get_config() { _start_server() { local socket=$1; shift - "$@" --skip-networking --socket="${socket}" & - local pid="$!" - - mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${socket}" ) - - for i in {30..0}; do - if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then - break - fi - sleep 1 - done - if [ "$i" = 0 ]; then - _error "Unable to start server." + result=0 + "$@" --daemonize --skip-networking --socket="${socket}" || result=$? + if [ ! "$result" = "0" ];then + _error "Unable to start server. Status code $result." fi - return $pid } _stop_server() { @@ -154,8 +144,8 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then SOCKET="$(_get_config 'socket' "$@")" _note "Starting server" - _start_server "${SOCKET}" "$@" || pid=$? - _note "Server started with pid $pid" + _start_server "${SOCKET}" "$@" + _note "Server started with." mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) From 03bdbade047e50342758046b1defb6311cd01c6c Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 13 Sep 2018 09:49:19 +0200 Subject: [PATCH 09/42] Add 5.5 and 5.6 to entrypoint templating This requires adding some variables that are replaced when generating the entrypoint scripts --- .template.Debian/docker-entrypoint.sh | 37 +++++++++++++++++++++------ update.sh | 23 +++++++++++++++-- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 1fe2d4ef3..efdc88820 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -93,12 +93,25 @@ _get_config() { _start_server() { local socket=$1; shift result=0 - "$@" --daemonize --skip-networking --socket="${socket}" || result=$? + %%SERVERSTARTUP%% if [ ! "$result" = "0" ];then _error "Unable to start server. Status code $result." fi } +_wait_for_server() { + local mysql=( "$@" ) + for i in {30..0}; do + if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then + break + fi + sleep 1 + done + if [ "$i" = 0 ]; then + _error "Unable to start server." + fi +} + _stop_server() { local passfile=$1 local socket=$2 @@ -132,7 +145,7 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then mkdir -p "$DATADIR" _note "Initializing database" - "$@" --initialize-insecure + %%DATABASEINIT%% _note "Database initialized" if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then @@ -143,11 +156,15 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then fi SOCKET="$(_get_config 'socket' "$@")" + mysql=( mysql --no-defaults --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) _note "Starting server" _start_server "${SOCKET}" "$@" - _note "Server started with." + if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then + _note "Waiting for server startup" + _wait_for_server "${mysql[@]}" + fi + _note "Server started." - mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then # sed is for https://bugs.mysql.com/bug.php?id=20545 @@ -176,7 +193,7 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; - ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; + %%PASSWORDSET%% GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; ${rootCreate} DROP DATABASE IF EXISTS test ; @@ -219,9 +236,13 @@ EOF done if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then - "${mysql[@]}" <<-EOSQL - ALTER USER 'root'@'%' PASSWORD EXPIRE; - EOSQL + if [ "${MYSQL_MAJOR}" = "5.5" ]; then + _warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" + else + "${mysql[@]}" <<-EOSQL + ALTER USER 'root'@'%' PASSWORD EXPIRE; + EOSQL + fi fi _note "Stopping server" _stop_server "${PASSFILE}" "${SOCKET}" diff --git a/update.sh b/update.sh index 41dfec7cb..78be6d40d 100755 --- a/update.sh +++ b/update.sh @@ -14,10 +14,29 @@ declare -A debianVariants=( #[5.5]='jessie' ) -# Copy entrypoint template -templateVersions=( "5.7 8.0" ) +# Templaing +templateVersions=( "5.5 5.6 5.7 8.0" ) +declare -A passwordset +passwordset["5.5"]="SET PASSWORD FOR 'root'@'localhost'=PASSWORD('\${MYSQL_ROOT_PASSWORD}');" +passwordset["5.6"]="SET PASSWORD FOR 'root'@'localhost'=PASSWORD('\${MYSQL_ROOT_PASSWORD}');" +passwordset["5.7"]="ALTER USER 'root'@'localhost' IDENTIFIED BY '\${MYSQL_ROOT_PASSWORD}';" +passwordset["8.0"]="ALTER USER 'root'@'localhost' IDENTIFIED BY '\${MYSQL_ROOT_PASSWORD}';" +declare -A database_init +database_init["5.5"]="mysql_install_db --datadir=\"\$DATADIR\" --rpm --basedir=\/usr\/local\/mysql \"\${@:2}\"" +database_init["5.6"]="mysql_install_db --user=mysql --datadir=\"\$DATADIR\" --rpm --keep-my-cnf" +database_init["5.7"]="\"\$@\" --initialize-insecure" +database_init["8.0"]="\"\$@\" --initialize-insecure" +declare -A server_startup +server_startup["5.5"]="\"\$@\" --skip-networking --basedir=\/usr\/local\/mysql --socket=\"\${socket}\" \&" +server_startup["5.6"]="\"\$@\" --skip-networking --socket=\"\${socket}\" \&" +server_startup["5.7"]="\"\$@\" --daemonize --skip-networking --socket=\"\${socket}\" || result=$?" +server_startup["8.0"]="\"\$@\" --daemonize --skip-networking --socket=\"\${socket}\" || result=$?" for version in ${templateVersions}; do cp ".template.Debian/docker-entrypoint.sh" "${version}/" + sed -e 's/%%PASSWORDSET%%/'"${passwordset["$version"]}"'/g' \ + -e 's/%%DATABASEINIT%%/'"${database_init["$version"]}"'/g' \ + -e 's/%%SERVERSTARTUP%%/'"${server_startup["$version"]}"'/g' \ + .template.Debian/docker-entrypoint.sh > "$version/docker-entrypoint.sh" done for version in "${versions[@]}"; do From 2242976d545619cd506c12309c6d374135f9d512 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 13 Sep 2018 10:08:34 +0200 Subject: [PATCH 10/42] Move flag for password file to when the client command is first defined All options regarding config files must be the first specified, so adding it later doesn't work. Using --defaults-file instead of --defaults-extra-file should also mounted config files from interfering with the connection --- .template.Debian/docker-entrypoint.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index efdc88820..3617f4265 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -156,7 +156,12 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then fi SOCKET="$(_get_config 'socket' "$@")" - mysql=( mysql --no-defaults --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) + # We create a file to store the root password in so we don''t use it on the command line + install -d -m0700 /tmp/mysql-files + PASSFILE=$(mktemp /tmp/mysql-files/XXXXXXXXXX) + install /dev/null -m0600 "${PASSFILE}" + + mysql=( mysql --defaults-file="${PASSFILE}" --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) _note "Starting server" _start_server "${SOCKET}" "$@" if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then @@ -200,16 +205,12 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then FLUSH PRIVILEGES ; EOSQL - # Store the password in a file so we don't use it on the command line - install -d -m0700 /tmp/mysql-files - PASSFILE=$(mktemp /tmp/mysql-files/XXXXXXXXXX) - install /dev/null -m0600 "${PASSFILE}" + # Write the password to the file the client uses if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then cat >"${PASSFILE}" < Date: Thu, 20 Sep 2018 10:14:41 +0200 Subject: [PATCH 11/42] Fix typo templaing->templating --- update.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update.sh b/update.sh index 78be6d40d..d3deac7e8 100755 --- a/update.sh +++ b/update.sh @@ -14,7 +14,7 @@ declare -A debianVariants=( #[5.5]='jessie' ) -# Templaing +# Templating templateVersions=( "5.5 5.6 5.7 8.0" ) declare -A passwordset passwordset["5.5"]="SET PASSWORD FOR 'root'@'localhost'=PASSWORD('\${MYSQL_ROOT_PASSWORD}');" From 4672559853c86e29f5387ea261bd7104c6811e6b Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Fri, 21 Sep 2018 11:51:21 +0200 Subject: [PATCH 12/42] Prefix function names in entrypoint with "docker" This is to reduce risk of collisions with any custom scripts users may attach --- .template.Debian/docker-entrypoint.sh | 106 +++++++++++++------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 3617f4265..5e6aacc74 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -3,18 +3,18 @@ set -eo pipefail shopt -s nullglob # logging functions -_log() { +docker_log() { local type=$1;shift printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" } -_note() { - _log Note "$@" +docker_note() { + docker_log Note "$@" } -_warn() { - _log Warn "$@" >&2 +docker_warn() { + docker_log Warn "$@" >&2 } -_error() { - _log ERROR "$@" >&2 +docker_error() { + docker_log ERROR "$@" >&2 exit 1 } @@ -34,16 +34,16 @@ for arg; do esac done -# usage: file_env VAR [DEFAULT] -# ie: file_env 'XYZ_DB_PASSWORD' 'example' +# usage: docker_file_env VAR [DEFAULT] +# ie: docker_file_env 'XYZ_DB_PASSWORD' 'example' # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) -file_env() { +docker_file_env() { local var="$1" local fileVar="${var}_FILE" local def="${2:-}" if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then - _error "Both $var and $fileVar are set (but are exclusive)" + docker_error "Both $var and $fileVar are set (but are exclusive)" fi local val="$def" if [ "${!var:-}" ]; then @@ -55,51 +55,51 @@ file_env() { unset "$fileVar" } -# usage: process_init_file FILENAME MYSQLCOMMAND... -# ie: process_init_file foo.sh mysql -uroot +# usage: docker_process_init_file FILENAME MYSQLCOMMAND... +# ie: docker_process_init_file foo.sh mysql -uroot # (process a single initializer file, based on its extension. we define this # function here, so that initializer scripts (*.sh) can use the same logic, # potentially recursively, or override the logic used in subsequent calls) -process_init_file() { +docker_process_init_file() { local f="$1"; shift local mysql=( "$@" ) case "$f" in - *.sh) _note "$0: running $f"; . "$f" ;; - *.sql) _note "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; - *.sql.gz) _note "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; - *) _warn "$0: ignoring $f" ;; + *.sh) docker_note "$0: running $f"; . "$f" ;; + *.sql) docker_note "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; + *.sql.gz) docker_note "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; + *) docker_warn "$0: ignoring $f" ;; esac echo } -_check_config() { +docker_check_config() { toRun=( "$@" --verbose --help ) if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - _error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" + docker_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" fi } # Fetch value from server config # We use mysqld --verbose --help instead of my_print_defaults because the # latter only show values present in config files, and not server defaults -_get_config() { +docker_get_config() { local conf="$1"; shift "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \ | awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" } -_start_server() { +docker_start_server() { local socket=$1; shift result=0 %%SERVERSTARTUP%% if [ ! "$result" = "0" ];then - _error "Unable to start server. Status code $result." + docker_error "Unable to start server. Status code $result." fi } -_wait_for_server() { +docker_wait_for_server() { local mysql=( "$@" ) for i in {30..0}; do if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then @@ -108,23 +108,23 @@ _wait_for_server() { sleep 1 done if [ "$i" = 0 ]; then - _error "Unable to start server." + docker_error "Unable to start server." fi } -_stop_server() { +docker_stop_server() { local passfile=$1 local socket=$2 result=0 mysqladmin --defaults-extra-file="${passfile}" shutdown -uroot --socket="${socket}" || result=$? if [ ! "$result" = "0" ]; then - _error "Unable to shut down server. Status code $result." + docker_error "Unable to shut down server. Status code $result." fi } # allow the container to be started with `--user` if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then - _check_config "$@" - DATADIR="$(_get_config 'datadir' "$@")" + docker_check_config "$@" + DATADIR="$(docker_get_config 'datadir' "$@")" mkdir -p "$DATADIR" chown -R mysql:mysql "$DATADIR" exec gosu mysql "$BASH_SOURCE" "$@" @@ -132,43 +132,43 @@ fi if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then # still need to check config, container may have started with --user - _check_config "$@" + docker_check_config "$@" # Get config - DATADIR="$(_get_config 'datadir' "$@")" + DATADIR="$(docker_get_config 'datadir' "$@")" if [ ! -d "$DATADIR/mysql" ]; then - file_env 'MYSQL_ROOT_PASSWORD' + docker_file_env 'MYSQL_ROOT_PASSWORD' if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - _error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" + docker_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" fi mkdir -p "$DATADIR" - _note "Initializing database" + docker_note "Initializing database" %%DATABASEINIT%% - _note "Database initialized" + docker_note "Database initialized" if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 - _note "Initializing certificates" + docker_note "Initializing certificates" mysql_ssl_rsa_setup --datadir="$DATADIR" - _note "Certificates initialized" + docker_note "Certificates initialized" fi - SOCKET="$(_get_config 'socket' "$@")" + SOCKET="$(docker_get_config 'socket' "$@")" # We create a file to store the root password in so we don''t use it on the command line install -d -m0700 /tmp/mysql-files PASSFILE=$(mktemp /tmp/mysql-files/XXXXXXXXXX) install /dev/null -m0600 "${PASSFILE}" mysql=( mysql --defaults-file="${PASSFILE}" --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) - _note "Starting server" - _start_server "${SOCKET}" "$@" + docker_note "Starting server" + docker_start_server "${SOCKET}" "$@" if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then - _note "Waiting for server startup" - _wait_for_server "${mysql[@]}" + docker_note "Waiting for server startup" + docker_wait_for_server "${mysql[@]}" fi - _note "Server started." + docker_note "Server started." if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then @@ -178,12 +178,12 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" - _note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" + docker_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" fi rootCreate= # default root to listen for connections from anywhere - file_env 'MYSQL_ROOT_HOST' '%' + docker_file_env 'MYSQL_ROOT_HOST' '%' if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then # no, we don't care if read finds a terminating character in this heredoc # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 @@ -213,14 +213,14 @@ password="${MYSQL_ROOT_PASSWORD}" EOF fi - file_env 'MYSQL_DATABASE' + docker_file_env 'MYSQL_DATABASE' if [ "$MYSQL_DATABASE" ]; then echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" mysql+=( "$MYSQL_DATABASE" ) fi - file_env 'MYSQL_USER' - file_env 'MYSQL_PASSWORD' + docker_file_env 'MYSQL_USER' + docker_file_env 'MYSQL_PASSWORD' if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}" @@ -233,7 +233,7 @@ EOF echo for f in /docker-entrypoint-initdb.d/*; do - process_init_file "$f" "${mysql[@]}" + docker_process_init_file "$f" "${mysql[@]}" done if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then @@ -245,13 +245,13 @@ EOF EOSQL fi fi - _note "Stopping server" - _stop_server "${PASSFILE}" "${SOCKET}" - _note "Server stopped" + docker_note "Stopping server" + docker_stop_server "${PASSFILE}" "${SOCKET}" + docker_note "Server stopped" rm -f "${PASSFILE}" unset PASSFILE echo - _note "MySQL init process done. Ready for start up." + docker_note "MySQL init process done. Ready for start up." echo fi fi From db1271315f1fdb93bc43bbd17c602a8ba124d551 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 18 Oct 2018 07:43:56 +0200 Subject: [PATCH 13/42] entrypoint: Use mktemp instead of install mktemp -d already creates a directory with mode 700, so the install command to ensure it is redundant The same applies to the temporary password file inside this directory. --- .template.Debian/docker-entrypoint.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 5e6aacc74..5ba91bc96 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -157,9 +157,8 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then SOCKET="$(docker_get_config 'socket' "$@")" # We create a file to store the root password in so we don''t use it on the command line - install -d -m0700 /tmp/mysql-files - PASSFILE=$(mktemp /tmp/mysql-files/XXXXXXXXXX) - install /dev/null -m0600 "${PASSFILE}" + TMPDIR="$(mktemp -d)" + PASSFILE="$(mktemp ${TMPDIR}/XXXXXXXXXX)" mysql=( mysql --defaults-file="${PASSFILE}" --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) docker_note "Starting server" From f9c185f01e1c9565f26cdfb5b112b1d6fd28ccf7 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 18 Oct 2018 09:46:33 +0200 Subject: [PATCH 14/42] entrypoint: Move more logic into functions Almost all logic in the entrypoint script is now separated into various functions --- .template.Debian/docker-entrypoint.sh | 253 ++++++++++++++++---------- update.sh | 8 +- 2 files changed, 162 insertions(+), 99 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 5ba91bc96..f4d5a5f68 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -90,8 +90,8 @@ docker_get_config() { # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" } +# Do a temporary startup of the MySQL server, for init purposes docker_start_server() { - local socket=$1; shift result=0 %%SERVERSTARTUP%% if [ ! "$result" = "0" ];then @@ -99,6 +99,8 @@ docker_start_server() { fi } +# Wait for the temporary server to be ready for connections. +# It is only used for versions older than 5.7 docker_wait_for_server() { local mysql=( "$@" ) for i in {30..0}; do @@ -112,123 +114,188 @@ docker_wait_for_server() { fi } +# Stop the server. When using a local socket file mysqladmin will block until +# the shutdown is complete. docker_stop_server() { - local passfile=$1 - local socket=$2 result=0 - mysqladmin --defaults-extra-file="${passfile}" shutdown -uroot --socket="${socket}" || result=$? + mysqladmin --defaults-extra-file="${PASSFILE}" shutdown -uroot --socket="${SOCKET}" || result=$? if [ ! "$result" = "0" ]; then docker_error "Unable to shut down server. Status code $result." fi } + +# Verify that the minimally required password settings are set for new databases. +docker_verify_env() { + if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + docker_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" + fi +} + +# Creates and initializes the database directory +docker_init_database_dir() { + mkdir -p "$DATADIR" + + docker_note "Initializing database files" + %%DATABASEINIT%% + docker_note "Database files initialized" + + if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then + # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 + docker_note "Initializing certificates" + mysql_ssl_rsa_setup --datadir="$DATADIR" + docker_note "Certificates initialized" + fi +} + +# Loads various settings that are used elsewhere in the script +docker_init_env() { + # Get config + DATADIR="$(docker_get_config 'datadir' "$@")" + SOCKET="$(docker_get_config 'socket' "$@")" + + # We create a file to store the root password in so we don''t use it on the command line + TMPDIR="$(mktemp -d)" + PASSFILE="$(mktemp ${TMPDIR}/XXXXXXXXXX)" + + # Initialize values that might be stored in a file + docker_file_env 'MYSQL_ROOT_HOST' '%' + docker_file_env 'MYSQL_DATABASE' + docker_file_env 'MYSQL_USER' + docker_file_env 'MYSQL_PASSWORD' + docker_file_env 'MYSQL_ROOT_PASSWORD' +} + +# Define the client command that's used in various places +docker_init_client_command() { + mysql=( mysql --defaults-file="${PASSFILE}" --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) +} + +# Store root password in a file for use with the client command +docker_write_password_file() { + # Write the password to the file the client uses + if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then + cat >"${PASSFILE}" < /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then - # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 - docker_note "Initializing certificates" - mysql_ssl_rsa_setup --datadir="$DATADIR" - docker_note "Certificates initialized" - fi - - SOCKET="$(docker_get_config 'socket' "$@")" - # We create a file to store the root password in so we don''t use it on the command line - TMPDIR="$(mktemp -d)" - PASSFILE="$(mktemp ${TMPDIR}/XXXXXXXXXX)" + docker_verify_env + docker_init_database_dir "$@" + docker_init_client_command - mysql=( mysql --defaults-file="${PASSFILE}" --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) - docker_note "Starting server" - docker_start_server "${SOCKET}" "$@" + docker_note "Starting temporary server" + docker_start_server "$@" + # For 5.7+ the server is ready for use as soon as startup command unblocks if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then docker_note "Waiting for server startup" docker_wait_for_server "${mysql[@]}" fi - docker_note "Server started." + docker_note "Temporary server started." if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then - # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql + docker_load_tzinfo fi if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" - docker_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" - fi - - rootCreate= - # default root to listen for connections from anywhere - docker_file_env 'MYSQL_ROOT_HOST' '%' - if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 - read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; - GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ; - EOSQL - fi - - "${mysql[@]}" <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work - SET @@SESSION.SQL_LOG_BIN=0; - - %%PASSWORDSET%% - GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; - ${rootCreate} - DROP DATABASE IF EXISTS test ; - FLUSH PRIVILEGES ; - EOSQL - - # Write the password to the file the client uses - if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then - cat >"${PASSFILE}" < Date: Mon, 22 Oct 2018 08:23:05 +0200 Subject: [PATCH 15/42] entrypoint: Move main script functionality to function Main mysql functionality is now executed from docker_main() Also fixes a bug where script would attempt to load mysql config for non-mysqld commands, which would cause errors --- .template.Debian/docker-entrypoint.sh | 131 +++++++++++++------------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index f4d5a5f68..50f6e5189 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -251,71 +251,76 @@ docker_load_tzinfo() { mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql } -docker_init_env "$@" - -# allow the container to be started with `--user` -if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then - docker_check_config "$@" - mkdir -p "$DATADIR" - chown -R mysql:mysql "$DATADIR" - exec gosu mysql "$BASH_SOURCE" "$@" -fi - -docker_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." - -if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then - # still need to check config, container may have started with --user - docker_check_config "$@" - - # If this is true then there's no database, and it needs to be initialized - if [ ! -d "$DATADIR/mysql" ]; then - docker_verify_env - docker_init_database_dir "$@" - docker_init_client_command - - docker_note "Starting temporary server" - docker_start_server "$@" - # For 5.7+ the server is ready for use as soon as startup command unblocks - if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then - docker_note "Waiting for server startup" - docker_wait_for_server "${mysql[@]}" +docker_main() { + docker_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." + + if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then + # Load various environment variables + docker_init_env "$@" + + # If container is started as root user, restart as dedicated mysql user + if [ "$(id -u)" = '0' ]; then + docker_check_config "$@" + mkdir -p "$DATADIR" + chown -R mysql:mysql "$DATADIR" + docker_note "Switching to dedicated user 'mysql'" + exec gosu mysql "$BASH_SOURCE" "$@" fi - docker_note "Temporary server started." - - if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then - docker_load_tzinfo + # still need to check config, container may have started with --user + docker_check_config "$@" + + # If this is true then there's no database, and it needs to be initialized + if [ ! -d "$DATADIR/mysql" ]; then + docker_verify_env + docker_init_database_dir "$@" + docker_init_client_command + + docker_note "Starting temporary server" + docker_start_server "$@" + # For 5.7+ the server is ready for use as soon as startup command unblocks + if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then + docker_note "Waiting for server startup" + docker_wait_for_server "${mysql[@]}" + fi + docker_note "Temporary server started." + + + if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then + docker_load_tzinfo + fi + + if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + docker_generate_root_password + fi + + docker_init_root_user + + docker_write_password_file + + docker_init_database_user + + echo + for f in /docker-entrypoint-initdb.d/*; do + docker_process_init_file "$f" "${mysql[@]}" + done + + if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then + docker_expire_root_user + fi + docker_note "Stopping temporary server" + docker_stop_server + docker_note "Temporary server stopped" + + # Remove the password file now that initialization is complete + rm -f "${PASSFILE}" + unset PASSFILE + echo + docker_note "MySQL init process done. Ready for start up." + echo fi - - if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - docker_generate_root_password - fi - - docker_init_root_user - - docker_write_password_file - - docker_init_database_user - - echo - for f in /docker-entrypoint-initdb.d/*; do - docker_process_init_file "$f" "${mysql[@]}" - done - - if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then - docker_expire_root_user - fi - docker_note "Stopping temporary server" - docker_stop_server - docker_note "Temporary server stopped" - - # Remove the password file now that initialization is complete - rm -f "${PASSFILE}" - unset PASSFILE - echo - docker_note "MySQL init process done. Ready for start up." - echo fi -fi + exec "$@" +} -exec "$@" +docker_main "$@" From 33ba3e51ef116a97b33e18864e6e9a41b4e0a0f7 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Mon, 5 Nov 2018 11:11:58 +0100 Subject: [PATCH 16/42] entrypoint: Only execute main function if the script is not sourced If entrypoint.sh is sourced from elsewhere we don't execute the main function, so custom entrypoint scripts can use the helper functions directly. --- .template.Debian/docker-entrypoint.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 50f6e5189..7b794ef79 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -322,5 +322,8 @@ docker_main() { fi exec "$@" } - -docker_main "$@" +# This checks if the script has been sourced from elsewhere. +# If so we don't perform any further actions +if [ ! "${FUNCNAME[${#FUNCNAME[@]} - 1]}" = 'source' ]; then + docker_main "$@" +fi From 9f77ea5ad3ac151b4c0c2fb9982e0c4b4a8ede97 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 8 Nov 2018 08:53:26 +0100 Subject: [PATCH 17/42] entrypoint: Make value checks more consistent To compare actual values, some checks used ! variable = value while others used variable != value. Changed all to the latter. --- .template.Debian/docker-entrypoint.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 7b794ef79..d7f8b7cdd 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -94,7 +94,7 @@ docker_get_config() { docker_start_server() { result=0 %%SERVERSTARTUP%% - if [ ! "$result" = "0" ];then + if [ "$result" != "0" ];then docker_error "Unable to start server. Status code $result." fi } @@ -119,7 +119,7 @@ docker_wait_for_server() { docker_stop_server() { result=0 mysqladmin --defaults-extra-file="${PASSFILE}" shutdown -uroot --socket="${SOCKET}" || result=$? - if [ ! "$result" = "0" ]; then + if [ "$result" != "0" ]; then docker_error "Unable to shut down server. Status code $result." fi } @@ -324,6 +324,6 @@ docker_main() { } # This checks if the script has been sourced from elsewhere. # If so we don't perform any further actions -if [ ! "${FUNCNAME[${#FUNCNAME[@]} - 1]}" = 'source' ]; then +if [ "${FUNCNAME[${#FUNCNAME[@]} - 1]}" != 'source' ]; then docker_main "$@" fi From ef9caa95ec42ffa6c5fcd5cd884b67cca0ae8b38 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Mon, 1 Apr 2019 10:37:09 +0200 Subject: [PATCH 18/42] Rename docker_check_config to mysql_check_config This function is mysql-only --- .template.Debian/docker-entrypoint.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index d7f8b7cdd..30adbfd6c 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -73,7 +73,7 @@ docker_process_init_file() { echo } -docker_check_config() { +mysql_check_config() { toRun=( "$@" --verbose --help ) if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then docker_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" @@ -260,7 +260,7 @@ docker_main() { # If container is started as root user, restart as dedicated mysql user if [ "$(id -u)" = '0' ]; then - docker_check_config "$@" + mysql_check_config "$@" mkdir -p "$DATADIR" chown -R mysql:mysql "$DATADIR" docker_note "Switching to dedicated user 'mysql'" @@ -268,7 +268,7 @@ docker_main() { fi # still need to check config, container may have started with --user - docker_check_config "$@" + mysql_check_config "$@" # If this is true then there's no database, and it needs to be initialized if [ ! -d "$DATADIR/mysql" ]; then From 2fcb086490233ff9e1b156d7296f54dba4b821e8 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Mon, 1 Apr 2019 10:37:49 +0200 Subject: [PATCH 19/42] Rename docker_file_env to file_env This function is not meant to be part of the public set --- .template.Debian/docker-entrypoint.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 30adbfd6c..f087af868 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -34,11 +34,11 @@ for arg; do esac done -# usage: docker_file_env VAR [DEFAULT] -# ie: docker_file_env 'XYZ_DB_PASSWORD' 'example' +# usage: file_env VAR [DEFAULT] +# ie: file_env 'XYZ_DB_PASSWORD' 'example' # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) -docker_file_env() { +file_env() { local var="$1" local fileVar="${var}_FILE" local def="${2:-}" @@ -158,11 +158,11 @@ docker_init_env() { PASSFILE="$(mktemp ${TMPDIR}/XXXXXXXXXX)" # Initialize values that might be stored in a file - docker_file_env 'MYSQL_ROOT_HOST' '%' - docker_file_env 'MYSQL_DATABASE' - docker_file_env 'MYSQL_USER' - docker_file_env 'MYSQL_PASSWORD' - docker_file_env 'MYSQL_ROOT_PASSWORD' + file_env 'MYSQL_ROOT_HOST' '%' + file_env 'MYSQL_DATABASE' + file_env 'MYSQL_USER' + file_env 'MYSQL_PASSWORD' + file_env 'MYSQL_ROOT_PASSWORD' } # Define the client command that's used in various places From 1503220c4b22c6f9bcba1ca27c95f86e6b1d7723 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Mon, 1 Apr 2019 10:39:23 +0200 Subject: [PATCH 20/42] Rename docker_verify_env to docker_verify_minimum_env --- .template.Debian/docker-entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index f087af868..c15e9a563 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -125,7 +125,7 @@ docker_stop_server() { } # Verify that the minimally required password settings are set for new databases. -docker_verify_env() { +docker_verify_minimum_env() { if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then docker_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" fi @@ -272,7 +272,7 @@ docker_main() { # If this is true then there's no database, and it needs to be initialized if [ ! -d "$DATADIR/mysql" ]; then - docker_verify_env + docker_verify_minimum_env docker_init_database_dir "$@" docker_init_client_command From 67f2bd3e099ade5bd2ca1c55d49be7bf1b9e17f6 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Mon, 1 Apr 2019 10:40:09 +0200 Subject: [PATCH 21/42] Rename docker_process_init_file to docker_process_init_files --- .template.Debian/docker-entrypoint.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index c15e9a563..11be41de9 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -55,12 +55,12 @@ file_env() { unset "$fileVar" } -# usage: docker_process_init_file FILENAME MYSQLCOMMAND... -# ie: docker_process_init_file foo.sh mysql -uroot +# usage: docker_process_init_files FILENAME MYSQLCOMMAND... +# ie: docker_process_init_files foo.sh mysql -uroot # (process a single initializer file, based on its extension. we define this # function here, so that initializer scripts (*.sh) can use the same logic, # potentially recursively, or override the logic used in subsequent calls) -docker_process_init_file() { +docker_process_init_files() { local f="$1"; shift local mysql=( "$@" ) @@ -302,7 +302,7 @@ docker_main() { echo for f in /docker-entrypoint-initdb.d/*; do - docker_process_init_file "$f" "${mysql[@]}" + docker_process_init_files "$f" "${mysql[@]}" done if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then From 5e10737f52e4b919581bbcc0bec8aee4cb5ce040 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Mon, 1 Apr 2019 11:05:12 +0200 Subject: [PATCH 22/42] Rename docker_write_password_file to mysql_write_password_file This function is MySQL-only --- .template.Debian/docker-entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 11be41de9..af4e11c2b 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -171,7 +171,7 @@ docker_init_client_command() { } # Store root password in a file for use with the client command -docker_write_password_file() { +mysql_write_password_file() { # Write the password to the file the client uses if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then cat >"${PASSFILE}" < Date: Thu, 4 Apr 2019 12:18:29 +0200 Subject: [PATCH 23/42] Rename docker_get_config to mysql_get_config This function is mysql-only --- .template.Debian/docker-entrypoint.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index af4e11c2b..a81307b23 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -83,7 +83,7 @@ mysql_check_config() { # Fetch value from server config # We use mysqld --verbose --help instead of my_print_defaults because the # latter only show values present in config files, and not server defaults -docker_get_config() { +mysql_get_config() { local conf="$1"; shift "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \ | awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' @@ -150,8 +150,8 @@ docker_init_database_dir() { # Loads various settings that are used elsewhere in the script docker_init_env() { # Get config - DATADIR="$(docker_get_config 'datadir' "$@")" - SOCKET="$(docker_get_config 'socket' "$@")" + DATADIR="$(mysql_get_config 'datadir' "$@")" + SOCKET="$(mysql_get_config 'socket' "$@")" # We create a file to store the root password in so we don''t use it on the command line TMPDIR="$(mktemp -d)" From 34ae313bd9d0f0d6152593c2a7f3bc59ec27d6c2 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 4 Apr 2019 12:19:19 +0200 Subject: [PATCH 24/42] Rename docker_init_env to docker_setup_env --- .template.Debian/docker-entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index a81307b23..369d4fb58 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -148,7 +148,7 @@ docker_init_database_dir() { } # Loads various settings that are used elsewhere in the script -docker_init_env() { +docker_setup_env() { # Get config DATADIR="$(mysql_get_config 'datadir' "$@")" SOCKET="$(mysql_get_config 'socket' "$@")" @@ -256,7 +256,7 @@ docker_main() { if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then # Load various environment variables - docker_init_env "$@" + docker_setup_env "$@" # If container is started as root user, restart as dedicated mysql user if [ "$(id -u)" = '0' ]; then From c9600d299c974d9b9588abc59da2944609ec95d3 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 4 Apr 2019 12:33:45 +0200 Subject: [PATCH 25/42] Rename log functions from docker to mysql --- .template.Debian/docker-entrypoint.sh | 66 +++++++++++++-------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 369d4fb58..bbe5fd1ca 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -3,18 +3,18 @@ set -eo pipefail shopt -s nullglob # logging functions -docker_log() { +mysql_log() { local type=$1;shift printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" } -docker_note() { - docker_log Note "$@" +mysql_note() { + mysql_log Note "$@" } -docker_warn() { - docker_log Warn "$@" >&2 +mysql_warn() { + mysql_log Warn "$@" >&2 } -docker_error() { - docker_log ERROR "$@" >&2 +mysql_error() { + mysql_log ERROR "$@" >&2 exit 1 } @@ -43,7 +43,7 @@ file_env() { local fileVar="${var}_FILE" local def="${2:-}" if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then - docker_error "Both $var and $fileVar are set (but are exclusive)" + mysql_error "Both $var and $fileVar are set (but are exclusive)" fi local val="$def" if [ "${!var:-}" ]; then @@ -65,10 +65,10 @@ docker_process_init_files() { local mysql=( "$@" ) case "$f" in - *.sh) docker_note "$0: running $f"; . "$f" ;; - *.sql) docker_note "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; - *.sql.gz) docker_note "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; - *) docker_warn "$0: ignoring $f" ;; + *.sh) mysql_note "$0: running $f"; . "$f" ;; + *.sql) mysql_note "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; + *.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; + *) mysql_warn "$0: ignoring $f" ;; esac echo } @@ -76,7 +76,7 @@ docker_process_init_files() { mysql_check_config() { toRun=( "$@" --verbose --help ) if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - docker_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" + mysql_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" fi } @@ -95,7 +95,7 @@ docker_start_server() { result=0 %%SERVERSTARTUP%% if [ "$result" != "0" ];then - docker_error "Unable to start server. Status code $result." + mysql_error "Unable to start server. Status code $result." fi } @@ -110,7 +110,7 @@ docker_wait_for_server() { sleep 1 done if [ "$i" = 0 ]; then - docker_error "Unable to start server." + mysql_error "Unable to start server." fi } @@ -120,14 +120,14 @@ docker_stop_server() { result=0 mysqladmin --defaults-extra-file="${PASSFILE}" shutdown -uroot --socket="${SOCKET}" || result=$? if [ "$result" != "0" ]; then - docker_error "Unable to shut down server. Status code $result." + mysql_error "Unable to shut down server. Status code $result." fi } # Verify that the minimally required password settings are set for new databases. docker_verify_minimum_env() { if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - docker_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" + mysql_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" fi } @@ -135,15 +135,15 @@ docker_verify_minimum_env() { docker_init_database_dir() { mkdir -p "$DATADIR" - docker_note "Initializing database files" + mysql_note "Initializing database files" %%DATABASEINIT%% - docker_note "Database files initialized" + mysql_note "Database files initialized" if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 - docker_note "Initializing certificates" + mysql_note "Initializing certificates" mysql_ssl_rsa_setup --datadir="$DATADIR" - docker_note "Certificates initialized" + mysql_note "Certificates initialized" fi } @@ -210,13 +210,13 @@ docker_init_root_user() { # Creates a custom database and user if specified docker_init_database_user() { if [ "$MYSQL_DATABASE" ]; then - docker_note "Creating database ${MYSQL_DATABASE}" + mysql_note "Creating database ${MYSQL_DATABASE}" echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" mysql+=( "$MYSQL_DATABASE" ) fi if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then - docker_note "Creating user ${MYSQL_USER}" + mysql_note "Creating user ${MYSQL_USER}" echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}" if [ "$MYSQL_DATABASE" ]; then @@ -231,7 +231,7 @@ docker_init_database_user() { # else can be done (only supported for 5.6+) docker_expire_root_user() { if [ "${MYSQL_MAJOR}" = "5.5" ]; then - _warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" + mysql_warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" else "${mysql[@]}" <<-EOSQL ALTER USER 'root'@'%' PASSWORD EXPIRE; @@ -242,7 +242,7 @@ docker_expire_root_user() { # Generate a random root password docker_generate_root_password() { export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" - docker_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" + mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" } # Load timezone info into database @@ -252,7 +252,7 @@ docker_load_tzinfo() { } docker_main() { - docker_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." + mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then # Load various environment variables @@ -263,7 +263,7 @@ docker_main() { mysql_check_config "$@" mkdir -p "$DATADIR" chown -R mysql:mysql "$DATADIR" - docker_note "Switching to dedicated user 'mysql'" + mysql_note "Switching to dedicated user 'mysql'" exec gosu mysql "$BASH_SOURCE" "$@" fi @@ -276,14 +276,14 @@ docker_main() { docker_init_database_dir "$@" docker_init_client_command - docker_note "Starting temporary server" + mysql_note "Starting temporary server" docker_start_server "$@" # For 5.7+ the server is ready for use as soon as startup command unblocks if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then - docker_note "Waiting for server startup" + mysql_note "Waiting for server startup" docker_wait_for_server "${mysql[@]}" fi - docker_note "Temporary server started." + mysql_note "Temporary server started." if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then @@ -308,15 +308,15 @@ docker_main() { if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then docker_expire_root_user fi - docker_note "Stopping temporary server" + mysql_note "Stopping temporary server" docker_stop_server - docker_note "Temporary server stopped" + mysql_note "Temporary server stopped" # Remove the password file now that initialization is complete rm -f "${PASSFILE}" unset PASSFILE echo - docker_note "MySQL init process done. Ready for start up." + mysql_note "MySQL init process done. Ready for start up." echo fi fi From f1abc95f9aa5671f0a54674140aa7da4ad8487a1 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 4 Apr 2019 12:34:25 +0200 Subject: [PATCH 26/42] Rename docker_main to _main The function is not meant to be overridden --- .template.Debian/docker-entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index bbe5fd1ca..f04ae6ff2 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -251,7 +251,7 @@ docker_load_tzinfo() { mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql } -docker_main() { +_main() { mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then @@ -325,5 +325,5 @@ docker_main() { # This checks if the script has been sourced from elsewhere. # If so we don't perform any further actions if [ "${FUNCNAME[${#FUNCNAME[@]} - 1]}" != 'source' ]; then - docker_main "$@" + _main "$@" fi From 915c79228f2bd6db1d7a413ec745cb5e3722c251 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 4 Apr 2019 12:35:34 +0200 Subject: [PATCH 27/42] Rename functions for starting and stopping server --- .template.Debian/docker-entrypoint.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index f04ae6ff2..c3645c0aa 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -91,7 +91,7 @@ mysql_get_config() { } # Do a temporary startup of the MySQL server, for init purposes -docker_start_server() { +docker_temp_server_start() { result=0 %%SERVERSTARTUP%% if [ "$result" != "0" ];then @@ -116,7 +116,7 @@ docker_wait_for_server() { # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. -docker_stop_server() { +docker_temp_server_stop() { result=0 mysqladmin --defaults-extra-file="${PASSFILE}" shutdown -uroot --socket="${SOCKET}" || result=$? if [ "$result" != "0" ]; then @@ -277,7 +277,7 @@ _main() { docker_init_client_command mysql_note "Starting temporary server" - docker_start_server "$@" + docker_temp_server_start "$@" # For 5.7+ the server is ready for use as soon as startup command unblocks if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then mysql_note "Waiting for server startup" @@ -309,7 +309,7 @@ _main() { docker_expire_root_user fi mysql_note "Stopping temporary server" - docker_stop_server + docker_temp_server_stop mysql_note "Temporary server stopped" # Remove the password file now that initialization is complete From 9b90b1c6fdbacdc2e9661fd87babf25f49608d55 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 4 Apr 2019 12:38:12 +0200 Subject: [PATCH 28/42] Move old-mysql-only logic for waiting for server startup into function --- .template.Debian/docker-entrypoint.sh | 29 +++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index c3645c0aa..424f1e0ed 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -97,21 +97,25 @@ docker_temp_server_start() { if [ "$result" != "0" ];then mysql_error "Unable to start server. Status code $result." fi + + # For 5.7+ the server is ready for use as soon as startup command unblocks + if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then + mysql_note "Waiting for server startup" + for i in {30..0}; do + if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then + break + fi + sleep 1 + done + if [ "$i" = 0 ]; then + mysql_error "Unable to start server." + fi + fi } # Wait for the temporary server to be ready for connections. # It is only used for versions older than 5.7 docker_wait_for_server() { - local mysql=( "$@" ) - for i in {30..0}; do - if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then - break - fi - sleep 1 - done - if [ "$i" = 0 ]; then - mysql_error "Unable to start server." - fi } # Stop the server. When using a local socket file mysqladmin will block until @@ -278,11 +282,6 @@ _main() { mysql_note "Starting temporary server" docker_temp_server_start "$@" - # For 5.7+ the server is ready for use as soon as startup command unblocks - if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then - mysql_note "Waiting for server startup" - docker_wait_for_server "${mysql[@]}" - fi mysql_note "Temporary server started." From dfa4cb4c99ec9fbc4efeb2582a6522741d3201bb Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Thu, 4 Apr 2019 12:49:52 +0200 Subject: [PATCH 29/42] Rename docker_expire_root_user to mysql_expire_root_user The function is mysql-only --- .template.Debian/docker-entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 424f1e0ed..6a6bf037e 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -233,7 +233,7 @@ docker_init_database_user() { # Mark root user as expired so the password must be changed before anything # else can be done (only supported for 5.6+) -docker_expire_root_user() { +mysql_expire_root_user() { if [ "${MYSQL_MAJOR}" = "5.5" ]; then mysql_warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" else @@ -305,7 +305,7 @@ _main() { done if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then - docker_expire_root_user + mysql_expire_root_user fi mysql_note "Stopping temporary server" docker_temp_server_stop From 880bb34fe5d5972ca948d88a65168dcb6a5ad6b5 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Wed, 8 May 2019 07:40:42 +0200 Subject: [PATCH 30/42] Rename docker_init_database_user to docker_setup_db_users --- .template.Debian/docker-entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 6a6bf037e..084a7b4f5 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -212,7 +212,7 @@ docker_init_root_user() { } # Creates a custom database and user if specified -docker_init_database_user() { +docker_setup_db_users() { if [ "$MYSQL_DATABASE" ]; then mysql_note "Creating database ${MYSQL_DATABASE}" echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" @@ -297,7 +297,7 @@ _main() { mysql_write_password_file - docker_init_database_user + docker_setup_db_users echo for f in /docker-entrypoint-initdb.d/*; do From 04b03e069f9a46c0798871605f7b4254c46b8e0c Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Wed, 8 May 2019 08:17:51 +0200 Subject: [PATCH 31/42] Merge functions mysql_write_password_file, docker_init_root_user and docker_setup_db_users into docker_setup_db --- .template.Debian/docker-entrypoint.sh | 34 ++++++++++----------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 084a7b4f5..f4a87da21 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -174,19 +174,9 @@ docker_init_client_command() { mysql=( mysql --defaults-file="${PASSFILE}" --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) } -# Store root password in a file for use with the client command -mysql_write_password_file() { - # Write the password to the file the client uses - if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then - cat >"${PASSFILE}" <"${PASSFILE}" < Date: Wed, 8 May 2019 08:25:26 +0200 Subject: [PATCH 32/42] Rename function docker_init_database_dir to docker_create_db_directories --- .template.Debian/docker-entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index f4a87da21..0775a95e2 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -136,7 +136,7 @@ docker_verify_minimum_env() { } # Creates and initializes the database directory -docker_init_database_dir() { +docker_create_db_directories() { mkdir -p "$DATADIR" mysql_note "Initializing database files" @@ -273,7 +273,7 @@ _main() { # If this is true then there's no database, and it needs to be initialized if [ ! -d "$DATADIR/mysql" ]; then docker_verify_minimum_env - docker_init_database_dir "$@" + docker_create_db_directories "$@" docker_init_client_command mysql_note "Starting temporary server" From ce4d14deff7c0eed988b1041ef98035df7db4687 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Wed, 8 May 2019 08:27:37 +0200 Subject: [PATCH 33/42] Merge function docker_generate_root_password into docker_setup_db --- .template.Debian/docker-entrypoint.sh | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 0775a95e2..6ffc75e3b 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -176,6 +176,11 @@ docker_init_client_command() { # Creates initial database users and schema docker_setup_db() { + # Generate random root password + if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" + mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" + fi # Sets root password and creates root users for non-localhost hosts rootCreate= # default root to listen for connections from anywhere @@ -239,12 +244,6 @@ mysql_expire_root_user() { fi } -# Generate a random root password -docker_generate_root_password() { - export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" - mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" -} - # Load timezone info into database docker_load_tzinfo() { # sed is for https://bugs.mysql.com/bug.php?id=20545 @@ -285,10 +284,6 @@ _main() { docker_load_tzinfo fi - if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - docker_generate_root_password - fi - docker_setup_db echo From 964f6c210806242ede6bc64ca0f0bee08e9d6606 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Wed, 8 May 2019 08:32:26 +0200 Subject: [PATCH 34/42] Merge function docker_load_tzinfo into docker_setup_db --- .template.Debian/docker-entrypoint.sh | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 6ffc75e3b..489d8c1d7 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -174,8 +174,13 @@ docker_init_client_command() { mysql=( mysql --defaults-file="${PASSFILE}" --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) } -# Creates initial database users and schema +# Initializes database with timezone info and root password, plus optional extra db/user docker_setup_db() { + # Load timezone info into database + if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then + # sed is for https://bugs.mysql.com/bug.php?id=20545 + mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql + fi # Generate random root password if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" @@ -244,12 +249,6 @@ mysql_expire_root_user() { fi } -# Load timezone info into database -docker_load_tzinfo() { - # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql -} - _main() { mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." @@ -280,10 +279,6 @@ _main() { mysql_note "Temporary server started." - if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then - docker_load_tzinfo - fi - docker_setup_db echo From 125ac54bb820e9b474ff39793208456a705e4105 Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Wed, 8 May 2019 09:34:28 +0200 Subject: [PATCH 35/42] Replace function docker_init_client_command with docker_process_sql Instead of setting a command variable, the new function takes sql scripts as input and executes them. --- .template.Debian/docker-entrypoint.sh | 38 ++++++++++++++++----------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 489d8c1d7..3004e375b 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -62,12 +62,11 @@ file_env() { # potentially recursively, or override the logic used in subsequent calls) docker_process_init_files() { local f="$1"; shift - local mysql=( "$@" ) case "$f" in *.sh) mysql_note "$0: running $f"; . "$f" ;; - *.sql) mysql_note "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; - *.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; + *.sql) mysql_note "$0: running $f"; docker_process_sql "$(cat $f)"; echo ;; + *.sql.gz) mysql_note "$0: running $f"; docker_process_sql "$(gunzip -c $f)"; echo ;; *) mysql_warn "$0: ignoring $f" ;; esac echo @@ -102,7 +101,7 @@ docker_temp_server_start() { if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then mysql_note "Waiting for server startup" for i in {30..0}; do - if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then + if docker_process_sql "SELECT 1" &> /dev/null; then break fi sleep 1 @@ -169,6 +168,16 @@ docker_setup_env() { file_env 'MYSQL_ROOT_PASSWORD' } +# Execute sql script +docker_process_sql() { + SQL=$1 + DB=$2 + if [ -z "$SQL" ]; then + mysql_error "Empty sql script provided" + fi + echo "$SQL" | mysql --defaults-file="${PASSFILE}" --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$DB" +} + # Define the client command that's used in various places docker_init_client_command() { mysql=( mysql --defaults-file="${PASSFILE}" --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) @@ -179,7 +188,7 @@ docker_setup_db() { # Load timezone info into database if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql + docker_process_sql "$(mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/')" mysql fi # Generate random root password if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then @@ -198,7 +207,7 @@ docker_setup_db() { EOSQL fi - "${mysql[@]}" <<-EOSQL + docker_process_sql " -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; @@ -208,7 +217,7 @@ docker_setup_db() { ${rootCreate} DROP DATABASE IF EXISTS test ; FLUSH PRIVILEGES ; - EOSQL + " # Write the password to the file the client uses if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then @@ -221,19 +230,20 @@ EOF # Creates a custom database and user if specified if [ "$MYSQL_DATABASE" ]; then mysql_note "Creating database ${MYSQL_DATABASE}" - echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" + docker_process_sql "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" mysql+=( "$MYSQL_DATABASE" ) fi if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then mysql_note "Creating user ${MYSQL_USER}" - echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}" + docker_process_sql "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" if [ "$MYSQL_DATABASE" ]; then - echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}" + mysql_note "Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE}" + docker_process_sql "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" fi - echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}" + docker_process_sql "FLUSH PRIVILEGES ;" fi } @@ -243,9 +253,7 @@ mysql_expire_root_user() { if [ "${MYSQL_MAJOR}" = "5.5" ]; then mysql_warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" else - "${mysql[@]}" <<-EOSQL - ALTER USER 'root'@'%' PASSWORD EXPIRE; - EOSQL + docker_process_sql "ALTER USER 'root'@'%' PASSWORD EXPIRE;" fi } @@ -283,7 +291,7 @@ _main() { echo for f in /docker-entrypoint-initdb.d/*; do - docker_process_init_files "$f" "${mysql[@]}" + docker_process_init_files "$f" done if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then From bbf5d013e6a7e3852be4c0d857e4a84941afa1ba Mon Sep 17 00:00:00 2001 From: Lars Tangvald Date: Wed, 8 May 2019 09:56:30 +0200 Subject: [PATCH 36/42] Remove empty function docker_wait_for_server --- .template.Debian/docker-entrypoint.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 3004e375b..7607d854e 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -112,11 +112,6 @@ docker_temp_server_start() { fi } -# Wait for the temporary server to be ready for connections. -# It is only used for versions older than 5.7 -docker_wait_for_server() { -} - # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. docker_temp_server_stop() { From 06acf82405f4b264b4f8dd5c127c9891ccc1ffa0 Mon Sep 17 00:00:00 2001 From: Joe Ferguson Date: Thu, 16 May 2019 16:58:37 -0700 Subject: [PATCH 37/42] A few entrypoint updates to increase usability `docker-entrypoint.sh`: - update `docker_process_init_files` to take in a list of file - update `docker_process_sql` to take sql input on stdin to maintain compatibility with `mysql` (and prevent us from loading an entire `.sql` file into memory) - move any code that was outside a function to `_main` or other functions - use `local` variables in functions to limit scope - move current `docker_create_db_directories` logic to `docker_init_database_dir` - new docker_create_db_directories that just creates and chowns directories - move `PASSFILE` to be in memory only so that we don't accidentally not clean it up - move `FLUSH PRIVILEGES` to fix https://github.com/docker-library/mysql/pull/549 update.sh: - add back `DELETE` statement for 5.5 and 5.6 - swap some template strings to single quotes to make it easier to read - swap `sed` to use ! for delimter - use one loop rather than two --- .template.Debian/docker-entrypoint.sh | 192 ++++++++++++++------------ update.sh | 33 ++--- 2 files changed, 117 insertions(+), 108 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 7607d854e..4b92486f7 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -4,7 +4,7 @@ shopt -s nullglob # logging functions mysql_log() { - local type=$1;shift + local type="$1"; shift printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" } mysql_note() { @@ -18,22 +18,6 @@ mysql_error() { exit 1 } -# if command starts with an option, prepend mysqld -if [ "${1:0:1}" = '-' ]; then - set -- mysqld "$@" -fi - -# skip setup if they want an option that stops mysqld -wantHelp= -for arg; do - case "$arg" in - -'?'|--help|--print-defaults|-V|--version) - wantHelp=1 - break - ;; - esac -done - # usage: file_env VAR [DEFAULT] # ie: file_env 'XYZ_DB_PASSWORD' 'example' # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of @@ -55,25 +39,28 @@ file_env() { unset "$fileVar" } -# usage: docker_process_init_files FILENAME MYSQLCOMMAND... -# ie: docker_process_init_files foo.sh mysql -uroot -# (process a single initializer file, based on its extension. we define this -# function here, so that initializer scripts (*.sh) can use the same logic, -# potentially recursively, or override the logic used in subsequent calls) +# usage: docker_process_init_files [file [file [...]]] +# ie: docker_process_init_files /always-initdb.d/* +# process initializer files, based on file extensions docker_process_init_files() { - local f="$1"; shift - - case "$f" in - *.sh) mysql_note "$0: running $f"; . "$f" ;; - *.sql) mysql_note "$0: running $f"; docker_process_sql "$(cat $f)"; echo ;; - *.sql.gz) mysql_note "$0: running $f"; docker_process_sql "$(gunzip -c $f)"; echo ;; - *) mysql_warn "$0: ignoring $f" ;; - esac + # mysql here for backwards compatibility "${mysql[@]}" + mysql=( docker_process_sql ) + echo + local f + for f; do + case "$f" in + *.sh) mysql_note "$0: running $f"; . "$f" ;; + *.sql) mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;; + *.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;; + *) mysql_warn "$0: ignoring $f" ;; + esac + echo + done } mysql_check_config() { - toRun=( "$@" --verbose --help ) + local toRun=( "$@" --verbose --help ) errors if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then mysql_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" fi @@ -85,13 +72,13 @@ mysql_check_config() { mysql_get_config() { local conf="$1"; shift "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \ - | awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' + | awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" } # Do a temporary startup of the MySQL server, for init purposes docker_temp_server_start() { - result=0 + local result=0 %%SERVERSTARTUP%% if [ "$result" != "0" ];then mysql_error "Unable to start server. Status code $result." @@ -100,8 +87,9 @@ docker_temp_server_start() { # For 5.7+ the server is ready for use as soon as startup command unblocks if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then mysql_note "Waiting for server startup" + local i for i in {30..0}; do - if docker_process_sql "SELECT 1" &> /dev/null; then + if docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then break fi sleep 1 @@ -115,8 +103,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. docker_temp_server_stop() { - result=0 - mysqladmin --defaults-extra-file="${PASSFILE}" shutdown -uroot --socket="${SOCKET}" || result=$? + local result=0 + mysqladmin --defaults-extra-file=<( echo "${PASSFILE}" ) shutdown -uroot --socket="${SOCKET}" || result=$? if [ "$result" != "0" ]; then mysql_error "Unable to shut down server. Status code $result." fi @@ -129,10 +117,23 @@ docker_verify_minimum_env() { fi } -# Creates and initializes the database directory +# creates folders for the database +# also ensures permission for user mysql of run as root docker_create_db_directories() { + local user; user="$(id -u)" + + # TODO other directories that are used by default? like /var/lib/mysql-files + # see https://github.com/docker-library/mysql/issues/562 mkdir -p "$DATADIR" + if [ "$user" = "0" ]; then + # this will cause less disk access than `chown -R` + find "$DATADIR" \! -user mysql -exec chown mysql '{}' + + fi +} + +# initializes the database directory +docker_init_database_dir() { mysql_note "Initializing database files" %%DATABASEINIT%% mysql_note "Database files initialized" @@ -150,11 +151,7 @@ docker_setup_env() { # Get config DATADIR="$(mysql_get_config 'datadir' "$@")" SOCKET="$(mysql_get_config 'socket' "$@")" - - # We create a file to store the root password in so we don''t use it on the command line - TMPDIR="$(mktemp -d)" - PASSFILE="$(mktemp ${TMPDIR}/XXXXXXXXXX)" - + # Initialize values that might be stored in a file file_env 'MYSQL_ROOT_HOST' '%' file_env 'MYSQL_DATABASE' @@ -163,19 +160,17 @@ docker_setup_env() { file_env 'MYSQL_ROOT_PASSWORD' } -# Execute sql script +# Execute sql script, passed via stdin +# usage: docker_process_sql [mysql-cli-args] +# ie: docker_process_sql --database=mydb <<<'INSERT ...' +# ie: docker_process_sql --database=mydb "${PASSFILE}" < "$version/docker-entrypoint.sh" -done +server_startup["5.5"]='"$@" --skip-networking --basedir=/usr/local/mysql --socket="${SOCKET}" \&' +server_startup["5.6"]='"$@" --skip-networking --socket="${SOCKET}" \&' +server_startup["5.7"]='"$@" --daemonize --skip-networking --socket="${SOCKET}" || result="$?"' +server_startup["8.0"]='"$@" --daemonize --skip-networking --socket="${SOCKET}" || result="$?"' for version in "${versions[@]}"; do + sed -e 's!%%PASSWORDSET%%!'"${passwordset["$version"]}"'!g' \ + -e 's!%%DATABASEINIT%%!'"${database_init["$version"]}"'!g' \ + -e 's!%%SERVERSTARTUP%%!'"${server_startup["$version"]}"'!g' \ + .template.Debian/docker-entrypoint.sh > "$version/docker-entrypoint.sh" + debianVariant="${debianVariants[$version]:-$defaultDebianVariant}" debianSuite="${debianVariant%%-*}" # "stretch", etc From 169471f8933036e74f80ff45f50db4e5953c8197 Mon Sep 17 00:00:00 2001 From: Joe Ferguson Date: Fri, 24 May 2019 14:52:36 -0700 Subject: [PATCH 38/42] Apply update.sh to update each entrypoint; drop 5.5 from update.sh --- 5.6/docker-entrypoint.sh | 404 ++++++++++++++++++++++++-------------- 5.7/docker-entrypoint.sh | 406 +++++++++++++++++++++++++-------------- 8.0/docker-entrypoint.sh | 406 +++++++++++++++++++++++++-------------- update.sh | 3 - 4 files changed, 774 insertions(+), 445 deletions(-) diff --git a/5.6/docker-entrypoint.sh b/5.6/docker-entrypoint.sh index 7dbd6975a..7a47b0161 100755 --- a/5.6/docker-entrypoint.sh +++ b/5.6/docker-entrypoint.sh @@ -2,21 +2,21 @@ set -eo pipefail shopt -s nullglob -# if command starts with an option, prepend mysqld -if [ "${1:0:1}" = '-' ]; then - set -- mysqld "$@" -fi - -# skip setup if they want an option that stops mysqld -wantHelp= -for arg; do - case "$arg" in - -'?'|--help|--print-defaults|-V|--version) - wantHelp=1 - break - ;; - esac -done +# logging functions +mysql_log() { + local type="$1"; shift + printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" +} +mysql_note() { + mysql_log Note "$@" +} +mysql_warn() { + mysql_log Warn "$@" >&2 +} +mysql_error() { + mysql_log ERROR "$@" >&2 + exit 1 +} # usage: file_env VAR [DEFAULT] # ie: file_env 'XYZ_DB_PASSWORD' 'example' @@ -27,8 +27,7 @@ file_env() { local fileVar="${var}_FILE" local def="${2:-}" if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then - echo >&2 "error: both $var and $fileVar are set (but are exclusive)" - exit 1 + mysql_error "Both $var and $fileVar are set (but are exclusive)" fi local val="$def" if [ "${!var:-}" ]; then @@ -40,171 +39,288 @@ file_env() { unset "$fileVar" } -# usage: process_init_file FILENAME MYSQLCOMMAND... -# ie: process_init_file foo.sh mysql -uroot -# (process a single initializer file, based on its extension. we define this -# function here, so that initializer scripts (*.sh) can use the same logic, -# potentially recursively, or override the logic used in subsequent calls) -process_init_file() { - local f="$1"; shift - local mysql=( "$@" ) - - case "$f" in - *.sh) echo "$0: running $f"; . "$f" ;; - *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; - *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; - *) echo "$0: ignoring $f" ;; - esac +# usage: docker_process_init_files [file [file [...]]] +# ie: docker_process_init_files /always-initdb.d/* +# process initializer files, based on file extensions +docker_process_init_files() { + # mysql here for backwards compatibility "${mysql[@]}" + mysql=( docker_process_sql ) + echo + local f + for f; do + case "$f" in + *.sh) mysql_note "$0: running $f"; . "$f" ;; + *.sql) mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;; + *.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;; + *) mysql_warn "$0: ignoring $f" ;; + esac + echo + done } -_check_config() { - toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" ) +mysql_check_config() { + local toRun=( "$@" --verbose --help ) errors if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - cat >&2 <<-EOM - - ERROR: mysqld failed while attempting to check config - command was: "${toRun[*]}" - - $errors - EOM - exit 1 + mysql_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" fi } # Fetch value from server config # We use mysqld --verbose --help instead of my_print_defaults because the # latter only show values present in config files, and not server defaults -_get_config() { +mysql_get_config() { local conf="$1"; shift - "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null | awk '$1 == "'"$conf"'" { print $2; exit }' + "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \ + | awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' + # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" } -# allow the container to be started with `--user` -if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then - _check_config "$@" - DATADIR="$(_get_config 'datadir' "$@")" - mkdir -p "$DATADIR" - chown -R mysql:mysql "$DATADIR" - exec gosu mysql "$BASH_SOURCE" "$@" -fi - -if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then - # still need to check config, container may have started with --user - _check_config "$@" - # Get config - DATADIR="$(_get_config 'datadir' "$@")" - - if [ ! -d "$DATADIR/mysql" ]; then - file_env 'MYSQL_ROOT_PASSWORD' - if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - echo >&2 'error: database is uninitialized and password option is not specified ' - echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' - exit 1 - fi - - mkdir -p "$DATADIR" - - echo 'Initializing database' - # "Other options are passed to mysqld." (so we pass all "mysqld" arguments directly here) - mysql_install_db --datadir="$DATADIR" --rpm --keep-my-cnf "${@:2}" - echo 'Database initialized' - - SOCKET="$(_get_config 'socket' "$@")" - "$@" --skip-networking --socket="${SOCKET}" & - pid="$!" - - mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) +# Do a temporary startup of the MySQL server, for init purposes +docker_temp_server_start() { + local result=0 + "$@" --skip-networking --socket="${SOCKET}" & + if [ "$result" != "0" ];then + mysql_error "Unable to start server. Status code $result." + fi + # For 5.7+ the server is ready for use as soon as startup command unblocks + if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then + mysql_note "Waiting for server startup" + local i for i in {30..0}; do - if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then + if docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then break fi - echo 'MySQL init process in progress...' sleep 1 done if [ "$i" = 0 ]; then - echo >&2 'MySQL init process failed.' - exit 1 + mysql_error "Unable to start server." fi + fi +} - if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then - # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql - fi +# Stop the server. When using a local socket file mysqladmin will block until +# the shutdown is complete. +docker_temp_server_stop() { + local result=0 + mysqladmin --defaults-extra-file=<( echo "${PASSFILE}" ) shutdown -uroot --socket="${SOCKET}" || result=$? + if [ "$result" != "0" ]; then + mysql_error "Unable to shut down server. Status code $result." + fi +} - if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" - echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" - fi +# Verify that the minimally required password settings are set for new databases. +docker_verify_minimum_env() { + if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + mysql_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" + fi +} - rootCreate= - # default root to listen for connections from anywhere - file_env 'MYSQL_ROOT_HOST' '%' - if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 - read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; - GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ; - EOSQL - fi +# creates folders for the database +# also ensures permission for user mysql of run as root +docker_create_db_directories() { + local user; user="$(id -u)" + + # TODO other directories that are used by default? like /var/lib/mysql-files + # see https://github.com/docker-library/mysql/issues/562 + mkdir -p "$DATADIR" - "${mysql[@]}" <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work - SET @@SESSION.SQL_LOG_BIN=0; - - DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ; - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ; - GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; - ${rootCreate} - DROP DATABASE IF EXISTS test ; - FLUSH PRIVILEGES ; + if [ "$user" = "0" ]; then + # this will cause less disk access than `chown -R` + find "$DATADIR" \! -user mysql -exec chown mysql '{}' + + fi +} + +# initializes the database directory +docker_init_database_dir() { + mysql_note "Initializing database files" + mysql_install_db --datadir="$DATADIR" --rpm --keep-my-cnf "${@:2}" + mysql_note "Database files initialized" + + if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then + # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 + mysql_note "Initializing certificates" + mysql_ssl_rsa_setup --datadir="$DATADIR" + mysql_note "Certificates initialized" + fi +} + +# Loads various settings that are used elsewhere in the script +docker_setup_env() { + # Get config + DATADIR="$(mysql_get_config 'datadir' "$@")" + SOCKET="$(mysql_get_config 'socket' "$@")" + + # Initialize values that might be stored in a file + file_env 'MYSQL_ROOT_HOST' '%' + file_env 'MYSQL_DATABASE' + file_env 'MYSQL_USER' + file_env 'MYSQL_PASSWORD' + file_env 'MYSQL_ROOT_PASSWORD' +} + +# Execute sql script, passed via stdin +# usage: docker_process_sql [mysql-cli-args] +# ie: docker_process_sql --database=mydb <<<'INSERT ...' +# ie: docker_process_sql --database=mydb /dev/null - for f in /docker-entrypoint-initdb.d/*; do - process_init_file "$f" "${mysql[@]}" - done + docker_process_sql --database=mysql <<<"FLUSH PRIVILEGES ;" + fi +} - if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then - "${mysql[@]}" <<-EOSQL +# Mark root user as expired so the password must be changed before anything +# else can be done (only supported for 5.6+) +mysql_expire_root_user() { + if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then + if [ "${MYSQL_MAJOR}" = "5.5" ]; then + mysql_warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" + else + docker_process_sql --database=mysql <<-EOSQL ALTER USER 'root'@'%' PASSWORD EXPIRE; EOSQL fi - if ! kill -s TERM "$pid" || ! wait "$pid"; then - echo >&2 'MySQL init process failed.' - exit 1 + fi +} + +# check arguments for an option that would cause mysqld to stop +# return true if there is one +_want_help() { + local arg + for arg; do + case "$arg" in + -'?'|--help|--print-defaults|-V|--version) + return 0 + ;; + esac + done + return 1 +} + +_main() { + # if command starts with an option, prepend mysqld + if [ "${1:0:1}" = '-' ]; then + set -- mysqld "$@" + fi + + # skip setup if they aren't running mysqld or want an option that stops mysqld + if [ "$1" = 'mysqld' ] && ! _want_help "$@"; then + mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." + + # Load various environment variables + docker_setup_env "$@" + mysql_check_config "$@" + + # If container is started as root user, restart as dedicated mysql user + if [ "$(id -u)" = "0" ]; then + docker_create_db_directories + mysql_note "Switching to dedicated user 'mysql'" + exec gosu mysql "$BASH_SOURCE" "$@" fi - echo - echo 'MySQL init process done. Ready for start up.' - echo + # just in case the script was not started as root + docker_create_db_directories + + # If this is true then there's no database, and it needs to be initialized + if [ ! -d "$DATADIR/mysql" ]; then + docker_verify_minimum_env + docker_init_database_dir "$@" + + mysql_note "Starting temporary server" + docker_temp_server_start "$@" + mysql_note "Temporary server started." + + docker_setup_db + docker_process_init_files /docker-entrypoint-initdb.d/* + + mysql_expire_root_user + + mysql_note "Stopping temporary server" + docker_temp_server_stop + mysql_note "Temporary server stopped" + + unset PASSFILE + echo + mysql_note "MySQL init process done. Ready for start up." + echo + fi fi -fi + exec "$@" +} -exec "$@" +# This checks if the script has been sourced from elsewhere. +# If so we don't perform any further actions +# https://unix.stackexchange.com/a/215279 +if [ "${FUNCNAME[${#FUNCNAME[@]} - 1]}" != 'source' ]; then + _main "$@" +fi diff --git a/5.7/docker-entrypoint.sh b/5.7/docker-entrypoint.sh index 720ef6d65..0bbab715a 100755 --- a/5.7/docker-entrypoint.sh +++ b/5.7/docker-entrypoint.sh @@ -2,21 +2,21 @@ set -eo pipefail shopt -s nullglob -# if command starts with an option, prepend mysqld -if [ "${1:0:1}" = '-' ]; then - set -- mysqld "$@" -fi - -# skip setup if they want an option that stops mysqld -wantHelp= -for arg; do - case "$arg" in - -'?'|--help|--print-defaults|-V|--version) - wantHelp=1 - break - ;; - esac -done +# logging functions +mysql_log() { + local type="$1"; shift + printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" +} +mysql_note() { + mysql_log Note "$@" +} +mysql_warn() { + mysql_log Warn "$@" >&2 +} +mysql_error() { + mysql_log ERROR "$@" >&2 + exit 1 +} # usage: file_env VAR [DEFAULT] # ie: file_env 'XYZ_DB_PASSWORD' 'example' @@ -27,8 +27,7 @@ file_env() { local fileVar="${var}_FILE" local def="${2:-}" if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then - echo >&2 "error: both $var and $fileVar are set (but are exclusive)" - exit 1 + mysql_error "Both $var and $fileVar are set (but are exclusive)" fi local val="$def" if [ "${!var:-}" ]; then @@ -40,178 +39,287 @@ file_env() { unset "$fileVar" } -# usage: process_init_file FILENAME MYSQLCOMMAND... -# ie: process_init_file foo.sh mysql -uroot -# (process a single initializer file, based on its extension. we define this -# function here, so that initializer scripts (*.sh) can use the same logic, -# potentially recursively, or override the logic used in subsequent calls) -process_init_file() { - local f="$1"; shift - local mysql=( "$@" ) - - case "$f" in - *.sh) echo "$0: running $f"; . "$f" ;; - *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; - *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; - *) echo "$0: ignoring $f" ;; - esac +# usage: docker_process_init_files [file [file [...]]] +# ie: docker_process_init_files /always-initdb.d/* +# process initializer files, based on file extensions +docker_process_init_files() { + # mysql here for backwards compatibility "${mysql[@]}" + mysql=( docker_process_sql ) + echo + local f + for f; do + case "$f" in + *.sh) mysql_note "$0: running $f"; . "$f" ;; + *.sql) mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;; + *.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;; + *) mysql_warn "$0: ignoring $f" ;; + esac + echo + done } -_check_config() { - toRun=( "$@" --verbose --help ) +mysql_check_config() { + local toRun=( "$@" --verbose --help ) errors if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - cat >&2 <<-EOM - - ERROR: mysqld failed while attempting to check config - command was: "${toRun[*]}" - - $errors - EOM - exit 1 + mysql_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" fi } # Fetch value from server config # We use mysqld --verbose --help instead of my_print_defaults because the # latter only show values present in config files, and not server defaults -_get_config() { +mysql_get_config() { local conf="$1"; shift "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \ - | awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' + | awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" } -# allow the container to be started with `--user` -if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then - _check_config "$@" - DATADIR="$(_get_config 'datadir' "$@")" - mkdir -p "$DATADIR" - chown -R mysql:mysql "$DATADIR" - exec gosu mysql "$BASH_SOURCE" "$@" -fi - -if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then - # still need to check config, container may have started with --user - _check_config "$@" - # Get config - DATADIR="$(_get_config 'datadir' "$@")" - - if [ ! -d "$DATADIR/mysql" ]; then - file_env 'MYSQL_ROOT_PASSWORD' - if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - echo >&2 'error: database is uninitialized and password option is not specified ' - echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' - exit 1 - fi - - mkdir -p "$DATADIR" - - echo 'Initializing database' - "$@" --initialize-insecure - echo 'Database initialized' - - if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then - # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 - echo 'Initializing certificates' - mysql_ssl_rsa_setup --datadir="$DATADIR" - echo 'Certificates initialized' - fi - - SOCKET="$(_get_config 'socket' "$@")" - "$@" --skip-networking --socket="${SOCKET}" & - pid="$!" - - mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) +# Do a temporary startup of the MySQL server, for init purposes +docker_temp_server_start() { + local result=0 + "$@" --daemonize --skip-networking --socket="${SOCKET}" || result="$?" + if [ "$result" != "0" ];then + mysql_error "Unable to start server. Status code $result." + fi + # For 5.7+ the server is ready for use as soon as startup command unblocks + if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then + mysql_note "Waiting for server startup" + local i for i in {30..0}; do - if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then + if docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then break fi - echo 'MySQL init process in progress...' sleep 1 done if [ "$i" = 0 ]; then - echo >&2 'MySQL init process failed.' - exit 1 + mysql_error "Unable to start server." fi + fi +} - if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then - # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql - fi +# Stop the server. When using a local socket file mysqladmin will block until +# the shutdown is complete. +docker_temp_server_stop() { + local result=0 + mysqladmin --defaults-extra-file=<( echo "${PASSFILE}" ) shutdown -uroot --socket="${SOCKET}" || result=$? + if [ "$result" != "0" ]; then + mysql_error "Unable to shut down server. Status code $result." + fi +} - if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" - echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" - fi +# Verify that the minimally required password settings are set for new databases. +docker_verify_minimum_env() { + if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + mysql_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" + fi +} - rootCreate= - # default root to listen for connections from anywhere - file_env 'MYSQL_ROOT_HOST' '%' - if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 - read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; - GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ; - EOSQL - fi +# creates folders for the database +# also ensures permission for user mysql of run as root +docker_create_db_directories() { + local user; user="$(id -u)" - "${mysql[@]}" <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work - SET @@SESSION.SQL_LOG_BIN=0; + # TODO other directories that are used by default? like /var/lib/mysql-files + # see https://github.com/docker-library/mysql/issues/562 + mkdir -p "$DATADIR" - ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; - GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; - ${rootCreate} - DROP DATABASE IF EXISTS test ; - FLUSH PRIVILEGES ; - EOSQL + if [ "$user" = "0" ]; then + # this will cause less disk access than `chown -R` + find "$DATADIR" \! -user mysql -exec chown mysql '{}' + + fi +} - if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then - mysql+=( -p"${MYSQL_ROOT_PASSWORD}" ) - fi +# initializes the database directory +docker_init_database_dir() { + mysql_note "Initializing database files" + "$@" --initialize-insecure + mysql_note "Database files initialized" + + if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then + # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 + mysql_note "Initializing certificates" + mysql_ssl_rsa_setup --datadir="$DATADIR" + mysql_note "Certificates initialized" + fi +} - file_env 'MYSQL_DATABASE' - if [ "$MYSQL_DATABASE" ]; then - echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" - mysql+=( "$MYSQL_DATABASE" ) - fi +# Loads various settings that are used elsewhere in the script +docker_setup_env() { + # Get config + DATADIR="$(mysql_get_config 'datadir' "$@")" + SOCKET="$(mysql_get_config 'socket' "$@")" + + # Initialize values that might be stored in a file + file_env 'MYSQL_ROOT_HOST' '%' + file_env 'MYSQL_DATABASE' + file_env 'MYSQL_USER' + file_env 'MYSQL_PASSWORD' + file_env 'MYSQL_ROOT_PASSWORD' +} - file_env 'MYSQL_USER' - file_env 'MYSQL_PASSWORD' - if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then - echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}" +# Execute sql script, passed via stdin +# usage: docker_process_sql [mysql-cli-args] +# ie: docker_process_sql --database=mydb <<<'INSERT ...' +# ie: docker_process_sql --database=mydb /dev/null - for f in /docker-entrypoint-initdb.d/*; do - process_init_file "$f" "${mysql[@]}" - done + docker_process_sql --database=mysql <<<"FLUSH PRIVILEGES ;" + fi +} - if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then - "${mysql[@]}" <<-EOSQL +# Mark root user as expired so the password must be changed before anything +# else can be done (only supported for 5.6+) +mysql_expire_root_user() { + if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then + if [ "${MYSQL_MAJOR}" = "5.5" ]; then + mysql_warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" + else + docker_process_sql --database=mysql <<-EOSQL ALTER USER 'root'@'%' PASSWORD EXPIRE; EOSQL fi - if ! kill -s TERM "$pid" || ! wait "$pid"; then - echo >&2 'MySQL init process failed.' - exit 1 + fi +} + +# check arguments for an option that would cause mysqld to stop +# return true if there is one +_want_help() { + local arg + for arg; do + case "$arg" in + -'?'|--help|--print-defaults|-V|--version) + return 0 + ;; + esac + done + return 1 +} + +_main() { + # if command starts with an option, prepend mysqld + if [ "${1:0:1}" = '-' ]; then + set -- mysqld "$@" + fi + + # skip setup if they aren't running mysqld or want an option that stops mysqld + if [ "$1" = 'mysqld' ] && ! _want_help "$@"; then + mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." + + # Load various environment variables + docker_setup_env "$@" + mysql_check_config "$@" + + # If container is started as root user, restart as dedicated mysql user + if [ "$(id -u)" = "0" ]; then + docker_create_db_directories + mysql_note "Switching to dedicated user 'mysql'" + exec gosu mysql "$BASH_SOURCE" "$@" fi - echo - echo 'MySQL init process done. Ready for start up.' - echo + # just in case the script was not started as root + docker_create_db_directories + + # If this is true then there's no database, and it needs to be initialized + if [ ! -d "$DATADIR/mysql" ]; then + docker_verify_minimum_env + docker_init_database_dir "$@" + + mysql_note "Starting temporary server" + docker_temp_server_start "$@" + mysql_note "Temporary server started." + + docker_setup_db + docker_process_init_files /docker-entrypoint-initdb.d/* + + mysql_expire_root_user + + mysql_note "Stopping temporary server" + docker_temp_server_stop + mysql_note "Temporary server stopped" + + unset PASSFILE + echo + mysql_note "MySQL init process done. Ready for start up." + echo + fi fi -fi + exec "$@" +} -exec "$@" +# This checks if the script has been sourced from elsewhere. +# If so we don't perform any further actions +# https://unix.stackexchange.com/a/215279 +if [ "${FUNCNAME[${#FUNCNAME[@]} - 1]}" != 'source' ]; then + _main "$@" +fi diff --git a/8.0/docker-entrypoint.sh b/8.0/docker-entrypoint.sh index 720ef6d65..0bbab715a 100755 --- a/8.0/docker-entrypoint.sh +++ b/8.0/docker-entrypoint.sh @@ -2,21 +2,21 @@ set -eo pipefail shopt -s nullglob -# if command starts with an option, prepend mysqld -if [ "${1:0:1}" = '-' ]; then - set -- mysqld "$@" -fi - -# skip setup if they want an option that stops mysqld -wantHelp= -for arg; do - case "$arg" in - -'?'|--help|--print-defaults|-V|--version) - wantHelp=1 - break - ;; - esac -done +# logging functions +mysql_log() { + local type="$1"; shift + printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" +} +mysql_note() { + mysql_log Note "$@" +} +mysql_warn() { + mysql_log Warn "$@" >&2 +} +mysql_error() { + mysql_log ERROR "$@" >&2 + exit 1 +} # usage: file_env VAR [DEFAULT] # ie: file_env 'XYZ_DB_PASSWORD' 'example' @@ -27,8 +27,7 @@ file_env() { local fileVar="${var}_FILE" local def="${2:-}" if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then - echo >&2 "error: both $var and $fileVar are set (but are exclusive)" - exit 1 + mysql_error "Both $var and $fileVar are set (but are exclusive)" fi local val="$def" if [ "${!var:-}" ]; then @@ -40,178 +39,287 @@ file_env() { unset "$fileVar" } -# usage: process_init_file FILENAME MYSQLCOMMAND... -# ie: process_init_file foo.sh mysql -uroot -# (process a single initializer file, based on its extension. we define this -# function here, so that initializer scripts (*.sh) can use the same logic, -# potentially recursively, or override the logic used in subsequent calls) -process_init_file() { - local f="$1"; shift - local mysql=( "$@" ) - - case "$f" in - *.sh) echo "$0: running $f"; . "$f" ;; - *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; - *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; - *) echo "$0: ignoring $f" ;; - esac +# usage: docker_process_init_files [file [file [...]]] +# ie: docker_process_init_files /always-initdb.d/* +# process initializer files, based on file extensions +docker_process_init_files() { + # mysql here for backwards compatibility "${mysql[@]}" + mysql=( docker_process_sql ) + echo + local f + for f; do + case "$f" in + *.sh) mysql_note "$0: running $f"; . "$f" ;; + *.sql) mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;; + *.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;; + *) mysql_warn "$0: ignoring $f" ;; + esac + echo + done } -_check_config() { - toRun=( "$@" --verbose --help ) +mysql_check_config() { + local toRun=( "$@" --verbose --help ) errors if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - cat >&2 <<-EOM - - ERROR: mysqld failed while attempting to check config - command was: "${toRun[*]}" - - $errors - EOM - exit 1 + mysql_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" fi } # Fetch value from server config # We use mysqld --verbose --help instead of my_print_defaults because the # latter only show values present in config files, and not server defaults -_get_config() { +mysql_get_config() { local conf="$1"; shift "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \ - | awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' + | awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)" } -# allow the container to be started with `--user` -if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then - _check_config "$@" - DATADIR="$(_get_config 'datadir' "$@")" - mkdir -p "$DATADIR" - chown -R mysql:mysql "$DATADIR" - exec gosu mysql "$BASH_SOURCE" "$@" -fi - -if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then - # still need to check config, container may have started with --user - _check_config "$@" - # Get config - DATADIR="$(_get_config 'datadir' "$@")" - - if [ ! -d "$DATADIR/mysql" ]; then - file_env 'MYSQL_ROOT_PASSWORD' - if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - echo >&2 'error: database is uninitialized and password option is not specified ' - echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' - exit 1 - fi - - mkdir -p "$DATADIR" - - echo 'Initializing database' - "$@" --initialize-insecure - echo 'Database initialized' - - if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then - # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 - echo 'Initializing certificates' - mysql_ssl_rsa_setup --datadir="$DATADIR" - echo 'Certificates initialized' - fi - - SOCKET="$(_get_config 'socket' "$@")" - "$@" --skip-networking --socket="${SOCKET}" & - pid="$!" - - mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" ) +# Do a temporary startup of the MySQL server, for init purposes +docker_temp_server_start() { + local result=0 + "$@" --daemonize --skip-networking --socket="${SOCKET}" || result="$?" + if [ "$result" != "0" ];then + mysql_error "Unable to start server. Status code $result." + fi + # For 5.7+ the server is ready for use as soon as startup command unblocks + if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then + mysql_note "Waiting for server startup" + local i for i in {30..0}; do - if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then + if docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then break fi - echo 'MySQL init process in progress...' sleep 1 done if [ "$i" = 0 ]; then - echo >&2 'MySQL init process failed.' - exit 1 + mysql_error "Unable to start server." fi + fi +} - if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then - # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql - fi +# Stop the server. When using a local socket file mysqladmin will block until +# the shutdown is complete. +docker_temp_server_stop() { + local result=0 + mysqladmin --defaults-extra-file=<( echo "${PASSFILE}" ) shutdown -uroot --socket="${SOCKET}" || result=$? + if [ "$result" != "0" ]; then + mysql_error "Unable to shut down server. Status code $result." + fi +} - if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" - echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" - fi +# Verify that the minimally required password settings are set for new databases. +docker_verify_minimum_env() { + if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + mysql_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" + fi +} - rootCreate= - # default root to listen for connections from anywhere - file_env 'MYSQL_ROOT_HOST' '%' - if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 - read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; - GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ; - EOSQL - fi +# creates folders for the database +# also ensures permission for user mysql of run as root +docker_create_db_directories() { + local user; user="$(id -u)" - "${mysql[@]}" <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work - SET @@SESSION.SQL_LOG_BIN=0; + # TODO other directories that are used by default? like /var/lib/mysql-files + # see https://github.com/docker-library/mysql/issues/562 + mkdir -p "$DATADIR" - ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; - GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; - ${rootCreate} - DROP DATABASE IF EXISTS test ; - FLUSH PRIVILEGES ; - EOSQL + if [ "$user" = "0" ]; then + # this will cause less disk access than `chown -R` + find "$DATADIR" \! -user mysql -exec chown mysql '{}' + + fi +} - if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then - mysql+=( -p"${MYSQL_ROOT_PASSWORD}" ) - fi +# initializes the database directory +docker_init_database_dir() { + mysql_note "Initializing database files" + "$@" --initialize-insecure + mysql_note "Database files initialized" + + if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then + # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84 + mysql_note "Initializing certificates" + mysql_ssl_rsa_setup --datadir="$DATADIR" + mysql_note "Certificates initialized" + fi +} - file_env 'MYSQL_DATABASE' - if [ "$MYSQL_DATABASE" ]; then - echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" - mysql+=( "$MYSQL_DATABASE" ) - fi +# Loads various settings that are used elsewhere in the script +docker_setup_env() { + # Get config + DATADIR="$(mysql_get_config 'datadir' "$@")" + SOCKET="$(mysql_get_config 'socket' "$@")" + + # Initialize values that might be stored in a file + file_env 'MYSQL_ROOT_HOST' '%' + file_env 'MYSQL_DATABASE' + file_env 'MYSQL_USER' + file_env 'MYSQL_PASSWORD' + file_env 'MYSQL_ROOT_PASSWORD' +} - file_env 'MYSQL_USER' - file_env 'MYSQL_PASSWORD' - if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then - echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}" +# Execute sql script, passed via stdin +# usage: docker_process_sql [mysql-cli-args] +# ie: docker_process_sql --database=mydb <<<'INSERT ...' +# ie: docker_process_sql --database=mydb /dev/null - for f in /docker-entrypoint-initdb.d/*; do - process_init_file "$f" "${mysql[@]}" - done + docker_process_sql --database=mysql <<<"FLUSH PRIVILEGES ;" + fi +} - if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then - "${mysql[@]}" <<-EOSQL +# Mark root user as expired so the password must be changed before anything +# else can be done (only supported for 5.6+) +mysql_expire_root_user() { + if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then + if [ "${MYSQL_MAJOR}" = "5.5" ]; then + mysql_warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" + else + docker_process_sql --database=mysql <<-EOSQL ALTER USER 'root'@'%' PASSWORD EXPIRE; EOSQL fi - if ! kill -s TERM "$pid" || ! wait "$pid"; then - echo >&2 'MySQL init process failed.' - exit 1 + fi +} + +# check arguments for an option that would cause mysqld to stop +# return true if there is one +_want_help() { + local arg + for arg; do + case "$arg" in + -'?'|--help|--print-defaults|-V|--version) + return 0 + ;; + esac + done + return 1 +} + +_main() { + # if command starts with an option, prepend mysqld + if [ "${1:0:1}" = '-' ]; then + set -- mysqld "$@" + fi + + # skip setup if they aren't running mysqld or want an option that stops mysqld + if [ "$1" = 'mysqld' ] && ! _want_help "$@"; then + mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." + + # Load various environment variables + docker_setup_env "$@" + mysql_check_config "$@" + + # If container is started as root user, restart as dedicated mysql user + if [ "$(id -u)" = "0" ]; then + docker_create_db_directories + mysql_note "Switching to dedicated user 'mysql'" + exec gosu mysql "$BASH_SOURCE" "$@" fi - echo - echo 'MySQL init process done. Ready for start up.' - echo + # just in case the script was not started as root + docker_create_db_directories + + # If this is true then there's no database, and it needs to be initialized + if [ ! -d "$DATADIR/mysql" ]; then + docker_verify_minimum_env + docker_init_database_dir "$@" + + mysql_note "Starting temporary server" + docker_temp_server_start "$@" + mysql_note "Temporary server started." + + docker_setup_db + docker_process_init_files /docker-entrypoint-initdb.d/* + + mysql_expire_root_user + + mysql_note "Stopping temporary server" + docker_temp_server_stop + mysql_note "Temporary server stopped" + + unset PASSFILE + echo + mysql_note "MySQL init process done. Ready for start up." + echo + fi fi -fi + exec "$@" +} -exec "$@" +# This checks if the script has been sourced from elsewhere. +# If so we don't perform any further actions +# https://unix.stackexchange.com/a/215279 +if [ "${FUNCNAME[${#FUNCNAME[@]} - 1]}" != 'source' ]; then + _main "$@" +fi diff --git a/update.sh b/update.sh index d7de76e49..e57663ef4 100755 --- a/update.sh +++ b/update.sh @@ -16,17 +16,14 @@ declare -A debianVariants=( # Templating declare -A passwordset -passwordset["5.5"]="DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;\nSET PASSWORD FOR 'root'@'localhost'=PASSWORD('\${MYSQL_ROOT_PASSWORD}');" passwordset["5.6"]="DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;\nSET PASSWORD FOR 'root'@'localhost'=PASSWORD('\${MYSQL_ROOT_PASSWORD}');" passwordset["5.7"]="ALTER USER 'root'@'localhost' IDENTIFIED BY '\${MYSQL_ROOT_PASSWORD}';" passwordset["8.0"]="ALTER USER 'root'@'localhost' IDENTIFIED BY '\${MYSQL_ROOT_PASSWORD}';" declare -A database_init -database_init["5.5"]='mysql_install_db --datadir="$DATADIR" --rpm --basedir=/usr/local/mysql "${@:2}"' database_init["5.6"]='mysql_install_db --datadir="$DATADIR" --rpm --keep-my-cnf "${@:2}"' database_init["5.7"]='"$@" --initialize-insecure' database_init["8.0"]='"$@" --initialize-insecure' declare -A server_startup -server_startup["5.5"]='"$@" --skip-networking --basedir=/usr/local/mysql --socket="${SOCKET}" \&' server_startup["5.6"]='"$@" --skip-networking --socket="${SOCKET}" \&' server_startup["5.7"]='"$@" --daemonize --skip-networking --socket="${SOCKET}" || result="$?"' server_startup["8.0"]='"$@" --daemonize --skip-networking --socket="${SOCKET}" || result="$?"' From 8a58acbab390df5a8f91467db60f5ab247786bcb Mon Sep 17 00:00:00 2001 From: Joe Ferguson Date: Mon, 3 Jun 2019 16:38:41 -0700 Subject: [PATCH 39/42] Adjustments from tianon's comments - move PASSFILE to a function `_mysql_passfile` - consistently use `-n` over `! -z` or `[ "$var" ]` - drop 5.5 specific blocks - move `docker_create_db_directories` up - rename `_want_help` to `_mysql_want_help` to match new `_mysql_passfile` --- .template.Debian/docker-entrypoint.sh | 74 ++++++++++++++------------- 5.6/docker-entrypoint.sh | 74 ++++++++++++++------------- 5.7/docker-entrypoint.sh | 74 ++++++++++++++------------- 8.0/docker-entrypoint.sh | 74 ++++++++++++++------------- 4 files changed, 152 insertions(+), 144 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 4b92486f7..704e1a1a6 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -85,11 +85,13 @@ docker_temp_server_start() { fi # For 5.7+ the server is ready for use as soon as startup command unblocks - if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then + if [ "${MYSQL_MAJOR}" = "5.6" ]; then mysql_note "Waiting for server startup" local i for i in {30..0}; do - if docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it hasn't been set yet + if MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then break fi sleep 1 @@ -104,7 +106,7 @@ docker_temp_server_start() { # the shutdown is complete. docker_temp_server_stop() { local result=0 - mysqladmin --defaults-extra-file=<( echo "${PASSFILE}" ) shutdown -uroot --socket="${SOCKET}" || result=$? + mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}" || result=$? if [ "$result" != "0" ]; then mysql_error "Unable to shut down server. Status code $result." fi @@ -170,7 +172,7 @@ docker_process_sql() { set -- --database="$MYSQL_DATABASE" "$@" fi - mysql --defaults-file=<( echo "${PASSFILE}" ) --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + mysql --defaults-file=<( _mysql_passfile ) --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" } # Initializes database with timezone info and root password, plus optional extra db/user @@ -178,17 +180,21 @@ docker_setup_db() { # Load timezone info into database if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | docker_process_sql --database=mysql + mysql_tzinfo_to_sql /usr/share/zoneinfo \ + | sed 's/Local time zone must be set--see zic manual page/FCTY/' \ + | MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it hasn't been set yet fi # Generate random root password - if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + if [ -n "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" fi # Sets root password and creates root users for non-localhost hosts rootCreate= # default root to listen for connections from anywhere - if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then + if [ -n "$MYSQL_ROOT_HOST" ] && [ "$MYSQL_ROOT_HOST" != 'localhost' ]; then # no, we don't care if read finds a terminating character in this heredoc # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 read -r -d '' rootCreate <<-EOSQL || true @@ -197,7 +203,9 @@ docker_setup_db() { EOSQL fi - docker_process_sql --database=mysql <<-EOSQL + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it is just now being set + MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql <<-EOSQL -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; @@ -209,27 +217,17 @@ docker_setup_db() { DROP DATABASE IF EXISTS test ; EOSQL - # Write the password to the "file" the client uses - # the client command will use process substitution to create a file on the fly - # ie: --defaults-file=<( echo "${PASSFILE}" ) - if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then - read -r -d '' PASSFILE <<-EOF || true - [client] - password="${MYSQL_ROOT_PASSWORD}" - EOF - fi - # Creates a custom database and user if specified - if [ "$MYSQL_DATABASE" ]; then + if [ -n "$MYSQL_DATABASE" ]; then mysql_note "Creating database ${MYSQL_DATABASE}" docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" fi - if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then + if [ -n "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then mysql_note "Creating user ${MYSQL_USER}" docker_process_sql --database=mysql <<<"CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" - if [ "$MYSQL_DATABASE" ]; then + if [ -n "$MYSQL_DATABASE" ]; then mysql_note "Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE}" docker_process_sql --database=mysql <<<"GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" fi @@ -238,23 +236,31 @@ docker_setup_db() { fi } +_mysql_passfile() { + # echo the password to the "file" the client uses + # the client command will use process substitution to create a file on the fly + # ie: --defaults-file=<( _mysql_passfile ) + if [ -n "$MYSQL_ROOT_PASSWORD" ]; then + cat <<-EOF + [client] + password="${MYSQL_ROOT_PASSWORD}" + EOF + fi +} + # Mark root user as expired so the password must be changed before anything # else can be done (only supported for 5.6+) mysql_expire_root_user() { - if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then - if [ "${MYSQL_MAJOR}" = "5.5" ]; then - mysql_warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" - else - docker_process_sql --database=mysql <<-EOSQL - ALTER USER 'root'@'%' PASSWORD EXPIRE; - EOSQL - fi + if [ -n "$MYSQL_ONETIME_PASSWORD" ]; then + docker_process_sql --database=mysql <<-EOSQL + ALTER USER 'root'@'%' PASSWORD EXPIRE; + EOSQL fi } # check arguments for an option that would cause mysqld to stop # return true if there is one -_want_help() { +_mysql_want_help() { local arg for arg; do case "$arg" in @@ -273,23 +279,20 @@ _main() { fi # skip setup if they aren't running mysqld or want an option that stops mysqld - if [ "$1" = 'mysqld' ] && ! _want_help "$@"; then + if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." # Load various environment variables docker_setup_env "$@" mysql_check_config "$@" + docker_create_db_directories # If container is started as root user, restart as dedicated mysql user if [ "$(id -u)" = "0" ]; then - docker_create_db_directories mysql_note "Switching to dedicated user 'mysql'" exec gosu mysql "$BASH_SOURCE" "$@" fi - # just in case the script was not started as root - docker_create_db_directories - # If this is true then there's no database, and it needs to be initialized if [ ! -d "$DATADIR/mysql" ]; then docker_verify_minimum_env @@ -308,7 +311,6 @@ _main() { docker_temp_server_stop mysql_note "Temporary server stopped" - unset PASSFILE echo mysql_note "MySQL init process done. Ready for start up." echo diff --git a/5.6/docker-entrypoint.sh b/5.6/docker-entrypoint.sh index 7a47b0161..d4cba6fc4 100755 --- a/5.6/docker-entrypoint.sh +++ b/5.6/docker-entrypoint.sh @@ -85,11 +85,13 @@ docker_temp_server_start() { fi # For 5.7+ the server is ready for use as soon as startup command unblocks - if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then + if [ "${MYSQL_MAJOR}" = "5.6" ]; then mysql_note "Waiting for server startup" local i for i in {30..0}; do - if docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it hasn't been set yet + if MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then break fi sleep 1 @@ -104,7 +106,7 @@ docker_temp_server_start() { # the shutdown is complete. docker_temp_server_stop() { local result=0 - mysqladmin --defaults-extra-file=<( echo "${PASSFILE}" ) shutdown -uroot --socket="${SOCKET}" || result=$? + mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}" || result=$? if [ "$result" != "0" ]; then mysql_error "Unable to shut down server. Status code $result." fi @@ -170,7 +172,7 @@ docker_process_sql() { set -- --database="$MYSQL_DATABASE" "$@" fi - mysql --defaults-file=<( echo "${PASSFILE}" ) --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + mysql --defaults-file=<( _mysql_passfile ) --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" } # Initializes database with timezone info and root password, plus optional extra db/user @@ -178,17 +180,21 @@ docker_setup_db() { # Load timezone info into database if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | docker_process_sql --database=mysql + mysql_tzinfo_to_sql /usr/share/zoneinfo \ + | sed 's/Local time zone must be set--see zic manual page/FCTY/' \ + | MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it hasn't been set yet fi # Generate random root password - if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + if [ -n "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" fi # Sets root password and creates root users for non-localhost hosts rootCreate= # default root to listen for connections from anywhere - if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then + if [ -n "$MYSQL_ROOT_HOST" ] && [ "$MYSQL_ROOT_HOST" != 'localhost' ]; then # no, we don't care if read finds a terminating character in this heredoc # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 read -r -d '' rootCreate <<-EOSQL || true @@ -197,7 +203,9 @@ docker_setup_db() { EOSQL fi - docker_process_sql --database=mysql <<-EOSQL + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it is just now being set + MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql <<-EOSQL -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; @@ -210,27 +218,17 @@ SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}'); DROP DATABASE IF EXISTS test ; EOSQL - # Write the password to the "file" the client uses - # the client command will use process substitution to create a file on the fly - # ie: --defaults-file=<( echo "${PASSFILE}" ) - if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then - read -r -d '' PASSFILE <<-EOF || true - [client] - password="${MYSQL_ROOT_PASSWORD}" - EOF - fi - # Creates a custom database and user if specified - if [ "$MYSQL_DATABASE" ]; then + if [ -n "$MYSQL_DATABASE" ]; then mysql_note "Creating database ${MYSQL_DATABASE}" docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" fi - if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then + if [ -n "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then mysql_note "Creating user ${MYSQL_USER}" docker_process_sql --database=mysql <<<"CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" - if [ "$MYSQL_DATABASE" ]; then + if [ -n "$MYSQL_DATABASE" ]; then mysql_note "Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE}" docker_process_sql --database=mysql <<<"GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" fi @@ -239,23 +237,31 @@ SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}'); fi } +_mysql_passfile() { + # echo the password to the "file" the client uses + # the client command will use process substitution to create a file on the fly + # ie: --defaults-file=<( _mysql_passfile ) + if [ -n "$MYSQL_ROOT_PASSWORD" ]; then + cat <<-EOF + [client] + password="${MYSQL_ROOT_PASSWORD}" + EOF + fi +} + # Mark root user as expired so the password must be changed before anything # else can be done (only supported for 5.6+) mysql_expire_root_user() { - if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then - if [ "${MYSQL_MAJOR}" = "5.5" ]; then - mysql_warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" - else - docker_process_sql --database=mysql <<-EOSQL - ALTER USER 'root'@'%' PASSWORD EXPIRE; - EOSQL - fi + if [ -n "$MYSQL_ONETIME_PASSWORD" ]; then + docker_process_sql --database=mysql <<-EOSQL + ALTER USER 'root'@'%' PASSWORD EXPIRE; + EOSQL fi } # check arguments for an option that would cause mysqld to stop # return true if there is one -_want_help() { +_mysql_want_help() { local arg for arg; do case "$arg" in @@ -274,23 +280,20 @@ _main() { fi # skip setup if they aren't running mysqld or want an option that stops mysqld - if [ "$1" = 'mysqld' ] && ! _want_help "$@"; then + if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." # Load various environment variables docker_setup_env "$@" mysql_check_config "$@" + docker_create_db_directories # If container is started as root user, restart as dedicated mysql user if [ "$(id -u)" = "0" ]; then - docker_create_db_directories mysql_note "Switching to dedicated user 'mysql'" exec gosu mysql "$BASH_SOURCE" "$@" fi - # just in case the script was not started as root - docker_create_db_directories - # If this is true then there's no database, and it needs to be initialized if [ ! -d "$DATADIR/mysql" ]; then docker_verify_minimum_env @@ -309,7 +312,6 @@ _main() { docker_temp_server_stop mysql_note "Temporary server stopped" - unset PASSFILE echo mysql_note "MySQL init process done. Ready for start up." echo diff --git a/5.7/docker-entrypoint.sh b/5.7/docker-entrypoint.sh index 0bbab715a..82184a16c 100755 --- a/5.7/docker-entrypoint.sh +++ b/5.7/docker-entrypoint.sh @@ -85,11 +85,13 @@ docker_temp_server_start() { fi # For 5.7+ the server is ready for use as soon as startup command unblocks - if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then + if [ "${MYSQL_MAJOR}" = "5.6" ]; then mysql_note "Waiting for server startup" local i for i in {30..0}; do - if docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it hasn't been set yet + if MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then break fi sleep 1 @@ -104,7 +106,7 @@ docker_temp_server_start() { # the shutdown is complete. docker_temp_server_stop() { local result=0 - mysqladmin --defaults-extra-file=<( echo "${PASSFILE}" ) shutdown -uroot --socket="${SOCKET}" || result=$? + mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}" || result=$? if [ "$result" != "0" ]; then mysql_error "Unable to shut down server. Status code $result." fi @@ -170,7 +172,7 @@ docker_process_sql() { set -- --database="$MYSQL_DATABASE" "$@" fi - mysql --defaults-file=<( echo "${PASSFILE}" ) --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + mysql --defaults-file=<( _mysql_passfile ) --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" } # Initializes database with timezone info and root password, plus optional extra db/user @@ -178,17 +180,21 @@ docker_setup_db() { # Load timezone info into database if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | docker_process_sql --database=mysql + mysql_tzinfo_to_sql /usr/share/zoneinfo \ + | sed 's/Local time zone must be set--see zic manual page/FCTY/' \ + | MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it hasn't been set yet fi # Generate random root password - if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + if [ -n "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" fi # Sets root password and creates root users for non-localhost hosts rootCreate= # default root to listen for connections from anywhere - if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then + if [ -n "$MYSQL_ROOT_HOST" ] && [ "$MYSQL_ROOT_HOST" != 'localhost' ]; then # no, we don't care if read finds a terminating character in this heredoc # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 read -r -d '' rootCreate <<-EOSQL || true @@ -197,7 +203,9 @@ docker_setup_db() { EOSQL fi - docker_process_sql --database=mysql <<-EOSQL + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it is just now being set + MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql <<-EOSQL -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; @@ -209,27 +217,17 @@ docker_setup_db() { DROP DATABASE IF EXISTS test ; EOSQL - # Write the password to the "file" the client uses - # the client command will use process substitution to create a file on the fly - # ie: --defaults-file=<( echo "${PASSFILE}" ) - if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then - read -r -d '' PASSFILE <<-EOF || true - [client] - password="${MYSQL_ROOT_PASSWORD}" - EOF - fi - # Creates a custom database and user if specified - if [ "$MYSQL_DATABASE" ]; then + if [ -n "$MYSQL_DATABASE" ]; then mysql_note "Creating database ${MYSQL_DATABASE}" docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" fi - if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then + if [ -n "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then mysql_note "Creating user ${MYSQL_USER}" docker_process_sql --database=mysql <<<"CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" - if [ "$MYSQL_DATABASE" ]; then + if [ -n "$MYSQL_DATABASE" ]; then mysql_note "Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE}" docker_process_sql --database=mysql <<<"GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" fi @@ -238,23 +236,31 @@ docker_setup_db() { fi } +_mysql_passfile() { + # echo the password to the "file" the client uses + # the client command will use process substitution to create a file on the fly + # ie: --defaults-file=<( _mysql_passfile ) + if [ -n "$MYSQL_ROOT_PASSWORD" ]; then + cat <<-EOF + [client] + password="${MYSQL_ROOT_PASSWORD}" + EOF + fi +} + # Mark root user as expired so the password must be changed before anything # else can be done (only supported for 5.6+) mysql_expire_root_user() { - if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then - if [ "${MYSQL_MAJOR}" = "5.5" ]; then - mysql_warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" - else - docker_process_sql --database=mysql <<-EOSQL - ALTER USER 'root'@'%' PASSWORD EXPIRE; - EOSQL - fi + if [ -n "$MYSQL_ONETIME_PASSWORD" ]; then + docker_process_sql --database=mysql <<-EOSQL + ALTER USER 'root'@'%' PASSWORD EXPIRE; + EOSQL fi } # check arguments for an option that would cause mysqld to stop # return true if there is one -_want_help() { +_mysql_want_help() { local arg for arg; do case "$arg" in @@ -273,23 +279,20 @@ _main() { fi # skip setup if they aren't running mysqld or want an option that stops mysqld - if [ "$1" = 'mysqld' ] && ! _want_help "$@"; then + if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." # Load various environment variables docker_setup_env "$@" mysql_check_config "$@" + docker_create_db_directories # If container is started as root user, restart as dedicated mysql user if [ "$(id -u)" = "0" ]; then - docker_create_db_directories mysql_note "Switching to dedicated user 'mysql'" exec gosu mysql "$BASH_SOURCE" "$@" fi - # just in case the script was not started as root - docker_create_db_directories - # If this is true then there's no database, and it needs to be initialized if [ ! -d "$DATADIR/mysql" ]; then docker_verify_minimum_env @@ -308,7 +311,6 @@ _main() { docker_temp_server_stop mysql_note "Temporary server stopped" - unset PASSFILE echo mysql_note "MySQL init process done. Ready for start up." echo diff --git a/8.0/docker-entrypoint.sh b/8.0/docker-entrypoint.sh index 0bbab715a..82184a16c 100755 --- a/8.0/docker-entrypoint.sh +++ b/8.0/docker-entrypoint.sh @@ -85,11 +85,13 @@ docker_temp_server_start() { fi # For 5.7+ the server is ready for use as soon as startup command unblocks - if [ "${MYSQL_MAJOR}" = "5.5" ] || [ "${MYSQL_MAJOR}" = "5.6" ]; then + if [ "${MYSQL_MAJOR}" = "5.6" ]; then mysql_note "Waiting for server startup" local i for i in {30..0}; do - if docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it hasn't been set yet + if MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then break fi sleep 1 @@ -104,7 +106,7 @@ docker_temp_server_start() { # the shutdown is complete. docker_temp_server_stop() { local result=0 - mysqladmin --defaults-extra-file=<( echo "${PASSFILE}" ) shutdown -uroot --socket="${SOCKET}" || result=$? + mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}" || result=$? if [ "$result" != "0" ]; then mysql_error "Unable to shut down server. Status code $result." fi @@ -170,7 +172,7 @@ docker_process_sql() { set -- --database="$MYSQL_DATABASE" "$@" fi - mysql --defaults-file=<( echo "${PASSFILE}" ) --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + mysql --defaults-file=<( _mysql_passfile ) --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" } # Initializes database with timezone info and root password, plus optional extra db/user @@ -178,17 +180,21 @@ docker_setup_db() { # Load timezone info into database if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then # sed is for https://bugs.mysql.com/bug.php?id=20545 - mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | docker_process_sql --database=mysql + mysql_tzinfo_to_sql /usr/share/zoneinfo \ + | sed 's/Local time zone must be set--see zic manual page/FCTY/' \ + | MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it hasn't been set yet fi # Generate random root password - if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then + if [ -n "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" fi # Sets root password and creates root users for non-localhost hosts rootCreate= # default root to listen for connections from anywhere - if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then + if [ -n "$MYSQL_ROOT_HOST" ] && [ "$MYSQL_ROOT_HOST" != 'localhost' ]; then # no, we don't care if read finds a terminating character in this heredoc # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 read -r -d '' rootCreate <<-EOSQL || true @@ -197,7 +203,9 @@ docker_setup_db() { EOSQL fi - docker_process_sql --database=mysql <<-EOSQL + # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # so that it won't try to fill in a password file when it is just now being set + MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql <<-EOSQL -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; @@ -209,27 +217,17 @@ docker_setup_db() { DROP DATABASE IF EXISTS test ; EOSQL - # Write the password to the "file" the client uses - # the client command will use process substitution to create a file on the fly - # ie: --defaults-file=<( echo "${PASSFILE}" ) - if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then - read -r -d '' PASSFILE <<-EOF || true - [client] - password="${MYSQL_ROOT_PASSWORD}" - EOF - fi - # Creates a custom database and user if specified - if [ "$MYSQL_DATABASE" ]; then + if [ -n "$MYSQL_DATABASE" ]; then mysql_note "Creating database ${MYSQL_DATABASE}" docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" fi - if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then + if [ -n "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then mysql_note "Creating user ${MYSQL_USER}" docker_process_sql --database=mysql <<<"CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" - if [ "$MYSQL_DATABASE" ]; then + if [ -n "$MYSQL_DATABASE" ]; then mysql_note "Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE}" docker_process_sql --database=mysql <<<"GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" fi @@ -238,23 +236,31 @@ docker_setup_db() { fi } +_mysql_passfile() { + # echo the password to the "file" the client uses + # the client command will use process substitution to create a file on the fly + # ie: --defaults-file=<( _mysql_passfile ) + if [ -n "$MYSQL_ROOT_PASSWORD" ]; then + cat <<-EOF + [client] + password="${MYSQL_ROOT_PASSWORD}" + EOF + fi +} + # Mark root user as expired so the password must be changed before anything # else can be done (only supported for 5.6+) mysql_expire_root_user() { - if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then - if [ "${MYSQL_MAJOR}" = "5.5" ]; then - mysql_warn "MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)" - else - docker_process_sql --database=mysql <<-EOSQL - ALTER USER 'root'@'%' PASSWORD EXPIRE; - EOSQL - fi + if [ -n "$MYSQL_ONETIME_PASSWORD" ]; then + docker_process_sql --database=mysql <<-EOSQL + ALTER USER 'root'@'%' PASSWORD EXPIRE; + EOSQL fi } # check arguments for an option that would cause mysqld to stop # return true if there is one -_want_help() { +_mysql_want_help() { local arg for arg; do case "$arg" in @@ -273,23 +279,20 @@ _main() { fi # skip setup if they aren't running mysqld or want an option that stops mysqld - if [ "$1" = 'mysqld' ] && ! _want_help "$@"; then + if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." # Load various environment variables docker_setup_env "$@" mysql_check_config "$@" + docker_create_db_directories # If container is started as root user, restart as dedicated mysql user if [ "$(id -u)" = "0" ]; then - docker_create_db_directories mysql_note "Switching to dedicated user 'mysql'" exec gosu mysql "$BASH_SOURCE" "$@" fi - # just in case the script was not started as root - docker_create_db_directories - # If this is true then there's no database, and it needs to be initialized if [ ! -d "$DATADIR/mysql" ]; then docker_verify_minimum_env @@ -308,7 +311,6 @@ _main() { docker_temp_server_stop mysql_note "Temporary server stopped" - unset PASSFILE echo mysql_note "MySQL init process done. Ready for start up." echo From 8d01eea76abfde27941fd37b0be6c4e293b058a6 Mon Sep 17 00:00:00 2001 From: Joe Ferguson Date: Fri, 16 Aug 2019 17:07:39 -0700 Subject: [PATCH 40/42] Fix source detection for centos, call check_config first, explicit global for DATADIR SOCKET --- .template.Debian/docker-entrypoint.sh | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index 704e1a1a6..c2d37e202 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -39,6 +39,12 @@ file_env() { unset "$fileVar" } +# check to see if this file is being run or sourced from another script +_is_sourced() { + # https://unix.stackexchange.com/a/215279 + [ "${FUNCNAME[${#FUNCNAME[@]} - 1]}" == 'source' ] +} + # usage: docker_process_init_files [file [file [...]]] # ie: docker_process_init_files /always-initdb.d/* # process initializer files, based on file extensions @@ -149,8 +155,10 @@ docker_init_database_dir() { } # Loads various settings that are used elsewhere in the script +# This should be called after mysql_check_config, but before any other functions docker_setup_env() { # Get config + declare -g DATADIR SOCKET DATADIR="$(mysql_get_config 'datadir' "$@")" SOCKET="$(mysql_get_config 'socket' "$@")" @@ -282,9 +290,9 @@ _main() { if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started." + mysql_check_config "$@" # Load various environment variables docker_setup_env "$@" - mysql_check_config "$@" docker_create_db_directories # If container is started as root user, restart as dedicated mysql user @@ -319,9 +327,7 @@ _main() { exec "$@" } -# This checks if the script has been sourced from elsewhere. -# If so we don't perform any further actions -# https://unix.stackexchange.com/a/215279 -if [ "${FUNCNAME[${#FUNCNAME[@]} - 1]}" != 'source' ]; then +# If we are sourced from elsewhere, don't perform any further actions +if ! _is_sourced; then _main "$@" fi From 91785a50c332947c3963018606680acc2608a405 Mon Sep 17 00:00:00 2001 From: Joe Ferguson Date: Tue, 17 Sep 2019 17:05:44 -0700 Subject: [PATCH 41/42] Add --dont-use-mysql-root-password flag for docker_process_sql - also remove bash generating bash --- .template.Debian/docker-entrypoint.sh | 79 +++++++++++++++------- 5.6/docker-entrypoint.sh | 96 ++++++++++++++++++--------- 5.7/docker-entrypoint.sh | 95 +++++++++++++++++--------- 8.0/docker-entrypoint.sh | 95 +++++++++++++++++--------- update.sh | 21 +----- 5 files changed, 251 insertions(+), 135 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index c2d37e202..ed2d88e48 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -84,20 +84,18 @@ mysql_get_config() { # Do a temporary startup of the MySQL server, for init purposes docker_temp_server_start() { - local result=0 - %%SERVERSTARTUP%% - if [ "$result" != "0" ];then - mysql_error "Unable to start server. Status code $result." - fi - - # For 5.7+ the server is ready for use as soon as startup command unblocks - if [ "${MYSQL_MAJOR}" = "5.6" ]; then + if [ "${MYSQL_MAJOR}" = '5.6' ]; then + "$@" --skip-networking --socket="${SOCKET}" & mysql_note "Waiting for server startup" local i for i in {30..0}; do - # unset MYSQL_ROOT_PASSWORD for just docker_process_sql + # only use the root password if the database has already been initializaed # so that it won't try to fill in a password file when it hasn't been set yet - if MYSQL_ROOT_PASSWORD= docker_process_sql --database=mysql <<<'SELECT 1' &> /dev/null; then + extraArgs=() + if [ -z "$DATABASE_ALREADY_EXISTS" ]; then + extraArgs+=( '--dont-use-mysql-root-password' ) + fi + if docker_process_sql "${extraArgs[@]}" --database=mysql <<<'SELECT 1' &> /dev/null; then break fi sleep 1 @@ -105,6 +103,11 @@ docker_temp_server_start() { if [ "$i" = 0 ]; then mysql_error "Unable to start server." fi + else + # For 5.7+ the server is ready for use as soon as startup command unblocks + if ! "$@" --daemonize --skip-networking --socket="${SOCKET}"; then + mysql_error "Unable to start server." + fi fi } @@ -143,7 +146,11 @@ docker_create_db_directories() { # initializes the database directory docker_init_database_dir() { mysql_note "Initializing database files" - %%DATABASEINIT%% + if [ "$MYSQL_MAJOR" = '5.6' ]; then + mysql_install_db --datadir="$DATADIR" --rpm --keep-my-cnf "${@:2}" + else + "$@" --initialize-insecure + fi mysql_note "Database files initialized" if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then @@ -168,19 +175,29 @@ docker_setup_env() { file_env 'MYSQL_USER' file_env 'MYSQL_PASSWORD' file_env 'MYSQL_ROOT_PASSWORD' + + declare -g DATABASE_ALREADY_EXISTS + if [ -d "$DATADIR/mysql" ]; then + DATABASE_ALREADY_EXISTS='true' + fi } # Execute sql script, passed via stdin -# usage: docker_process_sql [mysql-cli-args] +# usage: docker_process_sql [--dont-use-mysql-root-password] [mysql-cli-args] # ie: docker_process_sql --database=mydb <<<'INSERT ...' -# ie: docker_process_sql --database=mydb /dev/null; then + extraArgs=() + if [ -z "$DATABASE_ALREADY_EXISTS" ]; then + extraArgs+=( '--dont-use-mysql-root-password' ) + fi + if docker_process_sql "${extraArgs[@]}" --database=mysql <<<'SELECT 1' &> /dev/null; then break fi sleep 1 @@ -99,6 +103,11 @@ docker_temp_server_start() { if [ "$i" = 0 ]; then mysql_error "Unable to start server." fi + else + # For 5.7+ the server is ready for use as soon as startup command unblocks + if ! "$@" --daemonize --skip-networking --socket="${SOCKET}"; then + mysql_error "Unable to start server." + fi fi } @@ -137,7 +146,11 @@ docker_create_db_directories() { # initializes the database directory docker_init_database_dir() { mysql_note "Initializing database files" - mysql_install_db --datadir="$DATADIR" --rpm --keep-my-cnf "${@:2}" + if [ "$MYSQL_MAJOR" = '5.6' ]; then + mysql_install_db --datadir="$DATADIR" --rpm --keep-my-cnf "${@:2}" + else + "$@" --initialize-insecure + fi mysql_note "Database files initialized" if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then @@ -149,8 +162,10 @@ docker_init_database_dir() { } # Loads various settings that are used elsewhere in the script +# This should be called after mysql_check_config, but before any other functions docker_setup_env() { # Get config + declare -g DATADIR SOCKET DATADIR="$(mysql_get_config 'datadir' "$@")" SOCKET="$(mysql_get_config 'socket' "$@")" @@ -160,19 +175,29 @@ docker_setup_env() { file_env 'MYSQL_USER' file_env 'MYSQL_PASSWORD' file_env 'MYSQL_ROOT_PASSWORD' + + declare -g DATABASE_ALREADY_EXISTS + if [ -d "$DATADIR/mysql" ]; then + DATABASE_ALREADY_EXISTS='true' + fi } # Execute sql script, passed via stdin -# usage: docker_process_sql [mysql-cli-args] +# usage: docker_process_sql [--dont-use-mysql-root-password] [mysql-cli-args] # ie: docker_process_sql --database=mydb <<<'INSERT ...' -# ie: docker_process_sql --database=mydb /dev/null; then + extraArgs=() + if [ -z "$DATABASE_ALREADY_EXISTS" ]; then + extraArgs+=( '--dont-use-mysql-root-password' ) + fi + if docker_process_sql "${extraArgs[@]}" --database=mysql <<<'SELECT 1' &> /dev/null; then break fi sleep 1 @@ -99,6 +103,11 @@ docker_temp_server_start() { if [ "$i" = 0 ]; then mysql_error "Unable to start server." fi + else + # For 5.7+ the server is ready for use as soon as startup command unblocks + if ! "$@" --daemonize --skip-networking --socket="${SOCKET}"; then + mysql_error "Unable to start server." + fi fi } @@ -137,7 +146,11 @@ docker_create_db_directories() { # initializes the database directory docker_init_database_dir() { mysql_note "Initializing database files" - "$@" --initialize-insecure + if [ "$MYSQL_MAJOR" = '5.6' ]; then + mysql_install_db --datadir="$DATADIR" --rpm --keep-my-cnf "${@:2}" + else + "$@" --initialize-insecure + fi mysql_note "Database files initialized" if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then @@ -149,8 +162,10 @@ docker_init_database_dir() { } # Loads various settings that are used elsewhere in the script +# This should be called after mysql_check_config, but before any other functions docker_setup_env() { # Get config + declare -g DATADIR SOCKET DATADIR="$(mysql_get_config 'datadir' "$@")" SOCKET="$(mysql_get_config 'socket' "$@")" @@ -160,19 +175,29 @@ docker_setup_env() { file_env 'MYSQL_USER' file_env 'MYSQL_PASSWORD' file_env 'MYSQL_ROOT_PASSWORD' + + declare -g DATABASE_ALREADY_EXISTS + if [ -d "$DATADIR/mysql" ]; then + DATABASE_ALREADY_EXISTS='true' + fi } # Execute sql script, passed via stdin -# usage: docker_process_sql [mysql-cli-args] +# usage: docker_process_sql [--dont-use-mysql-root-password] [mysql-cli-args] # ie: docker_process_sql --database=mydb <<<'INSERT ...' -# ie: docker_process_sql --database=mydb /dev/null; then + extraArgs=() + if [ -z "$DATABASE_ALREADY_EXISTS" ]; then + extraArgs+=( '--dont-use-mysql-root-password' ) + fi + if docker_process_sql "${extraArgs[@]}" --database=mysql <<<'SELECT 1' &> /dev/null; then break fi sleep 1 @@ -99,6 +103,11 @@ docker_temp_server_start() { if [ "$i" = 0 ]; then mysql_error "Unable to start server." fi + else + # For 5.7+ the server is ready for use as soon as startup command unblocks + if ! "$@" --daemonize --skip-networking --socket="${SOCKET}"; then + mysql_error "Unable to start server." + fi fi } @@ -137,7 +146,11 @@ docker_create_db_directories() { # initializes the database directory docker_init_database_dir() { mysql_note "Initializing database files" - "$@" --initialize-insecure + if [ "$MYSQL_MAJOR" = '5.6' ]; then + mysql_install_db --datadir="$DATADIR" --rpm --keep-my-cnf "${@:2}" + else + "$@" --initialize-insecure + fi mysql_note "Database files initialized" if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then @@ -149,8 +162,10 @@ docker_init_database_dir() { } # Loads various settings that are used elsewhere in the script +# This should be called after mysql_check_config, but before any other functions docker_setup_env() { # Get config + declare -g DATADIR SOCKET DATADIR="$(mysql_get_config 'datadir' "$@")" SOCKET="$(mysql_get_config 'socket' "$@")" @@ -160,19 +175,29 @@ docker_setup_env() { file_env 'MYSQL_USER' file_env 'MYSQL_PASSWORD' file_env 'MYSQL_ROOT_PASSWORD' + + declare -g DATABASE_ALREADY_EXISTS + if [ -d "$DATADIR/mysql" ]; then + DATABASE_ALREADY_EXISTS='true' + fi } # Execute sql script, passed via stdin -# usage: docker_process_sql [mysql-cli-args] +# usage: docker_process_sql [--dont-use-mysql-root-password] [mysql-cli-args] # ie: docker_process_sql --database=mydb <<<'INSERT ...' -# ie: docker_process_sql --database=mydb "$version/docker-entrypoint.sh" - debianVariant="${debianVariants[$version]:-$defaultDebianVariant}" debianSuite="${debianVariant%%-*}" # "stretch", etc + cp -a .template.Debian/docker-entrypoint.sh "$version/docker-entrypoint.sh" + fullVersion="$( curl -fsSL "https://repo.mysql.com/apt/debian/dists/$debianSuite/mysql-$version/binary-amd64/Packages.gz" \ | gunzip \ From 206541a450b100a7c665592814e5e882121c5b0a Mon Sep 17 00:00:00 2001 From: Joe Ferguson Date: Tue, 17 Sep 2019 17:28:31 -0700 Subject: [PATCH 42/42] Adjust printf to be more resilient; use exit code directly instead of a variable --- .template.Debian/docker-entrypoint.sh | 12 +++++------- 5.6/docker-entrypoint.sh | 12 +++++------- 5.7/docker-entrypoint.sh | 12 +++++------- 8.0/docker-entrypoint.sh | 12 +++++------- 4 files changed, 20 insertions(+), 28 deletions(-) diff --git a/.template.Debian/docker-entrypoint.sh b/.template.Debian/docker-entrypoint.sh index ed2d88e48..c13f247c5 100755 --- a/.template.Debian/docker-entrypoint.sh +++ b/.template.Debian/docker-entrypoint.sh @@ -5,7 +5,7 @@ shopt -s nullglob # logging functions mysql_log() { local type="$1"; shift - printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" + printf '%s [%s] [Entrypoint]: %s\n' "$(date --rfc-3339=seconds)" "$type" "$*" } mysql_note() { mysql_log Note "$@" @@ -68,7 +68,7 @@ docker_process_init_files() { mysql_check_config() { local toRun=( "$@" --verbose --help ) errors if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - mysql_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" + mysql_error $'mysqld failed while attempting to check config\n\tcommand was: '"${toRun[*]}"$'\n\t'"$errors" fi } @@ -114,17 +114,15 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. docker_temp_server_stop() { - local result=0 - mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}" || result=$? - if [ "$result" != "0" ]; then - mysql_error "Unable to shut down server. Status code $result." + if ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then + mysql_error "Unable to shut down server." fi } # Verify that the minimally required password settings are set for new databases. docker_verify_minimum_env() { if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - mysql_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" + mysql_error $'Database is uninitialized and password option is not specified\n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' fi } diff --git a/5.6/docker-entrypoint.sh b/5.6/docker-entrypoint.sh index ed2d88e48..c13f247c5 100755 --- a/5.6/docker-entrypoint.sh +++ b/5.6/docker-entrypoint.sh @@ -5,7 +5,7 @@ shopt -s nullglob # logging functions mysql_log() { local type="$1"; shift - printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" + printf '%s [%s] [Entrypoint]: %s\n' "$(date --rfc-3339=seconds)" "$type" "$*" } mysql_note() { mysql_log Note "$@" @@ -68,7 +68,7 @@ docker_process_init_files() { mysql_check_config() { local toRun=( "$@" --verbose --help ) errors if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - mysql_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" + mysql_error $'mysqld failed while attempting to check config\n\tcommand was: '"${toRun[*]}"$'\n\t'"$errors" fi } @@ -114,17 +114,15 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. docker_temp_server_stop() { - local result=0 - mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}" || result=$? - if [ "$result" != "0" ]; then - mysql_error "Unable to shut down server. Status code $result." + if ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then + mysql_error "Unable to shut down server." fi } # Verify that the minimally required password settings are set for new databases. docker_verify_minimum_env() { if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - mysql_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" + mysql_error $'Database is uninitialized and password option is not specified\n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' fi } diff --git a/5.7/docker-entrypoint.sh b/5.7/docker-entrypoint.sh index ed2d88e48..c13f247c5 100755 --- a/5.7/docker-entrypoint.sh +++ b/5.7/docker-entrypoint.sh @@ -5,7 +5,7 @@ shopt -s nullglob # logging functions mysql_log() { local type="$1"; shift - printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" + printf '%s [%s] [Entrypoint]: %s\n' "$(date --rfc-3339=seconds)" "$type" "$*" } mysql_note() { mysql_log Note "$@" @@ -68,7 +68,7 @@ docker_process_init_files() { mysql_check_config() { local toRun=( "$@" --verbose --help ) errors if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - mysql_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" + mysql_error $'mysqld failed while attempting to check config\n\tcommand was: '"${toRun[*]}"$'\n\t'"$errors" fi } @@ -114,17 +114,15 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. docker_temp_server_stop() { - local result=0 - mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}" || result=$? - if [ "$result" != "0" ]; then - mysql_error "Unable to shut down server. Status code $result." + if ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then + mysql_error "Unable to shut down server." fi } # Verify that the minimally required password settings are set for new databases. docker_verify_minimum_env() { if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - mysql_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" + mysql_error $'Database is uninitialized and password option is not specified\n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' fi } diff --git a/8.0/docker-entrypoint.sh b/8.0/docker-entrypoint.sh index ed2d88e48..c13f247c5 100755 --- a/8.0/docker-entrypoint.sh +++ b/8.0/docker-entrypoint.sh @@ -5,7 +5,7 @@ shopt -s nullglob # logging functions mysql_log() { local type="$1"; shift - printf "$(date --rfc-3339=seconds) [${type}] [Entrypoint]: $@\n" + printf '%s [%s] [Entrypoint]: %s\n' "$(date --rfc-3339=seconds)" "$type" "$*" } mysql_note() { mysql_log Note "$@" @@ -68,7 +68,7 @@ docker_process_init_files() { mysql_check_config() { local toRun=( "$@" --verbose --help ) errors if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then - mysql_error "mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]}\n\t$errors" + mysql_error $'mysqld failed while attempting to check config\n\tcommand was: '"${toRun[*]}"$'\n\t'"$errors" fi } @@ -114,17 +114,15 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. docker_temp_server_stop() { - local result=0 - mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}" || result=$? - if [ "$result" != "0" ]; then - mysql_error "Unable to shut down server. Status code $result." + if ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then + mysql_error "Unable to shut down server." fi } # Verify that the minimally required password settings are set for new databases. docker_verify_minimum_env() { if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then - mysql_error "Database is uninitialized and password option is not specified \n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD" + mysql_error $'Database is uninitialized and password option is not specified\n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' fi }