Rather than require a heavy chown operation across various paths, just chown the files already in the built image during the release process. This ensures we can skip not-only the chown process during the container start that herokuish injects, but also the one that Dokku runs which modifies mounted container paths as well during the pre-deploy. Note that users will need to ensure any mounted volumes don't have permissions reset by other processes or containers won't be able to access them.
291 lines
8.5 KiB
Bash
Executable File
291 lines
8.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
|
source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions"
|
|
source "$PLUGIN_AVAILABLE_PATH/config/functions"
|
|
set -eo pipefail
|
|
[[ $DOKKU_TRACE ]] && set -x
|
|
|
|
cmd-scheduler-docker-local-report() {
|
|
declare desc="displays a scheduler-docker-local report for one or more apps"
|
|
declare cmd="scheduler-docker-local:report"
|
|
[[ "$1" == "$cmd" ]] && shift 1
|
|
declare APP="$1" INFO_FLAG="$2"
|
|
|
|
if [[ -n "$APP" ]] && [[ "$APP" == --* ]]; then
|
|
INFO_FLAG="$APP"
|
|
APP=""
|
|
fi
|
|
|
|
if [[ -z "$APP" ]] && [[ -z "$INFO_FLAG" ]]; then
|
|
INFO_FLAG="true"
|
|
fi
|
|
|
|
if [[ -z "$APP" ]]; then
|
|
for app in $(dokku_apps); do
|
|
cmd-scheduler-docker-local-report-single "$app" "$INFO_FLAG" | tee || true
|
|
done
|
|
else
|
|
cmd-scheduler-docker-local-report-single "$APP" "$INFO_FLAG"
|
|
fi
|
|
}
|
|
|
|
cmd-scheduler-docker-local-report-single() {
|
|
declare APP="$1" INFO_FLAG="$2"
|
|
if [[ "$INFO_FLAG" == "true" ]]; then
|
|
INFO_FLAG=""
|
|
fi
|
|
verify_app_name "$APP"
|
|
local flag_map=(
|
|
"--scheduler-docker-local-init-process: $(fn-plugin-property-get "scheduler-docker-local" "$APP" "init-process" "true")"
|
|
"--scheduler-docker-local-parallel-schedule-count: $(fn-plugin-property-get "scheduler-docker-local" "$APP" "parallel-schedule-count" "")"
|
|
)
|
|
|
|
if [[ -z "$INFO_FLAG" ]]; then
|
|
dokku_log_info2_quiet "${APP} scheduler-docker-local information"
|
|
for flag in "${flag_map[@]}"; do
|
|
key="$(echo "${flag#--}" | cut -f1 -d' ' | tr - ' ')"
|
|
dokku_log_verbose "$(printf "%-30s %-25s" "${key^}" "${flag#*: }")"
|
|
done
|
|
else
|
|
local match=false
|
|
local value_exists=false
|
|
for flag in "${flag_map[@]}"; do
|
|
valid_flags="${valid_flags} $(echo "$flag" | cut -d':' -f1)"
|
|
if [[ "$flag" == "${INFO_FLAG}:"* ]]; then
|
|
value=${flag#*: }
|
|
size="${#value}"
|
|
if [[ "$size" -ne 0 ]]; then
|
|
echo "$value" && match=true && value_exists=true
|
|
else
|
|
match=true
|
|
fi
|
|
fi
|
|
done
|
|
[[ "$match" == "true" ]] || dokku_log_fail "Invalid flag passed, valid flags:${valid_flags}"
|
|
[[ "$value_exists" == "true" ]] || dokku_log_fail "not deployed"
|
|
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
|
|
|
|
dokku_log_verbose_quiet "Attempting to retire $APP container $CID"
|
|
STATE="$("$DOCKER_BIN" container inspect --format "{{ .State.Status }}" "$CID" 2>/dev/null || true)"
|
|
if [[ -z "$STATE" ]]; then
|
|
return
|
|
fi
|
|
|
|
DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)"
|
|
[[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}"
|
|
|
|
if [[ "$STATE" == "restarting" ]]; then
|
|
"$DOCKER_BIN" container update --restart=no "$CID" &>/dev/null
|
|
fi
|
|
|
|
if [[ "$STATE" != "dead" ]] && [[ "$STATE" != "exited" ]]; then
|
|
# Attempt to stop, if that fails, then force a kill as docker seems
|
|
# to not send SIGKILL as the docs would indicate. If that fails, move
|
|
# on to the next.
|
|
"$DOCKER_BIN" container stop $DOCKER_STOP_TIME_ARG "$CID" \
|
|
|| "$DOCKER_BIN" container kill "$CID" \
|
|
|| dokku_log_warn "Unable to kill container ${CID}"
|
|
fi
|
|
|
|
STATE="$("$DOCKER_BIN" container inspect --format "{{ .State.Status }}" "$CID" 2>/dev/null || true)"
|
|
if [[ -z "$STATE" ]]; then
|
|
return
|
|
fi
|
|
|
|
if [[ "$STATE" != "dead" ]] && [[ "$STATE" != "exited" ]]; then
|
|
if ! "$DOCKER_BIN" container kill "$CID"; then
|
|
dokku_log_warn "Unable to kill container ${CID}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
fn-scheduler-docker-local-retire-containers() {
|
|
local DEAD_CONTAINER_FILE="${DOKKU_LIB_ROOT}/data/scheduler-docker-local/dead-containers"
|
|
local APP CID CURRENT_TIME DEAD_TIME STATE
|
|
declare SCHEDULER="$1" APP="$2"
|
|
|
|
if [[ ! -f "$DEAD_CONTAINER_FILE" ]]; then
|
|
return
|
|
fi
|
|
|
|
DEAD_CONTAINERS=()
|
|
while read line; do
|
|
[[ -z "$line" ]] && continue
|
|
CURRENT_TIME="$(date +%s)"
|
|
RETIRE_APP="$(echo "$line" | cut -d ' ' -f1)"
|
|
CID="$(echo "$line" | cut -d ' ' -f2)"
|
|
DEAD_TIME="$(echo "$line" | cut -d ' ' -f3)"
|
|
|
|
if [[ -n "$APP" ]] && [[ "$APP" != "$RETIRE_APP" ]]; then
|
|
continue
|
|
fi
|
|
|
|
if [[ "$CURRENT_TIME" -le "$DEAD_TIME" ]]; then
|
|
continue
|
|
fi
|
|
|
|
fn-scheduler-docker-local-retire-container "$RETIRE_APP" "$CID"
|
|
STATE="$("$DOCKER_BIN" container inspect --format "{{ .State.Status }}" "$CID" 2>/dev/null || true)"
|
|
if [[ -z "$STATE" ]]; then
|
|
DEAD_CONTAINERS+=("$CID")
|
|
continue
|
|
fi
|
|
|
|
if [[ "$STATE" == "running" ]]; then
|
|
dokku_log_warn "Container ${CID} still running"
|
|
continue
|
|
fi
|
|
|
|
"$DOCKER_BIN" container rm --force "$CID" &>/dev/null || true
|
|
if "$DOCKER_BIN" container inspect "${CID}" &>/dev/null; then
|
|
dokku_log_warn "Container ${CID} still running"
|
|
continue
|
|
fi
|
|
|
|
DEAD_CONTAINERS+=("$CID")
|
|
done <"$DEAD_CONTAINER_FILE"
|
|
|
|
for CID in "${DEAD_CONTAINERS[@]}"; do
|
|
sed -i "/${CID}/d" "$DEAD_CONTAINER_FILE"
|
|
done
|
|
}
|
|
|
|
fn-scheduler-docker-local-retire-images() {
|
|
local DEAD_IMAGE_FILE="${DOKKU_LIB_ROOT}/data/scheduler-docker-local/dead-images"
|
|
local APP IMAGE_ID CURRENT_TIME DEAD_TIME STATE RM_OUTPUT
|
|
declare SCHEDULER="$1" APP="$2"
|
|
|
|
if [[ ! -f "$DEAD_IMAGE_FILE" ]]; then
|
|
return
|
|
fi
|
|
|
|
DEAD_IMAGES=()
|
|
while read line; do
|
|
[[ -z "$line" ]] && continue
|
|
CURRENT_TIME="$(date +%s)"
|
|
RETIRE_APP="$(echo "$line" | cut -d ' ' -f1)"
|
|
IMAGE_ID="$(echo "$line" | cut -d ' ' -f2)"
|
|
DEAD_TIME="$(echo "$line" | cut -d ' ' -f3)"
|
|
|
|
if [[ -n "$APP" ]] && [[ "$APP" != "$RETIRE_APP" ]]; then
|
|
continue
|
|
fi
|
|
|
|
if [[ "$CURRENT_TIME" -le "$DEAD_TIME" ]]; then
|
|
continue
|
|
fi
|
|
|
|
STATE="$("$DOCKER_BIN" image inspect --format "{{ .Id }}" "$IMAGE_ID" 2>/dev/null || true)"
|
|
if [[ -z "$STATE" ]]; then
|
|
DEAD_IMAGES+=("$IMAGE_ID")
|
|
continue
|
|
fi
|
|
|
|
dokku_log_verbose_quiet "Attempting to retire $RETIRE_APP image $IMAGE_ID"
|
|
if RM_OUTPUT="$("$DOCKER_BIN" image remove "$IMAGE_ID" 2>&1)"; then
|
|
DEAD_IMAGES+=("$IMAGE_ID")
|
|
continue
|
|
fi
|
|
|
|
if echo "$RM_OUTPUT" | grep -q "image has dependent child images"; then
|
|
TAG_COUNT="$(docker inspect "$IMAGE_ID" --format '{{ json .RepoTags }}' | jq '. | length')"
|
|
if [[ "$TAG_COUNT" -eq 0 ]]; then
|
|
dokku_log_warn "Image ${IMAGE_ID} has children images and is untagged, skipping rm and marking dead"
|
|
DEAD_IMAGES+=("$IMAGE_ID")
|
|
continue
|
|
fi
|
|
|
|
dokku_log_warn "Image ${IMAGE_ID} has children images and has $TAG_COUNT tags, skipping rm"
|
|
continue
|
|
fi
|
|
|
|
if echo "$RM_OUTPUT" | grep -q "image is being used by running container"; then
|
|
dokku_log_warn "Image ${IMAGE_ID} has running containers, skipping rm"
|
|
continue
|
|
fi
|
|
|
|
dokku_log_warn "Image ${IMAGE_ID} still running"
|
|
done <"$DEAD_IMAGE_FILE"
|
|
|
|
for IMAGE_ID in "${DEAD_IMAGES[@]}"; do
|
|
sed -i "/${IMAGE_ID}/d" "$DEAD_IMAGE_FILE"
|
|
done
|
|
|
|
sort -o "$DEAD_IMAGE_FILE" -r "$DEAD_IMAGE_FILE"
|
|
}
|
|
|
|
fn-scheduler-docker-local-register-retired() {
|
|
declare TYPE="$1" APP="$2" DOCKER_ID="$3" WAIT="$4"
|
|
local DEAD_FILE="${DOKKU_LIB_ROOT}/data/scheduler-docker-local/dead-containers"
|
|
if [[ "$TYPE" == "image" ]]; then
|
|
local DEAD_FILE="${DOKKU_LIB_ROOT}/data/scheduler-docker-local/dead-images"
|
|
fi
|
|
local CURRENT_TIME DEAD_TIME
|
|
|
|
CURRENT_TIME="$(date +%s)"
|
|
DEAD_TIME=$((CURRENT_TIME + WAIT))
|
|
touch "$DEAD_FILE"
|
|
if ! grep -q "$DOCKER_ID" "$DEAD_FILE"; then
|
|
echo "${APP} ${DOCKER_ID} ${DEAD_TIME}" >>"${DEAD_FILE}"
|
|
fi
|
|
}
|
|
|
|
fn-scheduler-docker-local-start-app-container() {
|
|
declare desc="starts a single app container"
|
|
declare APP="$1"
|
|
shift
|
|
|
|
declare -a DOCKER_ARGS
|
|
for i in "$@"; do
|
|
DOCKER_ARGS+=("$i")
|
|
done
|
|
set -- "${DOCKER_ARGS[@]}"
|
|
|
|
eval "$(config_export app "$APP" --merged)"
|
|
# shellcheck disable=SC2124
|
|
"$DOCKER_BIN" container create "$@"
|
|
}
|