Separates three failures that are indistinguishable from a browser, and which
this deployment hit in sequence:
502 on every route the container is not running - it exited at startup,
so nothing reached the app and no route is special
200 + database:false the API is healthy, Postgres is not
200 + database:true working
It also catches AUTH_ALLOW_ANY_LOGIN being left on, by asserting that a
deliberately wrong password is rejected. That setting is a convenience locally
and a total auth bypass on a published host, and nothing else about a running
deployment looks different when it is true.
./scripts/check_deploy.sh
./scripts/check_deploy.sh http://localhost:3000
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
83 lines
3.5 KiB
Bash
Executable File
83 lines
3.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Post-deploy smoke test.
|
|
#
|
|
# ./scripts/check_deploy.sh # https://mcp.nearle.ai.in
|
|
# ./scripts/check_deploy.sh http://localhost:3000 # a local container
|
|
#
|
|
# Separates the three failures that all look alike from a browser:
|
|
# 502 everywhere - the container is not running (it exited at startup)
|
|
# 200 + database:false - the API is fine, Postgres is not
|
|
# 200 + database:true - working
|
|
set -uo pipefail
|
|
|
|
BASE="${1:-https://mcp.nearle.ai.in}"
|
|
pass=0; fail=0
|
|
|
|
hit() { curl -sS -m 20 -o /tmp/_body -w '%{http_code}' "$BASE$1" 2>/dev/null || echo 000; }
|
|
|
|
check() { # path expected label
|
|
local code; code=$(hit "$1")
|
|
if [ "$code" = "$2" ]; then printf ' \033[32mPASS\033[0m %-16s %s\n' "$1" "$3"; pass=$((pass+1))
|
|
else printf ' \033[31mFAIL\033[0m %-16s expected %s, got %s\n' "$1" "$2" "$code"; fail=$((fail+1)); fi
|
|
}
|
|
|
|
echo "Checking $BASE"
|
|
echo
|
|
echo "Is the container running at all?"
|
|
code=$(hit /)
|
|
if [ "$code" = "502" ] || [ "$code" = "000" ]; then
|
|
echo " FAIL / -> $code"
|
|
echo
|
|
echo " The container is not serving. It almost certainly exited at startup."
|
|
echo " Read the Dokploy logs; settings.py names the missing value explicitly."
|
|
echo " Confirm the image actually contains /app/.env - the build log must show"
|
|
echo " a 'COPY .env .' step, and .dockerignore must not list .env."
|
|
exit 1
|
|
fi
|
|
printf ' \033[32mPASS\033[0m %-16s container is up\n' "/"
|
|
pass=$((pass+1))
|
|
|
|
echo
|
|
echo "Routes that must not depend on the database:"
|
|
check /docs 200 "interactive API docs"
|
|
check /openapi.json 200 "OpenAPI schema"
|
|
check /api/health 200 "health endpoint answers"
|
|
|
|
echo
|
|
echo "Dependencies (reported in the body; 'false' does not mean the API is broken):"
|
|
health=$(curl -sS -m 20 "$BASE/api/health" 2>/dev/null)
|
|
db=$(printf '%s' "$health" | sed -n 's/.*"database":\([a-z]*\).*/\1/p')
|
|
ol=$(printf '%s' "$health" | sed -n 's/.*"ollama":\([a-z]*\).*/\1/p')
|
|
if [ "$db" = "true" ]; then printf ' \033[32mPASS\033[0m database connected\n'; pass=$((pass+1))
|
|
else
|
|
printf ' \033[33mWARN\033[0m database NOT connected\n'
|
|
echo " The API works; catalog pages will be empty. Check DB_HOST/DB_USER/"
|
|
echo " DB_PASSWORD/DB_NAME. A wrong password logs 'password authentication"
|
|
echo " failed' rather than a timeout."
|
|
fi
|
|
[ "$ol" = "true" ] \
|
|
&& printf ' \033[32mPASS\033[0m ollama connected\n' \
|
|
|| printf ' \033[33mWARN\033[0m ollama not connected (/api/chat unavailable; expected if USE_OLLAMA=false)\n'
|
|
|
|
echo
|
|
echo "Auth:"
|
|
code=$(curl -sS -m 20 -o /dev/null -w '%{http_code}' -X POST "$BASE/api/auth/login" \
|
|
-H 'Content-Type: application/json' -d '{"username":"admin","password":"definitely-not-the-password"}' 2>/dev/null)
|
|
if [ "$code" = "401" ]; then printf ' \033[32mPASS\033[0m wrong password rejected (401)\n'; pass=$((pass+1))
|
|
elif [ "$code" = "200" ]; then
|
|
printf ' \033[31mFAIL\033[0m a WRONG PASSWORD WAS ACCEPTED - AUTH_ALLOW_ANY_LOGIN is true.\n'
|
|
echo " Anyone who finds this host can sign in as admin. Set it to false."
|
|
fail=$((fail+1))
|
|
else printf ' \033[31mFAIL\033[0m login endpoint returned %s\n' "$code"; fail=$((fail+1)); fi
|
|
|
|
echo
|
|
echo "MCP:"
|
|
code=$(curl -sS -m 20 -o /dev/null -w '%{http_code}' "$BASE/api/mcp/info" 2>/dev/null)
|
|
[ "$code" = "401" ] \
|
|
&& { printf ' \033[32mPASS\033[0m /api/mcp/info guarded (401 without a token)\n'; pass=$((pass+1)); } \
|
|
|| printf ' \033[33mWARN\033[0m /api/mcp/info returned %s (expected 401)\n' "$code"
|
|
|
|
echo
|
|
echo "-------- $pass passed, $fail failed --------"
|
|
[ "$fail" -eq 0 ]
|