7.9 KiB
Архитектурный аудит polygon v0.2.5
Адресат: Claude Opus (новый чат) Дата: 2026-07-31 ⛔ ОТВЕТ — ТОЛЬКО В ЧАТ. Не редактировать файлы.
1. Что такое polygon
Polygon — отдельный managed-сервис (polygon.pythonk8s.dev.nubes.ru),
эмулирующий REST API облачной платформы Nubes. Нужен для интеграционных тестов
приложения app-autotest — чтобы тесты гонялись не на реальном облаке, а на
эмуляторе.
- Flask 3.0 + gunicorn (1 воркер), деплой на Nubes pythonk8s
- Всё состояние в памяти (MockState), без БД
- Data-driven: конфиги сервисов генерируются из 37 STANDS YAML через
from_stands.py - 17 API-эндпоинтов с префиксом
/api/v1/svc - 19 юнит-тестов (pytest) + 15 интеграционных (в app-autotest)
- Версия: v0.2.5, задеплоена и протестирована curl'ом
2. Архитектура
polygon/
├── requirements.txt # Flask>=3.0, gunicorn>=21.2, PyYAML>=6.0
├── tests/
│ ├── test_converter.py # 10 юнит-тестов from_stands.py
│ └── test_state_machine.py # 9 юнит-тестов state_machine.py
└── site/
├── app.py # 57 строк: Flask() + 6 blueprint'ов + app.run()
├── routes/
│ ├── root.py # /health, / (Jinja2)
│ ├── services_routes.py# /api/v1/svc/services
│ ├── instances_routes.py# /api/v1/svc/instances
│ ├── operations_routes.py# /api/v1/svc/instanceOperations/*
│ ├── run.py # /api/v1/svc/instanceOperations/<uid>/run
│ └── mock_routes.py # /api/v1/svc/_mock/*
├── mock_state.py # MockState: instances, operations, op_params
├── state_machine.py # apply_effect() — мутация состояния
├── config/
│ └── loader.py # SERVICES, OPS_INDEX, DELAY, VERSION
├── utils/
│ ├── now.py # now() — UTC ISO с 'Z'
│ └── pluralize.py # pluralize()
├── from_stands.py # конвертер STANDS YAML → polygon config
├── static/style.css # тёмная тема
├── templates/index.html # Jinja2-шаблон
└── services/ # 37 сгенерированных YAML-конфигов
Ключевые архитектурные решения:
| Решение | Обоснование |
|---|---|
| Blueprint'ы по доменам | По шаблону app-autotest. 6 файлов вместо монолитного app.py (было 418 строк → 57) |
| In-memory state, 1 воркер | Без БД, без гонок. WEB_CONCURRENCY=1 принудительно |
| Синхронный dtFinish | sleep(MOCK_OP_DELAY) прямо в /run — без потоков, просто |
| Data-driven из STANDS YAML | 37 сервисов генерится конвертером, не хардкод |
| Модульные переменные в config/loader.py | SERVICES/OPS_INDEX/DELAY/VERSION — единый источник |
import config.loader as _cfg |
Модульные переменные меняются на лету (_cfg.DELAY = x) |
| CSS/HTML отделены от Python | static/style.css + templates/index.html — не в f-строках |
3. Поток данных
STANDS YAML (37 файлов)
→ from_stands.py (html.unescape, sub_params→dataDescriptor, stateOut auto-gen)
→ services/*.yaml (37 конфигов)
→ config/loader.py (_load_all при импорте)
→ SERVICES + OPS_INDEX (модульные переменные)
→ routes/*.py (читают SERVICES/OPS_INDEX/DELAY)
→ MockState (create_instance → create_operation → run → apply_effect)
4. Что сделано (хронология)
- Этап 1:
from_stands.py— конвертер STANDS → polygon, 37 YAML - Этап 2:
mock_state.py+state_machine.py+config/loader.py - Этап 3:
app.py— 17 эндпоинтов (потом разнесены на blueprint'ы) - Code Review #1 (Sonnet): 12 находок, 2 крит. исправлены (workers=1, KeyError в _merge_params)
- Decouple: монолит → blueprint'ы + CSS/HTML разделение
- Code Review #2 (Sonnet): 9 находок —
_cfg.DELAY, VERSION в config/loader, мёртвые импорты - Интеграция с app-autotest:
auth.py— STANDS-check, v1.2.24 - 🟡 фиксы:
json.dumpsвместо ручного JSON, 409 при повторном run,MOCK_AUTH_TOKEN - Интеграционные тесты: 15 тестов в app-autotest, все PASS
5. Что проверять
Архитектура
- Правильно ли выбран паттерн blueprint'ов? Не переусложнено ли?
- Модульные переменные в
config/loader.py— адекватный подход или есть лучше? - In-memory state с 1 воркером — масштабируемо ли для CI (параллельные тесты)?
- Есть ли архитектурные дыры: что будет при 1000 инстансов? При рестарте сервера?
Поток данных
- Не теряются ли данные между этапами: STANDS → YAML → loader → state?
- Все ли поля маппятся корректно? (dataDescriptor, valueList, stateOut)
- Правильно ли обрабатываются subresources (create_user, create_database)?
API-совместимость
- Все ли форматы ответов совпадают с реальным Nubes API?
- Location-заголовки, пустое тело validate-cfs, 201/404/409 коды?
- Пагинация: стоп по
len < pageSize, cap 200?
Безопасность
_mock/*защищеныMOCK_AUTH_TOKEN— достаточно?- Нет ли утечек данных между тестами (autouse reset в conftest)?
- Что будет при отправке невалидного JSON в POST /instances?
Что дальше
- Готов ли polygon к CI/CD?
- Что нужно для продакшен-использования (не только тесты)?
- Какие мониторинг/логирование нужны?
6. Вопросы к Опусу
Q1. In-memory vs Redis
Сейчас всё в MockState.instances (dict). При рестарте сервера всё теряется.
Для CI это ок (тесты стартуют заново). Нужен ли персистентный слой? Redis? Файлы?
Q2. Масштабирование
1 воркер, 1 процесс. Если запустить параллельные тесты (несколько pytest-сессий) — каждая поднимет свой polygon на своём порту? Или один общий сервер?
Q3. Генерация YAML
Сейчас from_stands.py запускается вручную, результат коммитится. Стоит ли
генерить YAML при старте сервера (если STANDS_DIR задан)?
Q4. Мониторинг
Нужны ли метрики: количество инстансов, операций, latency? Prometheus-экспорт? Или только Grafana-логи как сейчас?
Q5. Общая оценка
Готов ли polygon к использованию в CI app-autotest? Что критично доделать?