#!/usr/bin/env bash # Hook to check server against list of checks specified in CHECKS file. # # The CHECKS file may contain empty lines, comments (lines starting with #), # settings (NAME=VALUE) and check instructions. # # The format of a check instruction is a path, optionally followed by the # expected content. For example: # # / My Amazing App # /stylesheets/index.css .body # /scripts/index.js $(function() # /images/logo.png # # To check an application that supports multiple hostnames, use relative URLs # that include the hostname, for example: # # //admin.example.com Admin Dashboard # //static.example.com/logo.png # # You can also specify the protocol to explicitly check HTTPS requests. # # The default behavior is to wait for 5 seconds before running the first check, # and timeout each check to 30 seconds. # # By default, checks will be retried 5 times. # You can change these by setting WAIT, TIMEOUT and ATTEMPTS to different values, for # example: # # WAIT=30 # Wait 1/2 minute # TIMEOUT=60 # Timeout after a minute # ATTEMPTS=10 # retry checks 10 times # set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_AVAILABLE_PATH/checks/functions" source "$PLUGIN_AVAILABLE_PATH/config/functions" source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" trigger-scheduler-docker-local-check-deploy() { declare desc="scheduler-docker-local check-deploy plugin trigger" declare trigger="check-deploy" declare APP="$1" DOKKU_APP_CONTAINER_ID="$2" DOKKU_APP_CONTAINER_TYPE="$3" DOKKU_APP_LISTEN_PORT="$4" DOKKU_APP_LISTEN_IP="$5" CONTAINER_INDEX="$6" local content local DOKKU_SCHEDULER=$(get_app_scheduler "$APP") if [[ "$DOKKU_SCHEDULER" != "docker-local" ]]; then return fi if [[ -z "$DOKKU_APP_LISTEN_PORT" ]] && [[ -f "$DOKKU_ROOT/$APP/PORT" ]]; then local DOKKU_APP_LISTEN_PORT=$(<"$DOKKU_ROOT/$APP/PORT") fi if [[ -z "$DOKKU_APP_CONTAINER_ID" ]]; then local DOKKU_APP_CIDS=($(get_app_container_ids "$APP")) local DOKKU_APP_CONTAINER_ID=${DOKKU_APP_CIDS[0]} fi # source global and in-app envs to get DOKKU_CHECKS_WAIT and any other necessary vars eval "$(config_export global)" eval "$(config_export app "$APP")" if [[ "$(is_app_proctype_checks_skipped "$APP" "$DOKKU_APP_CONTAINER_TYPE")" == "true" ]]; then dokku_log_info2_quiet "Zero downtime checks have been skipped ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" exit 0 fi # Wait this many seconds (default 5) for server to start before running checks. local WAIT="${DOKKU_CHECKS_WAIT:-5}" # Wait this many seconds (default 30) for each response. local TIMEOUT="${DOKKU_CHECKS_TIMEOUT:-30}" # use this number of retries for checks local ATTEMPTS="${DOKKU_CHECKS_ATTEMPTS:-5}" local CHECKS_FILENAME="$(fn-scheduler-docker-local-get-process-specific-checks-file-path "$APP")" local IMAGE_TAG="$(get_running_image_tag "$APP")" local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG") local TMP_APP_JSON_OUTPUT=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") trap "rm -rf '$TMP_APP_JSON_OUTPUT' >/dev/null" RETURN INT TERM EXIT plugn trigger app-json-get-content "$APP" >"$TMP_APP_JSON_OUTPUT" if [[ -s "${CHECKS_FILENAME}" ]]; then # Reads name/value pairs, sets the WAIT and TIMEOUT variables exec <"$CHECKS_FILENAME" local line local NAME local VALUE while read -r line; do line=$(strip_inline_comments "$line") # Name/value pair if [[ "$line" =~ ^.+= ]]; then NAME=${line%=*} VALUE=${line#*=} [[ "$NAME" == "WAIT" ]] && local WAIT=$VALUE [[ "$NAME" == "TIMEOUT" ]] && local TIMEOUT=$VALUE [[ "$NAME" == "ATTEMPTS" ]] && local ATTEMPTS=$VALUE fi done dokku_log_warn "Deprecated: Usage of the CHECKS file is deprecated in favor of healthchecks in app.json" dokku_log_warn "Please move your healthchecks to app.json." content="$(docker-container-healthchecker convert "$CHECKS_FILENAME" --app-json "$TMP_APP_JSON_OUTPUT" --pretty)" echo "$content" >"$TMP_APP_JSON_OUTPUT" fi checks_check_deploy_cleanup() { declare desc="print container output" declare APP="$1" DOKKU_APP_CONTAINER_TYPE="$2" CID="$3" CONTAINER_INDEX="$4" TMP_FILE="$5" if [[ $CID ]]; then dokku_log_info2_quiet "Start of $APP container output (${CID:0:12} $DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" dokku_container_log_verbose_quiet "$CID" dokku_log_info2_quiet "End of $APP container output (${CID:0:12} $DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" fi rm -rf "$TMP_FILE" >/dev/null } trap "checks_check_deploy_cleanup $APP $DOKKU_APP_CONTAINER_TYPE $DOKKU_APP_CONTAINER_ID $CONTAINER_INDEX $TMP_APP_JSON_OUTPUT" RETURN INT TERM EXIT local DOKKU_DEFAULT_CHECKS_WAIT="${DOKKU_DEFAULT_CHECKS_WAIT:-10}" content="$(docker-container-healthchecker add "$DOKKU_APP_CONTAINER_TYPE" --app-json "$TMP_APP_JSON_OUTPUT" --if-empty --pretty --uptime "$DOKKU_DEFAULT_CHECKS_WAIT")" echo "$content" >"$TMP_APP_JSON_OUTPUT" local FAILEDCHECKS=0 local SSL="$DOKKU_ROOT/$APP/tls" declare -a ARG_ARRAY ARG_ARRAY+=("--app-json") ARG_ARRAY+=("$TMP_APP_JSON_OUTPUT") ARG_ARRAY+=("--process-type") ARG_ARRAY+=("$DOKKU_APP_CONTAINER_TYPE") if [[ -e "$SSL/server.crt" && -e "$SSL/server.key" ]]; then ARG_ARRAY+=("--header") ARG_ARRAY+=("X-Forwarded-Proto: https") fi if [[ -n "$DOKKU_APP_LISTEN_IP" ]]; then ARG_ARRAY+=("--ip-address") ARG_ARRAY+=("$DOKKU_APP_LISTEN_IP") fi if [[ -n "$DOKKU_APP_LISTEN_PORT" ]]; then ARG_ARRAY+=("--port") ARG_ARRAY+=("$DOKKU_APP_LISTEN_PORT") fi local app_vhosts="$(plugn trigger domains-list "$APP")" if [[ -n "$app_vhosts" ]]; then for app_vhost in $app_vhosts; do if [[ "$app_vhost" == "localhost" ]] || [[ "$app_vhost" == '*' ]]; then continue fi ARG_ARRAY+=("--header") ARG_ARRAY+=("Host: $app_vhost") break done fi if [[ "$DOKKU_APP_CONTAINER_TYPE" == "web" ]]; then content="$(docker-container-healthchecker add "$DOKKU_APP_CONTAINER_TYPE" --app-json "$TMP_APP_JSON_OUTPUT" --listening-check --name "port listening check" --port "$DOKKU_APP_LISTEN_PORT" --pretty --warn-only)" echo "$content" >"$TMP_APP_JSON_OUTPUT" fi local docker_container_healthchecker_path docker_container_healthchecker_path="$(command -v docker-container-healthchecker)" sudo "$docker_container_healthchecker_path" check "$DOKKU_APP_CONTAINER_ID" "${ARG_ARRAY[@]}" || FAILEDCHECKS="$?" if [[ $FAILEDCHECKS -gt 0 ]]; then "$DOCKER_BIN" container update --restart=no "$DOKKU_APP_CONTAINER_ID" &>/dev/null || true "$DOCKER_BIN" container stop "$DOKKU_APP_CONTAINER_ID" || true dokku_log_warn "Could not start due to $FAILEDCHECKS failed checks ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" return 1 fi trap - EXIT dokku_log_verbose "All checks successful ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" } trigger-scheduler-docker-local-check-deploy "$@"