docs: agent analysis request — full project overview for new chat

This commit is contained in:
2026-07-30 14:32:31 +04:00
parent de560cb066
commit 6e2418b157
3 changed files with 445 additions and 0 deletions
+318
View File
@@ -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`)
+48
View File
@@ -0,0 +1,48 @@
# Sol — ответы по редактору сценариев
Дата: 2026-07-30
## 1. Структура `steps`
- JSONB массив, отдельная таблица не нужна
- Обязательно поле `resource` — идентификатор инстанса внутри сценария (для связи create→modify→delete)
- `scenario_definitions`: + `version INTEGER`, `updated_by`, `is_active`/`deleted_at`, уникальность имени case-insensitive
- `scenario_runs`: + `definition_id`, `definition_version`, `steps_snapshot`
- Runner работает со snapshot, не перечитывает определение
## 2. Параметры в редакторе
- Из Nubes API: сервис→операции→`/instanceOperations/default/{opId}`→коды
- Выпадающий список с типом, обязательностью, default
- Map/array — JSON-строка с валидацией
- Кеш: браузерный на время сессии редактора ИЛИ серверный (stand+opId, TTL 5 мин)
- Свободный ввод кода не нужен
## 3. Seed из config.yaml
- Не импортировать при каждом пустом старте
- Схема: marker импорта → `INSERT ON CONFLICT DO NOTHING` → marker `scenario_seed_v1=completed`
- YAML вынести в `scenario_seed.yaml`, после rollout удалить
- Остальной config.yaml не трогать
## 4. UI редактор
- Модальное окно или боковая панель (не inline)
- Имя, список шагов, сервис, операция, параметры
- Кнопки добавить/удалить/вверх/вниз
- Drag-and-drop не нужен
- Мягкое удаление: `is_active=false`
## 5. Валидация
- При сохранении: структура, существование сервиса, доступность операции, коды параметров, типы, уникальность resource, порядок (create→modify→delete, после delete ничего)
- При запуске: повторить по актуальному API
- Невалидный сценарий не запускать частично
## 6. Критически упущенное
1. `POST /api/scenario/run` не возвращает run ID — гонка при двух запусках. Создавать запись ДО thread, вернуть HTTP 202 + run_id
2. Нужен `GET /api/scenario/runs/<id>` — поллинг конкретного запуска
3. `save_run()` не пишет `scenario_run_id` и `step_number` — шаги не связаны со сценарием
4. TIMEOUT: `op_data` может быть неинициализирована
5. Шаг нужно создавать в `runs` со статусом RUNNING ДО обращения к API, потом обновлять
6. Блокировка параллельных запусков: HTTP 409 для одного client_id+stand
7. CRUD: проверять `client_id+stand` при всех операциях
8. `version` в definitions: PUT с текущей version, 409 при конфликте
9. Runner использует персональный токен — нужен сервисный токен/клиент по stand
10. Имя не идентификатор: API запуска принимает `definition_id`, имя — редактируемое поле