Files
dokku/tests/unit/test_helper.bash

555 lines
14 KiB
Bash
Raw Normal View History

2014-12-15 09:54:06 -08:00
#!/usr/bin/env bash
2014-12-13 18:56:31 -08:00
# constants
DOKKU_ROOT=${DOKKU_ROOT:=~dokku}
2019-11-19 15:09:54 -07:00
DOCKER_BIN=${DOCKER_BIN:="docker"}
DOKKU_LIB_ROOT=${DOKKU_LIB_PATH:="/var/lib/dokku"}
PLUGIN_PATH=${PLUGIN_PATH:="$DOKKU_LIB_ROOT/plugins"}
PLUGIN_AVAILABLE_PATH=${PLUGIN_AVAILABLE_PATH:="$PLUGIN_PATH/available"}
PLUGIN_ENABLED_PATH=${PLUGIN_ENABLED_PATH:="$PLUGIN_PATH/enabled"}
PLUGIN_CORE_PATH=${PLUGIN_CORE_PATH:="$DOKKU_LIB_ROOT/core-plugins"}
PLUGIN_CORE_AVAILABLE_PATH=${PLUGIN_CORE_AVAILABLE_PATH:="$PLUGIN_CORE_PATH/available"}
2016-02-14 18:43:40 -08:00
CUSTOM_TEMPLATE_SSL_DOMAIN=customssltemplate.dokku.me
UUID=$(tr -dc 'a-z0-9' </dev/urandom | fold -w 32 | head -n 1)
2016-03-03 18:18:09 -08:00
TEST_APP="rdmtestapp-${UUID}"
2018-12-27 23:32:28 +01:00
TEST_NETWORK="test-network-${UUID}"
SKIPPED_TEST_ERR_MSG="previous test failed! skipping remaining tests..."
# global setup() and teardown()
# skips remaining tests on first failure
global_setup() {
[[ ! -f "${BATS_PARENT_TMPNAME}.skip" ]] || skip "$SKIPPED_TEST_ERR_MSG"
2020-06-01 22:44:29 -04:00
2020-06-01 22:43:26 -04:00
free -m
cleanup_apps
cleanup_containers
}
global_teardown() {
[[ -n "$BATS_TEST_COMPLETED" ]] || touch "${BATS_PARENT_TMPNAME}.skip"
cleanup_apps
cleanup_containers
}
cleanup_apps() {
rm -rf $DOKKU_ROOT/*/nginx.conf
apps=$(dokku --quiet apps:list)
if [[ -n "${apps}" ]]; then
dokku --quiet apps:list | xargs -n1 dokku --force apps:destroy
fi
}
cleanup_containers() {
containers=$(docker container ls --quiet)
if [[ -n "$containers" ]]; then
docker container ls --quiet | xargs -n1 docker container rm -f || true
fi
}
2014-12-13 18:56:31 -08:00
# test functions
flunk() {
{
if [[ "$#" -eq 0 ]]; then
cat -
else
echo "$*"
2014-12-13 18:56:31 -08:00
fi
}
return 1
}
# ShellCheck doesn't know about $status from Bats
# shellcheck disable=SC2154
2016-02-14 18:43:40 -08:00
# shellcheck disable=SC2120
2014-12-13 18:56:31 -08:00
assert_success() {
2015-09-14 21:20:35 -07:00
if [[ "$status" -ne 0 ]]; then
2014-12-13 18:56:31 -08:00
flunk "command failed with exit status $status"
2015-09-14 21:20:35 -07:00
elif [[ "$#" -gt 0 ]]; then
2014-12-13 18:56:31 -08:00
assert_output "$1"
fi
}
# ShellCheck doesn't know about $status from Bats
# shellcheck disable=SC2154
# shellcheck disable=SC2120
2014-12-13 18:56:31 -08:00
assert_failure() {
2015-09-14 21:20:35 -07:00
if [[ "$status" -eq 0 ]]; then
2014-12-13 18:56:31 -08:00
flunk "expected failed exit status"
2015-09-14 21:20:35 -07:00
elif [[ "$#" -gt 0 ]]; then
2014-12-13 18:56:31 -08:00
assert_output "$1"
fi
}
assert_equal() {
2015-09-14 21:20:35 -07:00
if [[ "$1" != "$2" ]]; then
{
echo "expected: $1"
2014-12-13 18:56:31 -08:00
echo "actual: $2"
} | flunk
fi
}
assert_not_equal() {
if [[ "$1" == "$2" ]]; then
{
echo "unexpected: $1"
echo "actual: $2"
} | flunk
fi
}
# ShellCheck doesn't know about $output from Bats
# shellcheck disable=SC2154
2014-12-13 18:56:31 -08:00
assert_output() {
local expected
2015-09-14 21:20:35 -07:00
if [[ $# -eq 0 ]]; then
expected="$(cat -)"
else
expected="$1"
2014-12-13 18:56:31 -08:00
fi
assert_equal "$expected" "$output"
}
# ShellCheck doesn't know about $output from Bats
# shellcheck disable=SC2154
assert_not_output() {
local expected
if [[ $# -eq 0 ]]; then
unexpected="$(cat -)"
else
unexpected="$1"
fi
assert_not_equal "$unexpected" "$output"
}
# ShellCheck doesn't know about $output from Bats
# shellcheck disable=SC2154
assert_output_exists() {
[[ -n "$output" ]] || flunk "expected output, found none"
}
# ShellCheck doesn't know about $output from Bats
# shellcheck disable=SC2154
assert_output_not_exists() {
[[ -z "$output" ]] || flunk "expected no output, found some"
}
# ShellCheck doesn't know about $output from Bats
# shellcheck disable=SC2154
assert_output_contains() {
local input="$output"
local expected="$1"
local count="${2:-1}"
local found=0
until [ "${input/$expected/}" = "$input" ]; do
input="${input/$expected/}"
found=$((found + 1))
done
2016-02-22 10:16:57 -08:00
assert_equal "$count" "$found"
}
# ShellCheck doesn't know about $lines from Bats
# shellcheck disable=SC2154
2014-12-13 18:56:31 -08:00
assert_line() {
2015-09-14 21:20:35 -07:00
if [[ "$1" -ge 0 ]] 2>/dev/null; then
2014-12-13 18:56:31 -08:00
assert_equal "$2" "${lines[$1]}"
else
local line
for line in "${lines[@]}"; do
[[ "$line" == "$1" ]] && return 0
2014-12-13 18:56:31 -08:00
done
flunk "expected line \`$1'"
fi
}
refute_line() {
2015-09-14 21:20:35 -07:00
if [[ "$1" -ge 0 ]] 2>/dev/null; then
2014-12-13 18:56:31 -08:00
local num_lines="${#lines[@]}"
2015-09-14 21:20:35 -07:00
if [[ "$1" -lt "$num_lines" ]]; then
2014-12-13 18:56:31 -08:00
flunk "output has $num_lines lines"
fi
else
local line
for line in "${lines[@]}"; do
if [[ "$line" == "$1" ]]; then
2014-12-13 18:56:31 -08:00
flunk "expected to not find line \`$line'"
fi
done
fi
}
assert() {
2014-12-15 09:54:06 -08:00
if ! "$*"; then
flunk "failed: $*"
2014-12-13 18:56:31 -08:00
fi
}
assert_exit_status() {
assert_equal "$status" "$1"
}
2014-12-13 18:56:31 -08:00
# dokku functions
create_app() {
local APP="$1"
local TEST_APP=${APP:=$TEST_APP}
2016-03-03 18:18:09 -08:00
dokku apps:create "$TEST_APP"
2014-12-13 18:56:31 -08:00
}
2016-06-30 17:34:07 -07:00
create_key() {
ssh-keygen -P "" -f /tmp/testkey &>/dev/null
2016-06-30 17:34:07 -07:00
}
2014-12-13 18:56:31 -08:00
destroy_app() {
local RC="$1"
local RC=${RC:=0}
local APP="$2"
local TEST_APP=${APP:=$TEST_APP}
2016-03-03 18:18:09 -08:00
dokku --force apps:destroy "$TEST_APP"
2016-02-22 10:16:57 -08:00
return "$RC"
2014-12-13 18:56:31 -08:00
}
2016-06-30 17:34:07 -07:00
destroy_key() {
rm -f /tmp/testkey* &>/dev/null || true
2016-06-30 17:34:07 -07:00
}
add_domain() {
2016-03-03 18:18:09 -08:00
dokku domains:add "$TEST_APP" "$1"
}
2016-02-14 18:43:40 -08:00
# shellcheck disable=SC2119
check_urls() {
local PATTERN="$1"
run /bin/bash -c "dokku --quiet urls $TEST_APP | grep -E \"${PATTERN}\""
2016-02-22 10:16:57 -08:00
echo "output: $output"
echo "status: $status"
2016-02-14 18:43:40 -08:00
assert_success
}
assert_http_success() {
local url=$1
run curl -kSso /dev/null -w "%{http_code}" "${url}"
2016-02-22 10:16:57 -08:00
echo "output: $output"
echo "status: $status"
2016-02-14 18:43:40 -08:00
assert_output "200"
}
assert_ssl_domain() {
local domain=$1
assert_app_domain "${domain}"
assert_http_redirect "http://${domain}" "https://${domain}:443/"
assert_http_success "https://${domain}"
}
assert_nonssl_domain() {
local domain=$1
assert_app_domain "${domain}"
assert_http_success "http://${domain}"
}
assert_app_domain() {
local domain=$1
2020-02-17 04:39:52 -05:00
run /bin/bash -c "dokku domains:report $TEST_APP --domains-app-vhosts | tr \" \" \"\n\" | grep -xF ${domain}"
echo "app domains: $(dokku domains:report "$TEST_APP" --domains-app-vhosts | tr \" \" \"\n\")"
2016-02-22 10:16:57 -08:00
echo "output: $output"
echo "status: $status"
2016-02-14 18:43:40 -08:00
assert_output "${domain}"
}
assert_http_redirect() {
local from=$1
local to=$2
run curl -kSso /dev/null -w "%{redirect_url}" "${from}"
2016-02-22 10:16:57 -08:00
echo "output: $output"
echo "status: $status"
2016-02-14 18:43:40 -08:00
assert_output "${to}"
}
assert_external_port() {
local CID="$1"
local EXTERNAL_PORT_COUNT
EXTERNAL_PORT_COUNT="$(docker port "$CID" | wc -l)"
run /bin/bash -c "[[ $EXTERNAL_PORT_COUNT -gt 0 ]]"
assert_success
}
assert_not_external_port() {
local CID="$1"
local EXTERNAL_PORT_COUNT
EXTERNAL_PORT_COUNT="$(docker port "$CID" | wc -l)"
run /bin/bash -c "[[ $EXTERNAL_PORT_COUNT -gt 0 ]]"
assert_failure
}
assert_url() {
url=$1
run /bin/bash -c "dokku url $TEST_APP"
echo "output: $output"
echo "status: $status"
echo "url: ${url}"
assert_output "${url}"
}
assert_urls() {
2020-02-17 04:44:25 -05:00
urls=$@
run /bin/bash -c "dokku urls $TEST_APP"
echo "output: $output"
echo "status: $status"
2020-07-04 00:08:44 +02:00
echo "urls:" "$(tr ' ' '\n' <<<"${urls}" | sort)"
assert_output < <(tr ' ' '\n' <<<"${urls}" | sort)
}
deploy_app() {
declare APP_TYPE="$1" GIT_REMOTE="$2" CUSTOM_TEMPLATE="$3" CUSTOM_PATH="$4"
local APP_TYPE=${APP_TYPE:="python"}
local GIT_REMOTE=${GIT_REMOTE:="dokku@dokku.me:$TEST_APP"}
local GIT_REMOTE_BRANCH=${GIT_REMOTE_BRANCH:="master"}
2019-10-03 17:48:20 -04:00
local TMP=${CUSTOM_TMP:=$(mktemp -d "/tmp/dokku.me.XXXXX")}
2016-02-14 18:43:40 -08:00
rmdir "$TMP" && cp -r "${BATS_TEST_DIRNAME}/../../tests/apps/$APP_TYPE" "$TMP"
2016-02-22 10:16:57 -08:00
# shellcheck disable=SC2086
2016-02-14 18:43:40 -08:00
[[ -n "$CUSTOM_TEMPLATE" ]] && $CUSTOM_TEMPLATE $TEST_APP $TMP/$CUSTOM_PATH
2016-02-22 10:16:57 -08:00
pushd "$TMP" &>/dev/null || exit 1
2019-10-03 17:48:20 -04:00
[[ -z "$CUSTOM_TMP" ]] && trap 'popd &>/dev/null || true; rm -rf "$TMP"' RETURN INT TERM
2016-02-22 10:16:57 -08:00
git init
git config user.email "robot@example.com"
git config user.name "Test Robot"
echo "setting up remote: $GIT_REMOTE"
2016-02-22 10:16:57 -08:00
git remote add target "$GIT_REMOTE"
[[ -f gitignore ]] && mv gitignore .gitignore
git add .
git commit -m 'initial commit'
git push target "master:${GIT_REMOTE_BRANCH}" || destroy_app $?
}
setup_client_repo() {
local TMP
TMP=$(mktemp -d "/tmp/dokku.me.XXXXX")
rmdir "$TMP" && cp -r "${BATS_TEST_DIRNAME}/../../tests/apps/nodejs-express" "$TMP"
2016-02-22 10:16:57 -08:00
cd "$TMP" || exit 1
git init
git config user.email "robot@example.com"
git config user.name "Test Robot"
[[ -f gitignore ]] && mv gitignore .gitignore
git add .
git commit -m 'initial commit'
}
setup_test_tls() {
local TLS_TYPE="$1"
local TLS="/home/dokku/$TEST_APP/tls"
2016-03-03 18:18:09 -08:00
mkdir -p "$TLS"
2016-02-14 18:43:40 -08:00
case "$TLS_TYPE" in
wildcard)
local TLS_ARCHIVE=server_ssl_wildcard.tar
;;
sans)
local TLS_ARCHIVE=server_ssl_sans.tar
;;
*)
local TLS_ARCHIVE=server_ssl.tar
;;
esac
2016-03-03 18:18:09 -08:00
tar xf "$BATS_TEST_DIRNAME/$TLS_ARCHIVE" -C "$TLS"
sudo chown -R dokku:dokku "${TLS}/.."
}
custom_ssl_nginx_template() {
local APP="$1"
local APP_REPO_DIR="$2"
2016-02-14 18:43:40 -08:00
[[ -z "$APP" ]] && local APP="$TEST_APP"
2016-02-22 10:16:57 -08:00
mkdir -p "$APP_REPO_DIR"
2016-02-14 18:43:40 -08:00
echo "injecting custom_ssl_nginx_template -> $APP_REPO_DIR/nginx.conf.sigil"
cat <<EOF >"$APP_REPO_DIR/nginx.conf.sigil"
{{ range \$port_map := .PROXY_PORT_MAP | split " " }}
{{ \$port_map_list := \$port_map | split ":" }}
{{ \$scheme := index \$port_map_list 0 }}
{{ \$listen_port := index \$port_map_list 1 }}
{{ \$upstream_port := index \$port_map_list 2 }}
{{ if eq \$scheme "http" }}
server {
listen [::]:{{ \$listen_port }};
listen {{ \$listen_port }};
server_name {{ $.NOSSL_SERVER_NAME }} $CUSTOM_TEMPLATE_SSL_DOMAIN;
2017-07-03 11:07:07 +09:30
return 301 https://\$host:{{ $.PROXY_SSL_PORT }}\$request_uri;
}
{{ else if eq \$scheme "https"}}
server {
2017-07-03 11:07:07 +09:30
listen [::]:{{ $.PROXY_SSL_PORT }} ssl spdy;
listen {{ $.PROXY_SSL_PORT }} ssl spdy;
{{ if $.SSL_SERVER_NAME }}server_name {{ $.SSL_SERVER_NAME }} $CUSTOM_TEMPLATE_SSL_DOMAIN; {{ end }}
{{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }} $CUSTOM_TEMPLATE_SSL_DOMAIN; {{ end }}
ssl_certificate {{ $.APP_SSL_PATH }}/server.crt;
ssl_certificate_key {{ $.APP_SSL_PATH }}/server.key;
keepalive_timeout 70;
add_header Alternate-Protocol {{ \$listen_port }}:npn-spdy/2;
location / {
proxy_pass http://{{ $.APP }}-{{ \$upstream_port }};
proxy_http_version 1.1;
2016-02-14 18:43:40 -08:00
proxy_set_header Upgrade \$http_upgrade;
2019-07-13 01:25:55 -04:00
proxy_set_header Connection \$http_connection;
2016-02-14 18:43:40 -08:00
proxy_set_header Host \$http_host;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Forwarded-For \$remote_addr;
proxy_set_header X-Forwarded-Port \$server_port;
proxy_set_header X-Request-Start \$msec;
}
include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf;
}
{{ end }}{{ end }}
{{ if $.DOKKU_APP_WEB_LISTENERS }}
{{ range \$upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }}
upstream {{ $.APP }}-{{ \$upstream_port }} {
{{ range \$listeners := $.DOKKU_APP_WEB_LISTENERS | split " " }}
{{ \$listener_list := \$listeners | split ":" }}
{{ \$listener_ip := index \$listener_list 0 }}
server {{ \$listener_ip }}:{{ \$upstream_port }};{{ end }}
2016-02-14 18:43:40 -08:00
}
{{ end }}{{ end }}
EOF
cat "$APP_REPO_DIR/nginx.conf.sigil"
}
custom_nginx_template() {
local APP="$1"
local APP_REPO_DIR="$2"
2016-02-14 18:43:40 -08:00
[[ -z "$APP" ]] && local APP="$TEST_APP"
2016-02-22 10:16:57 -08:00
mkdir -p "$APP_REPO_DIR"
2016-02-14 18:43:40 -08:00
echo "injecting custom_nginx_template -> $APP_REPO_DIR/nginx.conf.sigil"
cat <<EOF >"$APP_REPO_DIR/nginx.conf.sigil"
{{ range \$port_map := .PROXY_PORT_MAP | split " " }}
{{ \$port_map_list := \$port_map | split ":" }}
{{ \$scheme := index \$port_map_list 0 }}
{{ \$listen_port := index \$port_map_list 1 }}
{{ \$upstream_port := index \$port_map_list 2 }}
server {
listen [::]:{{ \$listen_port }};
listen {{ \$listen_port }};
server_name {{ $.NOSSL_SERVER_NAME }} customtemplate.dokku.me;
location / {
proxy_pass http://{{ $.APP }}-{{ \$upstream_port }};
proxy_http_version 1.1;
2016-02-14 18:43:40 -08:00
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
2016-02-14 18:43:40 -08:00
proxy_set_header Host \$http_host;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Forwarded-For \$remote_addr;
proxy_set_header X-Forwarded-Port \$server_port;
proxy_set_header X-Request-Start \$msec;
}
include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf;
}
2016-02-14 18:43:40 -08:00
{{ end }}
{{ if $.DOKKU_APP_WEB_LISTENERS }}
{{ range \$upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }}
upstream {{ $.APP }}-{{ \$upstream_port }} {
{{ range \$listeners := $.DOKKU_APP_WEB_LISTENERS | split " " }}
{{ \$listener_list := \$listeners | split ":" }}
{{ \$listener_ip := index \$listener_list 0 }}
server {{ \$listener_ip }}:{{ \$upstream_port }};{{ end }}
}
{{ end }}{{ end }}
{{ if $.DOKKU_APP_WORKER_LISTENERS }}
{{ range \$upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }}
upstream {{ $.APP }}-worker-{{ \$upstream_port }} {
{{ range \$listeners := $.DOKKU_APP_WORKER_LISTENERS | split " " }}
server {{ \$listeners }};{{ end }}
}
{{ end }}{{ end }}
2016-02-14 18:43:40 -08:00
EOF
cat "$APP_REPO_DIR/nginx.conf.sigil"
2016-02-14 18:43:40 -08:00
}
bad_custom_nginx_template() {
local APP="$1"
local APP_REPO_DIR="$2"
2016-02-14 18:43:40 -08:00
[[ -z "$APP" ]] && local APP="$TEST_APP"
echo "injecting bad_custom_nginx_template -> $APP_REPO_DIR/nginx.conf.sigil"
cat <<EOF >"$APP_REPO_DIR/nginx.conf.sigil"
2016-02-14 18:43:40 -08:00
some lame nginx config
EOF
}
template_checks_file() {
local APP="$1"
local APP_REPO_DIR="$2"
[[ -z "$APP" ]] && local APP="$TEST_APP"
echo "injecting templated CHECKS file -> $APP_REPO_DIR/CHECKS"
cat <<EOF >"$APP_REPO_DIR/CHECKS"
WAIT=2 # wait 2 seconds
TIMEOUT=5 # set timeout to 5 seconds
ATTEMPTS=2 # try 2 times
{{ var "HEALTHCHECK_ENDPOINT" }} {{ var "HEALTHCHECK_ENDPOINT" }}
EOF
}
add_release_command() {
local APP="$1"
local APP_REPO_DIR="$2"
[[ -z "$APP" ]] && local APP="$TEST_APP"
echo "release: touch /app/release.test" >> "$APP_REPO_DIR/Procfile"
}
build_nginx_config() {
# simulate nginx post-deploy
dokku domains:setup "$TEST_APP"
dokku proxy:build-config "$TEST_APP"
}
2018-12-27 23:32:28 +01:00
create_network() {
local NETWORK_NAME="$1:=$TEST_NETWORK"
NETWORK=$(docker network ls -q -f name="$NETWORK_NAME")
[[ -z "$NETWORK" ]] && docker network create "$NETWORK_NAME"
}
attach_network() {
local NETWORK_NAME="$1:=$TEST_NETWORK"
NETWORK=$(docker network ls -q -f name="$NETWORK_NAME")
[[ -n "$NETWORK" ]] && docker network connect "$NETWORK_NAME" "${TEST_APP}.web.1"
}
create_attach_network() {
local NETWORK_NAME="$1:=$TEST_NETWORK"
create_network "$NETWORK_NAME"
attach_network "$NETWORK_NAME"
}
delete_network() {
local NETWORK_NAME="$1:=$TEST_NETWORK"
NETWORK=$(docker network ls -q -f name="$NETWORK_NAME")
[[ -n "$NETWORK" ]] && docker network rm "$NETWORK_NAME"
}
detach_network() {
local NETWORK_NAME="$1:=$TEST_NETWORK"
NETWORK=$(docker network ls -q -f name="$NETWORK_NAME")
[[ -z "$NETWORK" ]] && docker network disconnect "$NETWORK_NAME" "${TEST_APP}.web.1"
}
detach_delete_network() {
local NETWORK_NAME="$1:=$TEST_NETWORK"
detach_network "$NETWORK_NAME"
delete_network "$NETWORK_NAME"
}