# Диаграммы потоков данных
Три диаграммы, поясняющие устройство системы на уровне потоков данных —
дополнение к [API.md](API.md) (протокол) и [USAGE.md](USAGE.md) (работа
оператора). Диаграммы — в формате Mermaid, рендерятся нативно на
GitHub/GitLab и в большинстве современных Markdown-просмотрщиков.
## Как читать диаграммы
Единое условное обозначение стрелок для всех диаграмм:
| Обозначение | Значение |
|---|---|
| `-->` тонкая сплошная | Управляющий вызов / API-запрос |
| `-.->` пунктирная | Ответ, уведомление, асинхронный результат |
| `==>` жирная сплошная | Реальный сетевой трафик проверки (data-plane) — именно то, что валидируется, а не служебный вызов |
## Содержание
- [1. Control plane сервиса](#1-control-plane-сервиса)
- [2. Поток данных: от валидатора к целевому серверу (egress-проверка)](#2-поток-данных-от-валидатора-к-целевому-серверу-egress-проверка)
- [3. Поток данных телеметрии](#3-поток-данных-телеметрии)
---
## 1. Control plane сервиса
Кто кем управляет и через какой канал: конфигурация оператора, HTTP API
control-api, фоновый оркестратор, база данных, вызовы к OpenStack и опрос
со стороны validator-agent/prober.
```mermaid
flowchart TB
subgraph OP["Оператор"]
CFG["control-api.yaml
(bootstrap пустой БД:
validators, sites, targets,
check_types, ip_addresses)"]
ENV["control-api.env
(OS_AUTH_URL, OS_TOKEN, ...)"]
ADMIN["curl /api/v1/admin/*
(status/ips/validators,
ips submit/cancel,
config CRUD)"]
end
subgraph CAPI["control-api (управляющая машина, 1 экземпляр)"]
HTTP["HTTP API
/api/v1/agents/*
/api/v1/probers/*
/api/v1/admin/*
/healthz"]
ORCH["Оркестратор: Tick раз в
poll_interval_seconds
claim → associate FIP →
ожидание self-check →
checking → aggregate → release
+ lease sweep + heartbeat sweep"]
DB[("SQLite
validators / ip_queue / sites /
target_groups / check_types /
checks / events")]
OSCLIENT["OpenStack-клиент
(mode: mock | real)"]
end
OS["OpenStack Neutron
Floating IP API"]
VA["validator-agent ×N
(на каждой ВМ-валидаторе)"]
PR["prober ×3
(на каждой внешней площадке)"]
CFG -->|"читается только один раз,
на пустых таблицах (bootstrap)"| DB
ENV -->|"переменные окружения процесса"| OSCLIENT
ADMIN --> HTTP
HTTP -->|"config/queue CRUD:
источник истины после
первого изменения"| DB
HTTP --> ORCH
ORCH <--> DB
ORCH --> OSCLIENT
OSCLIENT -->|"associate / disassociate
floating ip"| OS
VA <-->|"register, heartbeat,
GET assignment,
POST self-check/events/
results/complete"| HTTP
PR <-->|"register,
GET assignments,
POST results"| HTTP
```
**Пояснение.** `control-api` — единственный компонент с состоянием и
единственная точка принятия решений (какой IP кому назначить, когда
считать проверку завершённой). `control-api.yaml` используется только как
одноразовый bootstrap для четырёх секций (`validators`, `sites`,
`targets`, `check_types`) — читается лишь пока соответствующая таблица в
БД пуста; `ip_addresses` — отдельный, всегда аддитивный путь постановки в
очередь при каждом старте. После bootstrap все изменения этих сущностей,
включая состав очереди и принудительные повтор/остановку проверки, идут
через `/api/v1/admin/*` — «на лету», без `systemctl restart control-api`
(см. [API.md](API.md#управление-очередью-и-конфигурацией)). Оркестратор
работает по таймеру независимо от HTTP-запросов — назначение IP
валидаторам и агрегация результатов не привязаны к конкретному входящему
запросу, читая актуальную конфигурацию из БД на каждом проходе, а не
единожды при старте. `validator-agent` и `prober` — активная сторона: они
сами инициируют все HTTP-запросы к control-api (pull-модель), сам
control-api к ним не обращается.
---
## 2. Поток данных: от валидатора к целевому серверу (egress-проверка)
Как валидатор проверяет, что через назначенный ему публичный адрес
реально работает исходящий доступ в интернет.
```mermaid
flowchart LR
subgraph VM["ВМ-валидатор (сервисный проект OpenStack)"]
AGENT["validator-agent"]
end
FIP(["Floating IP
203.0.113.10
(адрес под проверкой,
привязан оркестратором заранее)"])
IPECHO["Внешний IP-echo сервис
(api.ipify.org и т.п.,
вне облака)"]
CAPI["control-api"]
subgraph TARGETS["Целевые серверы (targets из конфига)"]
T1["hub.docker.com"]
T2["github.com"]
T3["packages.ubuntu.com"]
end
AGENT ==>|"1 self-check: исходящий трафик
ВМ идёт через FIP (SNAT облака)"| FIP
FIP ==>|"1 GET"| IPECHO
IPECHO -.->|"2 наблюдаемый исходящий IP"| AGENT
AGENT -->|"3 POST self-check {success}"| CAPI
AGENT ==>|"4 проверки: тоже через FIP"| FIP
FIP ==>|"4 HTTPS GET"| T1
FIP ==>|"4 HTTPS GET"| T2
FIP ==>|"4 ICMP echo"| T3
```
**Пояснение.** Шаги 1–2 — самопроверка (self-check): агент сам (без
участия control-api) обращается к внешнему IP-echo сервису и сравнивает
адрес, с которого пришёл его собственный запрос, с адресом, который ему
назначен. Ресурс для сравнения обязан быть вне облака: OpenStack
применяет SNAT через Floating IP только к трафику, уходящему через
внешнюю сеть — обращение к чему-либо внутри проекта (включая сам
control-api, если он в той же внутренней сети) показало бы приватный
адрес валидатора независимо от корректности привязки FIP. Итог
самопроверки агент затем сообщает control-api отдельным вызовом (шаг 3) —
это уже управляющий, а не проверяемый трафик. Только после успешного
self-check агент переходит к шагу 4 и выполняет проверки из
`check_config` (HTTPS/ICMP/опционально SSH) против целей из конфига —
тем же путём, через тот же FIP. Каждый результат отправляется обратно в
control-api сразу после выполнения (см. диаграмму телеметрии ниже) — сам
этот data-plane трафик (шаги 1 и 4) в control-api не проходит и им не
наблюдается напрямую, control-api видит только заявленный агентом
результат.
### Дополнительно: обратное направление (пробер к валидатору)
Тот же Floating IP одновременно проверяется и «снаружи» — с трёх внешних
площадок, независимо от исходящих проверок агента:
```mermaid
flowchart LR
subgraph SITES["3 внешние площадки"]
P1["prober site-1"]
P2["prober site-2"]
P3["prober site-3"]
end
FIP(["тот же Floating IP
203.0.113.10"])
VM["ВМ-валидатор
(слушает 22/80/443/8080)"]
P1 ==>|"TCP connect + ICMP echo"| FIP
P2 ==>|"TCP connect + ICMP echo"| FIP
P3 ==>|"TCP connect + ICMP echo"| FIP
FIP -.-> VM
```
Именно сочетание двух направлений (egress от валидатора и inbound от трёх
площадок) и даёт полную картину: адрес может нормально работать «наружу»,
но быть заблокирован для конкретных внешних сетей — это увидит только
inbound-проверка, и наоборот.
---
## 3. Поток данных телеметрии
Как результаты проверок и события аудита от validator-agent и трёх
проберов попадают в базу данных control-api, агрегируются в итоговый
результат и становятся видны оператору.
```mermaid
flowchart TB
subgraph SOURCES["Источники телеметрии"]
VA["validator-agent
(egress-проверки + self-check)"]
P1["prober site-1"]
P2["prober site-2"]
P3["prober site-3"]
end
subgraph API["HTTP API control-api"]
EP1["POST /agents/{id}/events"]
EP2["POST /agents/{id}/self-check"]
EP3["POST /agents/{id}/results"]
EP4["POST /probers/{site_id}/results"]
end
subgraph DB["SQLite (control-api)"]
EVENTS[("events
журнал аудита")]
CHECKS[("checks
результаты проверок,
source=egress|inbound-site-N")]
IPQ[("ip_queue
state, *_complete,
overall_result")]
end
AGG["Агрегация (оркестратор):
по достижении всех *_complete
или по таймауту checking_window_seconds"]
ADMIN["Оператор:
GET /api/v1/admin/status
GET /api/v1/admin/ips
GET /api/v1/admin/ips/{ip}"]
VA -->|"config_received,
self_check_result, ..."| EP1 --> EVENTS
VA -->|"success, detected_egress_ip"| EP2 --> EVENTS
EP2 -.->|"успех → egress_complete"| IPQ
VA -->|"check_type, target,
success, latency_ms"| EP3 --> CHECKS
P1 & P2 & P3 -->|"check_type, success,
latency_ms, complete"| EP4 --> CHECKS
EP4 -.->|"complete=true → siteN_complete"| IPQ
CHECKS --> AGG
IPQ --> AGG
AGG -->|"overall_result:
pass / partial / fail"| IPQ
EVENTS --> ADMIN
CHECKS --> ADMIN
IPQ --> ADMIN
```
**Пояснение.** Телеметрия стекается в БД из четырёх независимых потоков
(один от агента-валидатора, три — по одному с каждой площадки),
записываясь в две таблицы: `events` — сырой журнал аудита (что и когда
произошло), `checks` — результат каждой отдельной проверки с указанием
источника (`source`: `egress` для валидатора, `inbound-site-1/2/3` для
площадок). Отдельно, по мере поступления данных, выставляются флаги
завершения (`egress_complete`, `site1/2/3_complete`) в `ip_queue`. Как
только все ожидаемые флаги выставлены — либо истекло время ожидания
(`checking_window_seconds`) — фоновая агрегация суммирует все строки
`checks` по текущей попытке и записывает итог (`pass`/`partial`/`fail`)
обратно в `ip_queue`. Оператор в любой момент читает уже накопленные
данные через административные `GET`-методы, не дожидаясь завершения
проверки — подробнее о значениях полей см.
[USAGE.md](USAGE.md#значения-полей-ip).
> На диаграмме показан полный вариант с тремя площадками — это не
> обязательный минимум. Площадки опциональны: сколько их ожидать (0–3),
> определяется списком `sites` в конфиге control-api. При пустом списке
> четвёртый поток телеметрии (`inbound-site-N`) просто отсутствует, и
> агрегация ждёт только `egress_complete`. См.
> [USAGE.md](USAGE.md#управление-площадками-проберами).