Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 2 additions & 71 deletions .github/workflows/build-with-smoke-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,74 +24,5 @@ jobs:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0

- name: Create cache directory
run: mkdir -p /tmp/.buildx-cache

- name: Cache Docker layers
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-

- name: Build Docker image
run: |
docker build -t headscale:latest .

- name: Run container in detached mode
run: |
docker run -d --name headscale-container \
-p 127.0.0.1:8008:8008 \
--env LITESTREAM_REPLICA_URL=${{ env.LITESTREAM_REPLICA_URL }} \
--env PUBLIC_SERVER_URL=${{ env.PUBLIC_SERVER_URL }} \
--env HEADSCALE_DNS_BASE_DOMAIN=${{ env.HEADSCALE_DNS_BASE_DOMAIN }} \
--env CADDY_FRONTEND=${{ env.CADDY_FRONTEND }} \
headscale:latest

- name: Run smoke tests
run: |
docker exec headscale-container headscale version
docker exec headscale-container litestream version
docker exec headscale-container caddy version

- name: Check if container is listening on port 8008
run: |
docker exec headscale-container sh -c "netstat -tuln | grep ':8008 '"

- name: Wait for admin GUI to respond
run: |
for attempt in $(seq 1 30); do
if curl --silent --show-error --fail http://127.0.0.1:8008/admin/ > /tmp/admin-gui.html; then
exit 0
fi

sleep 2
done

echo "Admin GUI did not become ready in time"
docker logs headscale-container
exit 1

- name: Check admin GUI redirect and content
run: |
redirect_headers=$(mktemp)

curl --silent --show-error --fail \
--dump-header "$redirect_headers" \
--output /dev/null \
http://127.0.0.1:8008/admin

tr -d '\r' < "$redirect_headers" | grep -qi '^location: /admin/$'
grep -qi '<!doctype html>' /tmp/admin-gui.html
grep -qi 'data-sveltekit-preload-data="hover"' /tmp/admin-gui.html
grep -qi 'assets: "/admin"' /tmp/admin-gui.html

- name: Stop and remove container
if: always()
run: |
docker stop headscale-container
docker rm headscale-container
- name: Run local-equivalent smoke test
run: make smoke-test
64 changes: 62 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
SHELL := /bin/bash

DEFAULT_SMOKE_TEST_IMAGE := headscale:latest
DEFAULT_SMOKE_TEST_CONTAINER := headscale-container
DEFAULT_SMOKE_TEST_HOST := 127.0.0.1
DEFAULT_SMOKE_TEST_PORT := 8008

.DEFAULT_GOAL := help

.PHONY: help check-envsubst render-fly-config render-azure-container-apps
.PHONY: help check-envsubst render-fly-config render-azure-container-apps smoke-test

help:
@printf '%s\n' \
'Available targets:' \
' make render-fly-config Render fly.toml from templates/fly.template.toml' \
' make render-azure-container-apps Render azure-container-apps.yaml from templates/azure-container-apps.template.yaml'
' make render-azure-container-apps Render azure-container-apps.yaml from templates/azure-container-apps.template.yaml' \
' make smoke-test Build the image and run local smoke tests'

check-envsubst:
@command -v envsubst >/dev/null || { \
Expand All @@ -33,3 +39,57 @@ render-azure-container-apps: check-envsubst
@: $${HEADSCALE_DNS_BASE_DOMAIN:?Set HEADSCALE_DNS_BASE_DOMAIN}
@envsubst < templates/azure-container-apps.template.yaml > azure-container-apps.yaml
@printf '%s\n' 'Wrote azure-container-apps.yaml'

smoke-test:
@set -euo pipefail; \
image="$${SMOKE_TEST_IMAGE:-$(DEFAULT_SMOKE_TEST_IMAGE)}"; \
container="$${SMOKE_TEST_CONTAINER:-$(DEFAULT_SMOKE_TEST_CONTAINER)}"; \
host="$${SMOKE_TEST_HOST:-$(DEFAULT_SMOKE_TEST_HOST)}"; \
port="$${SMOKE_TEST_PORT:-$(DEFAULT_SMOKE_TEST_PORT)}"; \
admin_gui_html="$$(mktemp)"; \
redirect_headers="$$(mktemp)"; \
cleanup() { \
rm -f "$${admin_gui_html}" "$${redirect_headers}"; \
docker stop "$${container}" >/dev/null 2>&1 || true; \
docker rm "$${container}" >/dev/null 2>&1 || true; \
}; \
trap cleanup EXIT; \
docker rm -f "$${container}" >/dev/null 2>&1 || true; \
echo "Building Docker image: $${image}"; \
docker build -t "$${image}" .; \
echo "Starting container: $${container}"; \
docker run -d --name "$${container}" \
-p "$${host}:$${port}:8008" \
--env LITESTREAM_REPLICA_URL="$${SMOKE_TEST_LITESTREAM_REPLICA_URL:-DISABLED_I_KNOW_WHAT_IM_DOING}" \
--env PUBLIC_SERVER_URL="$${PUBLIC_SERVER_URL:-https://headscale.example.com}" \
--env HEADSCALE_DNS_BASE_DOMAIN="$${HEADSCALE_DNS_BASE_DOMAIN:-example.com}" \
--env CADDY_FRONTEND="$${CADDY_FRONTEND:-DISABLE_HTTPS}" \
"$${image}" >/dev/null; \
echo 'Running version checks'; \
docker exec "$${container}" headscale version; \
docker exec "$${container}" litestream version; \
docker exec "$${container}" caddy version; \
echo "Checking listener on port 8008 inside the container"; \
docker exec "$${container}" sh -c "netstat -tuln | grep ':8008 '"; \
echo "Waiting for admin GUI on http://$${host}:$${port}/admin/"; \
for attempt in $$(seq 1 30); do \
if curl --silent --show-error --fail "http://$${host}:$${port}/admin/" > "$${admin_gui_html}"; then \
break; \
fi; \
sleep 2; \
if [[ "$${attempt}" -eq 30 ]]; then \
echo 'Admin GUI did not become ready in time'; \
docker logs "$${container}"; \
exit 1; \
fi; \
done; \
echo 'Checking admin redirect and content'; \
curl --silent --show-error --fail \
--dump-header "$${redirect_headers}" \
--output /dev/null \
"http://$${host}:$${port}/admin"; \
tr -d '\r' < "$${redirect_headers}" | grep -qi '^location: /admin/$$'; \
grep -qi '<!doctype html>' "$${admin_gui_html}"; \
grep -qi 'data-sveltekit-preload-data="hover"' "$${admin_gui_html}"; \
grep -qi 'assets: "/admin"' "$${admin_gui_html}"; \
echo 'Smoke test passed'