diff --git a/tests.mk b/tests.mk index 08707ddd3..576f4fbd7 100644 --- a/tests.mk +++ b/tests.mk @@ -336,7 +336,11 @@ tests-ci-retry-failed: tar xzf /tmp/bats-retry.tgz -C /usr/local/bin bats-retry --execute test-results/bats -test-ci-docker: setup-docker-deploy-tests deploy-test-checks-root deploy-test-config deploy-test-multi deploy-test-go-fail-predeploy deploy-test-go-fail-postdeploy test-ci-binaries +deploy-test-storage-lib-host-root: + @echo deploy-test-storage-lib-host-root + cd tests/docker && bats storage.bats + +test-ci-docker: setup-docker-deploy-tests deploy-test-checks-root deploy-test-config deploy-test-multi deploy-test-go-fail-predeploy deploy-test-go-fail-postdeploy deploy-test-storage-lib-host-root test-ci-binaries generate-ssl-tars: generate-ssl-tar generate-ssl-sans-tar generate-ssl-wildcard-tar diff --git a/tests/ci/setup.sh b/tests/ci/setup.sh index 1cb1d4e80..e07edab5d 100755 --- a/tests/ci/setup.sh +++ b/tests/ci/setup.sh @@ -123,8 +123,13 @@ build_dokku_docker_image() { run_dokku_container() { echo "=====> run_dokku_container on CIRCLE_NODE_INDEX: $CIRCLE_NODE_INDEX" echo "redirect: https://github.com/dokku/dokku-redirect.git" | sudo tee /var/lib/dokku/plugin-list + # DOKKU_HOST_ROOT / DOKKU_LIB_HOST_ROOT match the install docs: the host + # mounts /var/lib/dokku at /mnt/dokku, and in-container /var/lib/dokku/{home,data} + # are symlinks into that volume. docker run -d \ --env DOKKU_HOSTNAME=dokku.me \ + --env DOKKU_HOST_ROOT=/var/lib/dokku/home/dokku \ + --env DOKKU_LIB_HOST_ROOT=/var/lib/dokku/var/lib/dokku \ --name dokku \ --publish 3022:22 \ --publish 80:80 \ diff --git a/tests/docker/storage.bats b/tests/docker/storage.bats new file mode 100644 index 000000000..bad567729 --- /dev/null +++ b/tests/docker/storage.bats @@ -0,0 +1,49 @@ +#!/usr/bin/env bats + +# Dokku-in-Docker coverage for DOKKU_LIB_HOST_ROOT. These tests talk to the +# `dokku` container started by tests/ci/setup.sh docker and read/write the +# host path that volume backs (/var/lib/dokku -> /mnt/dokku). + +setup_file() { + if ! docker inspect dokku >/dev/null 2>&1; then + echo "dokku container is not running; skip docker storage tests" >&2 + return 1 + fi + + DOKKU_LIB_HOST_ROOT="$(docker exec dokku bash -c 'echo -n "$DOKKU_LIB_HOST_ROOT"')" + if [[ -z "$DOKKU_LIB_HOST_ROOT" ]]; then + echo "DOKKU_LIB_HOST_ROOT is unset inside the dokku container" >&2 + return 1 + fi + export DOKKU_LIB_HOST_ROOT +} + +setup() { + ENTRY_NAME="rdmtest-docker-hostroot-$$" + export ENTRY_NAME +} + +teardown() { + docker exec dokku dokku storage:destroy "$ENTRY_NAME" --force >/dev/null 2>&1 || true + sudo rm -rf "${DOKKU_LIB_HOST_ROOT}/data/storage/${ENTRY_NAME}" +} + +@test "(docker) storage:exec sees a file written under DOKKU_LIB_HOST_ROOT" { + run docker exec dokku dokku storage:create "$ENTRY_NAME" --chown root + echo "output: $output" + echo "status: $status" + [[ "$status" -eq 0 ]] + + # storage:create mkdirs under DOKKU_LIB_ROOT inside the container, which is + # the same tree as DOKKU_LIB_HOST_ROOT on the host via the /mnt/dokku volume. + HOST_STORAGE_DIR="${DOKKU_LIB_HOST_ROOT}/data/storage/${ENTRY_NAME}" + [[ -d "$HOST_STORAGE_DIR" ]] + + echo "Hello World!" | sudo tee "${HOST_STORAGE_DIR}/welcome.txt" >/dev/null + + run docker exec dokku dokku storage:exec "$ENTRY_NAME" -- cat /data/welcome.txt + echo "output: $output" + echo "status: $status" + [[ "$status" -eq 0 ]] + [[ "$output" == *"Hello World!"* ]] +} diff --git a/tests/unit/storage-5.bats b/tests/unit/storage-5.bats new file mode 100644 index 000000000..dd9b6f78b --- /dev/null +++ b/tests/unit/storage-5.bats @@ -0,0 +1,117 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + global_setup + create_app + rm -rf "${DOKKU_LIB_ROOT}"/data/storage/rdmtest* +} + +teardown() { + destroy_app + global_teardown +} + +@test "(storage) without DOKKU_LIB_HOST_ROOT docker-args and storage:exec use DOKKU_LIB_ROOT" { + unset DOKKU_LIB_HOST_ROOT + + run /bin/bash -c "dokku storage:create rdmtest-bare" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku storage:mount $TEST_APP rdmtest-bare --container-dir /data" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "echo '' | dokku plugin:trigger docker-args-process-deploy $TEST_APP herokuish latest web | grep -o -- '-v [^ ]*'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "-v ${DOKKU_LIB_ROOT}/data/storage/rdmtest-bare:/data" + + run /bin/bash -c "docker pull alpine:3 >/dev/null" + assert_success + + run /bin/bash -c "dokku storage:exec rdmtest-bare -- /bin/sh -c 'echo bare-metal > /data/marker && cat /data/marker'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "bare-metal" + + run /bin/bash -c "cat ${DOKKU_LIB_ROOT}/data/storage/rdmtest-bare/marker" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "bare-metal" + + run /bin/bash -c "dokku storage:unmount --all $TEST_APP" + assert_success + run /bin/bash -c "dokku storage:destroy rdmtest-bare --force" + assert_success +} + +@test "(storage) with DOKKU_LIB_HOST_ROOT docker-args rewrites the mount host path" { + HOST_ROOT="$(mktemp -d /tmp/dokku-lib-host.XXXXXX)" + export DOKKU_LIB_HOST_ROOT="$HOST_ROOT" + + run /bin/bash -c "dokku storage:create rdmtest-hostroot" + echo "output: $output" + echo "status: $status" + assert_success + + # Create still lands under DOKKU_LIB_ROOT inside Dokku. + run /bin/bash -c "test -d ${DOKKU_LIB_ROOT}/data/storage/rdmtest-hostroot" + assert_success + + run /bin/bash -c "dokku storage:mount $TEST_APP rdmtest-hostroot --container-dir /data" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "echo '' | dokku plugin:trigger docker-args-process-deploy $TEST_APP herokuish latest web | grep -o -- '-v [^ ]*'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "-v ${HOST_ROOT}/data/storage/rdmtest-hostroot:/data" + + run /bin/bash -c "dokku storage:unmount --all $TEST_APP" + assert_success + run /bin/bash -c "dokku storage:destroy rdmtest-hostroot --force" + assert_success + rm -rf "$HOST_ROOT" +} + +@test "(storage) with DOKKU_LIB_HOST_ROOT storage:exec mounts the host-root path" { + # Simulate Dokku-in-Docker: filesystem ops use DOKKU_LIB_ROOT, but Docker -v + # mounts must resolve under DOKKU_LIB_HOST_ROOT. + HOST_ROOT="$(mktemp -d /tmp/dokku-lib-host.XXXXXX)" + export DOKKU_LIB_HOST_ROOT="$HOST_ROOT" + + run /bin/bash -c "dokku storage:create rdmtest-hostroot-exec --chown root" + echo "output: $output" + echo "status: $status" + assert_success + + mkdir -p "${HOST_ROOT}/data/storage/rdmtest-hostroot-exec" + echo "Hello World!" >"${HOST_ROOT}/data/storage/rdmtest-hostroot-exec/welcome.txt" + + run /bin/bash -c "docker pull alpine:3 >/dev/null" + assert_success + + run /bin/bash -c "dokku storage:exec rdmtest-hostroot-exec -- cat /data/welcome.txt" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "Hello World!" + + # The in-container create path must not have received the host-written file. + run /bin/bash -c "test ! -f ${DOKKU_LIB_ROOT}/data/storage/rdmtest-hostroot-exec/welcome.txt" + assert_success + + run /bin/bash -c "dokku storage:destroy rdmtest-hostroot-exec --force" + assert_success + rm -rf "$HOST_ROOT" +}