doc: промпт Опусу — архитектурный аудит polygon v0.2.5
This commit is contained in:
@@ -0,0 +1,136 @@
|
|||||||
|
# Архитектурный аудит 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? Что критично доделать?
|
||||||
Reference in New Issue
Block a user