diff --git a/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh b/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh index 89b6f050..4e6060e4 100755 --- a/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh +++ b/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh @@ -42,7 +42,7 @@ find "${ENTRYPOINT_TEMPLATE_DIR}" -follow -type f -print | while read -r templat fi # Create the output directory if it doesn't exists - ensure-directory "${output_file_dir}" + ensure-directory-exists "${output_file_dir}" # Render the template log-info "Running [gomplate] on [${template_file}] --> [${output_file_path}]" diff --git a/contrib/docker/shared/root/docker/entrypoint.sh b/contrib/docker/shared/root/docker/entrypoint.sh index 501fbced..c1e3064f 100755 --- a/contrib/docker/shared/root/docker/entrypoint.sh +++ b/contrib/docker/shared/root/docker/entrypoint.sh @@ -1,72 +1,80 @@ #!/bin/bash +# short curcuit the entrypoint if $ENTRYPOINT_SKIP isn't set to 0 if [[ ${ENTRYPOINT_SKIP:=0} != 0 ]]; then exec "$@" fi +# Directory where entrypoint scripts lives : ${ENTRYPOINT_ROOT:="/docker/entrypoint.d/"} -: ${ENTRYPOINT_SKIP_SCRIPTS:=""} - export ENTRYPOINT_ROOT +# Space separated list of scripts the entrypoint runner should skip +: ${ENTRYPOINT_SKIP_SCRIPTS:=""} + +# Load helper scripts source /docker/helpers.sh +# Set the entrypoint name for logging entrypoint-set-name "entrypoint.sh" +# Convert ENTRYPOINT_SKIP_SCRIPTS into a native bash array for easier lookup declare -a skip_scripts IFS=' ' read -a skip_scripts <<<"$ENTRYPOINT_SKIP_SCRIPTS" -declare script_name - -# ensure the entrypoint folder exists +# Ensure the entrypoint root folder exists mkdir -p "${ENTRYPOINT_ROOT}" -if /usr/bin/find "${ENTRYPOINT_ROOT}" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then - log-info "looking for shell scripts in /docker/entrypoint.d/" - - find "${ENTRYPOINT_ROOT}" -follow -type f -print | sort -V | while read -r file; do - script_name="$(get-entrypoint-script-name $file)" - - if in-array "${script_name}" skip_scripts; then - log-warning "Skipping script [${script_name}] since it's in the skip list (\$ENTRYPOINT_SKIP_SCRIPTS)" - - continue - fi - - case "${file}" in - *.envsh) - if ! is-executable "${file}"; then - # warn on shell scripts without exec bit - log-error-and-exit "File [${file}] is not executable (please 'chmod +x' it)" - fi - - log-info "Sourcing [${file}]" - - source "${file}" - - # the sourced file will (should) than the log prefix, so this restores our own - # "global" log prefix once the file is done being sourced - entrypoint-restore-name - ;; - - *.sh) - if ! is-executable "${file}"; then - # warn on shell scripts without exec bit - log-error-and-exit "File [${file}] is not executable (please 'chmod +x' it)" - fi - - log-info "Running [${file}]" - "${file}" - ;; - - *) - log-warning "Ignoring unrecognized file [${file}]" - ;; - esac - done - - log-info "Configuration complete; ready for start up" -else +# If ENTRYPOINT_ROOT directory is empty, warn and run the regular command +if is-directory-empty "${ENTRYPOINT_ROOT}"; then log-warning "No files found in ${ENTRYPOINT_ROOT}, skipping configuration" + + exec "$@" fi +# Start scanning for entrypoint.d files to source or run +log-info "looking for shell scripts in [${ENTRYPOINT_ROOT}]" + +find "${ENTRYPOINT_ROOT}" -follow -type f -print | sort -V | while read -r file; do + # Skip the script if it's in the skip-script list + if in-array $(get-entrypoint-script-name "${file}") skip_scripts; then + log-warning "Skipping script [${script_name}] since it's in the skip list (\$ENTRYPOINT_SKIP_SCRIPTS)" + + continue + fi + + # Inspect the file extension of the file we're processing + case "${file}" in + *.envsh) + if ! is-executable "${file}"; then + # warn on shell scripts without exec bit + log-error-and-exit "File [${file}] is not executable (please 'chmod +x' it)" + fi + + log-info "Sourcing [${file}]" + + source "${file}" + + # the sourced file will (should) than the log prefix, so this restores our own + # "global" log prefix once the file is done being sourced + entrypoint-restore-name + ;; + + *.sh) + if ! is-executable "${file}"; then + # warn on shell scripts without exec bit + log-error-and-exit "File [${file}] is not executable (please 'chmod +x' it)" + fi + + log-info "Running [${file}]" + "${file}" + ;; + + *) + log-warning "Ignoring unrecognized file [${file}]" + ;; + esac +done + +log-info "Configuration complete; ready for start up" + exec "$@" diff --git a/contrib/docker/shared/root/docker/helpers.sh b/contrib/docker/shared/root/docker/helpers.sh index 1a4f346b..ce9ab266 100644 --- a/contrib/docker/shared/root/docker/helpers.sh +++ b/contrib/docker/shared/root/docker/helpers.sh @@ -23,15 +23,22 @@ declare -ra dot_env_files=( # environment keys seen when source dot files (so we can [export] them) declare -ga seen_dot_env_variables=() +# @description Restore the log prefix to the previous value that was captured in [entrypoint-set-name] +# @arg $1 string The name (or path) of the entrypoint script being run function entrypoint-set-name() { log_prefix_previous="${log_prefix}" log_prefix="ENTRYPOINT - [$(get-entrypoint-script-name $1)] - " } +# @description Restore the log prefix to the previous value that was captured in [entrypoint-set-name] function entrypoint-restore-name() { log_prefix="${log_prefix_previous}" } +# @description Run a command as the [runtime user] +# @arg $@ string The command to run +# @exitcode 0 if the command succeeeds +# @exitcode 1 if the command fails function run-as-runtime-user() { local -i exit_code local target_user @@ -54,12 +61,14 @@ function run-as-runtime-user() { # @description Print the given error message to stderr # @arg $message string A error message. +# @stderr The error message provided with log prefix function log-error() { echo -e "${error_message_color}${log_prefix}ERROR - ${*}${color_clear}" >/dev/stderr } # @description Print the given error message to stderr and exit 1 # @arg $@ string A error message. +# @stderr The error message provided with log prefix # @exitcode 1 function log-error-and-exit() { log-error "$@" @@ -69,18 +78,22 @@ function log-error-and-exit() { # @description Print the given warning message to stderr # @arg $@ string A warning message. +# @stderr The warning message provided with log prefix function log-warning() { echo -e "${warn_message_color}${log_prefix}WARNING - ${*}${color_clear}" >/dev/stderr } -# @description Print the given message to stderr unless [ENTRYPOINT_QUIET_LOGS] is set -# @arg $@ string A warning message. +# @description Print the given message to stdout unless [ENTRYPOINT_QUIET_LOGS] is set +# @arg $@ string A info message. +# @stdout The info message provided with log prefix unless $ENTRYPOINT_QUIET_LOGS function log-info() { if [ -z "${ENTRYPOINT_QUIET_LOGS:-}" ]; then echo "${log_prefix}$*" fi } +# @description Loads the dot-env files used by Docker and track the keys present in the configuration. +# @sets seen_dot_env_variables array List of config keys discovered during loading function load-config-files() { # Associative array (aka map/dictionary) holding the unique keys found in dot-env files local -A _tmp_dot_env_keys @@ -103,6 +116,11 @@ function load-config-files() { seen_dot_env_variables=(${!_tmp_dot_env_keys[@]}) } +# @description Checks if $needle exists in $haystack +# @arg $1 string The needle (value) to search for +# @arg $2 array The haystack (array) to search in +# @exitcode 0 If $needle was found in $haystack +# @exitcode 1 If $needle was *NOT* found in $haystack function in-array() { local -r needle="\<${1}\>" local -nr haystack=$2 @@ -110,18 +128,41 @@ function in-array() { [[ ${haystack[*]} =~ $needle ]] } +# @description Checks if $1 has executable bit set or not +# @arg $1 string The path to check +# @exitcode 0 If $1 has executable bit +# @exitcode 1 If $1 does *NOT* have executable bit function is-executable() { [[ -x "$1" ]] } +# @description Checks if $1 is writable or not +# @arg $1 string The path to check +# @exitcode 0 If $1 is writable +# @exitcode 1 If $1 is *NOT* writable function is-writable() { [[ -w "$1" ]] } -function ensure-directory() { +# @description Checks if $1 contains any files or not +# @arg $1 string The path to check +# @exitcode 0 If $1 contains files +# @exitcode 1 If $1 does *NOT* contain files +function is-directory-empty() { + ! find "${1}" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v +} + +# @description Ensures a directory exists (via mkdir) +# @arg $1 string The path to create +# @exitcode 0 If $1 If the path exists *or* was created +# @exitcode 1 If $1 If the path does *NOT* exists and could *NOT* be created +function ensure-directory-exists() { mkdir -pv "$@" } +# @description Find the relative path for a entrypoint script by removing the ENTRYPOINT_ROOT prefix +# @arg $1 string The path to manipulate +# @stdout The relative path to the entrypoint script function get-entrypoint-script-name() { echo "${1#"$ENTRYPOINT_ROOT"}" }