From 1a86d6c7434c27d9d6f58dd61ed3c8a4b9d788f1 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Sun, 5 Apr 2026 13:10:38 +0700 Subject: [PATCH] docs: update EvoBGP architecture plan with detailed REST API section, including general agreements, system endpoints, and prefix module specifications. Enhanced clarity on API structure and added references to OpenAPI documentation. --- .../plans/evobgp_архитектура_0e73ef02.plan.md | 301 +++++++++++++++++- docs/evobgp-api-sketches.md | 2 +- 2 files changed, 292 insertions(+), 11 deletions(-) diff --git a/.cursor/plans/evobgp_архитектура_0e73ef02.plan.md b/.cursor/plans/evobgp_архитектура_0e73ef02.plan.md index bb30ea7..5d4cfe5 100644 --- a/.cursor/plans/evobgp_архитектура_0e73ef02.plan.md +++ b/.cursor/plans/evobgp_архитектура_0e73ef02.plan.md @@ -1,6 +1,6 @@ --- name: EvoBGP архитектура -overview: Оба профиля по умолчанию на PostgreSQL (один движок, одни миграции). reference — микросервисы + PG + брокер; microVPS — evobgp-all + контейнер PG (жёсткий тюнинг). SQLite — опция только для одного контейнера без PG. +overview: Оба профиля по умолчанию на PostgreSQL (один движок, одни миграции). reference — микросервисы + PG + брокер; microVPS — evobgp-all + контейнер PG (жёсткий тюнинг). SQLite — опция только для одного контейнера без PG. REST API проработан в §7 плана (пути /v1, jobs, бандлы нод); OpenAPI — канон при появлении схемы. todos: - id: schema-db content: "Схема БД: PostgreSQL (основной); опционально SQLite (microVPS single-container); модули, ревизии, jobs" @@ -54,6 +54,21 @@ Control-plane на **Go**, анонс префиксов через **BIRD**, п 5. [База данных, ETL, ER-схема](#5-база-данных-etl-er-схема) 6. [Модули префиксов и FQDN](#6-модули-префиксов-as-cdn-домены) 7. [REST API](#7-rest-api) + - [7.1. Общие соглашения](#71-общие-соглашения) + - [7.2. Системные и служебные](#72-системные-и-служебные) + - [7.3. Модули префиксов](#73-модули-префиксов-module) + - [7.4. DoH-профили](#74-doh-профили-doh_profile) + - [7.5. BGP community](#75-bgp-community-bgp_community) + - [7.6. Пиры](#76-пиры-bgp_peer) + - [7.7. Спикеры BIRD](#77-спикеры-bird-bgp_speaker) + - [7.8. Ревизии конфигурации](#78-ревизии-конфигурации-config_revision) + - [7.9. Применение конфигурации](#79-применение-конфигурации-deploy-bird) + - [7.10. Задачи](#710-задачи-job_audit) + - [7.11. Реплики и бандлы](#711-реплики-evobgp-node-бандлы) + - [7.12. Глобальные настройки](#712-глобальные-настройки-опционально) + - [7.13. Матрица прав](#713-матрица-прав-роли) + - [7.14. Следующие итерации](#714-следующие-итерации-api) + - [7.15. Связь API с разделами плана](#715-связь-api-с-разделами-плана) 8. [Генерация BIRD и ревизии](#8-генерация-bird-и-ревизии) 9. [Эксплуатация и масштаб пиров](#9-эксплуатация-и-масштаб) 10. [Реплика evobgp-node](#10-реплика-evobgp-node) @@ -460,15 +475,281 @@ erDiagram ## 7. REST API -Наброски путей, ролей и контрактов вынесены в **[docs/evobgp-api-sketches.md](docs/evobgp-api-sketches.md)** (`/v1`, задачи, бандлы нод). +**Статус:** черновик для согласования и реализации; каноничная машиночитаемая форма — **[docs/openapi.yaml](docs/openapi.yaml)** (по мере заполнения). Ниже — **единая проработка** путей и семантики (консолидация с [docs/evobgp-api-sketches.md](docs/evobgp-api-sketches.md)). Базовый префикс: `**/v1`**. Термины: [§3](#3-логическая-модель-общая-для-обоих-профилей), [§5](#5-база-данных-etl-er-схема), [§6](#6-модули-префиксов-as-cdn-домены). -- `POST /modules/{id}/refresh` -- `POST /apply`, `POST /speakers/{id}/apply` или `/bird/apply` -- `GET /revisions`, `POST /revisions/{id}/rollback` -- `POST|PATCH /peers`, CRUD DoH / community / модулей -- Реплики: `GET /v1/speakers/{id}/bundle/{revision}`, `GET .../revisions/latest`, enrollment нод -- Асинхронно: **202** + `job_id`, `GET /jobs/{id}` -- Auth: API keys / mTLS +### 7.1. Общие соглашения + + +| Тема | Решение (набросок) | +| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | +| **Аутентификация** | Заголовок `Authorization: Bearer ` или mTLS на edge; ключи привязаны к tenant и роли. | +| **Multi-tenant** | Все сущности в скоупе tenant: либо из ключа, либо явный префикс `X-Tenant-Id` (только для супер-ролей). | +| **Идентификаторы** | UUID v7 или ULID в URL; в JSON — строки. | +| **Время** | ISO 8601 UTC (`2026-04-03T12:00:00Z`). | +| **Ошибки** | Тело `application/problem+json` (RFC 9457): `type`, `title`, `status`, `detail`, `instance`, опционально `errors[]` по полям. | +| **Идемпотентность** | Для мутаций, создающих задачи или побочные эффекты: заголовок `Idempotency-Key` (опционально обязателен для `POST` apply/refresh). | +| **Пагинация** | `?cursor=&limit=50` (cursor-based); ответ: `items`, `next_cursor`, `has_more`. | +| **Асинхронные операции** | `202 Accepted`, заголовок `Location: /v1/jobs/{job_id}`; тело `{ "job_id", "status": "queued" }`. | +| **Версионирование** | Несовместимые изменения — новый префикс `/v2`. | + + +### 7.2. Системные и служебные + + +| Метод | Путь | Назначение | +| ----- | ------------- | ------------------------------------------------------------ | +| `GET` | `/v1/health` | Liveness (процесс жив). | +| `GET` | `/v1/ready` | Readiness (БД, брокер при reference, и т.д.). | +| `GET` | `/v1/version` | Версия сборки API и control-plane (`git_sha`, `build_time`). | + + +### 7.3. Модули префиксов (`module`) + +**Связь с продуктом.** В продуктовой формулировке **ASN / CDN / DOMAINS / IP-диапазоны** — это **четыре вида модулей**. Поле `**type`** в `POST /v1/modules` выбирает вид: `AS_PREFIXES`, `CDN_CIDRS`, `DOMAINS` или `**IP_RANGES`**. Дочерние ресурсы: `**as-entries`**, `**cdn-sources**`, `**domain-entries**`; `**ip-range-entries**` — статические **CIDR + `community_id`** (без ASN и без URL). `**module_id**` в пути — идентификатор **конкретного экземпляра** модуля, а не имя типа (согласовано с [§6](#6-модули-префиксов-as-cdn-домены)). + + +| Метод | Путь | Описание | +| -------- | ------------------------- | ----------------------------------------------------------------- | +| `GET` | `/v1/modules` | Список модулей tenant (фильтры: `?type=`, `?enabled=`). | +| `POST` | `/v1/modules` | Создать модуль. | +| `GET` | `/v1/modules/{module_id}` | Детали модуля. | +| `PATCH` | `/v1/modules/{module_id}` | Частичное обновление (расписание, DoH, приоритет, `enabled`). | +| `DELETE` | `/v1/modules/{module_id}` | Мягкое удаление или `enabled=false` — зафиксировать в реализации. | + + +**CDN-источники модуля** + + +| Метод | Путь | Описание | +| -------- | ------------------------------------------------- | --------------------------------------------------------------------- | +| `GET` | `/v1/modules/{module_id}/cdn-sources` | Список строк CDN. | +| `POST` | `/v1/modules/{module_id}/cdn-sources` | Добавить источник (URL + `source_kind` + опционально `community_id`). | +| `PATCH` | `/v1/modules/{module_id}/cdn-sources/{source_id}` | URL, `source_kind`, `community_id`, свой `refresh_interval_sec`. | +| `DELETE` | `/v1/modules/{module_id}/cdn-sources/{source_id}` | Удалить. | + + +**Записи AS / домены** + + +| Метод | Путь | Описание | +| -------- | --------------------------------------------------- | --------------------- | +| `GET` | `/v1/modules/{module_id}/as-entries` | Список ASN/префиксов. | +| `POST` | `/v1/modules/{module_id}/as-entries` | Добавить. | +| `PATCH` | `/v1/modules/{module_id}/as-entries/{entry_id}` | Обновить. | +| `DELETE` | `/v1/modules/{module_id}/as-entries/{entry_id}` | Удалить. | +| `GET` | `/v1/modules/{module_id}/domain-entries` | FQDN + community. | +| `POST` | `/v1/modules/{module_id}/domain-entries` | Добавить. | +| `PATCH` | `/v1/modules/{module_id}/domain-entries/{entry_id}` | Обновить. | +| `DELETE` | `/v1/modules/{module_id}/domain-entries/{entry_id}` | Удалить. | + + +**Записи IP-диапазонов** (только для `type: IP_RANGES`) + + +| Метод | Путь | Описание | +| -------- | ----------------------------------------------------- | ------------------------------------ | +| `GET` | `/v1/modules/{module_id}/ip-range-entries` | Список CIDR + `community_id`. | +| `POST` | `/v1/modules/{module_id}/ip-range-entries` | Добавить (`prefix`, `community_id`). | +| `PATCH` | `/v1/modules/{module_id}/ip-range-entries/{entry_id}` | Обновить. | +| `DELETE` | `/v1/modules/{module_id}/ip-range-entries/{entry_id}` | Удалить. | + + +**Refresh (ingest)** + + +| Метод | Путь | Описание | +| ------ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `POST` | `/v1/modules/{module_id}/refresh` | Запуск ingest для модуля (CDN / DoH / AS по типу). Для `**IP_RANGES`** обычно **не требуется** (данные только в БД); возможен `**204`** / no-op или `**400`**, если тип не поддерживает refresh — зафиксировать в реализации. | + + +**Пример тела создания модуля (набросок)** + +```json +{ + "type": "CDN_CIDRS", + "name": "edge-v4", + "enabled": true, + "priority": 10, + "doh_profile_id": null, + "refresh_interval_sec": 3600, + "cron_expr": null, + "default_community_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +Пример модуля `**IP_RANGES**`: `type: "IP_RANGES"`, `doh_profile_id: null`, далее строки через `ip-range-entries` с полями `prefix` (например `203.0.113.0/24`) и `community_id`. + +### 7.4. DoH-профили (`doh_profile`) + + +| Метод | Путь | Описание | +| -------- | ----------------------- | ------------------------------------------------------------------------------------- | +| `GET` | `/v1/doh-profiles` | Список. | +| `POST` | `/v1/doh-profiles` | Создать (URL, таймауты; секрет — ссылка на vault id или отдельный `POST .../secret`). | +| `GET` | `/v1/doh-profiles/{id}` | Детали (без раскрытия секрета). | +| `PATCH` | `/v1/doh-profiles/{id}` | Обновить. | +| `DELETE` | `/v1/doh-profiles/{id}` | Удалить, если не используется модулями. | + + +### 7.5. BGP community (bgp_community) + + +| Метод | Путь | Описание | +| -------- | ---------------------- | ------------------------------ | +| `GET` | `/v1/communities` | Список справочника. | +| `POST` | `/v1/communities` | Создать. | +| `GET` | `/v1/communities/{id}` | Детали. | +| `PATCH` | `/v1/communities/{id}` | Обновить. | +| `DELETE` | `/v1/communities/{id}` | Удалить при отсутствии ссылок. | + + +### 7.6. Пиры (`bgp_peer`) + + +| Метод | Путь | Описание | +| -------- | ---------------- | ------------------------------------------------------------------------------ | +| `GET` | `/v1/peers` | Список (`?speaker_id=`, пагинация). | +| `POST` | `/v1/peers` | Создать пира. | +| `GET` | `/v1/peers/{id}` | Детали. | +| `PATCH` | `/v1/peers/{id}` | Политики, neighbor, ASN, привязка к `bgp_speaker_id` или `null` = все спикеры. | +| `DELETE` | `/v1/peers/{id}` | Удалить / отключить. | + + +### 7.7. Спикеры BIRD (`bgp_speaker`) + + +| Метод | Путь | Описание | +| ------- | ------------------- | ------------------------------------------ | +| `GET` | `/v1/speakers` | Список (master / replica, endpoint). | +| `POST` | `/v1/speakers` | Зарегистрировать спикер (реплика, canary). | +| `GET` | `/v1/speakers/{id}` | Детали + `last_applied_revision_id`. | +| `PATCH` | `/v1/speakers/{id}` | Метаданные, endpoint. | + + +### 7.8. Ревизии конфигурации (config_revision) + + +| Метод | Путь | Описание | +| ------ | -------------------------------------- | --------------------------------------------------------------- | +| `GET` | `/v1/revisions` | История ревизий (`?module_id=`, `?limit=`). | +| `GET` | `/v1/revisions/{revision_id}` | Метаданные: хэш, родитель, время, артефакты. | +| `GET` | `/v1/revisions/{revision_id}/prefixes` | Материализованный снимок префиксов (пагинация). | +| `GET` | `/v1/revisions/{revision_id}/preview` | Превью фрагментов BIRD (read-only, без apply). | +| `POST` | `/v1/revisions/{revision_id}/rollback` | Создать **новую** ревизию с содержимым отката; часто `**202`**. | + + +**Сравнение ревизий (набросок)** + + +| Метод | Путь | Описание | +| ----- | ---------------------------- | ----------------------------------------------------------------------------- | +| `GET` | `/v1/revisions/{a}/diff/{b}` | Diff префиксов / метаданных (формат зафиксировать: JSON patch или табличный). | + + +### 7.9. Применение конфигурации (deploy, BIRD) + + +| Метод | Путь | Описание | +| ------ | ------------------------- | --------------------------------------------------------------------------------------------- | +| `POST` | `/v1/apply` | Применить текущую целевую ревизию на всех спикерах (или по политике по умолчанию). `**202`**. | +| `POST` | `/v1/speakers/{id}/apply` | Применить на одном спикере (canary). `**202`**. | +| `POST` | `/v1/bird/reload` | Опционально: явный мягкий reload политики (если отделён от apply); иначе часть `apply`. | + + +**Тело `POST /v1/apply` (набросок, опционально)** + +```json +{ + "revision_id": "01JQXYZ...", + "strategy": "all_speakers", + "dry_run": false +} +``` + +Связь с безопасным применением: [§13](#13-снижение-рисков-меры-и-процессы) (двухфазный deploy, LKG). + +### 7.10. Задачи (job_audit) + + +| Метод | Путь | Описание | +| ------ | -------------------------- | --------------------------------------------- | +| `GET` | `/v1/jobs` | Список задач (`?status=`, `?kind=`, cursor). | +| `GET` | `/v1/jobs/{job_id}` | Статус, прогресс, ошибка, связанные сущности. | +| `POST` | `/v1/jobs/{job_id}/cancel` | Запрос отмены (best-effort). | + + +**Пример ответа `GET /v1/jobs/{id}`** + +```json +{ + "job_id": "01JQXYZ...", + "kind": "module_refresh", + "status": "running", + "idempotency_key": "client-abc-123", + "created_at": "2026-04-03T10:00:00Z", + "started_at": "2026-04-03T10:00:01Z", + "finished_at": null, + "error": null, + "meta": { "module_id": "01JQM..." } +} +``` + +### 7.11. Реплики evobgp-node: бандлы + +Вызываются **нодой** с отдельным ключом / mTLS (роль `node`). См. также [§10](#10-реплика-evobgp-node). + + +| Метод | Путь | Описание | +| ------ | ------------------------------------------------ | -------------------------------------------------------------------------------------- | +| `GET` | `/v1/speakers/{speaker_id}/revisions/latest` | Указатель на последнюю опубликованную ревизию для ноды. | +| `GET` | `/v1/speakers/{speaker_id}/bundle/{revision_id}` | Скачивание подписанного бандла (архив + `manifest.json` + подпись). | +| `POST` | `/v1/nodes/enroll` | Регистрация ноды (обмен ключами, привязка к `speaker_id`) — детали протокола отдельно. | + + +Заголовки для бандла: `Content-Type: application/octet-stream` или multipart; целостность по `manifest` (SHA-256) и подписи (например Ed25519) — обязательно на ноде в production ([§13](#13-снижение-рисков-меры-и-процессы)). + +### 7.12. Глобальные настройки (опционально) + + +| Метод | Путь | Описание | +| ------- | -------------- | ------------------------------------------------------- | +| `GET` | `/v1/settings` | KV вроде `global_settings` (лимиты CDN, feature flags). | +| `PATCH` | `/v1/settings` | Частичное обновление (только роль operator). | + + +### 7.13. Матрица прав (роли) + + +| Ресурс | `viewer` | `editor` | `operator` | `node` | +| -------------------------- | -------- | -------- | ---------- | ------ | +| GET модули, ревизии, peers | да | да | да | нет* | +| PATCH модули, peers | нет | да | да | нет | +| apply, rollback | нет | нет | да | нет | +| bundle / enroll | нет | нет | нет | да | + + +Нода не ходит в общий CRUD; только [§7.11](#711-реплики-evobgp-node-бандлы). + +### 7.14. Следующие итерации API + +- Полная **OpenAPI 3.1** в `docs/openapi.yaml` по этому разделу. +- Webhooks: `POST` на URL клиента по завершении `job` (опционально). +- SSE/WebSocket для стрима статуса долгих jobs. +- Rate limits по ключу и по tenant в ответах (`RateLimit-*` заголовки). + +### 7.15. Связь API с разделами плана + + +| Тема плана | Подраздел §7 | +| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| REST, jobs, идемпотентность | [7.1](#71-общие-соглашения), [7.10](#710-задачи-job_audit) | +| refresh, apply, rollback, preview, `IP_RANGES` | [7.3](#73-модули-префиксов-module), [7.8](#78-ревизии-конфигурации-config_revision), [7.9](#79-применение-конфигурации-deploy-bird) | +| peers, speakers, communities, DoH | [7.4](#74-doh-профили-doh_profile)–[7.7](#77-спикеры-bird-bgp_speaker) | +| bundle API, нода | [7.11](#711-реплики-evobgp-node-бандлы) | +| Контрактные тесты HTTP | [§14](#14-тестирование-bird2-и-матрица-сценариев) + `openapi.yaml` | + + +Черновик в репозитории [docs/evobgp-api-sketches.md](docs/evobgp-api-sketches.md) держать **синхронным** с §7 при согласовании изменений (или пометить файл указателем на план как единый источник). --- @@ -644,7 +925,7 @@ Raft, HTTP/`gorqlite`; для HA control-plane; не замена дефолтн 1. **Unit / snapshot (Go):** пакет `internal/birdfmt` — вход из фикстур, выход сравнивается с `*.golden` или `txtar`. 2. **Синтаксис BIRD в CI:** для каждого сценария с `bird.conf` выполняется `**bird -c … -p`** в контейнере с **той же major/minor версией BIRD 2**, что в production ([§13](#13-снижение-рисков-меры-и-процессы)). -3. **Позже:** контрактные тесты HTTP по `docs/openapi.yaml` после реализации `internal/httpapi`. +3. **Позже:** контрактные тесты HTTP по `docs/openapi.yaml` и сценариям из [§7](#7-rest-api) после реализации `internal/httpapi`. --- diff --git a/docs/evobgp-api-sketches.md b/docs/evobgp-api-sketches.md index 3cc05d2..702fc77 100644 --- a/docs/evobgp-api-sketches.md +++ b/docs/evobgp-api-sketches.md @@ -267,4 +267,4 @@ | peers, speakers, communities, DoH | §4 - §7 | | bundle API, нода | §11 | -Файл плана: `.cursor/plans/evobgp_архитектура_0e73ef02.plan.md` (§6 REST API). +Файл плана: `.cursor/plans/evobgp_архитектура_0e73ef02.plan.md` (§7 REST API).