In cases where a docker arg uses backticks - which may happen with docker labels - scheduled containers would fail to support these due to performing a double-eval. This is particularly noticeable for traefik labels, where backticks and parenthesis are common. This fix properly handles docker args as an array instead of performing an extra eval when scheduling containers.
254 lines
7.7 KiB
Bash
Executable File
254 lines
7.7 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-disable-chown: $(fn-plugin-property-get "scheduler-docker-local" "$APP" "disable-chown" "")"
|
|
"--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-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 2>&1
|
|
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.
|
|
# shellcheck disable=SC2086
|
|
"$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 2>&1 || true
|
|
if "$DOCKER_BIN" container inspect "${CID}" >/dev/null 2>&1; 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 "$@"
|
|
}
|