From e753c46029b5ff85ef6f73d5b3466ba0674e35bf Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 15:34:24 -0400 Subject: [PATCH] refactor: move CHECKS file extraction to the beginning of the deploy This makes deploys faster for any that are built from source as the image artifact isn't involved in those cases while also unifying how the file is handled. Refs #2760 --- docs/appendices/0.31.0-migration-guide.md | 1 + docs/deployment/zero-downtime-deploys.md | 4 +- plugins/scheduler-docker-local/check-deploy | 20 ++---- .../scheduler-docker-local/core-post-deploy | 12 ++++ .../scheduler-docker-local/core-post-extract | 70 +++++++++++++++++++ .../scheduler-docker-local/internal-functions | 38 ++++++++++ plugins/scheduler-docker-local/pre-deploy | 11 ++- 7 files changed, 130 insertions(+), 26 deletions(-) create mode 100755 plugins/scheduler-docker-local/core-post-extract diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index b4ff4a82401..f873a9da17b 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -6,6 +6,7 @@ - The `vector` container integration now mounts config to `/etc/vector` instead of the path `/etc/vector/vector.json`, allowing users the ability to provide extra configuration for Vector Sinks. To take advantage of the new functionality, the vector container should be stopped (via `dokku logs:vector-stop`) and then started (via `dokku logs:vector-start`). - The `traefik` integration now mounts config to `/data` instead of the path `/acme.json`, fixing permissions issues under certain architectures. To take advantage of the new functionality, the traefik container should be stopped (via `dokku traefik:stop`) and then started (via `dokku traefik:start`). - Users no longer need to clear the `source-image` git property when transitioning from image-based deploys (`git:from-image` and `git:load-image`) to other deployment methods (git push, `git:from-archive`, `git:sync`). +- For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is now extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. ## Deprecations diff --git a/docs/deployment/zero-downtime-deploys.md b/docs/deployment/zero-downtime-deploys.md index c4e43779727..1d6bc3f8874 100644 --- a/docs/deployment/zero-downtime-deploys.md +++ b/docs/deployment/zero-downtime-deploys.md @@ -139,15 +139,13 @@ If your application needs a longer period to boot up - perhaps to load data into Checks are run against the detected `web` process from your application's `Procfile`. For non-web processes, Dokku will fallback to the aforementioned process uptime check. -To specify checks, add a `CHECKS` file to the root of your project directory. The `CHECKS` file should be plain text and may contain: +For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. The `CHECKS` file should be plain text and may contain: - check instructions - settings (NAME=VALUE) - comments (lines starting with #) - empty lines -> For Dockerfile and Docker Image based deploys, the file *must* be in the `WORKDIR` directory of the built image. `/app` is used by default as the root container directory for buildpack-based deploys. - ### Check instructions The format of a check instruction is a path or relative URL, optionally followed by the expected content: diff --git a/plugins/scheduler-docker-local/check-deploy b/plugins/scheduler-docker-local/check-deploy index 363b0c16883..678ae2677a5 100755 --- a/plugins/scheduler-docker-local/check-deploy +++ b/plugins/scheduler-docker-local/check-deploy @@ -38,6 +38,7 @@ set -eo pipefail 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" @@ -76,16 +77,14 @@ trigger-scheduler-docker-local-check-deploy() { # use this number of retries for checks local ATTEMPTS="${DOKKU_CHECKS_ATTEMPTS:-5}" - local CHECK_DEPLOY_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") - local CHECKS_FILENAME=${CHECK_DEPLOY_TMP_WORK_DIR}/CHECKS + 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") - copy_from_image "$IMAGE" "CHECKS" "$CHECKS_FILENAME" 2>/dev/null || true checks_check_deploy_cleanup() { - declare desc="cleans up CHECK_DEPLOY_TMP_WORK_DIR and print container output" + declare desc="print container output" local id="$1" - rm -rf "$CHECK_DEPLOY_TMP_WORK_DIR" &>/dev/null || true + if [[ $id ]]; then dokku_log_info2_quiet "Start of $APP container output ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" dokku_container_log_verbose_quiet "$id" @@ -96,8 +95,6 @@ trigger-scheduler-docker-local-check-deploy() { # We allow custom check for web instances only if [[ ! -s "${CHECKS_FILENAME}" ]] || [[ "$DOKKU_APP_CONTAINER_TYPE" != "web" ]]; then - rm -rf "$CHECK_DEPLOY_TMP_WORK_DIR" &>/dev/null || true - # simple default check to see if the container stuck around local DOKKU_DEFAULT_CHECKS_WAIT="${DOKKU_DEFAULT_CHECKS_WAIT:-10}" dokku_log_verbose "Waiting for $DOKKU_DEFAULT_CHECKS_WAIT seconds ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" @@ -115,15 +112,6 @@ trigger-scheduler-docker-local-check-deploy() { dokku_log_verbose "Default container check successful ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" && exit 0 fi - local NEW_CHECKS_FILENAME="${CHECK_DEPLOY_TMP_WORK_DIR}/NEW_CHECKS" - template_checks() { - eval "$(config_export app "$APP" --merged)" - sigil -f "$CHECKS_FILENAME" | cat -s >"$NEW_CHECKS_FILENAME" - } - - template_checks - CHECKS_FILENAME="$NEW_CHECKS_FILENAME" - # Reads name/value pairs, sets the WAIT and TIMEOUT variables exec <"$CHECKS_FILENAME" local line diff --git a/plugins/scheduler-docker-local/core-post-deploy b/plugins/scheduler-docker-local/core-post-deploy index bd02b2d4421..592bd0af105 100755 --- a/plugins/scheduler-docker-local/core-post-deploy +++ b/plugins/scheduler-docker-local/core-post-deploy @@ -15,6 +15,18 @@ trigger-scheduler-docker-local-core-post-deploy() { return fi + # move over the checks file appropriately + checks_file="${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS" + process_specific_checks_file="${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID" + if [[ -f "$process_specific_checks_file" ]]; then + mv "$process_specific_checks_file" "$checks_file" + elif [[ -f "$process_specific_checks_file.missing" ]]; then + rm -f "$process_specific_checks_file.missing" + if [[ -f "$checks_file" ]]; then + rm -f "$checks_file" + fi + fi + dokku_log_info1 "Renaming containers" local PROCTYPES="$(plugn trigger ps-current-scale "$APP" | awk -F '=' '{ print $1 }' | xargs)" local CONTAINER_FILES="$(find "$DOKKU_ROOT/$APP" -maxdepth 1 -name "CONTAINER.*" -printf "%f\n" 2>/dev/null | sort -t . -k 3 -n | xargs)" diff --git a/plugins/scheduler-docker-local/core-post-extract b/plugins/scheduler-docker-local/core-post-extract new file mode 100755 index 00000000000..f8809136f83 --- /dev/null +++ b/plugins/scheduler-docker-local/core-post-extract @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" + +fn-scheduler-docker-local-copy-from-image() { + declare APP="$1" IMAGE_NAME="$2" CHECKS_PATH="$3" + + mkdir -p "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP" + rm -f "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS."* + copy_from_image "$IMAGE_NAME" "$CHECKS_PATH" "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID" 2>/dev/null || true + if [[ ! -f "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID" ]]; then + touch "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID.missing" + fi +} + +fn-scheduler-docker-local-copy-from-directory() { + declare APP="$1" SOURCECODE_WORK_DIR="$2" CHECKS_PATH="$3" + + pushd "$SOURCECODE_WORK_DIR" >/dev/null + mkdir -p "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP" + + if [[ -z "$CHECKS_PATH" ]]; then + touch "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID.missing" + return + fi + + if [[ ! -f "$CHECKS_PATH" ]]; then + touch "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID.missing" + return + fi + + rm -f "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS."* + cp -f "$CHECKS_PATH" "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID" + popd &>/dev/null || pushd "/tmp" >/dev/null +} + +trigger-scheduler-docker-local-core-post-extract() { + declare desc="scheduler-docker-local post-extract plugin trigger" + declare trigger="post-extract" + declare APP="$1" SOURCECODE_WORK_DIR="$2" + local CHECKS_PATH="CHECKS" CHECKS_FILENAME="${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID" + local app_source_image + + app_source_image="$(plugn trigger git-get-property "$APP" "source-image")" + if [[ -n "$app_source_image" ]]; then + fn-scheduler-docker-local-copy-from-image "$APP" "$app_source_image" "$CHECKS_PATH" + else + fn-scheduler-docker-local-copy-from-directory "$APP" "$SOURCECODE_WORK_DIR" "$CHECKS_PATH" + fi + + if [[ -f "$CHECKS_FILENAME" ]]; then + local TMP_CHECKS_FILE=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") + trap "rm -rf '$TMP_CHECKS_FILE' >/dev/null" RETURN INT TERM EXIT + + template_checks() { + declare desc="templates out the checks file" + declare APP="$1" CHECKS_FILENAME="$2" + eval "$(config_export app "$APP" --format exports --merged)" + sigil -f "$CHECKS_FILENAME" | cat -s >"$TMP_CHECKS_FILE" + mv "$TMP_CHECKS_FILE" "$CHECKS_FILENAME" + } + + template_checks "$APP" "$CHECKS_FILENAME" + fi +} + +trigger-scheduler-docker-local-core-post-extract "$@" diff --git a/plugins/scheduler-docker-local/internal-functions b/plugins/scheduler-docker-local/internal-functions index a8985eef21e..33f16100e9b 100755 --- a/plugins/scheduler-docker-local/internal-functions +++ b/plugins/scheduler-docker-local/internal-functions @@ -67,6 +67,44 @@ cmd-scheduler-docker-local-report-single() { fi } +fn-scheduler-docker-local-get-checks-file-path() { + declare APP="$1" + + echo "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS" +} + +fn-scheduler-docker-local-get-process-specific-checks-file-path() { + declare APP="$1" + + checks_path="$(fn-scheduler-docker-local-get-checks-file-path "$APP")" + process_specific_checks_path="$checks_path.$DOKKU_PID" + if [[ -f "$process_specific_checks_path" ]]; then + echo "$process_specific_checks_path" + return + fi + + echo "$checks_path" +} + +fn-scheduler-docker-local-has-checks-file() { + declare APP="$1" + + checks_path="$(fn-scheduler-docker-local-get-checks-file-path "$APP")" + if [[ -f "$checks_path.$DOKKU_PID.missing" ]]; then + return 1 + fi + + if [[ -f "$checks_path.$DOKKU_PID" ]]; then + return 0 + fi + + if [[ -f "$checks_path" ]]; then + return 0 + fi + + return 1 +} + fn-scheduler-docker-local-retire-container() { declare APP="$1" CID="$2" local STATE diff --git a/plugins/scheduler-docker-local/pre-deploy b/plugins/scheduler-docker-local/pre-deploy index b6d1bb1973b..217ce6630e3 100755 --- a/plugins/scheduler-docker-local/pre-deploy +++ b/plugins/scheduler-docker-local/pre-deploy @@ -4,6 +4,7 @@ set -eo pipefail source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" trigger-scheduler-docker-local-pre-deploy() { declare desc="scheduler-docker-local pre-deploy plugin trigger" @@ -16,7 +17,7 @@ trigger-scheduler-docker-local-pre-deploy() { fi scheduler-docker-local-pre-deploy-chown-app "$APP" "$IMAGE_TAG" - scheduler-docker-local-pre-deploy-precheck "$APP" "$IMAGE_TAG" + scheduler-docker-local-pre-deploy-precheck "$APP" } scheduler-docker-local-pre-deploy-chown-app() { @@ -63,14 +64,10 @@ scheduler-docker-local-pre-deploy-chown-app() { scheduler-docker-local-pre-deploy-precheck() { declare desc="Outputs the checks messages if necessary" - declare APP="$1" IMAGE_TAG="$2" - local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG") - local CHECKS_FILE=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") - trap "rm -rf '$CHECKS_FILE' >/dev/null" RETURN INT TERM EXIT - copy_from_image "$IMAGE" "CHECKS" "$CHECKS_FILE" 2>/dev/null || true + declare APP="$1" dokku_log_info2 "Processing deployment checks" - if [[ ! -s "${CHECKS_FILE}" ]]; then + if ! fn-scheduler-docker-local-has-checks-file "$APP"; then local CHECKS_URL="${DOKKU_CHECKS_URL:-https://dokku.com/docs/deployment/zero-downtime-deploys/}" dokku_log_verbose "No CHECKS file found. Simple container checks will be performed." dokku_log_verbose "For more efficient zero downtime deployments, create a CHECKS file. See ${CHECKS_URL} for examples"