Files
dokku/plugins/scheduler-docker-local/internal-functions
Jose Diaz-Gonzalez 0786954d34 feat: schedule related images for cleanup when retiring containers
If the containers no longer exist and the images are unused, we should remove the images as well.

Note that images may sometimes stick around for a little while, depending on the retire schedule and the wait time specified for the container.

Closes #4104
2020-11-26 03:21:45 -05:00

221 lines
6.8 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"
local INSTALLED_APPS=$(dokku_apps)
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 $INSTALLED_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" "")"
)
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" DEAD_TIME="$3"
local STATE
dokku_log_info1_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
if [[ ! -f "$DEAD_CONTAINER_FILE" ]]; then
return
fi
DEAD_CONTAINERS=()
while read line; do
[[ -z "$line" ]] && continue
CURRENT_TIME="$(date +%s)"
APP="$(echo "$line" | cut -d ' ' -f1)"
CID="$(echo "$line" | cut -d ' ' -f2)"
DEAD_TIME="$(echo "$line" | cut -d ' ' -f3)"
if [[ "$CURRENT_TIME" -le "$DEAD_TIME" ]]; then
continue
fi
fn-scheduler-docker-local-retire-container "$APP" "$CID" "$DEAD_TIME"
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
if [[ ! -f "$DEAD_IMAGE_FILE" ]]; then
return
fi
DEAD_IMAGES=()
while read line; do
[[ -z "$line" ]] && continue
CURRENT_TIME="$(date +%s)"
APP="$(echo "$line" | cut -d ' ' -f1)"
IMAGE_ID="$(echo "$line" | cut -d ' ' -f2)"
DEAD_TIME="$(echo "$line" | cut -d ' ' -f3)"
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_info1_quiet "Attempting to retire $APP image $IMAGE_ID"
if RM_OUTPUT="$("$DOCKER_BIN" image rm "$IMAGE_ID" 2>&1)"; then
DEAD_IMAGES+=("$IMAGE_ID")
continue
fi
if echo "$RM_OUTPUT" | grep -q "image has dependent child images"; then
dokku_log_warn "Image ${IMAGE_ID} has children images, skipping rm and marking dead"
DEAD_IMAGES+=("$IMAGE_ID")
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))
if ! grep -q "$DOCKER_ID" "$DEAD_FILE"; then
echo "${APP} ${DOCKER_ID} ${DEAD_TIME}" >>"${DEAD_FILE}"
fi
}