mirror of
https://github.com/dokku/dokku.git
synced 2026-05-18 05:05:46 +02:00
Adds a bats lint test that guards the static wiring (nginx conf, dokku-restore finish-script ordering, my_init sentinel reset, and the Dockerfile HEALTHCHECK line) plus a docker smoke test that boots the built image, waits for the health flip, exercises the loopback endpoint, asserts the port is not published to the host, and verifies the negative path. The smoke test is invoked via a new `make test-image-healthcheck` target and runs automatically in the build-image action after `docker buildx --load`.
129 lines
4.1 KiB
Bash
Executable File
129 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
IMAGE_TAG="${IMAGE_TAG:-dokku/dokku:latest}"
|
|
CONTAINER_NAME="${CONTAINER_NAME:-dokku-healthcheck-smoke}"
|
|
START_TIMEOUT_SECONDS="${START_TIMEOUT_SECONDS:-360}"
|
|
UNHEALTHY_TIMEOUT_SECONDS="${UNHEALTHY_TIMEOUT_SECONDS:-180}"
|
|
|
|
log-info() {
|
|
echo "-----> $*"
|
|
}
|
|
|
|
log-fail() {
|
|
echo " ! $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
cleanup() {
|
|
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
require-image() {
|
|
if ! docker image inspect "$IMAGE_TAG" >/dev/null 2>&1; then
|
|
log-fail "Image '$IMAGE_TAG' not found in local docker daemon. Build it first (eg. 'docker build -t $IMAGE_TAG .')."
|
|
fi
|
|
}
|
|
|
|
start-container() {
|
|
log-info "Starting container '$CONTAINER_NAME' from image '$IMAGE_TAG'"
|
|
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
|
docker run -d \
|
|
--name "$CONTAINER_NAME" \
|
|
--env DOKKU_HOSTNAME=dokku.me \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
"$IMAGE_TAG" >/dev/null
|
|
}
|
|
|
|
wait-for-catchall-vhost() {
|
|
local deadline=$((SECONDS + 30))
|
|
while (( SECONDS < deadline )); do
|
|
if docker exec "$CONTAINER_NAME" test -f /etc/nginx/conf.d/00-default-vhost.conf; then
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
log-fail "Catch-all default vhost /etc/nginx/conf.d/00-default-vhost.conf was never created. \
|
|
Likely cause: a stale build/package/dokku-*.deb that predates the catch-all template shipped in \
|
|
plugins/nginx-vhosts/templates/default-site.conf. Rebuild the deb before running this test."
|
|
}
|
|
|
|
wait-for-healthy() {
|
|
local deadline=$((SECONDS + START_TIMEOUT_SECONDS))
|
|
local status
|
|
while (( SECONDS < deadline )); do
|
|
status=$(docker inspect --format='{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || echo "missing")
|
|
if [[ "$status" == "healthy" ]]; then
|
|
log-info "Container reached healthy status"
|
|
return 0
|
|
fi
|
|
sleep 5
|
|
done
|
|
|
|
log-info "Container failed to become healthy within ${START_TIMEOUT_SECONDS}s. Last logs:"
|
|
docker logs --tail 100 "$CONTAINER_NAME" || true
|
|
log-fail "Container '$CONTAINER_NAME' did not reach healthy status (last status: ${status:-unknown})"
|
|
}
|
|
|
|
assert-endpoint-ok() {
|
|
log-info "Verifying /_dokku/health returns ok from inside the container"
|
|
local body
|
|
body=$(docker exec "$CONTAINER_NAME" curl -fsS http://127.0.0.1:18080/_dokku/health)
|
|
if [[ "$body" != "ok" ]]; then
|
|
log-fail "Expected health endpoint body 'ok', got: ${body}"
|
|
fi
|
|
}
|
|
|
|
assert-port-not-published() {
|
|
log-info "Verifying port 18080 is not published to the host"
|
|
local ports
|
|
ports=$(docker inspect --format='{{json .NetworkSettings.Ports}}' "$CONTAINER_NAME")
|
|
if echo "$ports" | grep -q "18080/tcp"; then
|
|
log-fail "Port 18080/tcp should not be published to the host. Inspect output: $ports"
|
|
fi
|
|
|
|
if curl --max-time 2 -fsS http://127.0.0.1:18080/_dokku/health >/dev/null 2>&1; then
|
|
log-fail "Health endpoint is reachable from the host on 127.0.0.1:18080 - it must be loopback-only inside the container."
|
|
fi
|
|
}
|
|
|
|
assert-unhealthy-after-sentinel-removed() {
|
|
log-info "Removing /var/run/dokku/ready and confirming endpoint returns failure"
|
|
docker exec "$CONTAINER_NAME" rm -f /var/run/dokku/ready
|
|
|
|
local exit_code=0
|
|
docker exec "$CONTAINER_NAME" curl -fsS http://127.0.0.1:18080/_dokku/health >/dev/null 2>&1 || exit_code=$?
|
|
if (( exit_code == 0 )); then
|
|
log-fail "Health endpoint still returned success after the readiness sentinel was removed"
|
|
fi
|
|
|
|
log-info "Waiting for docker to flip the health status to unhealthy"
|
|
local deadline=$((SECONDS + UNHEALTHY_TIMEOUT_SECONDS))
|
|
local status
|
|
while (( SECONDS < deadline )); do
|
|
status=$(docker inspect --format='{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || echo "missing")
|
|
if [[ "$status" == "unhealthy" ]]; then
|
|
log-info "Container flipped to unhealthy"
|
|
return 0
|
|
fi
|
|
sleep 5
|
|
done
|
|
|
|
log-fail "Container did not flip to unhealthy within ${UNHEALTHY_TIMEOUT_SECONDS}s (last status: ${status:-unknown})"
|
|
}
|
|
|
|
main() {
|
|
require-image
|
|
start-container
|
|
wait-for-catchall-vhost
|
|
wait-for-healthy
|
|
assert-endpoint-ok
|
|
assert-port-not-published
|
|
assert-unhealthy-after-sentinel-removed
|
|
log-info "All healthcheck smoke tests passed."
|
|
}
|
|
|
|
main "$@"
|