Skip to content

Commit

Permalink
entrypoint: Move more logic into functions
Browse files Browse the repository at this point in the history
Almost all logic in the entrypoint script is now separated into
various functions
  • Loading branch information
ltangvald authored and yosifkit committed Sep 18, 2019
1 parent db12713 commit f9c185f
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 99 deletions.
253 changes: 158 additions & 95 deletions .template.Debian/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,17 @@ 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
docker_error "Unable to start server. Status code $result."
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
Expand All @@ -112,141 +114,202 @@ 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}" <<EOF
[client]
password="${MYSQL_ROOT_PASSWORD}"
EOF
fi
}

# Sets root password and creates root users for non-localhost hosts
docker_init_root_user() {
rootCreate=
# default root to listen for connections from anywhere
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
}

# Creates a custom database and user if specified
docker_init_database_user() {
if [ "$MYSQL_DATABASE" ]; then
docker_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}"
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
}

# 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() {
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
}

# Generate a random root password
docker_generate_root_password() {
export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)"
docker_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
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 "$@"
DATADIR="$(docker_get_config 'datadir' "$@")"
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 "$@"
# Get config
DATADIR="$(docker_get_config 'datadir' "$@")"

# If this is true then there's no database, and it needs to be initialized
if [ ! -d "$DATADIR/mysql" ]; then
docker_file_env 'MYSQL_ROOT_PASSWORD'
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

mkdir -p "$DATADIR"

docker_note "Initializing database"
%%DATABASEINIT%%
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
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}" <<EOF
[client]
password="${MYSQL_ROOT_PASSWORD}"
EOF
docker_generate_root_password
fi

docker_init_root_user

docker_file_env 'MYSQL_DATABASE'
if [ "$MYSQL_DATABASE" ]; then
echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
mysql+=( "$MYSQL_DATABASE" )
fi
docker_write_password_file

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[@]}"

if [ "$MYSQL_DATABASE" ]; then
echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
fi

echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}"
fi
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
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
docker_expire_root_user
fi
docker_note "Stopping server"
docker_stop_server "${PASSFILE}" "${SOCKET}"
docker_note "Server stopped"
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
Expand Down
8 changes: 4 additions & 4 deletions update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ database_init["5.6"]="mysql_install_db --user=mysql --datadir=\"\$DATADIR\" --rp
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=$?"
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' \
Expand Down

0 comments on commit f9c185f

Please sign in to comment.