chore(docker-dev): readiness-gated deploy.sh + Traefik access log for the first-POST 404
This commit is contained in:
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Readiness-gated headless deploy trigger for the docker-dev rig.
|
||||
#
|
||||
# Why the gate exists: the 2026-08-17 live gate saw the first
|
||||
# `POST http://localhost:9200/api/deployments` after `up -d` answer a transient 404
|
||||
# (~40 s into boot), which then cleared on a retry. That 404 can come from neither
|
||||
# end of the request: the AdminUI maps every route synchronously before RunAsync and
|
||||
# the deploy endpoint has no 404 path (503/401/422/409/500 only), and Traefik answers
|
||||
# a matched-router-with-all-servers-down as 503 "no available server". So the 404 means
|
||||
# no Traefik router matched at that instant. Polling /health/ready through :9200 until
|
||||
# it answers 200 proves both halves are in place — the router table carries the
|
||||
# otopcua-admin router AND a backend is up — before the single, un-retried POST.
|
||||
#
|
||||
# A 404 AFTER the gate is therefore new evidence, not the known boot race: it should be
|
||||
# investigated against the Traefik access log (`--accesslog=true` on the traefik service;
|
||||
# `docker compose -f docker-dev/docker-compose.yml logs traefik`), which records the
|
||||
# RouterName (empty on an unmatched request) for the failing call.
|
||||
#
|
||||
# Usage:
|
||||
# ./deploy.sh # no body (matches the bare README snippet)
|
||||
# ./deploy.sh artifact.json # send that file as the JSON request body
|
||||
# cat artifact.json | ./deploy.sh
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${OTOPCUA_ADMIN_URL:-http://localhost:9200}"
|
||||
DEPLOY_URL="$BASE_URL/api/deployments"
|
||||
READY_URL="$BASE_URL/health/ready"
|
||||
API_KEY="${OTOPCUA_DEPLOY_API_KEY:-docker-dev-deploy-key}"
|
||||
READY_TIMEOUT_SECONDS="${OTOPCUA_READY_TIMEOUT_SECONDS:-120}"
|
||||
READY_POLL_SECONDS=2
|
||||
|
||||
# --- 1. readiness gate -------------------------------------------------------
|
||||
echo "Waiting for $READY_URL (up to ${READY_TIMEOUT_SECONDS}s)..."
|
||||
deadline=$(( $(date +%s) + READY_TIMEOUT_SECONDS ))
|
||||
ready=0
|
||||
while [ "$(date +%s)" -lt "$deadline" ]; do
|
||||
code="$(curl -s -o /dev/null -w "%{http_code}" "$READY_URL" || true)"
|
||||
if [ "$code" = "200" ]; then
|
||||
ready=1
|
||||
break
|
||||
fi
|
||||
echo " not ready yet (HTTP ${code:-000}); retrying in ${READY_POLL_SECONDS}s"
|
||||
sleep "$READY_POLL_SECONDS"
|
||||
done
|
||||
|
||||
if [ "$ready" -ne 1 ]; then
|
||||
echo "ERROR: $READY_URL did not return 200 within ${READY_TIMEOUT_SECONDS}s — not posting." >&2
|
||||
echo " Check the stack is up: docker compose -f docker-dev/docker-compose.yml ps" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Ready. Posting deployment..."
|
||||
|
||||
# --- 2. the single, un-retried POST ------------------------------------------
|
||||
curl_args=(-s -w '\n%{http_code}' -X POST "$DEPLOY_URL"
|
||||
-H "X-Api-Key: $API_KEY"
|
||||
-H "Content-Type: application/json")
|
||||
|
||||
if [ "$#" -ge 1 ]; then
|
||||
[ -f "$1" ] || { echo "ERROR: body file not found: $1" >&2; exit 2; }
|
||||
curl_args+=(--data-binary "@$1")
|
||||
elif [ ! -t 0 ]; then
|
||||
curl_args+=(--data-binary @-)
|
||||
fi
|
||||
|
||||
response="$(curl "${curl_args[@]}")"
|
||||
status="$(printf '%s' "$response" | tail -n1)"
|
||||
body="$(printf '%s' "$response" | sed '$d')"
|
||||
|
||||
echo "HTTP $status"
|
||||
if [ -n "$body" ]; then printf '%s\n' "$body"; fi
|
||||
|
||||
case "$status" in
|
||||
2*)
|
||||
exit 0
|
||||
;;
|
||||
404)
|
||||
echo "ERROR: POST $DEPLOY_URL returned 404 AFTER the readiness gate passed." >&2
|
||||
echo " This is NEW evidence — the AdminUI deploy endpoint has no 404 path and a" >&2
|
||||
echo " down backend answers 503, so no Traefik router matched this request." >&2
|
||||
echo " Inspect the Traefik access log (RouterName is empty on an unmatched request):" >&2
|
||||
echo " docker compose -f docker-dev/docker-compose.yml logs traefik" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: POST $DEPLOY_URL failed with HTTP $status." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user