This plugin trigger should allow users to pair down the available commands for a given user. While this does not affect help output - or any output at all - this is a good first step in allowing administrative access to certain commands while still allowing users to deploy/manage given applications. Note that because of how dokku works, all parsing of commands must be done within the pluginhook, and we cannot give visibility into the actual command or application that will be affected by a given run.
262 lines
8.9 KiB
Bash
Executable File
262 lines
8.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -eo pipefail
|
|
shopt -s nullglob
|
|
|
|
case "$(lsb_release -si)" in
|
|
Debian)
|
|
export DOKKU_DISTRO=${DOKKU_DISTRO:="debian"}
|
|
;;
|
|
*)
|
|
export DOKKU_DISTRO=${DOKKU_DISTRO:="ubuntu"}
|
|
;;
|
|
esac
|
|
|
|
export DOKKU_IMAGE=${DOKKU_IMAGE:="gliderlabs/herokuish"}
|
|
export DOKKU_ROOT=${DOKKU_ROOT:=~dokku}
|
|
export DOKKU_LIB_ROOT=${DOKKU_LIB_PATH:="/var/lib/dokku"}
|
|
|
|
export PLUGIN_PATH=${PLUGIN_PATH:="$DOKKU_LIB_ROOT/plugins"}
|
|
export PLUGIN_AVAILABLE_PATH=${PLUGIN_AVAILABLE_PATH:="$PLUGIN_PATH/available"}
|
|
export PLUGIN_ENABLED_PATH=${PLUGIN_ENABLED_PATH:="$PLUGIN_PATH/enabled"}
|
|
export PLUGIN_CORE_PATH=${PLUGIN_CORE_PATH:="$DOKKU_LIB_ROOT/core-plugins"}
|
|
export PLUGIN_CORE_AVAILABLE_PATH=${PLUGIN_CORE_AVAILABLE_PATH:="$PLUGIN_CORE_PATH/available"}
|
|
export PLUGIN_CORE_ENABLED_PATH=${PLUGIN_CORE_ENABLED_PATH:="$PLUGIN_CORE_PATH/enabled"}
|
|
|
|
export DOKKU_API_VERSION=1
|
|
export DOKKU_NOT_IMPLEMENTED_EXIT=10
|
|
export DOKKU_VALID_EXIT=0
|
|
|
|
export DOKKU_LOGS_DIR=${DOKKU_LOGS_DIR:="/var/log/dokku"}
|
|
export DOKKU_EVENTS_LOGFILE=${DOKKU_EVENTS_LOGFILE:="$DOKKU_LOGS_DIR/events.log"}
|
|
|
|
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
|
|
|
[[ -f $DOKKU_ROOT/dokkurc ]] && source $DOKKU_ROOT/dokkurc
|
|
[[ -d $DOKKU_ROOT/.dokkurc ]] && for f in $DOKKU_ROOT/.dokkurc/*; do source $f; done
|
|
|
|
[[ $DOKKU_TRACE ]] && set -x
|
|
|
|
parse_args "$@"
|
|
args=("$@")
|
|
if [[ "${args[0]}" =~ ^--.* ]]; then
|
|
for arg in "$@"; do
|
|
if [[ "$arg" =~ ^--.* ]]; then
|
|
shift 1
|
|
else
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
! has_tty && DOKKU_QUIET_OUTPUT=1
|
|
|
|
if [[ $(id -un) != "dokku" && $1 != plugin:*install* && $1 != "plugin:update" ]]; then
|
|
export SSH_USER=$(id -un)
|
|
sudo -u dokku -E -H $0 "$@"
|
|
exit $?
|
|
fi
|
|
|
|
if [[ -n "$SSH_ORIGINAL_COMMAND" ]]; then
|
|
export -n SSH_ORIGINAL_COMMAND
|
|
if [[ $1 =~ config-* ]] || [[ $1 =~ docker-options* ]]; then
|
|
xargs $0 <<<$SSH_ORIGINAL_COMMAND
|
|
exit $?
|
|
else
|
|
$0 $SSH_ORIGINAL_COMMAND
|
|
exit $?
|
|
fi
|
|
fi
|
|
|
|
if ! dokku_auth "$@" ; then
|
|
dokku_log_fail "Access denied"
|
|
exit 1
|
|
fi
|
|
|
|
case "$1" in
|
|
receive)
|
|
APP="$2"; IMAGE=$(get_app_image_name $APP); IMAGE_SOURCE_TYPE="$3"; TMP_WORK_DIR="$4"
|
|
dokku_log_info1 "Cleaning up..."
|
|
docker_cleanup
|
|
dokku_log_info1 "Building $APP from $IMAGE_SOURCE_TYPE..."
|
|
dokku build "$APP" "$IMAGE_SOURCE_TYPE" "$TMP_WORK_DIR"
|
|
release_and_deploy "$APP"
|
|
;;
|
|
|
|
deploy)
|
|
[[ -z $2 ]] && dokku_log_fail "Please specify an app to deploy"
|
|
APP="$2"; IMAGE_TAG="$3"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG)
|
|
verify_app_name "$APP"
|
|
plugn trigger pre-deploy $APP $IMAGE_TAG
|
|
|
|
is_image_herokuish_based "$IMAGE" && DOKKU_HEROKUISH=true
|
|
DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE"
|
|
oldids=$(get_app_container_ids $APP)
|
|
|
|
DOKKU_APP_SKIP_ALL_CHECKS=$(dokku config:get $APP DOKKU_SKIP_ALL_CHECKS || true)
|
|
DOKKU_APP_SKIP_DEFAULT_CHECKS=$(dokku config:get $APP DOKKU_SKIP_DEFAULT_CHECKS || true)
|
|
DOKKU_GLOBAL_SKIP_ALL_CHECKS=$(dokku config:get --global DOKKU_SKIP_ALL_CHECKS || true)
|
|
DOKKU_GLOBAL_SKIP_DEFAULT_CHECKS=$(dokku config:get --global DOKKU_SKIP_DEFAULT_CHECKS || true)
|
|
|
|
DOKKU_SKIP_ALL_CHECKS=${DOKKU_APP_SKIP_ALL_CHECKS:="$DOKKU_GLOBAL_SKIP_ALL_CHECKS"}
|
|
DOKKU_SKIP_DEFAULT_CHECKS=${DOKKU_APP_SKIP_DEFAULT_CHECKS:="$DOKKU_GLOBAL_SKIP_DEFAULT_CHECKS"}
|
|
|
|
DOKKU_DEFAULT_DOCKER_ARGS=$(: | plugn trigger docker-args-deploy $APP $IMAGE_TAG)
|
|
|
|
while read -r line || [[ -n "$line" ]]; do
|
|
TRIM=${line%#*}
|
|
PROC_TYPE=${TRIM%%=*}
|
|
PROC_COUNT=${TRIM#*=}
|
|
CONTAINER_INDEX=1
|
|
|
|
while [[ $CONTAINER_INDEX -le $PROC_COUNT ]]; do
|
|
id=""; port=""; ipaddr=""
|
|
DOKKU_CONTAINER_ID_FILE="$DOKKU_ROOT/$APP/CONTAINER.$PROC_TYPE.$CONTAINER_INDEX"
|
|
DOKKU_IP_FILE="$DOKKU_ROOT/$APP/IP.$PROC_TYPE.$CONTAINER_INDEX"
|
|
DOKKU_PORT_FILE="$DOKKU_ROOT/$APP/PORT.$PROC_TYPE.$CONTAINER_INDEX"
|
|
|
|
# start the app
|
|
DOCKER_ARGS="$DOKKU_DEFAULT_DOCKER_ARGS"
|
|
DOCKER_ARGS+=" -e DYNO='$PROC_TYPE.$CONTAINER_INDEX' "
|
|
[[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" -e TRACE=true "
|
|
BIND_EXTERNAL=$(plugn trigger bind-external-ip $APP)
|
|
|
|
[[ -n "$DOKKU_HEROKUISH" ]] && START_CMD="/start $PROC_TYPE"
|
|
|
|
if [[ -z "$DOKKU_HEROKUISH" ]]; then
|
|
DOKKU_DOCKERFILE_PORT=$(dokku config:get $APP DOKKU_DOCKERFILE_PORT || true)
|
|
START_CMD=$(dokku config:get $APP DOKKU_DOCKERFILE_START_CMD || $START_CMD)
|
|
fi
|
|
|
|
if [[ "$PROC_TYPE" == "web" ]]; then
|
|
port=${DOKKU_DOCKERFILE_PORT:=5000}
|
|
if [[ "$BIND_EXTERNAL" = "true" ]]; then
|
|
id=$(docker run -d -p $port -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD)
|
|
port=$(docker port $id $port | sed 's/[0-9.]*://')
|
|
ipaddr=127.0.0.1
|
|
else
|
|
id=$(docker run -d -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD)
|
|
ipaddr=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $id)
|
|
fi
|
|
else
|
|
id=$(docker run -d $DOCKER_ARGS $IMAGE $START_CMD)
|
|
fi
|
|
|
|
# if we can't post-deploy successfully, kill new container
|
|
kill_new() {
|
|
docker inspect $id &> /dev/null && docker stop $id > /dev/null && docker kill $id > /dev/null
|
|
trap - INT TERM EXIT
|
|
kill -9 $$
|
|
}
|
|
|
|
# run checks first, then post-deploy hooks, which switches Nginx traffic
|
|
if [[ "$DOKKU_SKIP_ALL_CHECKS" = "true" ]]; then
|
|
dokku_log_info1 "Skipping pre-flight checks"
|
|
else
|
|
trap kill_new INT TERM EXIT
|
|
dokku_log_info1 "Running pre-flight checks"
|
|
plugn trigger check-deploy $APP $id $PROC_TYPE $port $ipaddr
|
|
trap - INT TERM EXIT
|
|
fi
|
|
|
|
# now using the new container
|
|
[[ -n "$id" ]] && echo $id > "$DOKKU_CONTAINER_ID_FILE"
|
|
[[ -n "$ipaddr" ]] && echo $ipaddr > "$DOKKU_IP_FILE"
|
|
[[ -n "$port" ]] && echo $port > "$DOKKU_PORT_FILE"
|
|
[[ -n "$port" ]] && echo "http://$(< "$DOKKU_ROOT/HOSTNAME"):$port" > "$DOKKU_ROOT/$APP/URL"
|
|
|
|
# cleanup pre-migration files
|
|
rm -f $DOKKU_ROOT/$APP/CONTAINER $DOKKU_ROOT/$APP/IP $DOKKU_ROOT/$APP/PORT
|
|
|
|
CONTAINER_INDEX=$(( CONTAINER_INDEX + 1 ))
|
|
done
|
|
# cleanup when we scale down
|
|
if [[ "$PROC_COUNT" == 0 ]]; then
|
|
CONTAINER_IDX_OFFSET=0
|
|
else
|
|
CONTAINER_IDX_OFFSET=$((PROC_COUNT + 1))
|
|
fi
|
|
for container_state_filetype in CONTAINER IP PORT; do
|
|
cd $DOKKU_ROOT/$APP
|
|
find . -maxdepth 1 -name "$container_state_filetype.$PROC_TYPE.*" -printf "%f\n" | sort -t . -k 3 -n | tail -n +$CONTAINER_IDX_OFFSET | xargs rm -f
|
|
done
|
|
done < "$DOKKU_SCALE_FILE"
|
|
|
|
dokku_log_info1 "Running post-deploy"
|
|
plugn trigger post-deploy $APP $port $ipaddr
|
|
|
|
# kill the old container
|
|
if [[ -n "$oldids" ]]; then
|
|
|
|
if [[ -z "$DOKKU_WAIT_TO_RETIRE" ]]; then
|
|
DOKKU_APP_DOKKU_WAIT_TO_RETIRE=$(dokku config:get $APP DOKKU_WAIT_TO_RETIRE || true)
|
|
DOKKU_GLOBAL_DOKKU_WAIT_TO_RETIRE=$(dokku config:get --global DOKKU_WAIT_TO_RETIRE || true)
|
|
DOKKU_WAIT_TO_RETIRE=${DOKKU_APP_DOKKU_WAIT_TO_RETIRE:="$DOKKU_GLOBAL_DOKKU_WAIT_TO_RETIRE"}
|
|
fi
|
|
|
|
# Let the old container finish processing requests, before terminating it
|
|
WAIT="${DOKKU_WAIT_TO_RETIRE:-60}"
|
|
dokku_log_info1 "Shutting down old containers in $WAIT seconds"
|
|
for oldid in $oldids; do
|
|
dokku_log_info2 "$oldid"
|
|
done
|
|
(
|
|
exec >/dev/null 2>/dev/null </dev/null
|
|
trap '' INT HUP
|
|
sleep $WAIT
|
|
for oldid in $oldids; do
|
|
# 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 stop $oldid \
|
|
|| docker kill $oldid \
|
|
|| plugn trigger retire-container-failed $APP # plugin trigger for event logging
|
|
done
|
|
) & disown -a
|
|
# Use trap since disown/nohup don't seem to keep child alive
|
|
# Give child process just enough time to set the traps
|
|
sleep 0.1
|
|
fi
|
|
;;
|
|
|
|
cleanup)
|
|
docker_cleanup
|
|
;;
|
|
|
|
# DEPRECATED as of v0.3.14
|
|
deploy:all)
|
|
echo "*DEPRECATED* in v0.3.14: deploy:all will be removed in future versions"
|
|
dokku ps:restartall
|
|
;;
|
|
|
|
help|'')
|
|
echo "Usage: dokku [--quiet|--trace|--rm-container|--rm|--force] COMMAND <app> [command-specific-options]"
|
|
echo ""
|
|
echo "Options:"
|
|
|
|
cat<<EOF | plugn trigger commands help | sort | column -c2 -t -s,
|
|
help, Print the list of commands
|
|
EOF
|
|
;;
|
|
|
|
*)
|
|
implemented=0
|
|
for script in $PLUGIN_ENABLED_PATH/*/commands; do
|
|
set +e; $script "$@" ; exit_code=$? ; set -e
|
|
if [[ "$exit_code" -eq "$DOKKU_NOT_IMPLEMENTED_EXIT" ]]; then
|
|
continue
|
|
fi
|
|
|
|
implemented=1
|
|
if [[ "$exit_code" -ne "$DOKKU_VALID_EXIT" ]]; then
|
|
exit $exit_code
|
|
fi
|
|
done
|
|
|
|
if [[ "$implemented" -eq 0 ]]; then
|
|
dokku_log_warn "\`$*\` is not a dokku command."
|
|
dokku_log_warn "See \`dokku help\` for a list of available commands."
|
|
exit 1
|
|
fi
|
|
;;
|
|
|
|
esac
|