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

18 KiB
Raw Blame History

Запрос на полный анализ 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/ ( добавлен 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)