docker-entrypoint.sh 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. #!/bin/bash
  2. #
  3. # Copyright IBM Corp. All Rights Reserved.
  4. #
  5. # SPDX-License-Identifier: Apache-2.0
  6. # Based this file on https://github.com/docker-library/mongo/blob/master/3.4/docker-entrypoint.sh
  7. set -Eeuo pipefail
  8. if [ "${1:0:1}" = '-' ]; then
  9. set -- mongod "$@"
  10. fi
  11. originalArgOne="$1"
  12. # allow the container to be started with `--user`
  13. # all mongo* commands should be dropped to the correct user
  14. if [[ "$originalArgOne" == mongo* ]] && [ "$(id -u)" = '0' ]; then
  15. if [ "$originalArgOne" = 'mongod' ]; then
  16. chown -R mongodb /data/configdb /data/db
  17. fi
  18. # make sure we can write to stdout and stderr as "mongodb"
  19. # (for our "initdb" code later; see "--logpath" below)
  20. chown --dereference mongodb "/proc/$$/fd/1" "/proc/$$/fd/2" || :
  21. # ignore errors thanks to https://github.com/docker-library/mongo/issues/149
  22. exec gosu mongodb "$BASH_SOURCE" "$@"
  23. fi
  24. # you should use numactl to start your mongod instances, including the config servers, mongos instances, and any clients.
  25. # https://docs.mongodb.com/manual/administration/production-notes/#configuring-numa-on-linux
  26. if [[ "$originalArgOne" == mongo* ]]; then
  27. numa='numactl --interleave=all'
  28. if $numa true &> /dev/null; then
  29. set -- $numa "$@"
  30. fi
  31. fi
  32. # usage: file_env VAR [DEFAULT]
  33. # ie: file_env 'XYZ_DB_PASSWORD' 'example'
  34. # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
  35. # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
  36. file_env() {
  37. local var="$1"
  38. local fileVar="${var}_FILE"
  39. local def="${2:-}"
  40. if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
  41. echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
  42. exit 1
  43. fi
  44. local val="$def"
  45. if [ "${!var:-}" ]; then
  46. val="${!var}"
  47. elif [ "${!fileVar:-}" ]; then
  48. val="$(< "${!fileVar}")"
  49. fi
  50. export "$var"="$val"
  51. unset "$fileVar"
  52. }
  53. # see https://github.com/docker-library/mongo/issues/147 (mongod is picky about duplicated arguments)
  54. _mongod_hack_have_arg() {
  55. local checkArg="$1"; shift
  56. local arg
  57. for arg; do
  58. case "$arg" in
  59. "$checkArg"|"$checkArg"=*)
  60. return 0
  61. ;;
  62. esac
  63. done
  64. return 1
  65. }
  66. declare -a mongodHackedArgs
  67. # _mongod_hack_ensure_arg '--some-arg' "$@"
  68. # set -- "${mongodHackedArgs[@]}"
  69. _mongod_hack_ensure_arg() {
  70. local ensureArg="$1"; shift
  71. mongodHackedArgs=( "$@" )
  72. if ! _mongod_hack_have_arg "$ensureArg" "$@"; then
  73. mongodHackedArgs+=( "$ensureArg" )
  74. fi
  75. }
  76. # _mongod_hack_ensure_arg_val '--some-arg' 'some-val' "$@"
  77. # set -- "${mongodHackedArgs[@]}"
  78. _mongod_hack_ensure_arg_val() {
  79. local ensureArg="$1"; shift
  80. local ensureVal="$1"; shift
  81. mongodHackedArgs=()
  82. while [ "$#" -gt 0 ]; do
  83. local arg="$1"; shift
  84. case "$arg" in
  85. "$ensureArg")
  86. shift # also skip the value
  87. continue
  88. ;;
  89. "$ensureArg"=*)
  90. # value is already included
  91. continue
  92. ;;
  93. esac
  94. mongodHackedArgs+=( "$arg" )
  95. done
  96. mongodHackedArgs+=( "$ensureArg" "$ensureVal" )
  97. }
  98. # TODO what do to about "--config" ? :(
  99. if [ "$originalArgOne" = 'mongod' ]; then
  100. file_env 'MONGO_INITDB_ROOT_USERNAME'
  101. file_env 'MONGO_INITDB_ROOT_PASSWORD'
  102. # pre-check a few factors to see if it's even worth bothering with initdb
  103. shouldPerformInitdb=
  104. if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
  105. # if we have a username/password, let's set "--auth"
  106. _mongod_hack_ensure_arg '--auth' "$@"
  107. set -- "${mongodHackedArgs[@]}"
  108. shouldPerformInitdb='true'
  109. elif [ "$MONGO_INITDB_ROOT_USERNAME" ] || [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
  110. cat >&2 <<-'EOF'
  111. error: missing 'MONGO_INITDB_ROOT_USERNAME' or 'MONGO_INITDB_ROOT_PASSWORD'
  112. both must be specified for a user to be created
  113. EOF
  114. exit 1
  115. fi
  116. if [ -z "$shouldPerformInitdb" ]; then
  117. # if we've got any /docker-entrypoint-initdb.d/* files to parse later, we should initdb
  118. for f in /docker-entrypoint-initdb.d/*; do
  119. case "$f" in
  120. *.sh|*.js) # this should match the set of files we check for below
  121. shouldPerformInitdb="$f"
  122. break
  123. ;;
  124. esac
  125. done
  126. fi
  127. # check for a few known paths (to determine whether we've already initialized and should thus skip our initdb scripts)
  128. if [ -n "$shouldPerformInitdb" ]; then
  129. for path in \
  130. /data/db/WiredTiger \
  131. /data/db/journal \
  132. /data/db/local.0 \
  133. /data/db/storage.bson \
  134. ; do
  135. if [ -e "$path" ]; then
  136. shouldPerformInitdb=
  137. break
  138. fi
  139. done
  140. fi
  141. if [ -n "$shouldPerformInitdb" ]; then
  142. if _mongod_hack_have_arg --config "$@"; then
  143. echo >&2
  144. echo >&2 'warning: database is not yet initialized, and "--config" is specified'
  145. echo >&2 ' the initdb database startup might fail as a result!'
  146. echo >&2
  147. fi
  148. pidfile="$(mktemp)"
  149. trap "rm -f '$pidfile'" EXIT
  150. _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "$@"
  151. _mongod_hack_ensure_arg_val --port 27017 "${mongodHackedArgs[@]}"
  152. sslMode="$(_mongod_hack_have_arg '--sslPEMKeyFile' "$@" && echo 'allowSSL' || echo 'disabled')" # "BadValue: need sslPEMKeyFile when SSL is enabled" vs "BadValue: need to enable SSL via the sslMode flag when using SSL configuration parameters"
  153. _mongod_hack_ensure_arg_val --sslMode "$sslMode" "${mongodHackedArgs[@]}"
  154. if stat "/proc/$$/fd/1" > /dev/null && [ -w "/proc/$$/fd/1" ]; then
  155. # https://github.com/mongodb/mongo/blob/38c0eb538d0fd390c6cb9ce9ae9894153f6e8ef5/src/mongo/db/initialize_server_global_state.cpp#L237-L251
  156. # https://github.com/docker-library/mongo/issues/164#issuecomment-293965668
  157. _mongod_hack_ensure_arg_val --logpath "/proc/$$/fd/1" "${mongodHackedArgs[@]}"
  158. else
  159. echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '/data/db/docker-initdb.log' instead"
  160. _mongod_hack_ensure_arg_val --logpath /data/db/docker-initdb.log "${mongodHackedArgs[@]}"
  161. fi
  162. _mongod_hack_ensure_arg --logappend "${mongodHackedArgs[@]}"
  163. _mongod_hack_ensure_arg_val --pidfilepath "$pidfile" "${mongodHackedArgs[@]}"
  164. "${mongodHackedArgs[@]}" --fork
  165. mongo=( mongo --host 127.0.0.1 --port 27017 --quiet )
  166. # check to see that our "mongod" actually did start up (catches "--help", "--version", MongoDB 3.2 being silly, slow prealloc, etc)
  167. # https://jira.mongodb.org/browse/SERVER-16292
  168. tries=30
  169. while true; do
  170. if ! { [ -s "$pidfile" ] && ps "$(< "$pidfile")" &> /dev/null; }; then
  171. # bail ASAP if "mongod" isn't even running
  172. echo >&2
  173. echo >&2 "error: $originalArgOne does not appear to have stayed running -- perhaps it had an error?"
  174. echo >&2
  175. exit 1
  176. fi
  177. if "${mongo[@]}" 'admin' --eval 'quit(0)' &> /dev/null; then
  178. # success!
  179. break
  180. fi
  181. (( tries-- ))
  182. if [ "$tries" -le 0 ]; then
  183. echo >&2
  184. echo >&2 "error: $originalArgOne does not appear to have accepted connections quickly enough -- perhaps it had an error?"
  185. echo >&2
  186. exit 1
  187. fi
  188. sleep 1
  189. done
  190. if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
  191. rootAuthDatabase='admin'
  192. "${mongo[@]}" "$rootAuthDatabase" <<-EOJS
  193. db.createUser({
  194. user: $(jq --arg 'user' "$MONGO_INITDB_ROOT_USERNAME" --null-input '$user'),
  195. pwd: $(jq --arg 'pwd' "$MONGO_INITDB_ROOT_PASSWORD" --null-input '$pwd'),
  196. roles: [ { role: 'root', db: $(jq --arg 'db' "$rootAuthDatabase" --null-input '$db') } ]
  197. })
  198. EOJS
  199. mongo+=(
  200. --username="$MONGO_INITDB_ROOT_USERNAME"
  201. --password="$MONGO_INITDB_ROOT_PASSWORD"
  202. --authenticationDatabase="$rootAuthDatabase"
  203. )
  204. fi
  205. export MONGO_INITDB_DATABASE="${MONGO_INITDB_DATABASE:-test}"
  206. echo
  207. for f in /docker-entrypoint-initdb.d/*; do
  208. case "$f" in
  209. *.sh) echo "$0: running $f"; . "$f" ;;
  210. *.js) echo "$0: running $f"; "${mongo[@]}" "$MONGO_INITDB_DATABASE" "$f"; echo ;;
  211. *) echo "$0: ignoring $f" ;;
  212. esac
  213. echo
  214. done
  215. "$@" --pidfilepath="$pidfile" --shutdown
  216. rm "$pidfile"
  217. trap - EXIT
  218. echo
  219. echo 'MongoDB init process complete; ready for start up.'
  220. echo
  221. fi
  222. unset "${!MONGO_INITDB_@}"
  223. fi
  224. exec "$@"