diff --git a/docs/firewall.md b/docs/firewall.md index 3ee2121..daa4987 100644 --- a/docs/firewall.md +++ b/docs/firewall.md @@ -23,6 +23,8 @@ Публичные URL (без API-ключа, вне `WEBUI_IP_WHITELIST` Traefik): `GET /v1/firewall/install.sh`, `GET /v1/firewall/sync-script`, `POST /v1/firewall/enroll`. Всегда **HTTPS**. +Требуется миграция **`000027_firewall`** в PostgreSQL (применяется при старте API с актуальным бинарём). Если enroll отвечает `503` / `database schema outdated` — перезапустите `evobgp-api` / `evobgp-all` после деплоя новой версии. + ```bash curl -fsSL https:///v1/firewall/install.sh | \ EVOBGP_CP_URL=https:// \ diff --git a/internal/firewallscripts/install.sh b/internal/firewallscripts/install.sh index 367446f..93130d1 100644 --- a/internal/firewallscripts/install.sh +++ b/internal/firewallscripts/install.sh @@ -38,10 +38,18 @@ CP_URL="${EVOBGP_CP_URL%/}" ENROLL_BODY=$(printf '{"name":"%s","hostname":"%s","client_token":"%s","client_version":"install.sh/1"}' \ "$EVOBGP_CLIENT_NAME" "$HOSTNAME" "$CLIENT_TOKEN") -RESP=$(curl -fsS -X POST "${CP_URL}/v1/firewall/enroll" \ +ENROLL_TMP=$(mktemp) +trap 'rm -f "$ENROLL_TMP"' EXIT +ENROLL_CODE=$(curl -sS -o "$ENROLL_TMP" -w "%{http_code}" -X POST "${CP_URL}/v1/firewall/enroll" \ -H "Content-Type: application/json" \ -H "X-EvoBGP-Seed: ${EVOBGP_SEED}" \ -d "$ENROLL_BODY") +if [[ "$ENROLL_CODE" != "201" ]]; then + echo "evobgp-firewall enroll failed: HTTP ${ENROLL_CODE} from ${CP_URL}/v1/firewall/enroll" >&2 + cat "$ENROLL_TMP" >&2 + exit 1 +fi +RESP=$(cat "$ENROLL_TMP") CLIENT_ID="" if command -v jq >/dev/null 2>&1; then diff --git a/internal/httpapi/routes_crud.go b/internal/httpapi/routes_crud.go index e928963..ff38314 100644 --- a/internal/httpapi/routes_crud.go +++ b/internal/httpapi/routes_crud.go @@ -16,6 +16,8 @@ import ( "evobgp/internal/pipeline" "evobgp/internal/runtimelogs" "evobgp/internal/store" + + "github.com/jackc/pgx/v5/pgconn" ) func (s *Server) registerCRUDRoutes(m *http.ServeMux) { @@ -188,9 +190,29 @@ func writeStoreErr(w http.ResponseWriter, err error) { writeProblem(w, http.StatusUnprocessableEntity, "Unprocessable Entity", invalidInputDetail) return } + if writePostgresStoreErr(w, err) { + return + } writeInternalError(w, "store", err) } +func writePostgresStoreErr(w http.ResponseWriter, err error) bool { + var pgErr *pgconn.PgError + if !errors.As(err, &pgErr) { + return false + } + switch pgErr.Code { + case "42P01": + writeProblem(w, http.StatusServiceUnavailable, "Service Unavailable", + "database schema outdated; restart API after deploy or apply migration 000027_firewall") + return true + case "23505": + writeProblem(w, http.StatusConflict, "Conflict", "resource already exists") + return true + } + return false +} + func (s *Server) handleListCDNSources(w http.ResponseWriter, r *http.Request) { a, ok := authFromContext(r.Context()) if !ok || !s.requireAtLeast(w, a, "viewer") { diff --git a/internal/httpapi/routes_firewall.go b/internal/httpapi/routes_firewall.go index 51a95a5..f2dee27 100644 --- a/internal/httpapi/routes_firewall.go +++ b/internal/httpapi/routes_firewall.go @@ -123,7 +123,7 @@ func (s *Server) handleFirewallEnrollPublic(w http.ResponseWriter, r *http.Reque writeProblem(w, http.StatusConflict, "Conflict", "client token already enrolled") return } - writeInternalError(w, "internal", err) + writeStoreErr(w, err) return } writeJSON(w, http.StatusCreated, map[string]any{ diff --git a/scripts/firewall/install.sh b/scripts/firewall/install.sh index 367446f..93130d1 100644 --- a/scripts/firewall/install.sh +++ b/scripts/firewall/install.sh @@ -38,10 +38,18 @@ CP_URL="${EVOBGP_CP_URL%/}" ENROLL_BODY=$(printf '{"name":"%s","hostname":"%s","client_token":"%s","client_version":"install.sh/1"}' \ "$EVOBGP_CLIENT_NAME" "$HOSTNAME" "$CLIENT_TOKEN") -RESP=$(curl -fsS -X POST "${CP_URL}/v1/firewall/enroll" \ +ENROLL_TMP=$(mktemp) +trap 'rm -f "$ENROLL_TMP"' EXIT +ENROLL_CODE=$(curl -sS -o "$ENROLL_TMP" -w "%{http_code}" -X POST "${CP_URL}/v1/firewall/enroll" \ -H "Content-Type: application/json" \ -H "X-EvoBGP-Seed: ${EVOBGP_SEED}" \ -d "$ENROLL_BODY") +if [[ "$ENROLL_CODE" != "201" ]]; then + echo "evobgp-firewall enroll failed: HTTP ${ENROLL_CODE} from ${CP_URL}/v1/firewall/enroll" >&2 + cat "$ENROLL_TMP" >&2 + exit 1 +fi +RESP=$(cat "$ENROLL_TMP") CLIENT_ID="" if command -v jq >/dev/null 2>&1; then