admin control features and admin dashboard

This commit is contained in:
ayurishchev committed 2026-08-23 20:39:22 +03:00
1 parent c630f13c57
commit 37910e410b
69 files changed
+4959 -400

No files matched your search

+172 -7
View File
@@ -6,7 +6,10 @@ JSON, базовый префикс прикладных методов — `/ap
> **Важно.** На данный момент API не защищён аутентификацией/авторизацией
> — эндпоинты доступны любому, кто может достучаться до порта control-api
> по сети. Для эксплуатации за пределами доверенного сегмента сети
> по сети. Это касается и методов из раздела
> [«Управление очередью и конфигурацией»](#управление-очередью-и-конфигурацией)
> ниже — они меняют, что и как проверяется, без подтверждения личности
> вызывающего. Для эксплуатации за пределами доверенного сегмента сети
> обязательно ограничьте доступ на уровне сети/файрвола (см.
> [SETUP.md](SETUP.md#сетевые-доступы)). Добавление bearer-токена — известное
> направление доработки, в текущей версии не реализовано.
@@ -14,12 +17,17 @@ JSON, базовый префикс прикладных методов — `/ap
Базовый URL в примерах — `http://control-api.internal:8080`, замените на
адрес вашего стенда (см. `server.listen_addr` в конфиге control-api).
> Для работы из браузера вместо `curl` есть `admin-dashboard` — веб-панель,
> дающая графический доступ ко всему административному API ниже, см.
> [DASHBOARD.md](DASHBOARD.md).
## Содержание
- [Общие соглашения](#общие-соглашения)
- [Методы для validator-agent](#методы-для-validator-agent)
- [Методы для prober](#методы-для-prober)
- [Служебные и административные методы](#служебные-и-административные-методы)
- [Управление очередью и конфигурацией](#управление-очередью-и-конфигурацией)
- [Модель состояний и связь методов с ней](#модель-состояний-и-связь-методов-с-ней)
- [Сквозной пример работы (curl)](#сквозной-пример-работы-curl)
@@ -37,9 +45,11 @@ JSON, базовый префикс прикладных методов — `/ap
не удалось распарсить, сервер молча подставит текущее время сервера — не
полагайтесь на это в продакшене, всегда передавайте валидную метку.
- `validator_id` и `site_id` в пути запроса должны совпадать со
значениями, заданными в конфиге control-api (`validators[].validator_id`,
`sites[].site_id`) — иначе методы, требующие существующую сущность,
вернут `404`.
значениями, известными control-api — заданными в `control-api.yaml`
при первом запуске (пустая база) либо созданными позже через
`/api/v1/admin/config/*` (см.
[«Управление очередью и конфигурацией»](#управление-очередью-и-конфигурацией))
— иначе методы, требующие существующую сущность, вернут `404`.
## Методы для validator-agent
@@ -297,6 +307,152 @@ IP на данном проходе". До этого момента control-api
`assigned`, `checking`, `unreachable`) и `CurrentIPID`, если валидатор
сейчас занят.
## Управление очередью и конфигурацией
Методы этого раздела — единственный способ менять состав очереди
(`ip_addresses`), список валидаторов, площадок (`sites`) и целей
проверки (`targets`/`check_types`) **без остановки процесса**: изменения
применяются немедленно и переживают последующий рестарт control-api. Все
тела запросов/ответов — `snake_case` (в отличие от `GET
/admin/status|ips|validators` выше, которые отдают сырые поля Go-структур
в PascalCase — эти два стиля сосуществуют осознанно, см. примечание к
`GET /api/v1/admin/ips/{ip}`).
**Источник истины.** `control-api.yaml` используется только как
одноразовый bootstrap для пустой базы данных: секции `validators`,
`sites`, `targets`, `check_types` читаются из YAML один раз, при самом
первом старте на пустых таблицах. Как только в соответствующей таблице
появилась хотя бы одна строка (через bootstrap либо через методы ниже) —
YAML для этой секции больше не перечитывается ни при одном последующем
рестарте; правки нужно вносить через API. Список IP-адресов
(`ip_addresses` в YAML) — исключение, он остаётся отдельным, всегда
аддитивным путём постановки в очередь при каждом старте (см.
[SETUP.md](SETUP.md#развёртывание-control-api)); он не конфликтует с
`POST /api/v1/admin/ips` ниже.
### `POST /api/v1/admin/ips`
Единая точка для двух задач: добавить новые адреса в очередь **и**
принудительно перепроверить уже завершённые — один и тот же вызов, разница
только в текущем состоянии каждого конкретного адреса. Список
обрабатывается в one transaction, в порядке следования адресов:
- адрес неизвестен control-api → добавляется в очередь как новый
(`queued`);
- адрес сейчас `done`/`failed` → принудительно перезапускается: сбрасывается
результат, `attempt_number` увеличивается, `retry_count` обнуляется,
адрес снова становится `queued`;
- адрес сейчас `queued` (ещё не взят в работу) → только переупорядочивается
под порядок текущего списка, повторно не добавляется;
- адрес сейчас активно проверяется (`assigning_fip` / `awaiting_self_check`
/ `checking` / `aggregating`) → не трогается вообще — нельзя запустить
вторую параллельную проверку одного и того же адреса.
Порядок обработки внутри одного вызова соответствует порядку адресов в
списке; повторная отправка того же списка позже даёт тот же относительный
порядок прогона.
Запрос:
```json
{"addresses": ["203.0.113.10", "203.0.113.11"]}
```
Ответ (`200`):
```json
{
"added": ["203.0.113.11"],
"requeued": ["203.0.113.10"],
"reordered": [],
"skipped_in_progress": []
}
```
`400`, если `addresses` пуст.
### `POST /api/v1/admin/ips/{ip}/cancel`
Принудительно останавливает проверку конкретного адреса, не дожидаясь
`checking_window_seconds` — работает из любого нетерминального состояния,
включая `queued` (в этом случае это просто удаление ещё не начатой
проверки из очереди). Если Floating IP уже привязан — отвязывается
(best-effort, как и при обычном завершении проверки); владеющий валидатор
освобождается. Итог записывается как `overall_result: "cancelled"`
(состояние `failed`).
Ответ: `{"ok": true}`. `404`, если адрес неизвестен. `409`, если адрес уже
в терминальном состоянии (`done`/`failed`/уже отменён) — отменять нечего.
### Валидаторы: `/api/v1/admin/config/validators`
| Метод | Путь | Тело | Успех | Ошибки |
|---|---|---|---|---|
| GET | `/api/v1/admin/config/validators` | — | `[{"validator_id","os_port_id","state"}]` | |
| POST | `/api/v1/admin/config/validators` | `{"validator_id","os_port_id"}` | `201` | `409`, если `validator_id` уже существует |
| PUT | `/api/v1/admin/config/validators/{id}` | `{"os_port_id"}` | `200` | `404` |
| DELETE | `/api/v1/admin/config/validators/{id}` | — | `200` | `404`; `409`, если валидатор сейчас владеет IP |
### Площадки: `/api/v1/admin/config/sites`
Слотов ровно три (`index` ∈ {1, 2, 3}) — это ограничение схемы БД
(`ip_queue.site{1,2,3}_complete`), а не искусственное. Пустой список слотов
— штатный сценарий, отключающий inbound-проверки целиком (см.
[USAGE.md](USAGE.md#управление-площадками-проберами)).
| Метод | Путь | Тело | Успех | Ошибки |
|---|---|---|---|---|
| GET | `/api/v1/admin/config/sites` | — | `[{"index","site_id"}]` (до 3 строк) | |
| PUT | `/api/v1/admin/config/sites/{index}` | `{"site_id"}` | `200` | `400`, если `index` не 1..3; `409`, если `site_id` уже занят другим слотом |
| DELETE | `/api/v1/admin/config/sites/{index}` | — | `200` | `404` |
### Группы целей: `/api/v1/admin/config/targets`
Группа целей — именованный список URL/адресов (например `stub-targets: [
"https://hub.docker.com", ...]`), на который затем ссылаются типы
проверок.
| Метод | Путь | Тело | Успех | Ошибки |
|---|---|---|---|---|
| GET | `/api/v1/admin/config/targets` | — | `[{"name","targets"}]` | |
| PUT | `/api/v1/admin/config/targets/{group}` | `{"targets":[...]}` | `200` | `400`, если список пуст |
| DELETE | `/api/v1/admin/config/targets/{group}` | — | `200` | `404`; `409`, если группа используется каким-то `check_type` |
### Типы проверок: `/api/v1/admin/config/check-types`
Тип проверки (`https`, `icmp`, `ssh`, ...) ссылается на одну или несколько
групп целей по имени; именно развёрнутый список отсюда validator-agent
получает в `check_config` при `GET /api/v1/agents/{id}/assignment`.
| Метод | Путь | Тело | Успех | Ошибки |
|---|---|---|---|---|
| GET | `/api/v1/admin/config/check-types` | — | `[{"name","enabled","targets"}]` (`targets` — имена групп) | |
| PUT | `/api/v1/admin/config/check-types/{name}` | `{"enabled","targets":["group",...]}` | `200` | `400`, если названа несуществующая группа |
| DELETE | `/api/v1/admin/config/check-types/{name}` | — | `200` | `404` |
### Пример: конфигурация целиком через API, без единой строки в YAML
```bash
BASE=http://127.0.0.1:8080
curl -s -X POST "$BASE/api/v1/admin/config/validators" \
-d '{"validator_id":"validator_01","os_port_id":"port-abc123"}'
curl -s -X PUT "$BASE/api/v1/admin/config/targets/web" \
-d '{"targets":["https://hub.docker.com","https://github.com"]}'
curl -s -X PUT "$BASE/api/v1/admin/config/check-types/https" \
-d '{"enabled":true,"targets":["web"]}'
curl -s -X PUT "$BASE/api/v1/admin/config/sites/1" -d '{"site_id":"site-1"}'
# Поставить адрес в очередь и, отдельным вызовом позже, принудительно
# перепроверить его ещё раз — тот же метод, разница только в состоянии:
curl -s -X POST "$BASE/api/v1/admin/ips" -d '{"addresses":["203.0.113.10"]}'
curl -s -X POST "$BASE/api/v1/admin/ips" -d '{"addresses":["203.0.113.10"]}' # forced recheck
# Остановить проверку, не дожидаясь checking_window_seconds:
curl -s -X POST "$BASE/api/v1/admin/ips/203.0.113.10/cancel"
```
## Модель состояний и связь методов с ней
```
@@ -327,9 +483,18 @@ queued ──(control-api сам, без вызова API)──▶ assigning_fi
нет — это фоновый цикл (`Tick`), а не запрос/ответ.
Площадки (`siteN_complete`) — опциональны: сколько их учитывается,
целиком определяется списком `sites` в конфиге control-api (0–3 записи).
Пустой список — агрегация ждёт только `egress_complete`, ни одна площадка
не требуется. Подробнее — [USAGE.md](USAGE.md#управление-площадками-проберами).
целиком определяется текущим списком `sites` (0–3 записи, управляется
через `/api/v1/admin/config/sites` — см.
[выше](#управление-очередью-и-конфигурацией)). Пустой список — агрегация
ждёт только `egress_complete`, ни одна площадка не требуется. Подробнее —
[USAGE.md](USAGE.md#управление-площадками-проберами).
Два дополнительных перехода, оба инициируются оператором через
`/api/v1/admin/ips`, а не самим оркестратором:
- **любое нетерминальное состояние → `failed` (`overall_result:
"cancelled"`)** — `POST /api/v1/admin/ips/{ip}/cancel`;
- **`done`/`failed` → `queued` (новая попытка)** — `POST
/api/v1/admin/ips` с уже завершённым адресом в списке.
## Сквозной пример работы (curl)