Files
dokku/dokku
2015-07-13 09:27:42 -07:00

255 lines
8.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -eo pipefail
shopt -s nullglob
export DOKKU_DISTRO=${DOKKU_DISTRO:="ubuntu"}
export DOKKU_IMAGE=${DOKKU_IMAGE:="progrium/buildstep"}
export DOKKU_ROOT=${DOKKU_ROOT:=~dokku}
export PLUGIN_PATH=${PLUGIN_PATH:="/var/lib/dokku/plugins"}
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_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 != plugins-install* && $1 != "plugins-update" ]]; then
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
case "$1" in
receive)
APP="$2"; IMAGE="dokku/$APP"; IMAGE_SOURCE_TYPE="$3"; TMP_WORK_DIR="$4"
dokku_log_info1 "Cleaning up..."
dokku cleanup
dokku_log_info1 "Building $APP from $IMAGE_SOURCE_TYPE..."
dokku build "$APP" "$IMAGE_SOURCE_TYPE" "$TMP_WORK_DIR"
dokku_log_info1 "Releasing $APP..."
dokku release "$APP" "$IMAGE_SOURCE_TYPE"
dokku_log_info1 "Deploying $APP..."
dokku deploy "$APP"
dokku_log_info2 "Application deployed:"
dokku urls "$APP" | sed "s/^/ /"
echo
;;
deploy)
[[ -z $2 ]] && dokku_log_fail "Please specify an app to deploy"
APP="$2"; IMAGE="dokku/$APP"
verify_app_name "$APP"
pluginhook pre-deploy $APP
is_image_buildstep_based "$IMAGE" && DOKKU_BUILDSTEP=true
DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE"
oldids=$(get_app_container_ids $APP)
while read line || [ -n "$line" ]
do
PROC_TYPE=${line%%=*}
PROC_COUNT=${line#*=}
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=$(: | pluginhook docker-args $APP deploy)
DOCKER_ARGS+=" -e DYNO=$PROC_TYPE "
DOCKER_ARGS+=$(: | pluginhook docker-args-deploy $APP)
BIND_EXTERNAL=$(pluginhook bind-external-ip $APP)
[[ -n "$DOKKU_BUILDSTEP" ]] && START_CMD="/start $PROC_TYPE"
if [[ -z "$DOKKU_BUILDSTEP" ]];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 [[ "$BIND_EXTERNAL" = "false" ]] && [[ "$PROC_TYPE" == "web" ]];then
port=${DOKKU_DOCKERFILE_PORT:=5000}
id=$(docker run -d -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD)
ipaddr=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $id)
elif [[ "$BIND_EXTERNAL" = "true" ]] && [[ "$PROC_TYPE" == "web" ]];then
id=$(docker run -d -p 5000 -e PORT=5000 $DOCKER_ARGS $IMAGE $START_CMD)
port=$(docker port $id 5000 | sed 's/[0-9.]*://')
ipaddr=127.0.0.1
elif [[ "$PROC_TYPE" != "web" ]];then
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 $$
}
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"}
# 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"
pluginhook 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
find $DOKKU_ROOT/$APP -maxdepth 1 -name "CONTAINER.$PROC_TYPE.*" | sort -t . -k 4 -n | tail -n +$CONTAINER_IDX_OFFSET | xargs rm -f
find $DOKKU_ROOT/$APP -maxdepth 1 -name "IP.$PROC_TYPE.*" | sort -t . -k 4 -n | tail -n +$CONTAINER_IDX_OFFSET | xargs rm -f
find $DOKKU_ROOT/$APP -maxdepth 1 -name "PORT.$PROC_TYPE.*" | sort -t . -k 4 -n | tail -n +$CONTAINER_IDX_OFFSET | xargs rm -f
done < "$DOKKU_SCALE_FILE"
dokku_log_info1 "Running post-deploy"
pluginhook post-deploy $APP $port $ipaddr
# kill the old container
if [[ -n "$oldids" ]]; then
# 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
docker stop $oldid &> /dev/null
docker kill $oldid &> /dev/null # force a kill as docker seems to not send SIGKILL as the docs would indicate
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)
# delete all non-running container
# shellcheck disable=SC2046
docker rm $(docker ps -a -f 'status=exited' -q) &> /dev/null || true
# delete unused images
# shellcheck disable=SC2046
docker rmi $(docker images -f 'dangling=true' -q) &> /dev/null &
;;
plugins)
ls -1 -d $PLUGIN_PATH/*/
;;
plugins-install)
pluginhook install
;;
plugins-install-dependencies)
pluginhook dependencies
;;
plugins-update)
pluginhook update
;;
# 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 | pluginhook commands help | sort
help Print the list of commands
plugins Print active plugins
plugins-install Install active plugins
plugins-update Update active plugins
EOF
;;
*)
implemented=0
for script in $PLUGIN_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