diff --git a/dokku b/dokku index 4589c3c0c..9b4bf7731 100755 --- a/dokku +++ b/dokku @@ -41,56 +41,20 @@ fi case "$1" in receive) - APP="$2"; IMAGE="dokku/$APP" - dokku_log_info1 "Cleaning up ..." + 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 ..." - cat | dokku build $APP - dokku_log_info1 "Releasing $APP ..." - dokku release $APP - dokku_log_info1 "Deploying $APP ..." - dokku deploy $APP + 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/^/ /" + dokku urls "$APP" | sed "s/^/ /" echo ;; - build) - APP="$2"; IMAGE="dokku/$APP"; CACHE_DIR="$DOKKU_ROOT/$APP/cache" - id=$(cat | docker run -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 - pluginhook pre-build $APP - - # *DEPRECATED* in v0.4.0: `pluginhook docker-args` will be removed in future releases - # https://github.com/progrium/dokku/issues/896 & https://github.com/progrium/dokku/issues/906 - DOCKER_ARGS=$(: | pluginhook docker-args $APP build) - DOCKER_ARGS+=$(: | pluginhook docker-args-build $APP) - id=$(docker run -d -v $CACHE_DIR:/cache -e CACHE_PATH=/cache $DOCKER_ARGS $IMAGE /build/builder) - docker attach $id - test "$(docker wait $id)" -eq 0 - docker commit $id $IMAGE > /dev/null - - pluginhook post-build $APP - ;; - - release) - APP="$2"; IMAGE="dokku/$APP" - pluginhook pre-release $APP - if [[ -f "$DOKKU_ROOT/ENV" ]]; then - id=$(docker run -i -a stdin $IMAGE /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/00-global-env.sh" < "$DOKKU_ROOT/ENV") - test "$(docker wait $id)" -eq 0 - docker commit $id $IMAGE > /dev/null - fi - if [[ -f "$DOKKU_ROOT/$APP/ENV" ]]; then - id=$(docker run -i -a stdin $IMAGE /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/01-app-env.sh" < "$DOKKU_ROOT/$APP/ENV") - test "$(docker wait $id)" -eq 0 - docker commit $id $IMAGE > /dev/null - fi - pluginhook post-release $APP - ;; - deploy) APP="$2"; IMAGE="dokku/$APP" pluginhook pre-deploy $APP @@ -104,12 +68,14 @@ case "$1" in DOCKER_ARGS+=$(: | pluginhook docker-args-deploy $APP) BIND_EXTERNAL=$(pluginhook bind-external-ip $APP) + is_image_buildstep_based "$IMAGE" && START_CMD="/start web" + if [[ "$BIND_EXTERNAL" = "false" ]];then port=5000 - id=$(docker run -d -e PORT=$port $DOCKER_ARGS $IMAGE /bin/bash -c "/start web") + id=$(docker run -d -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD) ipaddr=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $id) else - id=$(docker run -d -p 5000 -e PORT=5000 $DOCKER_ARGS $IMAGE /bin/bash -c "/start web") + 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 fi diff --git a/plugins/00_dokku-standard/commands b/plugins/00_dokku-standard/commands index 3052233e8..85173cca5 100755 --- a/plugins/00_dokku-standard/commands +++ b/plugins/00_dokku-standard/commands @@ -3,6 +3,76 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$(dirname $0)/../common/functions" case "$1" in + build) + APP="$2"; IMAGE="dokku/$APP"; IMAGE_SOURCE_TYPE="$3"; TMP_WORK_DIR="$4" + CACHE_DIR="$DOKKU_ROOT/$APP/cache" + + pushd "$TMP_WORK_DIR" &> /dev/null + + case "$IMAGE_SOURCE_TYPE" in + buildstep) + id=$(tar -c . | docker run -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 + pluginhook pre-build "$APP" + + # *DEPRECATED* in v0.4.0: `pluginhook docker-args` will be removed in future releases + # https://github.com/progrium/dokku/issues/896 & https://github.com/progrium/dokku/issues/906 + DOCKER_ARGS=$(: | pluginhook docker-args $APP build) + DOCKER_ARGS+=$(: | pluginhook docker-args-build $APP) + id=$(docker run -d -v $CACHE_DIR:/cache -e CACHE_PATH=/cache $DOCKER_ARGS $IMAGE /build/builder) + docker attach $id + test "$(docker wait $id)" -eq 0 + docker commit $id $IMAGE > /dev/null + + pluginhook post-build "$APP" + ;; + + dockerfile) + # sticking with same pattern of building app image before pre-build for now. + docker build -t "$IMAGE" . + # buildstep pluginhooks don't necessarily make sense for dockerfiles. call the new breed!!! + pluginhook pre-build-dockerfile "$APP" + pluginhook post-build-dockerfile "$APP" + ;; + + *) + dokku_log_fail "Building image source type $IMAGE_SOURCE_TYPE not supported!" + ;; + esac + ;; + + release) + APP="$2"; IMAGE="dokku/$APP"; IMAGE_SOURCE_TYPE="$3" + case "$IMAGE_SOURCE_TYPE" in + buildstep) + pluginhook pre-release "$APP" + if [[ -f "$DOKKU_ROOT/ENV" ]]; then + id=$(docker run -i -a stdin $IMAGE /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/00-global-env.sh" < "$DOKKU_ROOT/ENV") + test "$(docker wait $id)" -eq 0 + docker commit $id $IMAGE > /dev/null + fi + if [[ -f "$DOKKU_ROOT/$APP/ENV" ]]; then + id=$(docker run -i -a stdin $IMAGE /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/01-app-env.sh" < "$DOKKU_ROOT/$APP/ENV") + test "$(docker wait $id)" -eq 0 + docker commit $id $IMAGE > /dev/null + fi + pluginhook post-release "$APP" + ;; + + dockerfile) + # buildstep pluginhooks don't necessarily make sense for dockerfiles. call the new breed!!! + pluginhook pre-release-dockerfile "$APP" + pluginhook post-release-dockerfile "$APP" + ;; + + *) + dokku_log_fail "Releasing image source type $IMAGE_SOURCE_TYPE not supported!" + ;; + esac + ;; + trace) [[ -d $DOKKU_ROOT/.dokkurc ]] || mkdir -p $DOKKU_ROOT/.dokkurc [[ "$2" == "on" ]] || [[ "$2" == "off" ]] || { @@ -67,8 +137,9 @@ case "$1" in DOCKER_ARGS+=$(: | pluginhook docker-args-run $APP) [[ $DOKKU_RM_CONTAINER ]] && DOKKU_RUN_OPTS="--rm" has_tty && DOKKU_RUN_OPTS+=" -i -t" + is_image_buildstep_based "$IMAGE" && EXEC_CMD="/exec" - docker run $DOKKU_RUN_OPTS $DOCKER_ARGS $IMAGE /exec "$@" + docker run $DOKKU_RUN_OPTS $DOCKER_ARGS $IMAGE $EXEC_CMD "$@" ;; url | urls) diff --git a/plugins/common/functions b/plugins/common/functions old mode 100644 new mode 100755 index 08a0bb4f6..9785b7cdd --- a/plugins/common/functions +++ b/plugins/common/functions @@ -85,9 +85,16 @@ dokku_log_fail() { verify_app_name() { local APP="$1" [[ ! -d "$DOKKU_ROOT/$APP" ]] && dokku_log_fail "App $APP does not exist" + IMAGE="dokku/$APP" return 0 } +is_image_buildstep_based() { + # circleci can't support --rm as they run lxc in lxc + [[ ! -f "/home/ubuntu/.circlerc" ]] && local DOCKER_ARGS="--rm" + docker run --entrypoint="/bin/bash" $DOCKER_ARGS "$@" -c "[[ -f /exec ]]" +} + parse_args() { for arg in "$@"; do case "$arg" in diff --git a/plugins/config/docker-args-deploy b/plugins/config/docker-args-deploy new file mode 100755 index 000000000..28a358d40 --- /dev/null +++ b/plugins/config/docker-args-deploy @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$(dirname $0)/../common/functions" + +STDIN=$(cat); APP="$1"; IMAGE="dokku/$APP" +DOCKERFILE_ENV_FILE="$DOKKU_ROOT/$APP/DOCKERFILE_ENV_FILE" + +if ! is_image_buildstep_based "$IMAGE"; then + > "$DOCKERFILE_ENV_FILE" + [[ -f "$DOKKU_ROOT/ENV" ]] && sed -e "s:^export ::g" "$DOKKU_ROOT/ENV" > "$DOCKERFILE_ENV_FILE" + [[ -f "$DOKKU_ROOT/$APP/ENV" ]] && sed -e "s:^export ::g" "$DOKKU_ROOT/$APP/ENV" >> "$DOCKERFILE_ENV_FILE" + + echo "$STDIN --env-file=$DOCKERFILE_ENV_FILE" +else + echo "$STDIN" +fi diff --git a/plugins/git/commands b/plugins/git/commands index 621c59202..47934f493 100755 --- a/plugins/git/commands +++ b/plugins/git/commands @@ -1,19 +1,30 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$(dirname $0)/../common/functions" -git_archive_all() { - APP=$1; REV=$2 +git_build_app_repo() { + verify_app_name "$1" + APP="$1"; REV="$2" + + # clean up after ourselves TMP_WORK_DIR=$(mktemp -d) + trap 'rm -rf "$TMP_WORK_DIR" > /dev/null' RETURN + + # git clone chmod 755 $TMP_WORK_DIR unset GIT_DIR GIT_WORK_TREE pushd $TMP_WORK_DIR > /dev/null - git clone -q $DOKKU_ROOT/$APP $TMP_WORK_DIR &> /dev/null + git clone -q "$DOKKU_ROOT/$APP" "$TMP_WORK_DIR" &> /dev/null git config advice.detachedHead false - git checkout $REV &> /dev/null + git checkout "$REV" &> /dev/null git submodule update --init --recursive &> /dev/null find -name .git -prune -exec rm -rf {} \; > /dev/null - tar c . - rm -rf $TMP_WORK_DIR > /dev/null + + if [[ -f Dockerfile ]] && [[ "$([[ -f .env ]] && grep -q BUILDPACK_URL .env; echo $?)" != "0" ]] && [[ ! -f ".buildpacks" ]]; then + dokku receive "$APP" "dockerfile" "$TMP_WORK_DIR" | sed -u "s/^/"$'\e[1G'"/" + else + dokku receive "$APP" "buildstep" "$TMP_WORK_DIR" | sed -u "s/^/"$'\e[1G'"/" + fi } case "$1" in @@ -25,7 +36,8 @@ case "$1" in # Only run this script for the master branch. You can remove this # if block if you wish to run it for others as well. if [[ $refname = "refs/heads/master" ]] ; then - dokku git-build $APP $newrev + # broken out into pluginhook so we might support other methods to receive an app + pluginhook receive-app $APP $newrev else echo $'\e[1G\e[K'"-----> WARNING: deploy did not complete, you must push to master." echo $'\e[1G\e[K'"-----> for example, try 'git push ${refname/refs\/heads\/}:master'" @@ -56,7 +68,7 @@ case "$1" in else REF=$(< "$DOKKU_ROOT/$APP/refs/heads/master") fi - git_archive_all $APP $REF | dokku receive $APP | sed -u "s/^/"$'\e[1G'"/" + git_build_app_repo $APP $REF ;; git-*) diff --git a/plugins/git/receive-app b/plugins/git/receive-app new file mode 100755 index 000000000..847df876c --- /dev/null +++ b/plugins/git/receive-app @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$(dirname $0)/../common/functions" + +APP="$1"; REV="$2" + +dokku git-build $APP $REV diff --git a/plugins/ps/commands b/plugins/ps/commands index ed45415f7..4fd227b76 100755 --- a/plugins/ps/commands +++ b/plugins/ps/commands @@ -4,14 +4,19 @@ source "$(dirname $0)/../common/functions" release_and_deploy() { source "$(dirname $0)/../common/functions" - local APP="$1"; + local APP="$1"; local IMAGE="dokku/$APP" if [[ -f "$DOKKU_ROOT/$APP/CONTAINER" ]]; then + if is_image_buildstep_based "$IMAGE"; then + IMAGE_SOURCE_TYPE="buildstep" + else + IMAGE_SOURCE_TYPE="dockerfile" + fi + dokku_log_info1 "Releasing $APP ..." - dokku release $APP - dokku_log_info1 "Release complete!" + dokku release "$APP" "$IMAGE_SOURCE_TYPE" dokku_log_info1 "Deploying $APP ..." - dokku deploy $APP + dokku deploy "$APP" dokku_log_info2 "Application deployed:" dokku urls $APP | sed "s/^/ /" echo @@ -63,7 +68,7 @@ case "$1" in verify_app_name "$2" APP="$2" - dokku git-build $APP + pluginhook receive-app $APP ;; ps:rebuildall) diff --git a/tests.mk b/tests.mk index b3001bfa1..25bf763c2 100644 --- a/tests.mk +++ b/tests.mk @@ -71,6 +71,10 @@ deploy-test-config: @echo deploying config app... cd tests && ./test_deploy ./apps/config dokku.me +deploy-test-dockerfile: + @echo deploying dockerfile app... + cd tests && ./test_deploy ./apps/dockerfile dokku.me + deploy-test-gitsubmodules: @echo deploying gitsubmodules app... cd tests && ./test_deploy ./apps/gitsubmodules dokku.me @@ -116,6 +120,7 @@ deploy-tests: # @$(QUIET) bats tests/deploy @$(QUIET) $(MAKE) deploy-test-config @$(QUIET) $(MAKE) deploy-test-clojure + @$(QUIET) $(MAKE) deploy-test-dockerfile @$(QUIET) $(MAKE) deploy-test-gitsubmodules @$(QUIET) $(MAKE) deploy-test-go @$(QUIET) $(MAKE) deploy-test-java diff --git a/tests/apps/dockerfile/CHECKS b/tests/apps/dockerfile/CHECKS new file mode 100644 index 000000000..6439da05f --- /dev/null +++ b/tests/apps/dockerfile/CHECKS @@ -0,0 +1 @@ +/ Hello World! diff --git a/tests/apps/dockerfile/Dockerfile b/tests/apps/dockerfile/Dockerfile new file mode 100644 index 000000000..a229deba3 --- /dev/null +++ b/tests/apps/dockerfile/Dockerfile @@ -0,0 +1,11 @@ +FROM ubuntu:trusty +ENV LC_ALL C +ENV DEBIAN_FRONTEND noninteractive +ENV DEBCONF_NONINTERACTIVE_SEEN true +EXPOSE 5000 + +ADD . /app +RUN apt-get install -y software-properties-common && add-apt-repository ppa:chris-lea/node.js && apt-get update +RUN apt-get install -y build-essential curl postgresql-client-9.3 nodejs git +RUN cd /app && npm install +CMD cd /app && npm start diff --git a/tests/apps/dockerfile/check_deploy b/tests/apps/dockerfile/check_deploy new file mode 100755 index 000000000..784e90fc2 --- /dev/null +++ b/tests/apps/dockerfile/check_deploy @@ -0,0 +1,2 @@ +#!/bin/bash +set -e; output="$(curl -s -S $1)"; echo $output; test "$output" == "Hello World!" diff --git a/tests/apps/dockerfile/index.js b/tests/apps/dockerfile/index.js new file mode 100644 index 000000000..90221112f --- /dev/null +++ b/tests/apps/dockerfile/index.js @@ -0,0 +1,13 @@ +var express = require('express') +var app = express(); + +app.set('port', (process.env.PORT || 5000)) +app.use(express.static(__dirname + '/public')) + +app.get('/', function(request, response) { + response.send('Hello World!') +}) + +app.listen(app.get('port'), function() { + console.log("Node app is running at localhost:" + app.get('port')) +}) diff --git a/tests/apps/dockerfile/package.json b/tests/apps/dockerfile/package.json new file mode 100644 index 000000000..29e7c9f3e --- /dev/null +++ b/tests/apps/dockerfile/package.json @@ -0,0 +1,29 @@ +{ + "name": "node-js-sample", + "version": "0.1.0", + "description": "A sample Node.js app using Express 4", + "main": "index.js", + "scripts": { + "start": "node index.js" + }, + "dependencies": { + "express": "^4.0.0" + }, + "engines": { + "node": "0.10.x" + }, + "repository": { + "type": "git", + "url": "https://github.com/heroku/node-js-sample" + }, + "keywords": [ + "node", + "heroku", + "express" + ], + "author": "Mark Pundsack", + "contributors": [ + "Zeke Sikelianos (http://zeke.sikelianos.com)" + ], + "license": "MIT" +} diff --git a/tests/unit/apps.bats b/tests/unit/apps.bats old mode 100755 new mode 100644 diff --git a/tests/unit/config.bats b/tests/unit/config.bats index 2d09661bb..6485d571b 100644 --- a/tests/unit/config.bats +++ b/tests/unit/config.bats @@ -49,10 +49,18 @@ teardown() { assert_output "" } -@test "config (global)" { +@test "global config (buildstep)" { deploy_app run bash -c "dokku run $TEST_APP env | egrep '^global_test=true'" echo "output: "$output echo "status: "$status assert_success } + +@test "global config (dockerfile)" { + deploy_app dockerfile + run bash -c "docker inspect --format '{{ .Config.Env }}' $(< $DOKKU_ROOT/$TEST_APP/CONTAINER) | grep global_test" + echo "output: "$output + echo "status: "$status + assert_success +} diff --git a/tests/unit/ps.bats b/tests/unit/ps-buildstep.bats similarity index 91% rename from tests/unit/ps.bats rename to tests/unit/ps-buildstep.bats index a468b1ccf..9973731ef 100644 --- a/tests/unit/ps.bats +++ b/tests/unit/ps-buildstep.bats @@ -10,7 +10,7 @@ teardown() { destroy_app } -# @test "ps" { +# @test "ps (buildstep)" { # # CI support: 'Ah. I just spoke with our Docker expert -- # # looks like docker exec is built to work with docker-under-libcontainer, # # but we're using docker-under-lxc. I don't have an estimated time for the fix, sorry @@ -21,7 +21,7 @@ teardown() { # assert_success # } -@test "ps:start" { +@test "ps:start (buildstep)" { run bash -c "dokku ps:stop $TEST_APP" echo "output: "$output echo "status: "$status @@ -36,7 +36,7 @@ teardown() { assert_success } -@test "ps:stop" { +@test "ps:stop (buildstep)" { run bash -c "dokku ps:stop $TEST_APP" echo "output: "$output echo "status: "$status @@ -47,7 +47,7 @@ teardown() { assert_failure } -@test "ps:restart" { +@test "ps:restart (buildstep)" { run bash -c "dokku ps:restart $TEST_APP" echo "output: "$output echo "status: "$status @@ -58,7 +58,7 @@ teardown() { assert_success } -@test "ps:rebuild" { +@test "ps:rebuild (buildstep)" { run bash -c "dokku ps:rebuild $TEST_APP" echo "output: "$output echo "status: "$status diff --git a/tests/unit/ps-dockerfile.bats b/tests/unit/ps-dockerfile.bats new file mode 100644 index 000000000..ddc1d3706 --- /dev/null +++ b/tests/unit/ps-dockerfile.bats @@ -0,0 +1,70 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + deploy_app dockerfile +} + +teardown() { + destroy_app +} + +@test "ps (dockerfile)" { + # CI support: 'Ah. I just spoke with our Docker expert -- + # looks like docker exec is built to work with docker-under-libcontainer, + # but we're using docker-under-lxc. I don't have an estimated time for the fix, sorry + skip "circleci does not support docker exec at the moment." + run bash -c "dokku ps $TEST_APP | grep -q \"node web.js\"" + echo "output: "$output + echo "status: "$status + assert_success +} + +@test "ps:start (dockerfile)" { + run bash -c "dokku ps:stop $TEST_APP" + echo "output: "$output + echo "status: "$status + assert_success + run bash -c "dokku ps:start $TEST_APP" + echo "output: "$output + echo "status: "$status + assert_success + run bash -c "docker ps -q --no-trunc | grep -q $(< $DOKKU_ROOT/$TEST_APP/CONTAINER)" + echo "output: "$output + echo "status: "$status + assert_success +} + +@test "ps:stop (dockerfile)" { + run bash -c "dokku ps:stop $TEST_APP" + echo "output: "$output + echo "status: "$status + assert_success + run bash -c "docker ps -q --no-trunc | grep -q $(< $DOKKU_ROOT/$TEST_APP/CONTAINER)" + echo "output: "$output + echo "status: "$status + assert_failure +} + +@test "ps:restart (dockerfile)" { + run bash -c "dokku ps:restart $TEST_APP" + echo "output: "$output + echo "status: "$status + assert_success + run bash -c "docker ps -q --no-trunc | grep -q $(< $DOKKU_ROOT/$TEST_APP/CONTAINER)" + echo "output: "$output + echo "status: "$status + assert_success +} + +@test "ps:rebuild (dockerfile)" { + run bash -c "dokku ps:rebuild $TEST_APP" + echo "output: "$output + echo "status: "$status + assert_success + run bash -c "docker ps -q --no-trunc | grep -q $(< $DOKKU_ROOT/$TEST_APP/CONTAINER)" + echo "output: "$output + echo "status: "$status + assert_success +} diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 935e38ee9..f3f7a5a7f 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -93,8 +93,9 @@ destroy_app() { } deploy_app() { + APP_TYPE="$1"; APP_TYPE=${APP_TYPE:="nodejs-express"} TMP=$(mktemp -d -t "$TARGET.XXXXX") - rmdir $TMP && cp -r ./tests/apps/nodejs-express $TMP + rmdir $TMP && cp -r ./tests/apps/$APP_TYPE $TMP cd $TMP git init git config user.email "robot@example.com"