diff --git a/docs/processes/one-off-tasks.md b/docs/processes/one-off-tasks.md index 91f03d190d1..f6234379891 100644 --- a/docs/processes/one-off-tasks.md +++ b/docs/processes/one-off-tasks.md @@ -4,6 +4,7 @@ run [-e|--env KEY=VALUE] [--no-tty] # Run a command in a new container using the current app image run:detached [-e|-env KEY=VALUE] [--no-tty] # Run a command in a new detached container using the current app image run:list [--format json|stdout] [] # List all run containers for an app +run:stop # Stops all run containers for an app or a specified run container ``` Sometimes it is necessary to run a one-off command under an app. Dokku makes it easy to run a fresh container via the `run` command. @@ -102,3 +103,33 @@ dokku run:list node-js-app --format json } ] ``` + +### Stopping a one-off cotainer + +> New as of 0.29.0 + +Run containers for an app can be stopped via the `run:stop` command. The output will be the container id. + +```shell +# start a container +# the output will be something like: node-js-app.run.2313 +dokku run node-js-app sleep 300 + +# stop the container +dokku run:stop --container node-js-app.run.2313 +```` + +``` +node-js-app.run.2313 +``` + +All containers for a given app can be stopped by specifying the app name. + +```shell +dokku run:stop node-js-app +``` + +``` +node-js-app.run.2313 +node-js-app.run.574 +``` diff --git a/plugins/run/help-functions b/plugins/run/help-functions index 26934a197a1..28f77b627e6 100755 --- a/plugins/run/help-functions +++ b/plugins/run/help-functions @@ -30,5 +30,6 @@ fn-help-content() { run [-e|--env KEY=VALUE] [--no-tty] , Run a command in a new container using the current app image run:detached [-e|-env KEY=VALUE] [--no-tty] , Run a command in a new detached container using the current app image run:list [--format json|stdout] , List all run containers for an app + run:stop , Stops all run containers for an app or a specified run container help_content } diff --git a/plugins/run/internal-functions b/plugins/run/internal-functions index e52730367d4..0502cfaa7a7 100755 --- a/plugins/run/internal-functions +++ b/plugins/run/internal-functions @@ -109,3 +109,35 @@ cmd-run-list() { local DOKKU_SCHEDULER=$(get_app_scheduler "$APP") plugn trigger scheduler-run-list "$DOKKU_SCHEDULER" "$APP" "$FORMAT" } + +cmd-run-stop() { + declare desc="Stops all run containers for an app or a specified run container" + declare cmd="run:stop" + [[ "$1" == "$cmd" ]] && shift 1 + declare APP CONTAINER_NAME + + while [[ $# -gt 0 ]]; do + case $1 in + --container=*) + local arg=$(printf "%s" "$1" | sed -E 's/(^--container=)//g') + CONTAINER_NAME="$arg" + shift + ;; + --container) + if [[ ! $2 ]]; then + dokku_log_warn "expected $1 to have an argument" + break + fi + CONTAINER_NAME="$2" + shift 2 + ;; + *) + APP="$1" + shift + ;; + esac + done + + local DOKKU_SCHEDULER=$(get_app_scheduler "$APP") + plugn trigger scheduler-run-stop "$DOKKU_SCHEDULER" "$APP" "$CONTAINER_NAME" +} diff --git a/plugins/run/subcommands/stop b/plugins/run/subcommands/stop new file mode 100755 index 00000000000..7feae586d14 --- /dev/null +++ b/plugins/run/subcommands/stop @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -eo pipefail +source "$PLUGIN_AVAILABLE_PATH/run/internal-functions" +[[ $DOKKU_TRACE ]] && set -x + +cmd-run-stop "$@" diff --git a/plugins/scheduler-docker-local/scheduler-run-stop b/plugins/scheduler-docker-local/scheduler-run-stop new file mode 100755 index 00000000000..3b9f730d41d --- /dev/null +++ b/plugins/scheduler-docker-local/scheduler-run-stop @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/config/functions" + +fn-scheduler-docker-local-stop-container() { + declare CONTAINER_ID_OR_NAME="$1" DOCKER_STOP_TIMEOUT="$2" + + if [ ! "$("$DOCKER_BIN" container ls -q -f "name=$CONTAINER_ID_OR_NAME" 2>/dev/null)" ]; then + dokku_log_warn "Specified container does not exist" + return 0 + fi + + [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" + # shellcheck disable=SC2086 + if "$DOCKER_BIN" container stop $DOCKER_STOP_TIME_ARG "$CONTAINER_ID_OR_NAME"; then + return + fi + + if "$DOCKER_BIN" container kill "$CONTAINER_ID_OR_NAME"; then + return + fi + + dokku_log_warn "Unable to stop container ${CONTAINER_ID_OR_NAME}" + return 1 +} + +trigger-scheduler-docker-local-scheduler-run-stop() { + declare desc="stops an app run container" + declare trigger="scheduler-run-stop" + declare DOKKU_SCHEDULER="$1" APP="$2" CONTAINER_NAME="$3" + + if [[ "$DOKKU_SCHEDULER" != "docker-local" ]]; then + return + fi + + if [[ -z "$APP" ]]; then + if [[ -z "$CONTAINER_NAME" ]]; then + dokku_log_fail "No container or app specified" + fi + if [[ "$(echo "$CONTAINER_NAME" | grep -o '\.' | wc -l)" -ne 2 ]]; then + dokku_log_fail "Invalid container name specified: $CONTAINER_NAME" + fi + + APP="$(echo "$CONTAINER_NAME" | cut -d'.' -f1)" + STATE="$(echo "$CONTAINER_NAME" | cut -d'.' -f2)" + if [[ "$STATE" != "run" ]]; then + dokku_log_fail "Specified container must be a run container" + fi + fi + + verify_app_name "$APP" + + DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)" + if [[ -n "$CONTAINER_NAME" ]]; then + fn-scheduler-docker-local-stop-container "$CONTAINER_NAME" "$DOKKU_DOCKER_STOP_TIMEOUT" + return $? + fi + + CONTAINERS="$("$DOCKER_BIN" container ls --all --filter "label=com.dokku.app-name=$APP" --filter "label=com.dokku.container-type=run" --format '{{ .Names }}')" + if [[ -z "$CONTAINERS" ]]; then + dokku_log_quiet "No run containers exist" + return + fi + + exit_code=0 + while IFS= read -r CONTAINER_NAME; do + if ! fn-scheduler-docker-local-stop-container "$CONTAINER_NAME" "$DOKKU_DOCKER_STOP_TIMEOUT"; then + exit_code=1 + fi + done <<<"$CONTAINERS" + + return "$exit_code" +} + +trigger-scheduler-docker-local-scheduler-run-stop "$@"