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.
358 lines
7.8 KiB
Bash
Executable File
358 lines
7.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
|
|
|
has_tty() {
|
|
if [[ "$(/usr/bin/tty || true)" == "not a tty" ]]; then
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
dokku_log_info1() {
|
|
echo "-----> $*"
|
|
}
|
|
|
|
dokku_log_info2() {
|
|
echo "=====> $*"
|
|
}
|
|
|
|
dokku_log_info1_quiet() {
|
|
if [[ -z "$DOKKU_QUIET_OUTPUT" ]]; then
|
|
echo "-----> $*"
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
dokku_log_info2_quiet() {
|
|
if [[ -z "$DOKKU_QUIET_OUTPUT" ]]; then
|
|
echo "=====> $*"
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
dokku_col_log_info1() {
|
|
printf "%-6s %-18s %-25s %-25s %-25s\n" "----->" "$@"
|
|
}
|
|
|
|
dokku_col_log_info1_quiet() {
|
|
if [[ -z "$DOKKU_QUIET_OUTPUT" ]]; then
|
|
printf "%-6s %-18s %-25s %-25s %-25s\n" "----->" "$@"
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
dokku_col_log_info2() {
|
|
printf "%-6s %-18s %-25s %-25s %-25s\n" "=====>" "$@"
|
|
}
|
|
|
|
dokku_col_log_info2_quiet() {
|
|
if [[ -z "$DOKKU_QUIET_OUTPUT" ]]; then
|
|
printf "%-6s %-18s %-25s %-25s %-25s\n" "=====>" "$@"
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
dokku_col_log_msg() {
|
|
printf "%-25s %-25s %-25s %-25s\n" "$@"
|
|
}
|
|
|
|
dokku_col_log_msg_quiet() {
|
|
if [[ -z "$DOKKU_QUIET_OUTPUT" ]]; then
|
|
printf "%-25s %-25s %-25s %-25s\n" "$@"
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
dokku_log_verbose_quiet() {
|
|
if [[ -z "$DOKKU_QUIET_OUTPUT" ]]; then
|
|
echo " $*"
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
dokku_log_verbose() {
|
|
echo " $*"
|
|
}
|
|
|
|
dokku_log_warn() {
|
|
echo " ! $*"
|
|
}
|
|
|
|
dokku_log_fail() {
|
|
echo "$@" 1>&2
|
|
exit 1
|
|
}
|
|
|
|
dokku_log_event() {
|
|
logger -t dokku -i -- "$@"
|
|
}
|
|
|
|
dokku_log_plugn_trigger_call() {
|
|
local l_hook
|
|
|
|
l_hook="$1" ; shift
|
|
dokku_log_event "INVOKED: ${l_hook}( $* ) NAME=$NAME FINGERPRINT=$FINGERPRINT"
|
|
}
|
|
|
|
dokku_container_log_verbose_quiet() {
|
|
local CID=$1;
|
|
shift
|
|
|
|
OIFS=$IFS
|
|
IFS=$'\n'
|
|
for line in $(docker logs $CID 2>&1); do
|
|
dokku_log_verbose_quiet "$line"
|
|
done
|
|
IFS=$OIFS
|
|
}
|
|
|
|
verify_app_name() {
|
|
local APP="$1"
|
|
[[ ! -n "$APP" ]] && dokku_log_fail "(verify_app_name) APP must not be null"
|
|
[[ ! -d "$DOKKU_ROOT/$APP" ]] && dokku_log_fail "App $APP does not exist"
|
|
|
|
return 0
|
|
}
|
|
|
|
verify_image() {
|
|
local IMAGE="$1"
|
|
if (docker inspect "$IMAGE" &>/dev/null); then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
get_app_image_repo() {
|
|
# central definition of our image repo pattern
|
|
local APP="$1"; local IMAGE_REPO="dokku/$APP"
|
|
[[ -z "$APP" ]] && dokku_log_fail "(get_app_image_repo) APP must not be null"
|
|
echo $IMAGE_REPO
|
|
}
|
|
|
|
get_app_image_name() {
|
|
# return image identifier for a given app, tag tuple. validate if tag is presented
|
|
local APP="$1"; local IMAGE_TAG="$2"; local IMAGE_REPO=$(get_app_image_repo $APP)
|
|
[[ -z "$APP" ]] && dokku_log_fail "(get_app_image_name) APP must not be null"
|
|
|
|
if [[ -n "$IMAGE_TAG" ]]; then
|
|
IMAGE="$IMAGE_REPO:$IMAGE_TAG"
|
|
verify_image "$IMAGE" || dokku_log_fail "app image ($IMAGE) not found"
|
|
else
|
|
IMAGE="$IMAGE_REPO:latest"
|
|
fi
|
|
echo $IMAGE
|
|
}
|
|
|
|
get_running_image_tag() {
|
|
# retrieve current image tag for a given app. returns empty string if no deployed containers are found
|
|
local APP="$1"
|
|
[[ ! -n "$APP" ]] && dokku_log_fail "(get_running_image_tag) APP must not be null"
|
|
verify_app_name "$APP"
|
|
|
|
CIDS=( $(get_app_container_ids $APP) )
|
|
RUNNING_IMAGE_TAG=$(docker ps -a --no-trunc | egrep ${CIDS[0]} 2>/dev/null | awk '{ print $2 }' | awk -F: '{ print $2 }' || echo '')
|
|
echo $RUNNING_IMAGE_TAG
|
|
}
|
|
|
|
is_image_herokuish_based() {
|
|
# circleci can't support --rm as they run lxc in lxc
|
|
[[ ! -f "/home/ubuntu/.circlerc" ]] && local DOCKER_ARGS="--rm"
|
|
docker run --entrypoint="/bin/sh" $DOCKER_ARGS "$@" -c "test -f /exec"
|
|
}
|
|
|
|
is_number() {
|
|
local NUMBER=$1; local NUM_RE='^[0-9]+$'
|
|
if [[ $NUMBER =~ $NUM_RE ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
parse_args() {
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--quiet)
|
|
export DOKKU_QUIET_OUTPUT=1
|
|
;;
|
|
--trace)
|
|
export DOKKU_TRACE=1
|
|
;;
|
|
--rm-container|--rm)
|
|
export DOKKU_RM_CONTAINER=1
|
|
;;
|
|
--force)
|
|
export DOKKU_APPS_FORCE_DELETE=1
|
|
;;
|
|
esac
|
|
done
|
|
return 0
|
|
}
|
|
|
|
copy_from_image() {
|
|
local IMAGE="$1"; local SRC_FILE="$2"; local DST_DIR="$3"
|
|
verify_app_name $APP
|
|
|
|
if verify_image "$IMAGE"; then
|
|
CID=$(docker run -d $IMAGE bash)
|
|
docker cp "$CID:$SRC_FILE" "$DST_DIR"
|
|
docker rm -f "$CID" &> /dev/null
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
get_app_container_ids() {
|
|
local APP="$1"; local CONTAINER_TYPE="$2"
|
|
verify_app_name $APP
|
|
[[ -f $DOKKU_ROOT/$APP/CONTAINER ]] && DOKKU_CIDS+=$(< $DOKKU_ROOT/$APP/CONTAINER)
|
|
|
|
if [[ -n "$CONTAINER_TYPE" ]]; then
|
|
CONTAINER_PATTERN="$DOKKU_ROOT/$APP/CONTAINER.$CONTAINER_TYPE.*"
|
|
if [[ $CONTAINER_TYPE == *.* ]]; then
|
|
CONTAINER_PATTERN="$DOKKU_ROOT/$APP/CONTAINER.$CONTAINER_TYPE"
|
|
[[ ! -f $CONTAINER_PATTERN ]] && echo "" && return 0
|
|
fi
|
|
else
|
|
CONTAINER_PATTERN="$DOKKU_ROOT/$APP/CONTAINER.*"
|
|
fi
|
|
|
|
shopt -s nullglob
|
|
for DOKKU_CID_FILE in $CONTAINER_PATTERN; do
|
|
DOKKU_CIDS+=" "
|
|
DOKKU_CIDS+=$(< $DOKKU_CID_FILE)
|
|
DOKKU_CIDS+=" "
|
|
done
|
|
shopt -u nullglob
|
|
echo $DOKKU_CIDS
|
|
}
|
|
|
|
get_dockerfile_exposed_port() {
|
|
local DOCKERFILE_PORT=$(grep "^EXPOSE \+[[:digit:]]\+\(\/tcp\)\? *$" $1 | head -1 | sed 's/EXPOSE \+\([[:digit:]]\+\)\(\/tcp\)\?.*/\1/' || true)
|
|
echo "$DOCKERFILE_PORT"
|
|
}
|
|
|
|
get_app_running_container_ids() {
|
|
local APP=$1
|
|
verify_app_name $APP
|
|
|
|
! (is_deployed $APP) && dokku_log_fail "App $APP has not been deployed"
|
|
local CIDS=$(get_app_container_ids $APP)
|
|
|
|
for CID in $CIDS; do
|
|
local APP_CONTAINER_STATUS=$(docker inspect -f '{{.State.Running}}' "$CID" || true)
|
|
[[ "$APP_CONTAINER_STATUS" == "true" ]] && local APP_RUNNING_CONTAINER_IDS+="$CID "
|
|
done
|
|
|
|
echo "$APP_RUNNING_CONTAINER_IDS"
|
|
}
|
|
|
|
is_deployed() {
|
|
local APP="$1"
|
|
if [[ -f "$DOKKU_ROOT/$APP/CONTAINER" ]] || [[ $(ls $DOKKU_ROOT/$APP/CONTAINER.* &> /dev/null; echo $?) -eq 0 ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
is_container_running () {
|
|
local CID=$1
|
|
local CONTAINER_STATUS=$(docker inspect -f '{{.State.Running}}' "$CID" || true)
|
|
|
|
if [[ "$CONTAINER_STATUS" == "true" ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
is_container_status () {
|
|
local CID=$1
|
|
local TEMPLATE="{{.State.$2}}"
|
|
local CONTAINER_STATUS=$(docker inspect -f "$TEMPLATE" "$CID" || true)
|
|
|
|
if [[ "$CONTAINER_STATUS" == "true" ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
is_app_running() {
|
|
local APP="$1"
|
|
verify_app_name $APP
|
|
|
|
local APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids $APP)
|
|
|
|
if [[ -n "$APP_RUNNING_CONTAINER_IDS" ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
release_and_deploy() {
|
|
local APP="$1"; local IMAGE_TAG="$2"; local IMAGE=$(get_app_image_name $APP $IMAGE_TAG)
|
|
verify_app_name "$APP"
|
|
|
|
if verify_image "$IMAGE"; then
|
|
if is_image_herokuish_based "$IMAGE"; then
|
|
IMAGE_SOURCE_TYPE="herokuish"
|
|
else
|
|
IMAGE_SOURCE_TYPE="dockerfile"
|
|
fi
|
|
|
|
dokku_log_info1 "Releasing $APP ($IMAGE)..."
|
|
dokku release "$APP" "$IMAGE_SOURCE_TYPE" "$IMAGE_TAG"
|
|
dokku_log_info1 "Deploying $APP ($IMAGE)..."
|
|
dokku deploy "$APP" "$IMAGE_TAG"
|
|
dokku_log_info2 "Application deployed:"
|
|
dokku urls $APP | sed "s/^/ /"
|
|
echo
|
|
fi
|
|
}
|
|
|
|
docker_cleanup() {
|
|
# delete all non-running containers
|
|
# shellcheck disable=SC2046
|
|
docker rm $(docker ps -a -f 'status=exited' -q) &> /dev/null || true
|
|
|
|
# delete all dead containers
|
|
# shellcheck disable=SC2046
|
|
docker rm $(docker ps -a -f 'status=dead' -q) &> /dev/null || true
|
|
|
|
# delete unused images
|
|
# shellcheck disable=SC2046
|
|
docker rmi $(docker images -f 'dangling=true' -q) &> /dev/null &
|
|
}
|
|
|
|
get_available_port() {
|
|
while true; do
|
|
local port=$(shuf -i 1025-65535 -n 1)
|
|
if ! nc -z 0.0.0.0 $port; then
|
|
echo $port
|
|
return 0
|
|
else
|
|
continue
|
|
fi
|
|
done
|
|
}
|
|
|
|
dokku_auth() {
|
|
export SSH_USER=${SSH_USER:=$USER}
|
|
export SSH_NAME=${NAME:="default"}
|
|
if ! plugn trigger user-auth $SSH_USER $SSH_NAME "$@" ; then
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|