docker-entrypoint.sh 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #!/bin/bash
  2. set -eo pipefail
  3. shopt -s nullglob
  4. # if command starts with an option, prepend mysqld
  5. if [ "${1:0:1}" = '-' ]; then
  6. set -- mysqld "$@"
  7. fi
  8. # skip setup if they want an option that stops mysqld
  9. wantHelp=
  10. for arg; do
  11. case "$arg" in
  12. -'?'|--help|--print-defaults|-V|--version)
  13. wantHelp=1
  14. break
  15. ;;
  16. esac
  17. done
  18. # usage: file_env VAR [DEFAULT]
  19. # ie: file_env 'XYZ_DB_PASSWORD' 'example'
  20. # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
  21. # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
  22. file_env() {
  23. local var="$1"
  24. local fileVar="${var}_FILE"
  25. local def="${2:-}"
  26. if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
  27. echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
  28. exit 1
  29. fi
  30. local val="$def"
  31. if [ "${!var:-}" ]; then
  32. val="${!var}"
  33. elif [ "${!fileVar:-}" ]; then
  34. val="$(< "${!fileVar}")"
  35. fi
  36. export "$var"="$val"
  37. unset "$fileVar"
  38. }
  39. _check_config() {
  40. toRun=( "$@" --verbose --help )
  41. if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
  42. cat >&2 <<-EOM
  43. ERROR: mysqld failed while attempting to check config
  44. command was: "${toRun[*]}"
  45. $errors
  46. EOM
  47. exit 1
  48. fi
  49. }
  50. # Fetch value from server config
  51. # We use mysqld --verbose --help instead of my_print_defaults because the
  52. # latter only show values present in config files, and not server defaults
  53. _get_config() {
  54. local conf="$1"; shift
  55. "$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null | awk '$1 == "'"$conf"'" { print $2; exit }'
  56. }
  57. # allow the container to be started with `--user`
  58. if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then
  59. _check_config "$@"
  60. DATADIR="$(_get_config 'datadir' "$@")"
  61. mkdir -p "$DATADIR"
  62. chown -R mysql:mysql "$DATADIR"
  63. exec gosu mysql "$BASH_SOURCE" "$@"
  64. fi
  65. if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
  66. # still need to check config, container may have started with --user
  67. _check_config "$@"
  68. # Get config
  69. DATADIR="$(_get_config 'datadir' "$@")"
  70. if [ ! -d "$DATADIR/mysql" ]; then
  71. file_env 'MYSQL_ROOT_PASSWORD'
  72. if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
  73. echo >&2 'error: database is uninitialized and password option is not specified '
  74. echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
  75. exit 1
  76. fi
  77. mkdir -p "$DATADIR"
  78. echo 'Initializing database'
  79. "$@" --initialize-insecure
  80. echo 'Database initialized'
  81. if command -v mysql_ssl_rsa_setup > /dev/null && [ ! -e "$DATADIR/server-key.pem" ]; then
  82. # https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/packaging/deb-in/extra/mysql-systemd-start#L81-L84
  83. echo 'Initializing certificates'
  84. mysql_ssl_rsa_setup --datadir="$DATADIR"
  85. echo 'Certificates initialized'
  86. fi
  87. SOCKET="$(_get_config 'socket' "$@")"
  88. "$@" --skip-networking --socket="${SOCKET}" &
  89. pid="$!"
  90. mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" )
  91. for i in {30..0}; do
  92. if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then
  93. break
  94. fi
  95. echo 'MySQL init process in progress...'
  96. sleep 1
  97. done
  98. if [ "$i" = 0 ]; then
  99. echo >&2 'MySQL init process failed.'
  100. exit 1
  101. fi
  102. if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then
  103. # sed is for https://bugs.mysql.com/bug.php?id=20545
  104. mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql
  105. fi
  106. if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
  107. export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)"
  108. echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
  109. fi
  110. rootCreate=
  111. # default root to listen for connections from anywhere
  112. file_env 'MYSQL_ROOT_HOST' '%'
  113. if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then
  114. # no, we don't care if read finds a terminating character in this heredoc
  115. # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
  116. read -r -d '' rootCreate <<-EOSQL || true
  117. CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
  118. GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ;
  119. EOSQL
  120. fi
  121. "${mysql[@]}" <<-EOSQL
  122. -- What's done in this file shouldn't be replicated
  123. -- or products like mysql-fabric won't work
  124. SET @@SESSION.SQL_LOG_BIN=0;
  125. DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
  126. SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ;
  127. GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
  128. ${rootCreate}
  129. DROP DATABASE IF EXISTS test ;
  130. FLUSH PRIVILEGES ;
  131. EOSQL
  132. if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then
  133. mysql+=( -p"${MYSQL_ROOT_PASSWORD}" )
  134. fi
  135. file_env 'MYSQL_DATABASE'
  136. if [ "$MYSQL_DATABASE" ]; then
  137. echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
  138. mysql+=( "$MYSQL_DATABASE" )
  139. fi
  140. file_env 'MYSQL_USER'
  141. file_env 'MYSQL_PASSWORD'
  142. if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
  143. echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}"
  144. if [ "$MYSQL_DATABASE" ]; then
  145. echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
  146. fi
  147. echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}"
  148. fi
  149. echo
  150. for f in /docker-entrypoint-initdb.d/*; do
  151. case "$f" in
  152. *.sh) echo "$0: running $f"; . "$f" ;;
  153. *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
  154. *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
  155. *) echo "$0: ignoring $f" ;;
  156. esac
  157. echo
  158. done
  159. if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then
  160. "${mysql[@]}" <<-EOSQL
  161. ALTER USER 'root'@'%' PASSWORD EXPIRE;
  162. EOSQL
  163. fi
  164. if ! kill -s TERM "$pid" || ! wait "$pid"; then
  165. echo >&2 'MySQL init process failed.'
  166. exit 1
  167. fi
  168. echo
  169. echo 'MySQL init process done. Ready for start up.'
  170. echo
  171. fi
  172. fi
  173. exec "$@"