230 lines
11 KiB
Markdown
230 lines
11 KiB
Markdown
# Архитектура 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`. Конфликт с stdlib `site.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
|
||
|
||
```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. Данные в памяти (состояние прогона)
|
||
|
||
```python
|
||
# 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)
|
||
|
||
```json
|
||
{
|
||
"NUBES_API_TOKEN": "...",
|
||
"NUBES_API_ENDPOINT": "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc"
|
||
}
|
||
```
|
||
|
||
Всё. `test_org_uid` — в `config.yaml` или auto-detect.
|