diff --git a/docs/application-deployment.md b/docs/application-deployment.md index 0fee05ac758..61268bfabc9 100644 --- a/docs/application-deployment.md +++ b/docs/application-deployment.md @@ -121,7 +121,7 @@ This is in particular useful, then you want to deploy to root domain, as remote: -----> Application deployed: remote: http://dokku.me -# Zero downtime deploy +## Zero downtime deploy Following a deploy, dokku will now wait `DOKKU_DEFAULT_CHECKS_WAIT` seconds (default: `10`), and if the container is still running, then route traffic to the new container. @@ -147,7 +147,7 @@ Checks can be skipped entirely by setting `DOKKU_SKIP_ALL_CHECKS` to `true` eith See [checks-examples.md](checks-examples.md) for examples and output. -# Removing a deployed app +## Removing a deployed app SSH onto the server, then execute: @@ -155,6 +155,61 @@ SSH onto the server, then execute: dokku apps:destroy myapp ``` -# Dokku/Docker Container Management Compatibility +## Image tagging + +The dokku tags plugin allows you to add docker image tags to the currently deployed app image for versioning and subsequent deployment. + +``` +tags List all app image tags +tags:create Add tag to latest running app image +tags:deploy Deploy tagged app image +tags:destroy Remove app image tag +``` + +Example: +``` +root@dokku:~# dokku tags node-js-app +=====> Image tags for dokku/node-js-app +REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE +dokku/node-js-app latest 936a42f25901 About a minute ago 1.025 GB + +root@dokku:~# dokku tags:create node-js-app v0.9.0 +=====> Added v0.9.0 tag to dokku/node-js-app + +root@dokku:~# dokku tags node-js-app +=====> Image tags for dokku/node-js-app +REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE +dokku/node-js-app latest 936a42f25901 About a minute ago 1.025 GB +dokku/node-js-app v0.9.0 936a42f25901 About a minute ago 1.025 GB + +root@dokku:~# dokku tags:deploy node-js-app v0.9.0 +-----> Releasing node-js-app (dokku/node-js-app:v0.9.0)... +-----> Deploying node-js-app (dokku/node-js-app:v0.9.0)... +-----> Running pre-flight checks + For more efficient zero downtime deployments, create a file CHECKS. + See http://progrium.viewdocs.io/dokku/checks-examples.md for examples + CHECKS file not found in container: Running simple container check... +-----> Waiting for 10 seconds ... +-----> Default container check successful! +=====> node-js-app container output: + Detected 512 MB available memory, 512 MB limit per process (WEB_MEMORY) + Recommending WEB_CONCURRENCY=1 + > node-js-sample@0.1.0 start /app + > node index.js + Node app is running at localhost:5000 +=====> end node-js-app container output +-----> Running post-deploy +-----> Configuring node-js-app.dokku.me... +-----> Creating http nginx.conf +-----> Running nginx-pre-reload + Reloading nginx +-----> Shutting down old containers in 60 seconds +=====> 025eec3fa3b442fded90933d58d8ed8422901f0449f5ea0c23d00515af5d3137 +=====> Application deployed: + http://node-js-app.dokku.me + +``` + +## Dokku/Docker Container Management Compatibility Dokku is, at it's core, a docker container manager. Thus, it does not necessarily play well with other out-of-band processes interacting with the docker daemon. One thing to note as in [issue #1220](https://github.com/progrium/dokku/issues/1220), dokku executes a cleanup function prior to every deployment. This function removes all exited containers and all 'unattached' images. diff --git a/docs/development/plugin-creation.md b/docs/development/plugin-creation.md index 55098a82636..78f3e0f9ebf 100644 --- a/docs/development/plugin-creation.md +++ b/docs/development/plugin-creation.md @@ -20,8 +20,8 @@ source "$PLUGIN_PATH/common/functions" case "$1" in hello) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" - APP="$2"; + APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP); IMAGE=$(get_app_image_name $APP $IMAGE_TAG) + verify_app_name "$APP" echo "Hello $APP" ;; @@ -80,3 +80,4 @@ A few notes: dokku config:set --no-restart APP KEY1=VALUE1 [KEY2=VALUE2 ...] dokku config:unset --no-restart APP KEY1 [KEY2 ...] ``` +- As of 0.4.0, we allow image tagging and deployment of said tagged images. Therefore, hard-coding of `$IMAGE` as `dokku/$APP` is no longer sufficient. Instead, for non `pre/post-build-*` plugins, use `get_running_image_tag()` & `get_app_image_name()` as sourced from common/functions. See [pluginhooks](http://progrium.viewdocs.io/dokku/development/pluginhooks) doc for examples. diff --git a/docs/development/pluginhooks.md b/docs/development/pluginhooks.md index 130332d967d..957e77584c0 100644 --- a/docs/development/pluginhooks.md +++ b/docs/development/pluginhooks.md @@ -244,7 +244,7 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - Description: Allows you to run commands before environment variables are set for the release step of the deploy. Only applies to applications using buildpacks. - Invoked by: `dokku release` -- Arguments: `$APP` +- Arguments: `$APP $IMAGE_TAG` - Example: ```shell @@ -252,8 +252,9 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # Installs the graphicsmagick package into the container set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - source "$PLUGIN_PATH/common/functions" +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) +verify_app_name "$APP" dokku_log_info1" Installing GraphicsMagick..." @@ -270,7 +271,7 @@ docker commit $ID $IMAGE > /dev/null - Description: Allows you to run commands after environment variables are set for the release step of the deploy. Only applies to applications using buildpacks. - Invoked by: `dokku release` -- Arguments: `$APP` +- Arguments: `$APP $IMAGE_TAG` - Example: ```shell @@ -278,8 +279,9 @@ docker commit $ID $IMAGE > /dev/null # Installs a package specified by the `CONTAINER_PACKAGE` env var set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - source "$PLUGIN_PATH/common/functions" +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) +verify_app_name "$APP" dokku_log_info1" Installing $CONTAINER_PACKAGE..." @@ -296,13 +298,16 @@ docker commit $ID $IMAGE > /dev/null - Description: Allows you to run commands before environment variables are set for the release step of the deploy. Only applies to applications using a dockerfile. - Invoked by: `dokku release` -- Arguments: `$APP` +- Arguments: `$APP $IMAGE_TAG` - Example: ```shell #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_PATH/common/functions" +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) +verify_app_name "$APP" # TODO ``` @@ -311,13 +316,16 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - Description: Allows you to run commands after environment variables are set for the release step of the deploy. Only applies to applications using a dockerfile. - Invoked by: `dokku release` -- Arguments: `$APP` +- Arguments: `$APP $IMAGE_TAG` - Example: ```shell #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_PATH/common/functions" +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) +verify_app_name "$APP" # TODO ``` @@ -352,7 +360,7 @@ fi - Description: Allows the running of code before the container's process is started. - Invoked by: `dokku deploy` -- Arguments: `$APP` +- Arguments: `$APP $IMAGE_TAG` - Example: ```shell @@ -360,11 +368,9 @@ fi # Runs gulp in our container set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - source "$PLUGIN_PATH/common/functions" - -APP="$1" -IMAGE="dokku/$APP" +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) +verify_app_name "$APP" dokku_log_info1 "Running gulp" id=$(docker run -d $IMAGE /bin/bash -c "cd /app && gulp default") @@ -393,7 +399,7 @@ curl "http://httpstat.us/200" - Description: Can be used to run commands before an app is deleted. - Invoked by: `dokku apps:destroy` -- Arguments: `$APP` +- Arguments: `$APP $IMAGE_TAG` - Example: ```shell @@ -401,8 +407,10 @@ curl "http://httpstat.us/200" # Clears out the gulp asset build cache for applications set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_PATH/common/functions" -APP="$1"; IMAGE="dokku/$APP"; GULP_CACHE_DIR="$DOKKU_ROOT/$APP/gulp" +APP="$1"; GULP_CACHE_DIR="$DOKKU_ROOT/$APP/gulp"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) +verify_app_name "$APP" if [[ -d $GULP_CACHE_DIR ]]; then docker run --rm -v "$GULP_CACHE_DIR:/gulp" "$IMAGE" find /gulp -depth -mindepth 1 -maxdepth 1 -exec rm -Rf {} \; || true @@ -413,7 +421,7 @@ fi - Description: Can be used to run commands after an application is deleted. - Invoked by: `dokku apps:destroy` -- Arguments: `$APP` +- Arguments: `$APP $IMAGE_TAG` - Example: ```shell @@ -447,13 +455,16 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - Description: - Invoked by: `dokku deploy` -- Arguments: `$APP` +- Arguments: `$APP $IMAGE_TAG` - Example: ```shell #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_PATH/common/functions" +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) +verify_app_name "$APP" # TODO ``` @@ -462,13 +473,16 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - Description: - Invoked by: `dokku run` -- Arguments: `$APP` +- Arguments: `$APP $IMAGE_TAG` - Example: ```shell #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_PATH/common/functions" +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) +verify_app_name "$APP" # TODO ``` @@ -614,3 +628,39 @@ if [[ ! -d "$DOKKU_ROOT/$APP" ]]; then fi pluginhook receive-app $APP $newrev ``` + +### `tags-create` + +- Description: Allows you to run commands once a tag for an application image has been added +- Invoked by: `dokku tags:create` +- Arguments: `$APP $IMAGE_TAG` +- Example: + +```shell +#!/usr/bin/env bash +# Upload an application image to docker hub + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) + +IMAGE_ID=$(docker inspect --format '{{ .Id }}' $IMAGE) +docker tag -f $IMAGE_ID $DOCKER_HUB_USER/$APP:$IMAGE_TAG +docker push $DOCKER_HUB_USER/$APP:$IMAGE_TAG +``` + +### `tags-destroy` + +- Description: Allows you to run commands once a tag for an application image has been removed +- Invoked by: `dokku tags:destroy` +- Arguments: `$APP $IMAGE_TAG` +- Example: + +```shell +#!/usr/bin/env bash +# Remove an image tag from docker hub + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +APP="$1"; IMAGE_TAG="$2" + +some code to remove a docker hub tag because it's not implemented in the CLI.... +``` diff --git a/dokku b/dokku index f1adf488e9b..8142338f181 100755 --- a/dokku +++ b/dokku @@ -51,26 +51,19 @@ fi case "$1" in receive) - APP="$2"; IMAGE="dokku/$APP"; IMAGE_SOURCE_TYPE="$3"; TMP_WORK_DIR="$4" + APP="$2"; IMAGE=$(get_app_image_name $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 + release_and_deploy "$APP" ;; deploy) [[ -z $2 ]] && dokku_log_fail "Please specify an app to deploy" - APP="$2"; IMAGE="dokku/$APP" + APP="$2"; IMAGE_TAG="$3"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) verify_app_name "$APP" - - pluginhook pre-deploy $APP + pluginhook pre-deploy $APP $IMAGE_TAG is_image_herokuish_based "$IMAGE" && DOKKU_HEROKUISH=true DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" @@ -90,10 +83,10 @@ case "$1" in DOKKU_PORT_FILE="$DOKKU_ROOT/$APP/PORT.$PROC_TYPE.$CONTAINER_INDEX" # start the app - DOCKER_ARGS=$(: | pluginhook docker-args $APP deploy) + DOCKER_ARGS=$(: | pluginhook docker-args $APP deploy $IMAGE_TAG) DOCKER_ARGS+=" -e DYNO=$PROC_TYPE " DOCKER_ARGS+=" -e DYNO_TYPE_NUMBER='$PROC_TYPE.$CONTAINER_INDEX' " - DOCKER_ARGS+=$(: | pluginhook docker-args-deploy $APP) + DOCKER_ARGS+=$(: | pluginhook docker-args-deploy $APP $IMAGE_TAG) BIND_EXTERNAL=$(pluginhook bind-external-ip $APP) [[ -n "$DOKKU_HEROKUISH" ]] && START_CMD="/start $PROC_TYPE" diff --git a/plugins/00_dokku-standard/commands b/plugins/00_dokku-standard/commands index 2a450c864d5..bd986485109 100755 --- a/plugins/00_dokku-standard/commands +++ b/plugins/00_dokku-standard/commands @@ -5,7 +5,9 @@ source "$PLUGIN_PATH/config/functions" case "$1" in build) - APP="$2"; IMAGE="dokku/$APP"; IMAGE_SOURCE_TYPE="$3"; TMP_WORK_DIR="$4" + APP="$2"; IMAGE_SOURCE_TYPE="$3"; TMP_WORK_DIR="$4"; IMAGE=$(get_app_image_name $APP) + verify_app_name "$APP" + CACHE_DIR="$DOKKU_ROOT/$APP/cache" eval "$(config_export app $APP)" @@ -58,14 +60,16 @@ case "$1" in ;; release) - APP="$2"; IMAGE="dokku/$APP"; IMAGE_SOURCE_TYPE="$3" + APP="$2"; IMAGE_SOURCE_TYPE="$3"; IMAGE_TAG="$4"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) + verify_app_name "$APP" + case "$IMAGE_SOURCE_TYPE" in herokuish) # *DEPRECATED* in v0.3.15: `pluginhook pre-release` will be removed in future releases - pluginhook pre-release "$APP" + pluginhook pre-release $APP $IMAGE_TAG # *DEPRECATED* in v0.3.22: `pluginhook pre-release-buildstep` will be removed in future releases - pluginhook pre-release-buildstep "$APP" - pluginhook pre-release-buildpack "$APP" + pluginhook pre-release-buildstep $APP $IMAGE_TAG + pluginhook pre-release-buildpack $APP $IMAGE_TAG if [[ -n $(config_export global) ]]; then id=$(config_export global | docker run -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 @@ -77,15 +81,15 @@ case "$1" in docker commit $id $IMAGE > /dev/null fi # *DEPRECATED* in v0.3.15: `pluginhook post-release` will be removed in future releases - pluginhook post-release "$APP" + pluginhook post-release $APP $IMAGE_TAG # *DEPRECATED* in v0.3.22: `pluginhook post-release-buildstep` will be removed in future releases - pluginhook post-release-buildstep "$APP" - pluginhook post-release-buildpack "$APP" + pluginhook post-release-buildstep $APP $IMAGE_TAG + pluginhook post-release-buildpack $APP $IMAGE_TAG ;; dockerfile) - pluginhook pre-release-dockerfile "$APP" - pluginhook post-release-dockerfile "$APP" + pluginhook pre-release-dockerfile $APP $IMAGE_TAG + pluginhook post-release-dockerfile $APP $IMAGE_TAG ;; *) @@ -140,8 +144,7 @@ case "$1" in logs) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" - APP="$2"; + APP="$2"; verify_app_name "$2" if (is_deployed $APP); then CONTAINER_IDS=( $(get_app_container_ids $APP) ) @@ -167,13 +170,13 @@ case "$1" in run) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" - APP="$2"; IMAGE="dokku/$APP" + APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP); IMAGE=$(get_app_image_name $APP $IMAGE_TAG) + verify_app_name "$APP" shift 2 - DOCKER_ARGS=$(: | pluginhook docker-args $APP run) - DOCKER_ARGS+=$(: | pluginhook docker-args-run $APP) + DOCKER_ARGS=$(: | pluginhook docker-args $APP run $IMAGE_TAG) + DOCKER_ARGS+=$(: | pluginhook docker-args-run $APP $IMAGE_TAG) [[ $DOKKU_RM_CONTAINER ]] && DOKKU_RUN_OPTS="--rm" has_tty && DOKKU_RUN_OPTS+=" -i -t" is_image_herokuish_based "$IMAGE" && EXEC_CMD="/exec" @@ -183,9 +186,7 @@ case "$1" in url | urls) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" - APP="$2"; - + APP="$2"; verify_app_name "$2" eval "$(config_export app $APP)" if [[ -s "$DOKKU_ROOT/$APP/URLS" ]]; then diff --git a/plugins/apps/commands b/plugins/apps/commands index 4e303e26d46..f2e29666ddc 100755 --- a/plugins/apps/commands +++ b/plugins/apps/commands @@ -19,11 +19,11 @@ case "$1" in apps:destroy) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" [[ "$2" == "tls" ]] && echo "Unable to destroy tls directory" && exit 1 - APP="$2"; IMAGE="dokku/$APP" - [[ "$3" == "force" ]] && DOKKU_APPS_FORCE_DELETE=1 + APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP) + verify_app_name "$APP" + if [[ -z "$DOKKU_APPS_FORCE_DELETE" ]]; then dokku_log_warn "WARNING: Potentially Destructive Action" dokku_log_warn "This command will destroy $APP (including all add-ons)." @@ -39,7 +39,7 @@ case "$1" in echo "Destroying $APP (including all add-ons)" - pluginhook pre-delete $APP + pluginhook pre-delete $APP $IMAGE_TAG DOKKU_APP_CIDS=$(get_app_container_ids $APP) if [[ -n $DOKKU_APP_CIDS ]]; then for ID in $DOKKU_APP_CIDS;do @@ -48,9 +48,7 @@ case "$1" in done fi - docker images | grep $IMAGE | awk '{print $3}' | xargs docker rmi &> /dev/null & - - pluginhook post-delete $APP + pluginhook post-delete $APP $IMAGE_TAG ;; help | apps:help) diff --git a/plugins/apps/post-delete b/plugins/apps/post-delete index b164bb44b2c..5c71e01a262 100755 --- a/plugins/apps/post-delete +++ b/plugins/apps/post-delete @@ -1,4 +1,12 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_PATH/common/functions" +APP="$1"; IMAGE_REPO=$(get_app_image_repo $APP) -[[ -n $1 ]] && rm -r "$DOKKU_ROOT/$1" > /dev/null +[[ -n $APP ]] && rm -r "$DOKKU_ROOT/$APP" > /dev/null + +# remove all application containers & images +# shellcheck disable=SC2046 +docker rm -f $(docker ps -a --no-trunc | egrep "dokku/${APP}:" | awk '{ print $1 }' | xargs) &>/dev/null || true +# shellcheck disable=SC2046 +docker rmi $(docker images -q $IMAGE_REPO | xargs) &>/dev/null || true diff --git a/plugins/apps/pre-delete b/plugins/apps/pre-delete index 55ca79e73e2..a8eb8facb88 100755 --- a/plugins/apps/pre-delete +++ b/plugins/apps/pre-delete @@ -1,7 +1,10 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_PATH/common/functions" + +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG); CACHE_DIR="$DOKKU_ROOT/$APP/cache" +verify_app_name "$APP" -APP="$1"; IMAGE="dokku/$APP"; CACHE_DIR="$DOKKU_ROOT/$APP/cache" if [[ -d $CACHE_DIR ]]; then docker run --rm -v "$CACHE_DIR:/cache" "$IMAGE" find /cache -depth -mindepth 1 -maxdepth 1 -exec rm -Rf {} \; || true fi diff --git a/plugins/build-env/pre-build b/plugins/build-env/pre-build index 74e11f8615d..98e9afd99ff 100755 --- a/plugins/build-env/pre-build +++ b/plugins/build-env/pre-build @@ -3,7 +3,8 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_PATH/common/functions" source "$PLUGIN_PATH/config/functions" -APP="$1"; IMAGE="dokku/$APP"; BUILD_ENV="" +APP="$1"; IMAGE=$(get_app_image_name $APP); BUILD_ENV="" +verify_app_name "$APP" [[ -f "$DOKKU_ROOT/BUILD_ENV" ]] && cat "$DOKKU_ROOT/BUILD_ENV" >> "$DOKKU_ROOT/ENV" && { dokku_log_info2 "Using a global BUILD_ENV file is deprecated as of 0.3.26" diff --git a/plugins/common/functions b/plugins/common/functions index a55ea466d4f..44cab7fc802 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -102,7 +102,7 @@ dokku_log_pluginhook_call() { } dokku_container_log_verbose_quiet() { - CID=$1; + local CID=$1; shift OIFS=$IFS @@ -117,20 +117,51 @@ 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" - IMAGE="dokku/$APP" + return 0 } verify_image() { local IMAGE="$1" - DOKKU_APP_IMAGES=$(docker images -q $IMAGE) - if [[ -n "$DOKKU_APP_IMAGES" ]]; then + 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" @@ -167,7 +198,8 @@ parse_args() { } copy_from_image() { - local IMAGE="$1"; local SRC_FILE="$2"; local DST_DIR="$3"; verify_app_name $APP + 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) @@ -179,7 +211,7 @@ copy_from_image() { } get_app_container_ids() { - APP="$1"; CONTAINER_TYPE="$2" + local APP="$1"; local CONTAINER_TYPE="$2" verify_app_name $APP [[ -f $DOKKU_ROOT/$APP/CONTAINER ]] && DOKKU_CIDS+=$(< $DOKKU_ROOT/$APP/CONTAINER) @@ -224,7 +256,7 @@ get_app_running_container_ids() { } is_deployed() { - APP="$1" + local APP="$1" if [[ -f "$DOKKU_ROOT/$APP/CONTAINER" ]] || [[ $(ls $DOKKU_ROOT/$APP/CONTAINER.* &> /dev/null; echo $?) -eq 0 ]];then return 0 else @@ -267,3 +299,24 @@ is_app_running() { 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 +} diff --git a/plugins/config/docker-args-deploy b/plugins/config/docker-args-deploy index 14263ef9694..f63a2cb5b79 100755 --- a/plugins/config/docker-args-deploy +++ b/plugins/config/docker-args-deploy @@ -3,8 +3,9 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_PATH/common/functions" source "$PLUGIN_PATH/config/functions" -STDIN=$(cat); APP="$1"; IMAGE="dokku/$APP" +STDIN=$(cat); APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) DOCKERFILE_ENV_FILE="$DOKKU_ROOT/$APP/DOCKERFILE_ENV_FILE" +verify_app_name "$APP" if ! is_image_herokuish_based "$IMAGE"; then > "$DOCKERFILE_ENV_FILE" diff --git a/plugins/enter/commands b/plugins/enter/commands index 5f5f2d4a82d..dd5a592ef9c 100755 --- a/plugins/enter/commands +++ b/plugins/enter/commands @@ -1,10 +1,10 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -source "$(dirname "$0")/../common/functions" +source "$PLUGIN_PATH/common/functions" case "$1" in enter) - APP="$2"; IMAGE="dokku/$APP"; CONTAINER_TYPE="$3" + APP="$2"; CONTAINER_TYPE="$3"; IMAGE_TAG=$(get_running_image_tag $APP); IMAGE=$(get_app_image_name $APP $IMAGE_TAG) verify_app_name "$APP" [[ ! -n "$3" ]] && dokku_log_fail "No container id specified" diff --git a/plugins/ps/commands b/plugins/ps/commands index bdebeaa3ba9..0acf81ac537 100755 --- a/plugins/ps/commands +++ b/plugins/ps/commands @@ -6,24 +6,25 @@ source "$(dirname $0)/functions" case "$1" in ps) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" - - APP="$2"; CONTAINER_IDS=$(get_app_container_ids $APP) + APP="$2"; verify_app_name "$APP"; DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids $APP) ! (is_deployed $APP) && echo "App $APP has not been deployed" && exit 0 - for CID in $CONTAINER_IDS; do + for CID in $DOKKU_APP_RUNNING_CONTAINER_IDS; do has_tty && DOKKU_RUN_OPTS="-i -t" + dokku_log_info1_quiet "running processes in container: $CID" docker exec $DOKKU_RUN_OPTS $CID /bin/sh -c "ps auxwww" done ;; ps:start) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2"; APP="$2" + APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP); + verify_app_name "$APP" + ! (is_deployed $APP) && echo "App $APP has not been deployed" && exit 0 if ! (is_app_running $APP); then - release_and_deploy $APP + release_and_deploy "$APP" "$IMAGE_TAG" else echo "App $APP already running" fi @@ -31,12 +32,11 @@ case "$1" in ps:stop) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" + APP="$2"; DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids $APP) + verify_app_name "$APP" - APP="$2"; CONTAINER_IDS=$(get_app_container_ids $APP) ! (is_deployed $APP) && echo "App $APP has not been deployed" && exit 0 - DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids $APP) if [[ -n "$DOKKU_APP_RUNNING_CONTAINER_IDS" ]]; then echo "Stopping $APP ..." docker stop $DOKKU_APP_RUNNING_CONTAINER_IDS > /dev/null || true @@ -47,8 +47,7 @@ case "$1" in ps:rebuild) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" - APP="$2" + APP="$2"; verify_app_name "$APP" pluginhook -p receive-app $APP ;; @@ -65,12 +64,12 @@ case "$1" in ps:restart) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" + APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP) + verify_app_name "$APP" - APP="$2" ! (is_deployed $APP) && echo "App $APP has not been deployed" && exit 0 - release_and_deploy $APP + release_and_deploy "$APP" "$IMAGE_TAG" ;; ps:restartall) @@ -85,18 +84,19 @@ case "$1" in ps:scale) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" + APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP) + verify_app_name "$APP" - APP="$2"; DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" + DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" shift 2 - generate_scale_file "$APP" + generate_scale_file "$APP" "$IMAGE_TAG" if [[ -z "$@" ]];then dokku_log_info1 "Scaling for $APP" dokku_log_info2 "$(< $DOKKU_SCALE_FILE)" else set_scale "$APP" "$@" - release_and_deploy "$APP" + release_and_deploy "$APP" "$IMAGE_TAG" fi ;; diff --git a/plugins/ps/functions b/plugins/ps/functions index 6517d8259a7..f0e5aef7840 100755 --- a/plugins/ps/functions +++ b/plugins/ps/functions @@ -2,27 +2,6 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_PATH/common/functions" -release_and_deploy() { - source "$PLUGIN_PATH/common/functions" - local APP="$1"; local IMAGE="dokku/$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 ..." - 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 - fi -} - print_dokku_scale_file(){ local APP="$1"; local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" while read line || [ -n "$line" ] @@ -32,7 +11,9 @@ print_dokku_scale_file(){ } generate_scale_file() { - local APP="$1"; local IMAGE="dokku/$APP"; local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" + local APP="$1"; local IMAGE_TAG="$3"; local IMAGE=$(get_app_image_name $APP $IMAGE_TAG); local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" + verify_app_name "$APP" + copy_from_image "$IMAGE" "/app/DOKKU_SCALE" "$DOKKU_ROOT/$APP" 2>/dev/null || true if [[ ! -f $DOKKU_SCALE_FILE ]]; then dokku_log_info1_quiet "DOKKU_SCALE file not found in app image. Defaulting to a single web process" @@ -62,4 +43,3 @@ set_scale() { fi done } - diff --git a/plugins/tags/commands b/plugins/tags/commands new file mode 100755 index 00000000000..a77f8bc93dc --- /dev/null +++ b/plugins/tags/commands @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_PATH/common/functions" +source "$(dirname $0)/functions" + +case "$1" in + tags) + [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + APP="$2"; IMAGE_REPO=$(get_app_image_repo $APP) + verify_app_name "$APP" + + dokku_log_info2_quiet "Image tags for $IMAGE_REPO" + docker images "$IMAGE_REPO" + ;; + + tags:create) + [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + APP="$2"; IMAGE_TAG="$3"; IMAGE_REPO=$(get_app_image_repo $APP) + verify_app_name "$APP" + + tag_image "$IMAGE_REPO:latest" "$IMAGE_REPO:$IMAGE_TAG" + dokku_log_info2_quiet "Added $IMAGE_TAG tag to $IMAGE_REPO" + pluginhook tags-create $APP $IMAGE_TAG + ;; + + tags:deploy) + [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + APP="$2"; IMAGE_TAG="$3" + verify_app_name "$APP" + + release_and_deploy $APP $IMAGE_TAG + ;; + + tags:destroy) + [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + APP="$2"; IMAGE_TAG="$3"; IMAGE_REPO=$(get_app_image_repo $APP) + verify_app_name "$2" + + case "$IMAGE_TAG" in + latest) + dokku_log_fail "You can't remove internal dokku tag ($IMAGE_TAG) for $IMAGE_REPO" + ;; + + *) + docker rmi "$IMAGE_REPO:$IMAGE_TAG" + ;; + esac + pluginhook tags-destroy $APP $IMAGE_TAG + ;; + + help | tags:help) + cat && cat<, List all app image tags + tags:create , Add tag to latest running app image + tags:deploy , Deploy tagged app image + tags:destroy , Remove app image tag +EOF + ;; + + *) + exit $DOKKU_NOT_IMPLEMENTED_EXIT + ;; + +esac diff --git a/plugins/tags/functions b/plugins/tags/functions new file mode 100755 index 00000000000..7dd55157b36 --- /dev/null +++ b/plugins/tags/functions @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_PATH/common/functions" + +tag_image() { + docker tag -f "$1" "$2" +} diff --git a/plugins/tar/commands b/plugins/tar/commands index f18898e6618..cb6575fbd5b 100755 --- a/plugins/tar/commands +++ b/plugins/tar/commands @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -source "$(dirname $0)/../common/functions" +source "$PLUGIN_PATH/common/functions" case "$1" in tar-from|tar:from) @@ -14,7 +14,7 @@ case "$1" in tar-in|tar:in) APP="$2" verify_app_name "$2" - tee "$DOKKU_ROOT/$APP/src.tar" | wc -c + tee "$DOKKU_ROOT/$APP/src.tar" | wc -c pluginhook -p receive-app $APP ;; diff --git a/plugins/tar/receive-app b/plugins/tar/receive-app index 1c9428ccb9b..0bf09425ba8 100755 --- a/plugins/tar/receive-app +++ b/plugins/tar/receive-app @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -source "$(dirname $0)/../common/functions" +source "$PLUGIN_PATH/common/functions" APP="$1"; REV="$2" diff --git a/tests/unit/tags.bats b/tests/unit/tags.bats new file mode 100644 index 00000000000..02d15450604 --- /dev/null +++ b/tests/unit/tags.bats @@ -0,0 +1,46 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + deploy_app +} + +teardown() { + destroy_app +} + +@test "(tags) tags:create, tags, tags:destroy" { + run /bin/bash -c "dokku tags:create $TEST_APP v0.9.0" + echo "output: "$output + echo "status: "$status + assert_success + run /bin/bash -c "dokku tags $TEST_APP | egrep -q 'v0.9.0'" + echo "output: "$output + echo "status: "$status + assert_success + + run /bin/bash -c "dokku tags:destroy $TEST_APP v0.9.0" + echo "output: "$output + echo "status: "$status + assert_success + run /bin/bash -c "dokku tags $TEST_APP | egrep -q 'v0.9.0'" + echo "output: "$output + echo "status: "$status + assert_failure +} + +@test "(tags) tags:deploy" { + run /bin/bash -c "dokku tags:create $TEST_APP v0.9.0" + echo "output: "$output + echo "status: "$status + assert_success + run /bin/bash -c "dokku tags:deploy $TEST_APP v0.9.0" + echo "output: "$output + echo "status: "$status + assert_success + run /bin/bash -c "docker ps | egrep '/start web'| egrep -q dokku/${TEST_APP}:v0.9.0" + echo "output: "$output + echo "status: "$status + assert_success +}