Files
autotest/DOCS/agent-analysis-request.md

319 lines
18 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.
# Запрос на полный анализ 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`)