diff --git a/dokku b/dokku index 91bdcf660..cb7e7da84 100755 --- a/dokku +++ b/dokku @@ -100,17 +100,29 @@ execute_dokku_cmd() { nginx|nginx:*) local PLUGIN_NAME=${PLUGIN_NAME/nginx/nginx-vhosts} ;; + receive|deploy|build|release) + local PLUGIN_NAME="00_dokku-standard" + ;; + trace|delete|ls|run|cleanup) + local PLUGIN_NAME="00_dokku-standard" + ;; + url|urls|report|version) + local PLUGIN_NAME="00_dokku-standard" + ;; esac - if [[ "$(readlink -f "$PLUGIN_ENABLED_PATH/${PLUGIN_NAME%%:*}")" == *core-plugins* ]]; then + if [[ "$(readlink -f "$PLUGIN_ENABLED_PATH/${PLUGIN_NAME%%:*}")" == *core-plugins* ]] ; then [[ ${argv[0]} == "$PLUGIN_CMD" ]] && shift 1 [[ ! -z $DOKKU_APP_NAME ]] && set -- "$DOKKU_APP_NAME" "$@" - set -- "$PLUGIN_NAME" "$@" + set -- "$PLUGIN_CMD" "$@" fi if [[ -x $PLUGIN_ENABLED_PATH/$PLUGIN_NAME/subcommands/default ]]; then "$PLUGIN_ENABLED_PATH/$PLUGIN_NAME/subcommands/default" "$@" implemented=1 + elif [[ -x $PLUGIN_ENABLED_PATH/$PLUGIN_NAME/subcommands/$PLUGIN_CMD ]]; then + "$PLUGIN_ENABLED_PATH/$PLUGIN_NAME/subcommands/$PLUGIN_CMD" "$@" + implemented=1 elif [[ -x $PLUGIN_ENABLED_PATH/${PLUGIN_NAME%%:*}/subcommands/${1#*:} ]]; then "$PLUGIN_ENABLED_PATH/${PLUGIN_NAME%%:*}/subcommands/${1#*:}" "$@" implemented=1 diff --git a/plugins/00_dokku-standard/commands b/plugins/00_dokku-standard/commands index 2f13af10d..679c9e658 100755 --- a/plugins/00_dokku-standard/commands +++ b/plugins/00_dokku-standard/commands @@ -1,400 +1,8 @@ #!/usr/bin/env bash -[[ " receive deploy build release trace delete ls run cleanup url urls report version help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT" +[[ " help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT" set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/checks/functions" -source "$PLUGIN_AVAILABLE_PATH/config/functions" -source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" -source "$PLUGIN_CORE_AVAILABLE_PATH/proxy/functions" - -dokku_receive() { - local desc="receives an app kicks off deploy process" - local cmd="receive" - local APP="$2"; local IMAGE=$(get_app_image_name "$APP"); local IMAGE_SOURCE_TYPE="$3"; local TMP_WORK_DIR="$4" - if [[ -z "$DOKKU_SKIP_CLEANUP" ]]; then - dokku_log_info1 "Cleaning up..." - docker_cleanup - else - dokku_log_info1 "DOKKU_SKIP_CLEANUP set. Skipping dokku cleanup" - fi - dokku_log_info1 "Building $APP from $IMAGE_SOURCE_TYPE..." - dokku build "$APP" "$IMAGE_SOURCE_TYPE" "$TMP_WORK_DIR" - release_and_deploy "$APP" -} - -dokku_deploy() { - local desc="deploy phase" - local cmd="deploy" - [[ -z $2 ]] && dokku_log_fail "Please specify an app to deploy" - local APP="$2"; local IMAGE_TAG="$3"; local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG") - verify_app_name "$APP" - plugn trigger pre-deploy "$APP" "$IMAGE_TAG" - - is_image_herokuish_based "$IMAGE" && local DOKKU_HEROKUISH=true - local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" - local oldids=$(get_app_container_ids "$APP") - - local DOKKU_DEFAULT_DOCKER_ARGS=$(: | plugn trigger docker-args-deploy "$APP" "$IMAGE_TAG") - local DOKKU_IS_APP_PROXY_ENABLED="$(is_app_proxy_enabled "$APP")" - - local line - while read -r line || [[ -n "$line" ]]; do - [[ "$line" =~ ^#.* ]] && continue - local TRIM=${line%#*} - local PROC_TYPE=${TRIM%%=*} - local PROC_COUNT=${TRIM#*=} - local CONTAINER_INDEX=1 - - while [[ $CONTAINER_INDEX -le $PROC_COUNT ]]; do - local id=""; local port=""; local ipaddr="" - local DOKKU_CONTAINER_ID_FILE="$DOKKU_ROOT/$APP/CONTAINER.$PROC_TYPE.$CONTAINER_INDEX" - local DOKKU_IP_FILE="$DOKKU_ROOT/$APP/IP.$PROC_TYPE.$CONTAINER_INDEX" - local DOKKU_PORT_FILE="$DOKKU_ROOT/$APP/PORT.$PROC_TYPE.$CONTAINER_INDEX" - - # start the app - local DOCKER_ARGS="$DOKKU_DEFAULT_DOCKER_ARGS" - local DOCKER_ARGS+=" -e DYNO='$PROC_TYPE.$CONTAINER_INDEX' " - [[ "$DOKKU_TRACE" ]] && local DOCKER_ARGS+=" -e TRACE=true " - - [[ -n "$DOKKU_HEROKUISH" ]] && local START_CMD="/start $PROC_TYPE" - - if [[ -z "$DOKKU_HEROKUISH" ]]; then - local DOKKU_DOCKERFILE_PORTS=($(config_get "$APP" DOKKU_DOCKERFILE_PORTS || true)) - local DOKKU_DOCKERFILE_PORT=$(config_get "$APP" DOKKU_DOCKERFILE_PORT || true) - local DOKKU_DOCKERFILE_START_CMD=$(config_get "$APP" DOKKU_DOCKERFILE_START_CMD || true) - local DOKKU_PROCFILE_START_CMD=$(get_cmd_from_procfile "$APP" "$PROC_TYPE") - local START_CMD=${DOKKU_DOCKERFILE_START_CMD:-$DOKKU_PROCFILE_START_CMD} - fi - - if [[ "$PROC_TYPE" == "web" ]]; then - if [[ -z "${DOKKU_DOCKERFILE_PORTS[*]}" ]]; then - local port=5000 - local DOKKU_DOCKER_PORT_ARGS+="-p $port" - else - local p - for p in ${DOKKU_DOCKERFILE_PORTS[*]};do - if [[ ! "$p" =~ .*udp.* ]]; then - # set port to first non-udp port - local p=${p//\/tcp} - local port=${port:="$p"} - fi - local DOKKU_DOCKER_PORT_ARGS+=" -p $p " - done - fi - if [[ "$DOKKU_IS_APP_PROXY_ENABLED" == "true" ]]; then - # shellcheck disable=SC2086 - local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD) - local ipaddr=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$id") - # Docker < 1.9 compatibility - if [[ -z $ipaddr ]]; then - local ipaddr=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' "$id") - fi - else - # shellcheck disable=SC2086 - local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d $DOKKU_DOCKER_PORT_ARGS -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD) - local port=$(docker port "$id" "$port" | sed 's/[0-9.]*://') - local ipaddr=127.0.0.1 - fi - else - # shellcheck disable=SC2086 - local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d $DOCKER_ARGS $IMAGE $START_CMD) - fi - - # if we can't post-deploy successfully, kill new container - kill_new() { - local desc="wrapper function to kill newly started app container" - local id="$1" - 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 [[ "$(is_app_checks_enabled "$APP")" == "false" ]]; then - dokku_log_info1 "zero downtime is disabled for app ($APP). skipping pre-flight checks" - else - trap 'kill_new $id' 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" - - # cleanup pre-migration files - rm -f "$DOKKU_ROOT/$APP/CONTAINER" "$DOKKU_ROOT/$APP/IP" "$DOKKU_ROOT/$APP/PORT" - - local CONTAINER_INDEX=$(( CONTAINER_INDEX + 1 )) - done - # cleanup when we scale down - if [[ "$PROC_COUNT" == 0 ]]; then - local CONTAINER_IDX_OFFSET=0 - else - local CONTAINER_IDX_OFFSET=$((PROC_COUNT + 1)) - fi - local container_state_filetype - 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" "$IMAGE_TAG" - - # kill the old container - if [[ -n "$oldids" ]]; then - - if [[ -z "$DOKKU_WAIT_TO_RETIRE" ]]; then - local DOKKU_APP_DOKKU_WAIT_TO_RETIRE=$(config_get "$APP" DOKKU_WAIT_TO_RETIRE || true) - local DOKKU_GLOBAL_DOKKU_WAIT_TO_RETIRE=$(config_get --global DOKKU_WAIT_TO_RETIRE || true) - local 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 - local WAIT="${DOKKU_WAIT_TO_RETIRE:-60}" - dokku_log_info1 "Shutting down old containers in $WAIT seconds" - local oldid - for oldid in $oldids; do - dokku_log_info2 "$oldid" - done - ( - exec >/dev/null 2>/dev/null /dev/null - - case "$IMAGE_SOURCE_TYPE" in - herokuish) - local id=$(tar -c . | docker run "$DOKKU_GLOBAL_RUN_ARGS" -i -a stdin "$DOKKU_IMAGE" /bin/bash -c "mkdir -p /app && tar -xC /app") - test "$(docker wait "$id")" -eq 0 - docker commit "$id" "$IMAGE" > /dev/null - [[ -d $CACHE_DIR ]] || mkdir "$CACHE_DIR" - plugn trigger pre-build-buildpack "$APP" - - local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$IMAGE_SOURCE_TYPE") - [[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" -e TRACE=true " - # shellcheck disable=SC2086 - local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -v $CACHE_DIR:/cache -e CACHE_PATH=/cache $DOCKER_ARGS $IMAGE /build) - docker attach "$id" - test "$(docker wait "$id")" -eq 0 - docker commit "$id" "$IMAGE" > /dev/null - - plugn trigger post-build-buildpack "$APP" - ;; - - dockerfile) - # extract first port from Dockerfile - local DOCKERFILE_PORTS=$(get_dockerfile_exposed_ports Dockerfile) - [[ -n "$DOCKERFILE_PORTS" ]] && config_set --no-restart "$APP" DOKKU_DOCKERFILE_PORTS="$DOCKERFILE_PORTS" - plugn trigger pre-build-dockerfile "$APP" - - [[ "$DOKKU_DOCKERFILE_CACHE_BUILD" == "false" ]] && DOKKU_DOCKER_BUILD_OPTS="$DOKKU_DOCKER_BUILD_OPTS --no-cache" - local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$IMAGE_SOURCE_TYPE") - # shellcheck disable=SC2086 - docker build $DOCKER_ARGS $DOKKU_DOCKER_BUILD_OPTS -t $IMAGE . - - plugn trigger post-build-dockerfile "$APP" - ;; - - *) - dokku_log_fail "Building image source type $IMAGE_SOURCE_TYPE not supported!" - ;; - esac -} - -dokku_release() { - local desc="release phase" - local cmd="release" - local APP="$2"; local IMAGE_SOURCE_TYPE="$3"; local IMAGE_TAG="$4"; local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG") - verify_app_name "$APP" - - case "$IMAGE_SOURCE_TYPE" in - herokuish) - plugn trigger pre-release-buildpack "$APP" "$IMAGE_TAG" - if [[ -n $(config_export global) ]]; then - local id=$(config_export global | docker run "$DOKKU_GLOBAL_RUN_ARGS" -i -a stdin "$IMAGE" /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/00-global-env.sh") - test "$(docker wait "$id")" -eq 0 - docker commit "$id" "$IMAGE" > /dev/null - fi - if [[ -n $(config_export app "$APP") ]]; then - local id=$(config_export app "$APP" | docker run "$DOKKU_GLOBAL_RUN_ARGS" -i -a stdin "$IMAGE" /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/01-app-env.sh") - test "$(docker wait "$id")" -eq 0 - docker commit "$id" "$IMAGE" > /dev/null - fi - plugn trigger post-release-buildpack "$APP" "$IMAGE_TAG" - ;; - - dockerfile) - # buildstep plugins don't necessarily make sense for dockerfiles. call the new breed!!! - plugn trigger pre-release-dockerfile "$APP" "$IMAGE_TAG" - plugn trigger post-release-dockerfile "$APP" "$IMAGE_TAG" - ;; - - *) - dokku_log_fail "Releasing image source type $IMAGE_SOURCE_TYPE not supported!" - ;; - esac -} - -dokku_trace() { - local desc="enables/disables DOKKU_TRACE" - local cmd="trace" - [[ -d $DOKKU_ROOT/.dokkurc ]] || mkdir -p "$DOKKU_ROOT/.dokkurc" - [[ "$2" == "on" ]] || [[ "$2" == "off" ]] || { - dokku_log_fail "Valid trace options are [on/off]" - } - - if [[ "$2" == "on" ]]; then - echo "Enabling dokku trace" - echo "export DOKKU_TRACE=1" > "$DOKKU_ROOT/.dokkurc/DOKKU_TRACE" - fi - - if [[ "$2" == "off" ]]; then - echo "Disabling dokku trace" - rm -f "$DOKKU_ROOT/.dokkurc/DOKKU_TRACE" - fi -} - -dokku_ls() { - local desc="lists installed apps with status" - local cmd="ls" - local installed_apps=$(dokku_apps) - local dokku_app - - dokku_col_log_info1_quiet "App Name" "Container Type" "Container Id" "Status" - - for dokku_app in $installed_apps; do - local APP=$(basename "$dokku_app") - local DOKKU_APP_CIDS=$(get_app_container_ids "$APP") - local DOCKER_RUNNING_CONTAINERS=$(docker ps -q --no-trunc) - if [[ -n $DOKKU_APP_CIDS ]]; then - local DOKKU_APP_CID - for DOKKU_APP_CID in $DOKKU_APP_CIDS; do - local DOKKU_APP_CONTAINER_STATUS="stopped" - [[ $DOCKER_RUNNING_CONTAINERS =~ $DOKKU_APP_CID ]] && local DOKKU_APP_CONTAINER_STATUS="running" - local DOKKU_APP_CONTAINER_TYPE=$(grep -l "$DOKKU_APP_CID" "$DOKKU_ROOT/$APP"/CONTAINER.* | awk -F '/' '{ print $5 }' | awk -F '.' '{ print $2 }') - dokku_col_log_msg "$APP" "$DOKKU_APP_CONTAINER_TYPE" "${DOKKU_APP_CID:0:12}" "$DOKKU_APP_CONTAINER_STATUS" - done - else - dokku_col_log_msg "$APP" "NOT_DEPLOYED" "NOT_DEPLOYED" "NOT_DEPLOYED" - fi - done -} - -dokku_run() { - local desc="runs command in container based on app image" - local cmd="run" - [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" - local APP="$2"; local IMAGE_TAG=$(get_running_image_tag "$APP"); local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG") - verify_app_name "$APP" - - shift 2 - - if [[ -z "$DOKKU_RM_CONTAINER" ]]; then - local DOKKU_APP_RM_CONTAINER=$(config_get "$APP" DOKKU_RM_CONTAINER || true) - local DOKKU_GLOBAL_RM_CONTAINER=$(config_get --global DOKKU_RM_CONTAINER || true) - local DOKKU_RM_CONTAINER=${DOKKU_APP_RM_CONTAINER:="$DOKKU_GLOBAL_RM_CONTAINER"} - fi - - local DOCKER_ARGS=$(: | plugn trigger docker-args-run "$APP" "$IMAGE_TAG") - [[ "$DOKKU_TRACE" ]] && local DOCKER_ARGS+=" -e TRACE=true " - [[ "$DOKKU_RM_CONTAINER" ]] && local DOKKU_RUN_OPTS="--rm" - has_tty && local DOKKU_RUN_OPTS+=" -i -t" - is_image_herokuish_based "$IMAGE" && local EXEC_CMD="/exec" - # shellcheck disable=SC2086 - docker run $DOKKU_GLOBAL_RUN_ARGS $DOKKU_RUN_OPTS $DOCKER_ARGS $IMAGE $EXEC_CMD "$@" -} case "$1" in - receive) - dokku_receive "$@" - ;; - - deploy) - dokku_deploy "$@" - ;; - - build) - dokku_build "$@" - ;; - - release) - dokku_release "$@" - ;; - - trace) - dokku_trace "$@" - ;; - - delete) - dokku apps:destroy "$2" - ;; - - ls) - dokku_ls "$@" - ;; - - run) - dokku_run "$@" - ;; - - cleanup) - docker_cleanup - ;; - - url | urls) - [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" - get_app_urls "$@" - ;; - - report) - dokku_log_info1 "uname: $(uname -a)" - dokku_log_info1 "memory: " - free -m - dokku_log_info1 "docker version: " - docker version - dokku_log_info1 "docker daemon info: " - docker -D info - dokku_log_info1 "sigil version: $(sigil -v)" - dokku_log_info1 "herokuish version: " - docker run --rm -ti gliderlabs/herokuish:latest herokuish version - dokku_log_info1 "dokku version: $(dokku version)" - dokku_log_info1 "dokku plugins: " - dokku plugin - ;; - - version) - cat "$DOKKU_ROOT/VERSION" || dokku_log_fail "Unable to determine dokku's version" - ;; - help) cat< /dev/null + + case "$IMAGE_SOURCE_TYPE" in + herokuish) + local id=$(tar -c . | docker run "$DOKKU_GLOBAL_RUN_ARGS" -i -a stdin "$DOKKU_IMAGE" /bin/bash -c "mkdir -p /app && tar -xC /app") + test "$(docker wait "$id")" -eq 0 + docker commit "$id" "$IMAGE" > /dev/null + [[ -d $CACHE_DIR ]] || mkdir "$CACHE_DIR" + plugn trigger pre-build-buildpack "$APP" + + local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$IMAGE_SOURCE_TYPE") + [[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" -e TRACE=true " + # shellcheck disable=SC2086 + local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -v $CACHE_DIR:/cache -e CACHE_PATH=/cache $DOCKER_ARGS $IMAGE /build) + docker attach "$id" + test "$(docker wait "$id")" -eq 0 + docker commit "$id" "$IMAGE" > /dev/null + + plugn trigger post-build-buildpack "$APP" + ;; + + dockerfile) + # extract first port from Dockerfile + local DOCKERFILE_PORTS=$(get_dockerfile_exposed_ports Dockerfile) + [[ -n "$DOCKERFILE_PORTS" ]] && config_set --no-restart "$APP" DOKKU_DOCKERFILE_PORTS="$DOCKERFILE_PORTS" + plugn trigger pre-build-dockerfile "$APP" + + [[ "$DOKKU_DOCKERFILE_CACHE_BUILD" == "false" ]] && DOKKU_DOCKER_BUILD_OPTS="$DOKKU_DOCKER_BUILD_OPTS --no-cache" + local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$IMAGE_SOURCE_TYPE") + # shellcheck disable=SC2086 + docker build $DOCKER_ARGS $DOKKU_DOCKER_BUILD_OPTS -t $IMAGE . + + plugn trigger post-build-dockerfile "$APP" + ;; + + *) + dokku_log_fail "Building image source type $IMAGE_SOURCE_TYPE not supported!" + ;; + esac +} + +dokku_build_cmd "$@" diff --git a/plugins/00_dokku-standard/subcommands/cleanup b/plugins/00_dokku-standard/subcommands/cleanup new file mode 100755 index 000000000..db79caac9 --- /dev/null +++ b/plugins/00_dokku-standard/subcommands/cleanup @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" + +dokku_cleanup_cmd(){ + local desc="cleans up old deployment cruft" + local cmd="cleanup" + + docker_cleanup +} + + +dokku_cleanup_cmd "$@" diff --git a/plugins/00_dokku-standard/subcommands/deploy b/plugins/00_dokku-standard/subcommands/deploy new file mode 100755 index 000000000..5c8349073 --- /dev/null +++ b/plugins/00_dokku-standard/subcommands/deploy @@ -0,0 +1,166 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/checks/functions" +source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/proxy/functions" + +dokku_deploy_cmd() { + local desc="deploy phase" + local cmd="deploy" + [[ -z $2 ]] && dokku_log_fail "Please specify an app to deploy" + local APP="$2"; local IMAGE_TAG="$3"; local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG") + verify_app_name "$APP" + plugn trigger pre-deploy "$APP" "$IMAGE_TAG" + + is_image_herokuish_based "$IMAGE" && local DOKKU_HEROKUISH=true + local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" + local oldids=$(get_app_container_ids "$APP") + + local DOKKU_DEFAULT_DOCKER_ARGS=$(: | plugn trigger docker-args-deploy "$APP" "$IMAGE_TAG") + local DOKKU_IS_APP_PROXY_ENABLED="$(is_app_proxy_enabled "$APP")" + + local line + while read -r line || [[ -n "$line" ]]; do + [[ "$line" =~ ^#.* ]] && continue + local TRIM=${line%#*} + local PROC_TYPE=${TRIM%%=*} + local PROC_COUNT=${TRIM#*=} + local CONTAINER_INDEX=1 + + while [[ $CONTAINER_INDEX -le $PROC_COUNT ]]; do + local id=""; local port=""; local ipaddr="" + local DOKKU_CONTAINER_ID_FILE="$DOKKU_ROOT/$APP/CONTAINER.$PROC_TYPE.$CONTAINER_INDEX" + local DOKKU_IP_FILE="$DOKKU_ROOT/$APP/IP.$PROC_TYPE.$CONTAINER_INDEX" + local DOKKU_PORT_FILE="$DOKKU_ROOT/$APP/PORT.$PROC_TYPE.$CONTAINER_INDEX" + + # start the app + local DOCKER_ARGS="$DOKKU_DEFAULT_DOCKER_ARGS" + local DOCKER_ARGS+=" -e DYNO='$PROC_TYPE.$CONTAINER_INDEX' " + [[ "$DOKKU_TRACE" ]] && local DOCKER_ARGS+=" -e TRACE=true " + + [[ -n "$DOKKU_HEROKUISH" ]] && local START_CMD="/start $PROC_TYPE" + + if [[ -z "$DOKKU_HEROKUISH" ]]; then + local DOKKU_DOCKERFILE_PORTS=($(config_get "$APP" DOKKU_DOCKERFILE_PORTS || true)) + local DOKKU_DOCKERFILE_PORT=$(config_get "$APP" DOKKU_DOCKERFILE_PORT || true) + local DOKKU_DOCKERFILE_START_CMD=$(config_get "$APP" DOKKU_DOCKERFILE_START_CMD || true) + local DOKKU_PROCFILE_START_CMD=$(get_cmd_from_procfile "$APP" "$PROC_TYPE") + local START_CMD=${DOKKU_DOCKERFILE_START_CMD:-$DOKKU_PROCFILE_START_CMD} + fi + + if [[ "$PROC_TYPE" == "web" ]]; then + if [[ -z "${DOKKU_DOCKERFILE_PORTS[*]}" ]]; then + local port=5000 + local DOKKU_DOCKER_PORT_ARGS+="-p $port" + else + local p + for p in ${DOKKU_DOCKERFILE_PORTS[*]};do + if [[ ! "$p" =~ .*udp.* ]]; then + # set port to first non-udp port + local p=${p//\/tcp} + local port=${port:="$p"} + fi + local DOKKU_DOCKER_PORT_ARGS+=" -p $p " + done + fi + if [[ "$DOKKU_IS_APP_PROXY_ENABLED" == "true" ]]; then + # shellcheck disable=SC2086 + local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD) + local ipaddr=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$id") + # Docker < 1.9 compatibility + if [[ -z $ipaddr ]]; then + local ipaddr=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' "$id") + fi + else + # shellcheck disable=SC2086 + local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d $DOKKU_DOCKER_PORT_ARGS -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD) + local port=$(docker port "$id" "$port" | sed 's/[0-9.]*://') + local ipaddr=127.0.0.1 + fi + else + # shellcheck disable=SC2086 + local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d $DOCKER_ARGS $IMAGE $START_CMD) + fi + + # if we can't post-deploy successfully, kill new container + kill_new() { + local desc="wrapper function to kill newly started app container" + local id="$1" + 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 [[ "$(is_app_checks_enabled "$APP")" == "false" ]]; then + dokku_log_info1 "zero downtime is disabled for app ($APP). skipping pre-flight checks" + else + trap 'kill_new $id' 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" + + # cleanup pre-migration files + rm -f "$DOKKU_ROOT/$APP/CONTAINER" "$DOKKU_ROOT/$APP/IP" "$DOKKU_ROOT/$APP/PORT" + + local CONTAINER_INDEX=$(( CONTAINER_INDEX + 1 )) + done + # cleanup when we scale down + if [[ "$PROC_COUNT" == 0 ]]; then + local CONTAINER_IDX_OFFSET=0 + else + local CONTAINER_IDX_OFFSET=$((PROC_COUNT + 1)) + fi + local container_state_filetype + 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" "$IMAGE_TAG" + + # kill the old container + if [[ -n "$oldids" ]]; then + + if [[ -z "$DOKKU_WAIT_TO_RETIRE" ]]; then + local DOKKU_APP_DOKKU_WAIT_TO_RETIRE=$(config_get "$APP" DOKKU_WAIT_TO_RETIRE || true) + local DOKKU_GLOBAL_DOKKU_WAIT_TO_RETIRE=$(config_get --global DOKKU_WAIT_TO_RETIRE || true) + local 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 + local WAIT="${DOKKU_WAIT_TO_RETIRE:-60}" + dokku_log_info1 "Shutting down old containers in $WAIT seconds" + local oldid + for oldid in $oldids; do + dokku_log_info2 "$oldid" + done + ( + exec >/dev/null 2>/dev/null /app/.profile.d/00-global-env.sh") + test "$(docker wait "$id")" -eq 0 + docker commit "$id" "$IMAGE" > /dev/null + fi + if [[ -n $(config_export app "$APP") ]]; then + local id=$(config_export app "$APP" | docker run "$DOKKU_GLOBAL_RUN_ARGS" -i -a stdin "$IMAGE" /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/01-app-env.sh") + test "$(docker wait "$id")" -eq 0 + docker commit "$id" "$IMAGE" > /dev/null + fi + plugn trigger post-release-buildpack "$APP" "$IMAGE_TAG" + ;; + + dockerfile) + # buildstep plugins don't necessarily make sense for dockerfiles. call the new breed!!! + plugn trigger pre-release-dockerfile "$APP" "$IMAGE_TAG" + plugn trigger post-release-dockerfile "$APP" "$IMAGE_TAG" + ;; + + *) + dokku_log_fail "Releasing image source type $IMAGE_SOURCE_TYPE not supported!" + ;; + esac +} + +dokku_release_cmd "$@" diff --git a/plugins/00_dokku-standard/subcommands/report b/plugins/00_dokku-standard/subcommands/report new file mode 100755 index 000000000..38cf15d6b --- /dev/null +++ b/plugins/00_dokku-standard/subcommands/report @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" + +dokku_report_cmd() { + local desc="reports dokku vitals for troubleshooting" + local cmd="report" + + dokku_log_info1 "uname: $(uname -a)" + dokku_log_info1 "memory: " + free -m + dokku_log_info1 "docker version: " + docker version + dokku_log_info1 "docker daemon info: " + docker -D info + dokku_log_info1 "sigil version: $(sigil -v)" + dokku_log_info1 "herokuish version: " + docker run --rm -ti gliderlabs/herokuish:latest herokuish version + dokku_log_info1 "dokku version: $(dokku version)" + dokku_log_info1 "dokku plugins: " + dokku plugin +} + +dokku_report_cmd "$@" diff --git a/plugins/00_dokku-standard/subcommands/run b/plugins/00_dokku-standard/subcommands/run new file mode 100755 index 000000000..b94cee5cc --- /dev/null +++ b/plugins/00_dokku-standard/subcommands/run @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/config/functions" + +dokku_run_cmd() { + local desc="runs command in container based on app image" + local cmd="run" + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" + local APP="$2"; local IMAGE_TAG=$(get_running_image_tag "$APP"); local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG") + verify_app_name "$APP" + + shift 2 + + if [[ -z "$DOKKU_RM_CONTAINER" ]]; then + local DOKKU_APP_RM_CONTAINER=$(config_get "$APP" DOKKU_RM_CONTAINER || true) + local DOKKU_GLOBAL_RM_CONTAINER=$(config_get --global DOKKU_RM_CONTAINER || true) + local DOKKU_RM_CONTAINER=${DOKKU_APP_RM_CONTAINER:="$DOKKU_GLOBAL_RM_CONTAINER"} + fi + + local DOCKER_ARGS=$(: | plugn trigger docker-args-run "$APP" "$IMAGE_TAG") + [[ "$DOKKU_TRACE" ]] && local DOCKER_ARGS+=" -e TRACE=true " + [[ "$DOKKU_RM_CONTAINER" ]] && local DOKKU_RUN_OPTS="--rm" + has_tty && local DOKKU_RUN_OPTS+=" -i -t" + is_image_herokuish_based "$IMAGE" && local EXEC_CMD="/exec" + # shellcheck disable=SC2086 + docker run $DOKKU_GLOBAL_RUN_ARGS $DOKKU_RUN_OPTS $DOCKER_ARGS $IMAGE $EXEC_CMD "$@" +} + +dokku_run_cmd "$@" diff --git a/plugins/00_dokku-standard/subcommands/trace b/plugins/00_dokku-standard/subcommands/trace new file mode 100755 index 000000000..7982acef1 --- /dev/null +++ b/plugins/00_dokku-standard/subcommands/trace @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" + +dokku_trace_cmd() { + local desc="enables/disables DOKKU_TRACE" + local cmd="trace" + [[ -d $DOKKU_ROOT/.dokkurc ]] || mkdir -p "$DOKKU_ROOT/.dokkurc" + [[ "$2" == "on" ]] || [[ "$2" == "off" ]] || { + dokku_log_fail "Valid trace options are [on/off]" + } + + if [[ "$2" == "on" ]]; then + echo "Enabling dokku trace" + echo "export DOKKU_TRACE=1" > "$DOKKU_ROOT/.dokkurc/DOKKU_TRACE" + fi + + if [[ "$2" == "off" ]]; then + echo "Disabling dokku trace" + rm -f "$DOKKU_ROOT/.dokkurc/DOKKU_TRACE" + fi +} + +dokku_trace_cmd "$@" diff --git a/plugins/00_dokku-standard/subcommands/url b/plugins/00_dokku-standard/subcommands/url new file mode 120000 index 000000000..f46456a1f --- /dev/null +++ b/plugins/00_dokku-standard/subcommands/url @@ -0,0 +1 @@ +urls \ No newline at end of file diff --git a/plugins/00_dokku-standard/subcommands/urls b/plugins/00_dokku-standard/subcommands/urls new file mode 100755 index 000000000..8e6f1f304 --- /dev/null +++ b/plugins/00_dokku-standard/subcommands/urls @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" + +dokku_urls_cmd() { + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" + local APP="$2" + + get_app_urls "$@" +} + +dokku_urls_cmd "$@" diff --git a/plugins/00_dokku-standard/subcommands/version b/plugins/00_dokku-standard/subcommands/version new file mode 100755 index 000000000..f26f86a56 --- /dev/null +++ b/plugins/00_dokku-standard/subcommands/version @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/checks/functions" +source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/proxy/functions" + +dokku_version_cmd() { + local desc="prints dokku version" + local cmd="version" + cat "$DOKKU_ROOT/VERSION" || dokku_log_fail "Unable to determine dokku's version" +} + +dokku_version_cmd "$@" diff --git a/plugins/common/functions b/plugins/common/functions index 0d971d31b..86f9c1436 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -602,6 +602,7 @@ get_container_ports() { get_app_urls() { local desc="print an app's available urls" source "$PLUGIN_AVAILABLE_PATH/config/functions" + source "$PLUGIN_AVAILABLE_PATH/proxy/functions" local APP="$2"; verify_app_name "$APP" local RAW_TCP_PORTS="$(get_app_raw_tcp_ports "$APP")"