docs: agent analysis request — full project overview for new chat
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
# Запрос на полный анализ autotest — для нового чата с агентом
|
||||
|
||||
Дата: 2026-07-30
|
||||
Версия: v1.1.48
|
||||
|
||||
---
|
||||
|
||||
## 1. ЧТО ЭТО ЗА ПРОЕКТ
|
||||
|
||||
**autotest** — веб-приложение для автоматизированного тестирования сервисов платформы Nubes (Cloud Director). Позволяет запускать операции (create/modify/delete/suspend/resume/redeploy) над инстансами сервисов через Nubes REST API, отслеживать статус, вести историю запусков.
|
||||
|
||||
Приложение запущено на тестовом стенде: `atest.pythonk8s.dev.nubes.ru`
|
||||
|
||||
**Стек:**
|
||||
- Backend: Flask 3.0 + gunicorn (multi-worker)
|
||||
- DB: PostgreSQL 17 (Zalando Operator), схемы: `runs`, `scenario_runs`, `scenario_definitions` (pending)
|
||||
- Frontend: ванильный JS (один файл `app.js`, 550 строк), Jinja2-шаблон
|
||||
- Nubes REST API: `https://lk-api-gateway-test.ngcloud.ru/api/v1/svc`
|
||||
- Аутентификация: JWT-токен (cookie или env), автоопределение стенда (dev/test)
|
||||
- Деплой: Nubes pythonk8s, кластер iot-naeel
|
||||
|
||||
**Автор:** naeel (tazetdinovn@gmail.com, WZ03709)
|
||||
**Репозитории:**
|
||||
- `https://gitea.services.ngcloud.ru/forcloud/app-autotest` — код приложения
|
||||
- `https://gitea.services.ngcloud.ru/forcloud/autotest` — документация, YAML-описания сервисов
|
||||
|
||||
---
|
||||
|
||||
## 2. АРХИТЕКТУРА — ФАЙЛЫ И ИХ РОЛИ
|
||||
|
||||
### site/app.py — точка входа
|
||||
Flask-приложение, регистрирует blueprint'ы:
|
||||
- `main_bp` — главная страница, токен, инфраструктура
|
||||
- `api_test_bp` — основной API (run/params/status/log/history)
|
||||
- `api_scenario_bp` — API сценариев (run/status/CRUD — pending)
|
||||
- `api_bp` — **УДАЛЁН в v1.1.45** (legacy: /api/run, /api/status, /api/config)
|
||||
- VERSION — меняется при КАЖДОМ изменении
|
||||
|
||||
### site/api/http_client.py — HTTP-клиент
|
||||
- `HttpClient` — обёртка над requests.Session
|
||||
- GET: `raise_for_status()` → `.json()`, пустой ответ → `{}`
|
||||
- POST: `json=data`, извлекает Location-заголовок → `_location` в ответе
|
||||
- `raw_delete(url)` — DELETE без auth (для CMDB API)
|
||||
- `detect_endpoint(token)` — автоопределение стенда (dev→test)
|
||||
- `stand_name(endpoint)` — "dev"/"test" по URL
|
||||
|
||||
### site/api/auth.py — аутентификация
|
||||
- `get_token()` — из cookie или env
|
||||
- `get_client()` — HttpClient с автостендом
|
||||
- `get_client_id()` — ClientID из JWT
|
||||
- `get_stand()` — "dev"/"test"
|
||||
- `get_token_info()` — {email, company, client_id}
|
||||
|
||||
### site/routes/main.py — главная страница
|
||||
- `GET/POST /` — Jinja2-рендер: организация, инфраструктура, сервисы, форма токена
|
||||
- `GET /api/operations/<svc_id>` — cloud-first инстансы + tracked-fallback
|
||||
- `_resolve_instance_status()` — explainedStatus из облака или "creating" из трекера
|
||||
|
||||
### site/routes/api_test.py — ОСНОВНОЙ API (400 строк)
|
||||
Эндпоинты:
|
||||
- `GET /api/services` — список сервисов
|
||||
- `GET /api/instances/list` — все инстансы
|
||||
- `GET /api/params/<op_id>[?instanceUid=xxx]` — параметры операции (текущие или шаблон)
|
||||
- `POST /api/test` — запуск операции (CREATE или non-CREATE)
|
||||
- `GET /api/test/status/<op_uid>` — поллинг
|
||||
- `GET /api/log` — логи
|
||||
- `GET /api/history` — история из БД
|
||||
|
||||
Ключевые функции:
|
||||
- `_send_params_terraform()` — шаги 3-7 Terraform (cfsParams → resolveRefSvc → send → validate)
|
||||
- `_normalize_value()` — normalizeUniversalValueV6 (Terraform-equivalent)
|
||||
- `_resolve_ref_svc()` — автоподстановка UUID инстанса для refSvcId-параметров
|
||||
- `_finish_op()` — фоновый поллинг до dtFinish, сохранение в БД
|
||||
- `_redact_params()` — замена secret/password/token на ***
|
||||
- `_unique_display_name()` — проверка на дубликат + суффикс
|
||||
- `_find_uid(resp)` — извлечение UUID из вложенных dict
|
||||
- `_uid_from_location(loc)` — извлечение UUID из Location-заголовка
|
||||
|
||||
### site/routes/api_scenario.py — API СЦЕНАРИЕВ (90 строк)
|
||||
- `GET /api/scenarios` — список из config.yaml (должен быть переключён на БД)
|
||||
- `POST /api/scenario/run` — запуск (создаёт запись ДО потока, возвращает run_id + 202)
|
||||
- `GET /api/scenario/run/<int:run_id>` — статус конкретного запуска
|
||||
- `GET /api/scenario/status` — последние 10 запусков (legacy, должен уйти)
|
||||
|
||||
### site/operations/scenario.py — ЯДРО СЦЕНАРИЕВ (290 строк)
|
||||
- `run_scenario()` — главный исполнитель:
|
||||
1. service имя → svc_id (из config.yaml services)
|
||||
2. operation имя → svcOperationId (GET /services/{id})
|
||||
3. param коды → numeric IDs (GET /instanceOperations/default/{opId})
|
||||
4. create: новый инстанс; остальные: переиспользовать instance_map
|
||||
5. POST /instances → POST /instanceOperations → _send_params_terraform → run → poll
|
||||
6. Сохранить в runs (RUNNING до API, финальный после) + scenario_runs
|
||||
- `_resolve_service()` — поиск svc_id по символическому имени
|
||||
- `_resolve_params()` — symbolic codes → numeric IDs
|
||||
- `_find_uid()`, `_uid_from_location()` — ДУБЛИКАТЫ из api_test.py (!)
|
||||
- `_save_scenario_run()` — UPDATE scenario_runs
|
||||
- `_create_scenario_run()` — INSERT scenario_runs
|
||||
|
||||
### site/operations/get_params.py — параметры с текущими значениями
|
||||
- `get_params_with_current_values()` — смержить state.params инстанса с шаблоном операции
|
||||
- `_normalize_value_list()` — CSV-строка/массив → list
|
||||
|
||||
### site/operations/get_services.py — сервисы
|
||||
- `get_services()` — GET /services → все сервисы
|
||||
- `get_service_detail()` — GET /services/{id} → детали + операции
|
||||
|
||||
### site/operations/get_instances.py — инстансы
|
||||
- `get_organization()` — инстанс с serviceId=19
|
||||
- `get_instances()` — GET /instances?pageSize=500 → все инстансы
|
||||
|
||||
### site/operations/service_list.py — разрешённые сервисы
|
||||
- `load_service_ids(stand)` — чтение `config/services_{stand}.txt`
|
||||
|
||||
### site/operations/tracker.py — трекер инстансов (файловый)
|
||||
- JSON-файлы в /tmp/: `instances-{clientId}-{stand}.json`
|
||||
- fcntl.flock для multi-worker safety
|
||||
- Используется как КРАТКОСРОЧНЫЙ fallback (инстанс создан, но облако ещё не показывает)
|
||||
|
||||
### site/db/pool.py — connection pool
|
||||
- Lazy-init ThreadedConnectionPool (1-5)
|
||||
- `_ensure_schema()` → `init_db()` после успешного коннекта
|
||||
- ENV: DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD, DB_SSLMODE
|
||||
|
||||
### site/db/init_db.py — схема БД
|
||||
- Таблица `runs` — история операций (id, client_id, stand, svc_id, op_name, status, duration, params JSONB, stages JSONB, scenario_run_id, step_number)
|
||||
- Таблица `scenario_runs` — запуски сценариев (id, scenario_name, status, current_step, total_steps, duration, error_log)
|
||||
- Миграции: ALTER TABLE ADD COLUMN IF NOT EXISTS
|
||||
- **НЕТ** таблицы `scenario_definitions` — её ещё предстоит создать
|
||||
|
||||
### site/db/save_run.py — сохранение в БД
|
||||
- `save_run()` — INSERT в runs
|
||||
- Принимает опциональные `scenario_run_id` + `step_number` (добавлены в v1.1.48)
|
||||
|
||||
### site/runner.py — LEGACY (будет удалён)
|
||||
- Старый механизм запуска тестов из config.yaml
|
||||
- Используется только `load_config()` из него
|
||||
|
||||
### site/static/app.js — ФРОНТЕНД (550 строк)
|
||||
- Глобальное состояние: svcInstances, selectedInst, selectedOp, pollTimer, currentSvcId, busy, AUTOTEST_PREFIX
|
||||
- selectService(), toggleInstance(), startCreate(), runOp(), showParams(), executeOp()
|
||||
- Поллинг: setFinishedState(), showStages(), stopPoll(), refreshInstances()
|
||||
- История: toggleHistory(), loadHistory()
|
||||
- Сценарии: toggleScenario(), loadScenarios(), runScenario(), stopScenarioPoll()
|
||||
- Хелперы: _esc() (HTML-escape), validateJson()
|
||||
- Логи: toggleLog(), startLogPoll()
|
||||
|
||||
### site/static/style.css — стили
|
||||
### site/templates/index.html — Jinja2-шаблон
|
||||
- Три колонки: инфраструктура | сервисы | инстансы+операции+параметры
|
||||
- Секции: история (сворачиваемая), сценарии (сворачиваемая)
|
||||
- window.APP: version, stand, hasUserToken, firstServiceId
|
||||
|
||||
### site/config.yaml — конфигурация
|
||||
- services: маппинг name → service_id
|
||||
- scenarios: один сценарий dummy_test (create → delete)
|
||||
- **Проблема:** сценарии в контейнере, требуют redeploy для изменения
|
||||
|
||||
### STANDS/dev/resources_yaml/ — YAML-описания сервисов (50+ файлов)
|
||||
- Полные описания: name, service_id, outputs, operations с параметрами (коды, типы, valueList, sub_params)
|
||||
- Используются ТОЛЬКО как документация, код их не читает
|
||||
|
||||
### DOCS/ — документация
|
||||
- ARCHITECTURE-FULL.md — полный обзор
|
||||
- sol-answers.md — 14 ответов Sol (MVP-план)
|
||||
- sol-scenario-editor.md — ответы Sol по редактору сценариев
|
||||
|
||||
---
|
||||
|
||||
## 3. ПОТОК ОПЕРАЦИИ CREATE (Terraform parity)
|
||||
|
||||
1. POST /instances → `instance_uid` из Location-заголовка `./UUID`
|
||||
2. POST /instanceOperations → `op_uid` из Location-заголовка
|
||||
3. GET /instanceOperations/{op_uid}?fields=cfsParams → шаблон параметров
|
||||
4. _resolve_ref_svc → автоподстановка UUID для refSvcId-параметров
|
||||
5. POST /instanceOperationCfsParams → отправить пользовательские параметры
|
||||
6. POST /instanceOperationCfsParams → дослать неотправленные (с normalize)
|
||||
7. GET /instanceOperations/{op_uid}/validate-cfs → валидация
|
||||
8. POST /instanceOperations/{op_uid}/run → запуск
|
||||
9. Поллинг GET /instanceOperations/{op_uid}?fields=dtFinish,... → dtFinish
|
||||
|
||||
---
|
||||
|
||||
## 4. ТЕКУЩЕЕ СОСТОЯНИЕ (v1.1.48)
|
||||
|
||||
### Работает:
|
||||
- ✅ Ручной запуск операций (create/modify/delete/suspend/resume/redeploy)
|
||||
- ✅ Terraform-совместимая отправка параметров (9 шагов)
|
||||
- ✅ Поллинг с этапами (⏳/✅/❌)
|
||||
- ✅ История в БД (runs)
|
||||
- ✅ Лог-панель (fcntl.flock, ротация)
|
||||
- ✅ HTML-escape (statusError, d.error, логи, currentSvcName)
|
||||
- ✅ Redact secrets перед сохранением
|
||||
- ✅ Валидация входов (UUID, int, dict)
|
||||
- ✅ refSvcId — верхнеуровневый + в dataDescriptor
|
||||
- ✅ Убран legacy api_bp
|
||||
|
||||
### Частично работает:
|
||||
- ⚠️ Сценарии — запуск есть, но источник = config.yaml (redeploy для правки)
|
||||
- ⚠️ save_run пишет scenario_run_id + step_number (v1.1.48), но не протестировано
|
||||
- ⚠️ run_id возвращается клиенту (v1.1.48), поллинг по конкретному запуску
|
||||
|
||||
### Не работает / pending:
|
||||
- ❌ Редактор сценариев в UI (нужна таблица scenario_definitions + CRUD API + UI)
|
||||
- ❌ Сценарии требуют redeploy для изменения (запечены в config.yaml)
|
||||
- ❌ Дубликаты _find_uid / _uid_from_location в scenario.py и api_test.py
|
||||
- ❌ scenario.py не протестирован на полный цикл create→delete
|
||||
- ❌ seed из config.yaml в БД при первом старте не реализован
|
||||
- ❌ Нет сервисных токенов (runner использует персональный токен)
|
||||
- ❌ Нет блокировки параллельных запусков сценариев
|
||||
|
||||
---
|
||||
|
||||
## 5. КЛЮЧЕВЫЕ НАХОДКИ ИЗ HAR-ТРАССИРОВКИ
|
||||
|
||||
POST /instances:
|
||||
- Request: `{"serviceId":1,"displayName":"dummy-11255555","descr":""}`
|
||||
- Response: status 201, body `{}`, Location: `./CDEBB216-E5EB-4C09-8736-7E5F01A4EE12`
|
||||
- **UUID ТОЛЬКО в Location-заголовке**, тело ответа — пустой объект `{}`
|
||||
|
||||
POST /instanceOperations:
|
||||
- Request: `{"instanceUid":"...","operation":"create"}`
|
||||
- Response: также Location-заголовок с opUid
|
||||
|
||||
**Следствие:** `_find_uid(resp)` никогда не найдёт UUID в теле ответа (тело пустое). UUID всегда в Location. `_uid_from_location(resp.get("_location",""))` — ЕДИНСТВЕННЫЙ работающий метод извлечения.
|
||||
|
||||
**Но:** `_find_uid` нужен для других ответов API (get instanceOperation), где UUID внутри `instanceOperation.instanceOperationUid`.
|
||||
|
||||
---
|
||||
|
||||
## 6. РЕКОМЕНДАЦИИ SOL (2026-07-30)
|
||||
|
||||
### По редактору сценариев (sol-scenario-editor.md):
|
||||
|
||||
1. **Структура steps:** JSONB, поле `resource` для связи create→modify→delete одного инстанса
|
||||
2. **Параметры:** выпадающие списки из живого API (сервис→операции→коды), кеш на время сессии
|
||||
3. **Seed:** marker `scenario_seed_v1=completed`, `INSERT ON CONFLICT DO NOTHING`, отдельный `scenario_seed.yaml`
|
||||
4. **UI:** модальное окно, кнопки вверх/вниз, мягкое удаление (is_active=false)
|
||||
5. **Валидация:** при сохранении + при запуске, невалидный не запускать
|
||||
6. **Критические дыры:**
|
||||
- run_id до потока (✅ исправлено v1.1.48)
|
||||
- GET /api/scenario/run/<id> (✅ добавлен v1.1.48)
|
||||
- save_run пишет scenario_run_id/step_number (✅ исправлено v1.1.48)
|
||||
- TIMEOUT: op_data инициализировать (✅ исправлено v1.1.48)
|
||||
- RUNNING до API (✅ исправлено v1.1.48)
|
||||
- Блокировка параллельных запусков (pending)
|
||||
- version в definitions для optimistic locking (pending)
|
||||
- Сервисные токены (pending)
|
||||
|
||||
---
|
||||
|
||||
## 7. ЧТО НУЖНО ОТ АГЕНТА
|
||||
|
||||
### Проанализировать и ответить:
|
||||
|
||||
1. **Общая архитектура** — оценить, найти слабые места, предложить улучшения
|
||||
|
||||
2. **Дублирование кода** — `_find_uid` и `_uid_from_location` есть и в api_test.py и в scenario.py. Вынести в общий модуль? Куда?
|
||||
|
||||
3. **scenario_definitions** — спроектировать таблицу:
|
||||
- Поля: id, client_id, stand, name, steps JSONB, version, is_active, created_at, updated_at, updated_by
|
||||
- Индексы: UNIQUE(client_id, stand, lower(name))
|
||||
- API CRUD: какие именно эндпоинты, какие проверки
|
||||
- Seed: как именно импортировать из scenario_seed.yaml при первом старте
|
||||
|
||||
4. **UI редактора сценариев:**
|
||||
- Модальное окно: структура HTML
|
||||
- JS-логика: загрузка списка, создание/редактирование/удаление
|
||||
- Выпадающие списки: сервисы из GET /api/services, операции из GET /api/operations/{svcId}, параметры из GET /api/params/{opId}
|
||||
- Валидация на фронте перед отправкой
|
||||
|
||||
5. **Runner** — нужно ли что-то менять в `run_scenario()`? Он сейчас берёт шаги из config.yaml, должен из БД. Также нужно сохранять snapshot шагов в scenario_runs.
|
||||
|
||||
6. **Безопасность:**
|
||||
- CRUD сценариев: проверка client_id+stand
|
||||
- Optimistic locking (version)
|
||||
- Блокировка параллельных запусков (HTTP 409)
|
||||
|
||||
7. **Что ещё критично упущено?** — любые дыры, которые не покрыты текущим планом
|
||||
|
||||
8. **Приоритетный порядок реализации:**
|
||||
- Что делать сначала, что потом
|
||||
- Что можно выпустить в v1.1.49, что отложить
|
||||
|
||||
### НЕ ДЕЛАТЬ:
|
||||
- Не писать код (только анализ и план)
|
||||
- Не трогать git
|
||||
- Не менять файлы
|
||||
|
||||
---
|
||||
|
||||
## 8. ФАЙЛЫ ДЛЯ АНАЛИЗА (читать в этом порядке)
|
||||
|
||||
1. `app-autotest/site/app.py`
|
||||
2. `app-autotest/site/api/http_client.py`
|
||||
3. `app-autotest/site/api/auth.py`
|
||||
4. `app-autotest/site/routes/main.py`
|
||||
5. `app-autotest/site/routes/api_test.py` (самый важный)
|
||||
6. `app-autotest/site/routes/api_scenario.py`
|
||||
7. `app-autotest/site/operations/scenario.py`
|
||||
8. `app-autotest/site/operations/get_params.py`
|
||||
9. `app-autotest/site/operations/get_services.py`
|
||||
10. `app-autotest/site/operations/get_instances.py`
|
||||
11. `app-autotest/site/operations/tracker.py`
|
||||
12. `app-autotest/site/operations/service_list.py`
|
||||
13. `app-autotest/site/db/pool.py`
|
||||
14. `app-autotest/site/db/init_db.py`
|
||||
15. `app-autotest/site/db/save_run.py`
|
||||
16. `app-autotest/site/runner.py`
|
||||
17. `app-autotest/site/config.yaml`
|
||||
18. `app-autotest/site/static/app.js`
|
||||
19. `app-autotest/site/templates/index.html`
|
||||
20. `app-autotest/site/static/style.css`
|
||||
21. `development/dummycreate.har` (HAR-трассировка CREATE)
|
||||
22. `DOCS/ARCHITECTURE-FULL.md`
|
||||
23. `DOCS/sol-answers.md`
|
||||
24. `DOCS/sol-scenario-editor.md`
|
||||
25. `app-autotest/site/config/services_test.txt`
|
||||
26. Любой YAML из `STANDS/dev/resources_yaml/` (например `1_dummy.yaml`, `115_mariadb.yaml`)
|
||||
Reference in New Issue
Block a user