Prepare 0.1.0 for lab release: durable handlers, HTTP Compose, CI, and pulumi-tests.
- Harden DB-backed handlers and seed profiles; align client wire shapes for cluster resources, QEMU config, and node SSL fields - Serve plain HTTP on Compose :8006; keep TLS optional (--profile tls) and terminate HTTPS at Kubernetes Ingress - Add pulumi-tests (full contract surface majors 6–9 + BPG lifecycle) and make pulumi-tests - Ship bilingual docs, CHANGELOG, SECURITY, CONTRIBUTING, and GitHub Actions (make ci + Compose/Helm validation)
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
**Language / Язык:** [English](../architecture.md) | [Русский](architecture.md)
|
||||
|
||||
# Архитектура
|
||||
|
||||
## Цели
|
||||
|
||||
`proxmox-api-simulator` — stateful асинхронный эмулятор Proxmox VE API. Главная
|
||||
цель проектирования — измеримая совместимость с контрактом: маршруты, валидация,
|
||||
аутентификация, права доступа, формы ответов, переходы состояния и персистентные
|
||||
долгоживущие задачи проверяются независимо, а не объявляются «универсально
|
||||
совместимыми». В комплекте majors **6–9** поставляются с **100%** регистрацией
|
||||
семантических обработчиков для каждого объявленного метода контракта и поддержкой
|
||||
горячей замены между этими majors во время работы.
|
||||
|
||||
Для обычной работы симулятору не нужна живая установка Proxmox. Официальные
|
||||
артефакты API и санитизированные наблюдения импортируются заранее и хранятся как
|
||||
версионируемые снимки.
|
||||
|
||||
## Контекст системы
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Client["API clients<br/>proxmoxer / Terraform / Ansible"]
|
||||
Admin["Simulator operator"]
|
||||
Docs["Official Proxmox API Viewer"]
|
||||
API["FastAPI application"]
|
||||
Importer["Contract importer and CLI"]
|
||||
Contract["Versioned API contract"]
|
||||
Engine["Simulation engine"]
|
||||
Worker["Persistent task workers"]
|
||||
DB[(PostgreSQL)]
|
||||
Obs["Logs / Prometheus / OpenTelemetry"]
|
||||
|
||||
Client -->|"/api2/json"| API
|
||||
Admin -->|"CLI, Make/Helm, Web UI /ui/api"| API
|
||||
Docs -->|"explicit import only"| Importer
|
||||
Importer --> Contract
|
||||
Contract --> DB
|
||||
API --> Contract
|
||||
API --> Engine
|
||||
Engine --> DB
|
||||
Engine --> Worker
|
||||
Worker --> DB
|
||||
API --> Obs
|
||||
Worker --> Obs
|
||||
```
|
||||
|
||||
## Архитектура компонентов
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph ContractPlane["API contract plane"]
|
||||
Sources["Remote, local, and recorded sources"] --> Parse["Source adapters and parser"]
|
||||
Parse --> Normalize["Version-independent normalized model"]
|
||||
Normalize --> Validate["Validation, checksums, manifests"]
|
||||
Validate --> Registry["Contract registry"]
|
||||
Registry --> Diff["Semantic version diff"]
|
||||
Registry --> Routes["Dynamic route and schema factory"]
|
||||
Registry --> Reports["Compatibility reports"]
|
||||
end
|
||||
|
||||
subgraph RequestPlane["Request plane"]
|
||||
Middleware["Request ID, logging, metrics"] --> Auth["Ticket or API-token authentication"]
|
||||
Auth --> Permission["ACL and privilege evaluation"]
|
||||
Permission --> Input["Contract-driven request validation"]
|
||||
Input --> Handler["Semantic handler registry"]
|
||||
Handler --> Render["Proxmox response and error renderer"]
|
||||
end
|
||||
|
||||
subgraph SimulationPlane["Simulation plane"]
|
||||
Handler --> Services["Node, QEMU, LXC, storage services"]
|
||||
Services --> State["State machines and resource locks"]
|
||||
Services --> Tasks["Transactional persistent tasks"]
|
||||
Tasks --> Workers["asyncio workers with PostgreSQL leases"]
|
||||
Faults["Scenarios, faults, virtual clock"] --> Services
|
||||
end
|
||||
|
||||
Routes --> Input
|
||||
Registry --> Permission
|
||||
State --> PG[(PostgreSQL)]
|
||||
Workers --> PG
|
||||
Auth --> PG
|
||||
```
|
||||
|
||||
## Границы и направление зависимостей
|
||||
|
||||
Плоскость контракта владеет объявленными фактами API. Она импортирует
|
||||
исходные артефакты, сохраняет неизвестные поля источника, формирует
|
||||
детерминированный нормализованный JSON и предоставляет неизменяемые
|
||||
версионируемые контракты. Она не знает о состоянии ВМ и не выполняет операции.
|
||||
|
||||
Плоскость симуляции владеет изменяемым состоянием кластера и семантикой
|
||||
операций. Она использует доменные модели и репозитории, не зависящие от FastAPI
|
||||
и структур контракта, специфичных для источника. PostgreSQL — система записи
|
||||
для ресурсов, состояния безопасности, блокировок, сценариев и задач.
|
||||
|
||||
Долговечные задачи подтверждаются только после совместной фиксации строки задачи,
|
||||
события, ключа идемпотентности и опциональной блокировки ресурса. Воркеры
|
||||
захватывают задачи через `SKIP LOCKED`, продлевают аренды в реальном времени,
|
||||
сохраняют прогресс и append-only логи/события и позволяют повторно захватить
|
||||
просроченную работу после сбоя процесса. Lifespan владеет ограниченным набором
|
||||
asyncio-воркеров и ждёт упорядоченного завершения; PostgreSQL остаётся очередью
|
||||
и источником истины между репликами.
|
||||
|
||||
Длительности симуляции используют внедрённые часы: реальные, ускоренные или
|
||||
продвигаемые вручную. Операции ВМ — явные переходы конечного автомата, а
|
||||
засеянные правила сбоев оцениваются детерминированно. Аренды воркеров намеренно
|
||||
исключены из виртуального времени: они используют wall time PostgreSQL и
|
||||
monotonic sleep процесса, чтобы приостановленный или ускоренный сценарий не
|
||||
нарушил безопасность распределённых воркеров.
|
||||
|
||||
Секреты аутентификации хранятся как salted scrypt-хеши. Сессионные тикеты
|
||||
подписаны и имеют срок действия; мутационные запросы используют CSRF-токены,
|
||||
привязанные к тикету. Привилегии API-токена пересекаются с эффективными
|
||||
распространёнными ACL владельца-принципала, поэтому токен не может эскалировать
|
||||
права владельца. Логи редактируют распознанные представления тикетов, паролей и
|
||||
токенов перед записью.
|
||||
|
||||
API-слой — адаптер. Он аутентифицирует, авторизует, валидирует по выбранному
|
||||
контракту, диспетчеризует семантический обработчик и формирует
|
||||
версионно-совместимый ответ. Маршрут без семантического обработчика явно
|
||||
сообщается как неподдерживаемый, если оператор не включил нестандартный режим
|
||||
fallback.
|
||||
|
||||
Зависимости направлены внутрь: HTTP- и CLI-адаптеры зависят от прикладных
|
||||
сервисов; прикладные сервисы — от доменных интерфейсов; PostgreSQL, файлы
|
||||
контрактов, метрики и часы реализуют эти интерфейсы. Доменные сервисы никогда не
|
||||
импортируют FastAPI.
|
||||
|
||||
## Жизненный цикл запроса
|
||||
|
||||
1. Middleware назначает или проверяет request ID и запускает безопасную
|
||||
структурированную телеметрию.
|
||||
2. Выбранный профиль совместимости разрешает неизменяемый снимок API и
|
||||
версионно-специфичное поведение.
|
||||
3. Аутентификация определяет принципала без раскрытия учётных данных в логах.
|
||||
4. Объявленные контрактом и специфичные для обработчика права проверяются до
|
||||
раскрытия или изменения ресурсов.
|
||||
5. Значения path, query и body валидируются схемами, полученными из контракта.
|
||||
6. Семантический обработчик выполняется через прикладной сервис и явную границу
|
||||
транзакции.
|
||||
7. Долгие операции атомарно обновляют блокировку ресурса и создают
|
||||
персистентную задачу, затем возвращают её UPID.
|
||||
8. Рендерер ответа применяет Proxmox-обёртку, заголовки, cookies и
|
||||
версионно-специфичные шаблоны ошибок.
|
||||
|
||||
## Персистентность и конкурентность
|
||||
|
||||
Используется `asyncpg` напрямую. Репозитории принимают явное соединение или
|
||||
контекст транзакции; SQL параметризован и расположен рядом с репозиторием.
|
||||
Изменяемые глобальные переменные процесса не являются авторитетным состоянием.
|
||||
|
||||
Воркеры захватывают задачи через `FOR UPDATE SKIP LOCKED`, устанавливают
|
||||
продлеваемые аренды и используют метаданные идемпотентности для восстановления
|
||||
после сбоя процесса. Состояние ресурса, блокировки ресурсов и создание задачи
|
||||
изменяются в одной транзакции, когда это требуется. Оптимистичные колонки версии
|
||||
обнаруживают конкурентные обновления, а ограничения БД защищают инварианты,
|
||||
например уникальность VMID в пределах кластера.
|
||||
|
||||
Application lifespan владеет пулом соединений и ограниченным набором
|
||||
asyncio-задач воркеров. При shutdown захват прекращается, выполняемая работа
|
||||
достигает безопасной границы, отмена происходит только после настроенного grace
|
||||
period, затем пул закрывается.
|
||||
|
||||
## Получение контракта и доверие
|
||||
|
||||
Сетевой доступ ограничен явными командами import и recorder. Импортёры
|
||||
принудительно используют HTTPS, по умолчанию allowlist официальных хостов,
|
||||
лимиты размера ответа и редиректов, таймауты и ограниченные повторы. Каждый
|
||||
сырой артефакт неизменяем и имеет SHA-256 checksum. Его manifest фиксирует
|
||||
происхождение, версию, предупреждения парсера и checksum нормализованного
|
||||
снимка. Локальные снимки позволяют запуску и тестам работать офлайн.
|
||||
|
||||
Объявленная документация и санитизированное наблюдаемое поведение остаются
|
||||
разделёнными. Профиль совместимости выбирает поведение `strict-docs`, `observed`
|
||||
или `hybrid` без разброса проверок версий по сервисам.
|
||||
|
||||
## Модель безопасности
|
||||
|
||||
- Пароли и секреты API-токенов хранятся только как password hash.
|
||||
- Тикеты подписаны, краткоживущие и редактируются в телеметрии.
|
||||
- Мутации с ticket-аутентификацией требуют CSRF-валидации; запросы с API-токеном
|
||||
CSRF не требуют.
|
||||
- Интерактивный Web UI и вспомогательные `/admin/compatibility*` — лабораторные
|
||||
поверхности без отдельного admin-токена в текущей сборке; границей доверия
|
||||
является сетевая экспозиция.
|
||||
- Контейнеры в упакованных образах работают от непривилегированного пользователя.
|
||||
|
||||
## Горячая замена контракта во время работы
|
||||
|
||||
При холодном старте загружается `CONTRACT_SNAPSHOT`. Операторы могут заменить
|
||||
таблицу маршрутов в памяти для majors 6–9 через
|
||||
`POST /ui/api/contract/apply?major=N` (также доступно в Web UI). Замена
|
||||
обновляет `/version`, OpenAPI и состояние совместимости и действует только в
|
||||
пределах процесса (перезапуск восстанавливает снимок из env).
|
||||
|
||||
## Наблюдаемость
|
||||
|
||||
JSON-логи содержат request ID, шаблон маршрута, статус, длительность и
|
||||
редактированные поля идентичности. Процессные экспортёры Prometheus/OpenTelemetry
|
||||
пока не поставляются; обработчики Proxmox `/cluster/metrics*` симулируют только
|
||||
конфигурацию metrics-server PVE.
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
Unit-тесты покрывают обработку контракта и доменные правила. Интеграционные
|
||||
тесты проверяют репозитории, транзакции, воркеров и lifespan на PostgreSQL.
|
||||
Наборы contract и compatibility нацелены на majors **6–9** с **100%** покрытием
|
||||
реестра обработчиков. Внешний proxmoxer smoke выполняется против TLS-шлюза
|
||||
Compose. Concurrency-тесты проверяют аренды задач и переходы состояния.
|
||||
|
||||
Готовность БД включает последнюю упакованную версию миграции, а не только
|
||||
успешный connectivity-запрос. Воркеры повторяют неудачные захваты, пока не
|
||||
появятся таблицы миграций. Нормализованные записи ресурсов используют
|
||||
compare-and-swap обновления версии через типизированный репозиторий, поэтому
|
||||
устаревшие писатели получают domain conflict.
|
||||
|
||||
## Модель развёртывания
|
||||
|
||||
На контейнер приходится один процесс Uvicorn. Горизонтальные реплики
|
||||
координируются через PostgreSQL, а не через локальные очереди. Миграции БД и
|
||||
операции seed — явные команды и в Kubernetes становятся отдельными job. PostgreSQL
|
||||
включён в локальный Docker Compose, но в production chart — внешняя зависимость.
|
||||
|
||||
## Архитектурные решения
|
||||
|
||||
1. Маршруты FastAPI регистрируются из нормализованных снимков при старте;
|
||||
сотни вручную поддерживаемых объявлений маршрутов не нужны.
|
||||
2. SQLAlchemy не используется. Прямые asyncpg-репозитории делают поведение
|
||||
транзакций и конкурентности явным.
|
||||
3. Задачи на PostgreSQL — граница долговечности; фоновые задачи FastAPI и
|
||||
in-memory очереди не используются для критичной работы.
|
||||
4. Совместимость capability-driven и версионирована, а не реализована через
|
||||
разбросанные условия по строкам версий.
|
||||
5. Отсутствующие обработчики честно завершаются через `CONTRACT_FALLBACK`
|
||||
(по умолчанию `error` → HTTP 501). Majors 6–9 поставляются с полной
|
||||
регистрацией обработчиков, поэтому объявленные методы не должны попадать на
|
||||
этот путь при нормальной работе.
|
||||
6. Лабораторная документация и cookbooks живут в `docs/` и `examples/`; внутренние
|
||||
research/prompt-заметки не входят в пользовательское руководство.
|
||||
Reference in New Issue
Block a user