Files
autotest/polygon-docs/opus-architecture-audit-prompt.md

137 lines
7.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Архитектурный аудит 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? Что критично доделать?