diff --git a/3.0/Dockerfile b/3.0/Dockerfile index 4cbd651eb8..60c5bbce4b 100644 --- a/3.0/Dockerfile +++ b/3.0/Dockerfile @@ -15,19 +15,33 @@ RUN apt-get update \ procps \ && rm -rf /var/lib/apt/lists/* -# grab gosu for easy step-down from root -ENV GOSU_VERSION 1.7 -RUN set -x \ - && apt-get update && apt-get install -y --no-install-recommends wget && rm -rf /var/lib/apt/lists/* \ - && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \ - && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \ - && export GNUPGHOME="$(mktemp -d)" \ - && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \ - && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \ - && rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \ - && chmod +x /usr/local/bin/gosu \ - && gosu nobody true \ - && apt-get purge -y --auto-remove wget +# grab gosu for easy step-down from root (https://github.com/tianon/gosu/releases) +ENV GOSU_VERSION 1.10 +# grab "js-yaml" for parsing mongod's YAML config files (https://github.com/nodeca/js-yaml/releases) +ENV JSYAML_VERSION 3.10.0 + +RUN set -ex; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + wget \ + ; \ + rm -rf /var/lib/apt/lists/*; \ + \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + chmod +x /usr/local/bin/gosu; \ + gosu nobody true; \ + \ + wget -O /js-yaml.js "https://github.com/nodeca/js-yaml/raw/${JSYAML_VERSION}/dist/js-yaml.js"; \ +# TODO some sort of download verification here + \ + apt-get purge -y --auto-remove wget RUN mkdir /docker-entrypoint-initdb.d diff --git a/3.0/docker-entrypoint.sh b/3.0/docker-entrypoint.sh index e1d5642dcc..736f1dd1d3 100755 --- a/3.0/docker-entrypoint.sh +++ b/3.0/docker-entrypoint.sh @@ -66,6 +66,24 @@ _mongod_hack_have_arg() { done return 1 } +# _mongod_hack_get_arg_val '--some-arg' "$@" +_mongod_hack_get_arg_val() { + local checkArg="$1"; shift + while [ "$#" -gt 0 ]; do + local arg="$1"; shift + case "$arg" in + "$checkArg") + echo "$1" + return 0 + ;; + "$checkArg"=*) + echo "${arg#$checkArg=}" + return 0 + ;; + esac + done + return 1 +} declare -a mongodHackedArgs # _mongod_hack_ensure_arg '--some-arg' "$@" # set -- "${mongodHackedArgs[@]}" @@ -98,7 +116,47 @@ _mongod_hack_ensure_arg_val() { done mongodHackedArgs+=( "$ensureArg" "$ensureVal" ) } -# TODO what do to about "--config" ? :( + +# _js_escape 'some "string" value' +_js_escape() { + jq --null-input --arg 'str' "$1" '$str' +} + +tempConfigFile="${TMPDIR:-/tmp}/docker-entrypoint-temp-config.json" +_parse_config() { + if [ -s "$tempConfigFile" ]; then + return 0 + fi + + local configPath + if configPath="$(_mongod_hack_get_arg_val --config "$@")"; then + # if --config is specified, parse it into a JSON file so we can remove a few problematic keys (especially SSL-related keys) + # see https://docs.mongodb.com/manual/reference/configuration-options/ + mongo --norc --nodb --quiet --eval "load('/js-yaml.js'); printjson(jsyaml.load(cat($(_js_escape "$configPath"))))" \ + | jq 'del(.systemLog, .processManagement, .net, .security)' \ + > "$tempConfigFile" + return 0 + fi + + return 1 +} +dbPath= +_dbPath() { + if [ -n "$dbPath" ]; then + echo "$dbPath" + return + fi + + if ! dbPath="$(_mongod_hack_get_arg_val --dbpath "$@")"; then + if _parse_config "$@"; then + dbPath="$(jq '.storage.dbPath' "$tempConfigFile")" + fi + fi + + : "${dbPath:=/data/db}" + + echo "$dbPath" +} if [ "$originalArgOne" = 'mongod' ]; then file_env 'MONGO_INITDB_ROOT_USERNAME' @@ -134,11 +192,12 @@ if [ "$originalArgOne" = 'mongod' ]; then # check for a few known paths (to determine whether we've already initialized and should thus skip our initdb scripts) if [ -n "$shouldPerformInitdb" ]; then + dbPath="$(_dbPath "$@")" for path in \ - /data/db/WiredTiger \ - /data/db/journal \ - /data/db/local.0 \ - /data/db/storage.bson \ + "$dbPath/WiredTiger" \ + "$dbPath/journal" \ + "$dbPath/local.0" \ + "$dbPath/storage.bson" \ ; do if [ -e "$path" ]; then shouldPerformInitdb= @@ -148,17 +207,11 @@ if [ "$originalArgOne" = 'mongod' ]; then fi if [ -n "$shouldPerformInitdb" ]; then - if _mongod_hack_have_arg --config "$@"; then - echo >&2 - echo >&2 'warning: database is not yet initialized, and "--config" is specified' - echo >&2 ' the initdb database startup might fail as a result!' - echo >&2 + mongodHackedArgs=( "$@" ) + if _parse_config "$@"; then + _mongod_hack_ensure_arg_val --config "$tempConfigFile" "${mongodHackedArgs[@]}" fi - - pidfile="$(mktemp)" - trap "rm -f '$pidfile'" EXIT - - _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "$@" + _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "${mongodHackedArgs[@]}" _mongod_hack_ensure_arg_val --port 27017 "${mongodHackedArgs[@]}" 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" @@ -169,12 +222,16 @@ if [ "$originalArgOne" = 'mongod' ]; then # https://github.com/docker-library/mongo/issues/164#issuecomment-293965668 _mongod_hack_ensure_arg_val --logpath "/proc/$$/fd/1" "${mongodHackedArgs[@]}" else - echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '/data/db/docker-initdb.log' instead" - _mongod_hack_ensure_arg_val --logpath /data/db/docker-initdb.log "${mongodHackedArgs[@]}" + initdbLogPath="$(_dbPath "$@")/docker-initdb.log" + echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '$initdbLogPath' instead" + _mongod_hack_ensure_arg_val --logpath "$initdbLogPath" "${mongodHackedArgs[@]}" fi _mongod_hack_ensure_arg --logappend "${mongodHackedArgs[@]}" + pidfile="${TMPDIR:-/tmp}/docker-entrypoint-temp-mongod.pid" + rm -f "$pidfile" _mongod_hack_ensure_arg_val --pidfilepath "$pidfile" "${mongodHackedArgs[@]}" + "${mongodHackedArgs[@]}" --fork mongo=( mongo --host 127.0.0.1 --port 27017 --quiet ) @@ -209,9 +266,9 @@ if [ "$originalArgOne" = 'mongod' ]; then "${mongo[@]}" "$rootAuthDatabase" <<-EOJS db.createUser({ - user: $(jq --arg 'user' "$MONGO_INITDB_ROOT_USERNAME" --null-input '$user'), - pwd: $(jq --arg 'pwd' "$MONGO_INITDB_ROOT_PASSWORD" --null-input '$pwd'), - roles: [ { role: 'root', db: $(jq --arg 'db' "$rootAuthDatabase" --null-input '$db') } ] + user: $(_js_escape "$MONGO_INITDB_ROOT_USERNAME"), + pwd: $(_js_escape "$MONGO_INITDB_ROOT_PASSWORD"), + roles: [ { role: 'root', db: $(_js_escape "$rootAuthDatabase") } ] }) EOJS @@ -235,8 +292,7 @@ if [ "$originalArgOne" = 'mongod' ]; then done "$@" --pidfilepath="$pidfile" --shutdown - rm "$pidfile" - trap - EXIT + rm -f "$pidfile" echo echo 'MongoDB init process complete; ready for start up.' @@ -246,4 +302,6 @@ if [ "$originalArgOne" = 'mongod' ]; then unset "${!MONGO_INITDB_@}" fi +rm -f "$tempConfigFile" + exec "$@" diff --git a/3.2/Dockerfile b/3.2/Dockerfile index ae1e49924d..87fc1b1f82 100644 --- a/3.2/Dockerfile +++ b/3.2/Dockerfile @@ -5,24 +5,38 @@ RUN groupadd -r mongodb && useradd -r -g mongodb mongodb RUN apt-get update \ && apt-get install -y --no-install-recommends \ - ca-certificates \ + ca-certificates \ jq \ numactl \ && rm -rf /var/lib/apt/lists/* -# grab gosu for easy step-down from root -ENV GOSU_VERSION 1.7 -RUN set -x \ - && apt-get update && apt-get install -y --no-install-recommends wget && rm -rf /var/lib/apt/lists/* \ - && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \ - && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \ - && export GNUPGHOME="$(mktemp -d)" \ - && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \ - && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \ - && rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \ - && chmod +x /usr/local/bin/gosu \ - && gosu nobody true \ - && apt-get purge -y --auto-remove wget +# grab gosu for easy step-down from root (https://github.com/tianon/gosu/releases) +ENV GOSU_VERSION 1.10 +# grab "js-yaml" for parsing mongod's YAML config files (https://github.com/nodeca/js-yaml/releases) +ENV JSYAML_VERSION 3.10.0 + +RUN set -ex; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + wget \ + ; \ + rm -rf /var/lib/apt/lists/*; \ + \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + chmod +x /usr/local/bin/gosu; \ + gosu nobody true; \ + \ + wget -O /js-yaml.js "https://github.com/nodeca/js-yaml/raw/${JSYAML_VERSION}/dist/js-yaml.js"; \ +# TODO some sort of download verification here + \ + apt-get purge -y --auto-remove wget RUN mkdir /docker-entrypoint-initdb.d diff --git a/3.2/docker-entrypoint.sh b/3.2/docker-entrypoint.sh index e1d5642dcc..736f1dd1d3 100755 --- a/3.2/docker-entrypoint.sh +++ b/3.2/docker-entrypoint.sh @@ -66,6 +66,24 @@ _mongod_hack_have_arg() { done return 1 } +# _mongod_hack_get_arg_val '--some-arg' "$@" +_mongod_hack_get_arg_val() { + local checkArg="$1"; shift + while [ "$#" -gt 0 ]; do + local arg="$1"; shift + case "$arg" in + "$checkArg") + echo "$1" + return 0 + ;; + "$checkArg"=*) + echo "${arg#$checkArg=}" + return 0 + ;; + esac + done + return 1 +} declare -a mongodHackedArgs # _mongod_hack_ensure_arg '--some-arg' "$@" # set -- "${mongodHackedArgs[@]}" @@ -98,7 +116,47 @@ _mongod_hack_ensure_arg_val() { done mongodHackedArgs+=( "$ensureArg" "$ensureVal" ) } -# TODO what do to about "--config" ? :( + +# _js_escape 'some "string" value' +_js_escape() { + jq --null-input --arg 'str' "$1" '$str' +} + +tempConfigFile="${TMPDIR:-/tmp}/docker-entrypoint-temp-config.json" +_parse_config() { + if [ -s "$tempConfigFile" ]; then + return 0 + fi + + local configPath + if configPath="$(_mongod_hack_get_arg_val --config "$@")"; then + # if --config is specified, parse it into a JSON file so we can remove a few problematic keys (especially SSL-related keys) + # see https://docs.mongodb.com/manual/reference/configuration-options/ + mongo --norc --nodb --quiet --eval "load('/js-yaml.js'); printjson(jsyaml.load(cat($(_js_escape "$configPath"))))" \ + | jq 'del(.systemLog, .processManagement, .net, .security)' \ + > "$tempConfigFile" + return 0 + fi + + return 1 +} +dbPath= +_dbPath() { + if [ -n "$dbPath" ]; then + echo "$dbPath" + return + fi + + if ! dbPath="$(_mongod_hack_get_arg_val --dbpath "$@")"; then + if _parse_config "$@"; then + dbPath="$(jq '.storage.dbPath' "$tempConfigFile")" + fi + fi + + : "${dbPath:=/data/db}" + + echo "$dbPath" +} if [ "$originalArgOne" = 'mongod' ]; then file_env 'MONGO_INITDB_ROOT_USERNAME' @@ -134,11 +192,12 @@ if [ "$originalArgOne" = 'mongod' ]; then # check for a few known paths (to determine whether we've already initialized and should thus skip our initdb scripts) if [ -n "$shouldPerformInitdb" ]; then + dbPath="$(_dbPath "$@")" for path in \ - /data/db/WiredTiger \ - /data/db/journal \ - /data/db/local.0 \ - /data/db/storage.bson \ + "$dbPath/WiredTiger" \ + "$dbPath/journal" \ + "$dbPath/local.0" \ + "$dbPath/storage.bson" \ ; do if [ -e "$path" ]; then shouldPerformInitdb= @@ -148,17 +207,11 @@ if [ "$originalArgOne" = 'mongod' ]; then fi if [ -n "$shouldPerformInitdb" ]; then - if _mongod_hack_have_arg --config "$@"; then - echo >&2 - echo >&2 'warning: database is not yet initialized, and "--config" is specified' - echo >&2 ' the initdb database startup might fail as a result!' - echo >&2 + mongodHackedArgs=( "$@" ) + if _parse_config "$@"; then + _mongod_hack_ensure_arg_val --config "$tempConfigFile" "${mongodHackedArgs[@]}" fi - - pidfile="$(mktemp)" - trap "rm -f '$pidfile'" EXIT - - _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "$@" + _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "${mongodHackedArgs[@]}" _mongod_hack_ensure_arg_val --port 27017 "${mongodHackedArgs[@]}" 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" @@ -169,12 +222,16 @@ if [ "$originalArgOne" = 'mongod' ]; then # https://github.com/docker-library/mongo/issues/164#issuecomment-293965668 _mongod_hack_ensure_arg_val --logpath "/proc/$$/fd/1" "${mongodHackedArgs[@]}" else - echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '/data/db/docker-initdb.log' instead" - _mongod_hack_ensure_arg_val --logpath /data/db/docker-initdb.log "${mongodHackedArgs[@]}" + initdbLogPath="$(_dbPath "$@")/docker-initdb.log" + echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '$initdbLogPath' instead" + _mongod_hack_ensure_arg_val --logpath "$initdbLogPath" "${mongodHackedArgs[@]}" fi _mongod_hack_ensure_arg --logappend "${mongodHackedArgs[@]}" + pidfile="${TMPDIR:-/tmp}/docker-entrypoint-temp-mongod.pid" + rm -f "$pidfile" _mongod_hack_ensure_arg_val --pidfilepath "$pidfile" "${mongodHackedArgs[@]}" + "${mongodHackedArgs[@]}" --fork mongo=( mongo --host 127.0.0.1 --port 27017 --quiet ) @@ -209,9 +266,9 @@ if [ "$originalArgOne" = 'mongod' ]; then "${mongo[@]}" "$rootAuthDatabase" <<-EOJS db.createUser({ - user: $(jq --arg 'user' "$MONGO_INITDB_ROOT_USERNAME" --null-input '$user'), - pwd: $(jq --arg 'pwd' "$MONGO_INITDB_ROOT_PASSWORD" --null-input '$pwd'), - roles: [ { role: 'root', db: $(jq --arg 'db' "$rootAuthDatabase" --null-input '$db') } ] + user: $(_js_escape "$MONGO_INITDB_ROOT_USERNAME"), + pwd: $(_js_escape "$MONGO_INITDB_ROOT_PASSWORD"), + roles: [ { role: 'root', db: $(_js_escape "$rootAuthDatabase") } ] }) EOJS @@ -235,8 +292,7 @@ if [ "$originalArgOne" = 'mongod' ]; then done "$@" --pidfilepath="$pidfile" --shutdown - rm "$pidfile" - trap - EXIT + rm -f "$pidfile" echo echo 'MongoDB init process complete; ready for start up.' @@ -246,4 +302,6 @@ if [ "$originalArgOne" = 'mongod' ]; then unset "${!MONGO_INITDB_@}" fi +rm -f "$tempConfigFile" + exec "$@" diff --git a/3.4/Dockerfile b/3.4/Dockerfile index e6301281ee..57edd7e903 100644 --- a/3.4/Dockerfile +++ b/3.4/Dockerfile @@ -10,19 +10,33 @@ RUN apt-get update \ numactl \ && rm -rf /var/lib/apt/lists/* -# grab gosu for easy step-down from root -ENV GOSU_VERSION 1.7 -RUN set -x \ - && apt-get update && apt-get install -y --no-install-recommends wget && rm -rf /var/lib/apt/lists/* \ - && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \ - && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \ - && export GNUPGHOME="$(mktemp -d)" \ - && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \ - && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \ - && rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \ - && chmod +x /usr/local/bin/gosu \ - && gosu nobody true \ - && apt-get purge -y --auto-remove wget +# grab gosu for easy step-down from root (https://github.com/tianon/gosu/releases) +ENV GOSU_VERSION 1.10 +# grab "js-yaml" for parsing mongod's YAML config files (https://github.com/nodeca/js-yaml/releases) +ENV JSYAML_VERSION 3.10.0 + +RUN set -ex; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + wget \ + ; \ + rm -rf /var/lib/apt/lists/*; \ + \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + chmod +x /usr/local/bin/gosu; \ + gosu nobody true; \ + \ + wget -O /js-yaml.js "https://github.com/nodeca/js-yaml/raw/${JSYAML_VERSION}/dist/js-yaml.js"; \ +# TODO some sort of download verification here + \ + apt-get purge -y --auto-remove wget RUN mkdir /docker-entrypoint-initdb.d diff --git a/3.4/docker-entrypoint.sh b/3.4/docker-entrypoint.sh index e1d5642dcc..736f1dd1d3 100755 --- a/3.4/docker-entrypoint.sh +++ b/3.4/docker-entrypoint.sh @@ -66,6 +66,24 @@ _mongod_hack_have_arg() { done return 1 } +# _mongod_hack_get_arg_val '--some-arg' "$@" +_mongod_hack_get_arg_val() { + local checkArg="$1"; shift + while [ "$#" -gt 0 ]; do + local arg="$1"; shift + case "$arg" in + "$checkArg") + echo "$1" + return 0 + ;; + "$checkArg"=*) + echo "${arg#$checkArg=}" + return 0 + ;; + esac + done + return 1 +} declare -a mongodHackedArgs # _mongod_hack_ensure_arg '--some-arg' "$@" # set -- "${mongodHackedArgs[@]}" @@ -98,7 +116,47 @@ _mongod_hack_ensure_arg_val() { done mongodHackedArgs+=( "$ensureArg" "$ensureVal" ) } -# TODO what do to about "--config" ? :( + +# _js_escape 'some "string" value' +_js_escape() { + jq --null-input --arg 'str' "$1" '$str' +} + +tempConfigFile="${TMPDIR:-/tmp}/docker-entrypoint-temp-config.json" +_parse_config() { + if [ -s "$tempConfigFile" ]; then + return 0 + fi + + local configPath + if configPath="$(_mongod_hack_get_arg_val --config "$@")"; then + # if --config is specified, parse it into a JSON file so we can remove a few problematic keys (especially SSL-related keys) + # see https://docs.mongodb.com/manual/reference/configuration-options/ + mongo --norc --nodb --quiet --eval "load('/js-yaml.js'); printjson(jsyaml.load(cat($(_js_escape "$configPath"))))" \ + | jq 'del(.systemLog, .processManagement, .net, .security)' \ + > "$tempConfigFile" + return 0 + fi + + return 1 +} +dbPath= +_dbPath() { + if [ -n "$dbPath" ]; then + echo "$dbPath" + return + fi + + if ! dbPath="$(_mongod_hack_get_arg_val --dbpath "$@")"; then + if _parse_config "$@"; then + dbPath="$(jq '.storage.dbPath' "$tempConfigFile")" + fi + fi + + : "${dbPath:=/data/db}" + + echo "$dbPath" +} if [ "$originalArgOne" = 'mongod' ]; then file_env 'MONGO_INITDB_ROOT_USERNAME' @@ -134,11 +192,12 @@ if [ "$originalArgOne" = 'mongod' ]; then # check for a few known paths (to determine whether we've already initialized and should thus skip our initdb scripts) if [ -n "$shouldPerformInitdb" ]; then + dbPath="$(_dbPath "$@")" for path in \ - /data/db/WiredTiger \ - /data/db/journal \ - /data/db/local.0 \ - /data/db/storage.bson \ + "$dbPath/WiredTiger" \ + "$dbPath/journal" \ + "$dbPath/local.0" \ + "$dbPath/storage.bson" \ ; do if [ -e "$path" ]; then shouldPerformInitdb= @@ -148,17 +207,11 @@ if [ "$originalArgOne" = 'mongod' ]; then fi if [ -n "$shouldPerformInitdb" ]; then - if _mongod_hack_have_arg --config "$@"; then - echo >&2 - echo >&2 'warning: database is not yet initialized, and "--config" is specified' - echo >&2 ' the initdb database startup might fail as a result!' - echo >&2 + mongodHackedArgs=( "$@" ) + if _parse_config "$@"; then + _mongod_hack_ensure_arg_val --config "$tempConfigFile" "${mongodHackedArgs[@]}" fi - - pidfile="$(mktemp)" - trap "rm -f '$pidfile'" EXIT - - _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "$@" + _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "${mongodHackedArgs[@]}" _mongod_hack_ensure_arg_val --port 27017 "${mongodHackedArgs[@]}" 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" @@ -169,12 +222,16 @@ if [ "$originalArgOne" = 'mongod' ]; then # https://github.com/docker-library/mongo/issues/164#issuecomment-293965668 _mongod_hack_ensure_arg_val --logpath "/proc/$$/fd/1" "${mongodHackedArgs[@]}" else - echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '/data/db/docker-initdb.log' instead" - _mongod_hack_ensure_arg_val --logpath /data/db/docker-initdb.log "${mongodHackedArgs[@]}" + initdbLogPath="$(_dbPath "$@")/docker-initdb.log" + echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '$initdbLogPath' instead" + _mongod_hack_ensure_arg_val --logpath "$initdbLogPath" "${mongodHackedArgs[@]}" fi _mongod_hack_ensure_arg --logappend "${mongodHackedArgs[@]}" + pidfile="${TMPDIR:-/tmp}/docker-entrypoint-temp-mongod.pid" + rm -f "$pidfile" _mongod_hack_ensure_arg_val --pidfilepath "$pidfile" "${mongodHackedArgs[@]}" + "${mongodHackedArgs[@]}" --fork mongo=( mongo --host 127.0.0.1 --port 27017 --quiet ) @@ -209,9 +266,9 @@ if [ "$originalArgOne" = 'mongod' ]; then "${mongo[@]}" "$rootAuthDatabase" <<-EOJS db.createUser({ - user: $(jq --arg 'user' "$MONGO_INITDB_ROOT_USERNAME" --null-input '$user'), - pwd: $(jq --arg 'pwd' "$MONGO_INITDB_ROOT_PASSWORD" --null-input '$pwd'), - roles: [ { role: 'root', db: $(jq --arg 'db' "$rootAuthDatabase" --null-input '$db') } ] + user: $(_js_escape "$MONGO_INITDB_ROOT_USERNAME"), + pwd: $(_js_escape "$MONGO_INITDB_ROOT_PASSWORD"), + roles: [ { role: 'root', db: $(_js_escape "$rootAuthDatabase") } ] }) EOJS @@ -235,8 +292,7 @@ if [ "$originalArgOne" = 'mongod' ]; then done "$@" --pidfilepath="$pidfile" --shutdown - rm "$pidfile" - trap - EXIT + rm -f "$pidfile" echo echo 'MongoDB init process complete; ready for start up.' @@ -246,4 +302,6 @@ if [ "$originalArgOne" = 'mongod' ]; then unset "${!MONGO_INITDB_@}" fi +rm -f "$tempConfigFile" + exec "$@" diff --git a/3.6/Dockerfile b/3.6/Dockerfile index 3993d9adb8..d68345060d 100644 --- a/3.6/Dockerfile +++ b/3.6/Dockerfile @@ -10,19 +10,33 @@ RUN apt-get update \ numactl \ && rm -rf /var/lib/apt/lists/* -# grab gosu for easy step-down from root -ENV GOSU_VERSION 1.7 -RUN set -x \ - && apt-get update && apt-get install -y --no-install-recommends wget && rm -rf /var/lib/apt/lists/* \ - && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \ - && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \ - && export GNUPGHOME="$(mktemp -d)" \ - && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \ - && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \ - && rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \ - && chmod +x /usr/local/bin/gosu \ - && gosu nobody true \ - && apt-get purge -y --auto-remove wget +# grab gosu for easy step-down from root (https://github.com/tianon/gosu/releases) +ENV GOSU_VERSION 1.10 +# grab "js-yaml" for parsing mongod's YAML config files (https://github.com/nodeca/js-yaml/releases) +ENV JSYAML_VERSION 3.10.0 + +RUN set -ex; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + wget \ + ; \ + rm -rf /var/lib/apt/lists/*; \ + \ + dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ + wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \ + wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ + rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \ + chmod +x /usr/local/bin/gosu; \ + gosu nobody true; \ + \ + wget -O /js-yaml.js "https://github.com/nodeca/js-yaml/raw/${JSYAML_VERSION}/dist/js-yaml.js"; \ +# TODO some sort of download verification here + \ + apt-get purge -y --auto-remove wget RUN mkdir /docker-entrypoint-initdb.d diff --git a/3.6/docker-entrypoint.sh b/3.6/docker-entrypoint.sh index 7cbb955692..a1d14cffe3 100755 --- a/3.6/docker-entrypoint.sh +++ b/3.6/docker-entrypoint.sh @@ -66,6 +66,24 @@ _mongod_hack_have_arg() { done return 1 } +# _mongod_hack_get_arg_val '--some-arg' "$@" +_mongod_hack_get_arg_val() { + local checkArg="$1"; shift + while [ "$#" -gt 0 ]; do + local arg="$1"; shift + case "$arg" in + "$checkArg") + echo "$1" + return 0 + ;; + "$checkArg"=*) + echo "${arg#$checkArg=}" + return 0 + ;; + esac + done + return 1 +} declare -a mongodHackedArgs # _mongod_hack_ensure_arg '--some-arg' "$@" # set -- "${mongodHackedArgs[@]}" @@ -111,7 +129,47 @@ _mongod_hack_ensure_arg_val() { done mongodHackedArgs+=( "$ensureArg" "$ensureVal" ) } -# TODO what do to about "--config" ? :( + +# _js_escape 'some "string" value' +_js_escape() { + jq --null-input --arg 'str' "$1" '$str' +} + +tempConfigFile="${TMPDIR:-/tmp}/docker-entrypoint-temp-config.json" +_parse_config() { + if [ -s "$tempConfigFile" ]; then + return 0 + fi + + local configPath + if configPath="$(_mongod_hack_get_arg_val --config "$@")"; then + # if --config is specified, parse it into a JSON file so we can remove a few problematic keys (especially SSL-related keys) + # see https://docs.mongodb.com/manual/reference/configuration-options/ + mongo --norc --nodb --quiet --eval "load('/js-yaml.js'); printjson(jsyaml.load(cat($(_js_escape "$configPath"))))" \ + | jq 'del(.systemLog, .processManagement, .net, .security)' \ + > "$tempConfigFile" + return 0 + fi + + return 1 +} +dbPath= +_dbPath() { + if [ -n "$dbPath" ]; then + echo "$dbPath" + return + fi + + if ! dbPath="$(_mongod_hack_get_arg_val --dbpath "$@")"; then + if _parse_config "$@"; then + dbPath="$(jq '.storage.dbPath' "$tempConfigFile")" + fi + fi + + : "${dbPath:=/data/db}" + + echo "$dbPath" +} if [ "$originalArgOne" = 'mongod' ]; then file_env 'MONGO_INITDB_ROOT_USERNAME' @@ -147,11 +205,12 @@ if [ "$originalArgOne" = 'mongod' ]; then # check for a few known paths (to determine whether we've already initialized and should thus skip our initdb scripts) if [ -n "$shouldPerformInitdb" ]; then + dbPath="$(_dbPath "$@")" for path in \ - /data/db/WiredTiger \ - /data/db/journal \ - /data/db/local.0 \ - /data/db/storage.bson \ + "$dbPath/WiredTiger" \ + "$dbPath/journal" \ + "$dbPath/local.0" \ + "$dbPath/storage.bson" \ ; do if [ -e "$path" ]; then shouldPerformInitdb= @@ -161,17 +220,11 @@ if [ "$originalArgOne" = 'mongod' ]; then fi if [ -n "$shouldPerformInitdb" ]; then - if _mongod_hack_have_arg --config "$@"; then - echo >&2 - echo >&2 'warning: database is not yet initialized, and "--config" is specified' - echo >&2 ' the initdb database startup might fail as a result!' - echo >&2 + mongodHackedArgs=( "$@" ) + if _parse_config "$@"; then + _mongod_hack_ensure_arg_val --config "$tempConfigFile" "${mongodHackedArgs[@]}" fi - - pidfile="$(mktemp)" - trap "rm -f '$pidfile'" EXIT - - _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "$@" + _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "${mongodHackedArgs[@]}" _mongod_hack_ensure_arg_val --port 27017 "${mongodHackedArgs[@]}" _mongod_hack_ensure_no_arg --bind_ip_all "${mongodHackedArgs[@]}" @@ -183,12 +236,16 @@ if [ "$originalArgOne" = 'mongod' ]; then # https://github.com/docker-library/mongo/issues/164#issuecomment-293965668 _mongod_hack_ensure_arg_val --logpath "/proc/$$/fd/1" "${mongodHackedArgs[@]}" else - echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '/data/db/docker-initdb.log' instead" - _mongod_hack_ensure_arg_val --logpath /data/db/docker-initdb.log "${mongodHackedArgs[@]}" + initdbLogPath="$(_dbPath "$@")/docker-initdb.log" + echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '$initdbLogPath' instead" + _mongod_hack_ensure_arg_val --logpath "$initdbLogPath" "${mongodHackedArgs[@]}" fi _mongod_hack_ensure_arg --logappend "${mongodHackedArgs[@]}" + pidfile="${TMPDIR:-/tmp}/docker-entrypoint-temp-mongod.pid" + rm -f "$pidfile" _mongod_hack_ensure_arg_val --pidfilepath "$pidfile" "${mongodHackedArgs[@]}" + "${mongodHackedArgs[@]}" --fork mongo=( mongo --host 127.0.0.1 --port 27017 --quiet ) @@ -223,9 +280,9 @@ if [ "$originalArgOne" = 'mongod' ]; then "${mongo[@]}" "$rootAuthDatabase" <<-EOJS db.createUser({ - user: $(jq --arg 'user' "$MONGO_INITDB_ROOT_USERNAME" --null-input '$user'), - pwd: $(jq --arg 'pwd' "$MONGO_INITDB_ROOT_PASSWORD" --null-input '$pwd'), - roles: [ { role: 'root', db: $(jq --arg 'db' "$rootAuthDatabase" --null-input '$db') } ] + user: $(_js_escape "$MONGO_INITDB_ROOT_USERNAME"), + pwd: $(_js_escape "$MONGO_INITDB_ROOT_PASSWORD"), + roles: [ { role: 'root', db: $(_js_escape "$rootAuthDatabase") } ] }) EOJS @@ -249,8 +306,7 @@ if [ "$originalArgOne" = 'mongod' ]; then done "$@" --pidfilepath="$pidfile" --shutdown - rm "$pidfile" - trap - EXIT + rm -f "$pidfile" echo echo 'MongoDB init process complete; ready for start up.' @@ -260,4 +316,6 @@ if [ "$originalArgOne" = 'mongod' ]; then unset "${!MONGO_INITDB_@}" fi +rm -f "$tempConfigFile" + exec "$@"