From 72045afcdeb86aa9ae5adeb1447dd9b58c5a6fa5 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Wed, 8 Jul 2026 21:00:27 +0700 Subject: [PATCH] feat(firewall): improve error handling and documentation for firewall enrollment Enhanced the firewall enrollment process by implementing better error handling for HTTP responses, specifically addressing database schema issues. Updated the documentation to include migration requirements for PostgreSQL and clarified the steps to take if enrollment fails due to an outdated schema. This ensures users are better informed about necessary actions during deployment. --- docs/firewall.md | 2 ++ internal/firewallscripts/install.sh | 10 +++++++++- internal/httpapi/routes_crud.go | 22 ++++++++++++++++++++++ internal/httpapi/routes_firewall.go | 2 +- scripts/firewall/install.sh | 10 +++++++++- 5 files changed, 43 insertions(+), 3 deletions(-) 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