11 KiB
11 KiB
Архитектура app-autotest — финальный документ
1. Цель проекта
Автотесты операций сервисов облачной платформы Nubes. Веб-интерфейс с запуском тестов прямо на платформе.
Заказчик: Сергей Мищук. Исполнитель: Наиль Тазетдинов.
2. Текущее состояние
- Flask-приложение задепоено на Nubes (pythonk8s) в тестовом стенде
- Репозиторий:
https://gitea.services.ngcloud.ru/forcloud/app-autotest.git - Сервис:
https://a-test.pythonk8s.dev.nubes.ru/(instanceUid:01a2d5b2-..., кластерiot-naeel) - Сейчас умеет: вход по токену, показ организации, список инстансов, таблица сервисов с операциями
3. Ключевые ограничения платформы
- Структура:
site/app.py— точка входа. Платформа запускаетpython site/app.py site/— НЕ пакет. Без__init__.py. Конфликт с stdlibsite.py- Импорты: без префикса
site..from api.client import ...работает (Python добавляет директорию скрипта в sys.path[0]) app.run(host="0.0.0.0", port=5000)— обязательно, иначе контейнер падаетUser-Agent: Mozilla/5.0— обязательно для API (DDoS-Guard)- Нет persistent volume. Данные теряются при рестарте/редеплое
- Gunicorn: запускается платформой. Вероятно 1 worker.
4. Принятые архитектурные решения
| Решение | Обоснование |
|---|---|
| Нет БД на MVP | Нет PV. Конфиг в YAML-файле, результаты в памяти |
| Config в git | site/config.yaml. Менять → коммит → редеплой |
| Pipeline последовательный | Безопаснее. Один сервис за другим, операции по порядку |
| threading.Thread | Достаточно для 1-2 пользователей. 1 gunicorn worker |
| Поллинг статуса | GET /instances каждые 10s, таймаут 5 мин на операцию |
| Результаты в памяти | dict в модуле. Видны пока под живёт |
| API для данных сервисов | GET /services, /instanceOperations/default/{id} — не нужны YAML-файлы |
| JSON textarea для параметров | Пользователи — разработчики, понимают JSON |
5. Структура файлов
app-autotest/
├── requirements.txt # Flask>=3.0, gunicorn>=21.2, requests>=2.31, PyYAML
├── site/
│ ├── app.py # Точка входа: Flask(__name__), blueprint, app.run()
│ ├── config.yaml # Конфигурация тестов (см. раздел 7)
│ ├── api/
│ │ └── http_client.py # HttpClient: get(), post(), Bearer, User-Agent
│ ├── operations/
│ │ ├── get_services.py # get_services(), get_service_detail()
│ │ ├── get_instances.py # get_instances(), get_organization()
│ │ └── get_params.py # get_operation_params(svcOperationId)
│ ├── runner.py # Pipeline: читает config → выполняет → результаты в memory
│ ├── routes/
│ │ ├── main.py # GET/POST / — главная страница + токен
│ │ └── api.py # /api/run (запуск), /api/status (статус прогона)
│ ├── static/
│ │ ├── favicon.svg
│ │ ├── logo.svg
│ │ └── style.css
│ └── templates/
│ └── index.html # Токен, организация, инстансы, сервисы, кнопка «Запустить», таблица результатов
6. Pipeline (runner.py)
Алгоритм
1. Читаем site/config.yaml
2. test_org_uid берём из config или определяем через API (get_organization)
3. Для каждого сервиса в config.services:
a. Если service.enabled = false → пропускаем
b. instance_uid = None
c. Для каждой операции в service.operations (в порядке: create, modify, suspend, resume, delete):
- Если operation.enabled = false → статус SKIP
- Определяем svcOperationId через API: get_service_detail(service_id) → ищем operation по имени → svcOperationId
- Если create:
POST /instances {serviceId, displayName, cfsParams}
→ сохраняем instance_uid из ответа
→ поллинг: GET /instances, ищем UID, ждём explainedStatus = running
- Иначе:
POST /instanceOperations {instanceUid, svcOperationId, cfsParams}
→ поллинг: ждём operationIsInProgress = false
- Результат: OK / FAIL / TIMEOUT → пишем в memory
- Если create упал → прерываем этот сервис, переходим к следующему
4. Все результаты — в глобальный dict last_run
Поллинг
- Интервал: 10 секунд
- Таймаут: 5 минут (300 секунд)
- Проверка: GET /instances → ищем instanceUid → explainedStatus
- Если таймаут → статус TIMEOUT
7. Формат config.yaml
test_org_uid: "87a34374-7dc6-488c-afde-1246bf754f6d" # или auto — найти через API
services:
- service_id: 90
name: PostgreSQL
display_name: "autotest-pg" # для create
enabled: true
operations:
- name: create
enabled: true
params:
# cfsParams для POST /instances (без displayName — оно отдельно)
- name: modify
enabled: false
params: {}
- name: delete
enabled: true
params: {}
- service_id: 19
name: "Организация в Cloud Director"
display_name: ""
enabled: false # нельзя тестировать на проде
operations:
- name: create
enabled: false
params: {}
test_org_uid: "auto" — найти организацию через API автоматически.
8. Данные в памяти (состояние прогона)
# runner.py — глобальное состояние
last_run = {
"run_id": "2026-07-23-140000",
"status": "running", # running | done | error
"started_at": "2026-07-23T14:00:00",
"finished_at": None,
"results": [
{
"service_id": 90,
"service_name": "PostgreSQL",
"operation": "create",
"status": "OK", # OK | FAIL | TIMEOUT | SKIP | RUNNING
"instance_uid": "xxx-xxx",
"error": None,
"duration_sec": 45.2,
},
...
]
}
9. UI (index.html)
Одна страница, три секции:
┌──────────────────────────────────────────────────────┐
│ [token input] [OK] [Вых] │
├──────────────────┬───────────────────────────────────┤
│ ОРГАНИЗАЦИЯ │ СЕРВИСЫ (свёрнуты) │
│ WZ03709-saas │ ┌─────────────────────────────┐ │
│ uid: 87a... │ │ # │ Сервис │ Опер │ │
│ status: running │ │ 90 │ PostgreSQL │ 5 │ │
│ │ │ 91 │ Redis │ 3 │ │
│ ИНСТАНСЫ (40) │ │ ... │
│ ┌──────────────┐ │ └─────────────────────────────┘ │
│ │ name │ svc │ │ │
│ │ ... │ ... │ │ [ЗАПУСТИТЬ ТЕСТЫ] │
│ └──────────────┘ │ │
│ │ РЕЗУЛЬТАТЫ │
│ │ ┌─────────────────────────────┐ │
│ │ │ Сервис │ Операция│Статус │ │
│ │ │ PostgreSQL│ create │ OK │ │
│ │ │ Redis │ create │ FAIL │ │
│ │ └─────────────────────────────┘ │
└──────────────────┴───────────────────────────────────┘
Кнопка «Запустить тесты» → POST /api/run → запускает runner.py в threading.Thread → JS поллинг GET /api/status каждые 3 секунды → обновляет таблицу результатов.
10. API-роуты
| Метод | Путь | Назначение |
|---|---|---|
| GET/POST | / |
Главная страница + вход по токену |
| GET | /api/operations/<svc_id> |
Операции сервиса (уже есть) |
| POST | /api/run |
Запустить прогон тестов |
| GET | /api/status |
Текущий статус прогона (JSON last_run) |
11. Что НЕ делаем в MVP
- ❌ База данных (SQLite/PostgreSQL)
- ❌ UI конфигурации (правим config.yaml в git)
- ❌ История прогонов (только текущий)
- ❌ Мобильная версия
- ❌ Авторизация пользователей
- ❌ Параллельный запуск тестов
- ❌ Drag-and-drop, динамические формы параметров
- ❌ WebSocket
12. Фазы разработки
Фаза 0 — MVP (сейчас)
config.yamlв репоrunner.pyс pipeline- Кнопка «Запустить» + таблица результатов
- Всё в памяти, без БД
Фаза 1 (после успешных тестов)
- UI редактирования config.yaml через браузер (пока без авто-коммита — пользователь копирует и коммитит сам)
Фаза 2 (если нужна история)
- Подключить Nubes PostgreSQL
- Таблица test_runs + test_results
- Страница истории
13. Переменные окружения (jsonEnv)
{
"NUBES_API_TOKEN": "...",
"NUBES_API_ENDPOINT": "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc"
}
Всё. test_org_uid — в config.yaml или auto-detect.