docs: receive all docs from app-autotest
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
# Архитектура app-autotest — текущее состояние
|
||||
|
||||
v1.0.93, 28.07.2026
|
||||
|
||||
---
|
||||
|
||||
## 1. Обзор
|
||||
|
||||
Flask-приложение (один HTML-файл, без SPA-фреймворка) для ручного тестирования операций
|
||||
сервисов облачной платформы Nubes. Работает через REST API Nubes, автоопределяет стенд
|
||||
(dev/test) по JWT-токену.
|
||||
|
||||
**Деплой**: Nubes pythonk8s (gunicorn, несколько воркеров).
|
||||
**Кластер**: `iot-naeel` (K8s resourceRealm, DEV-стенд)
|
||||
**URL**: `https://atest.pythonk8s.dev.nubes.ru`
|
||||
**Репозиторий**: `https://gitea.services.ngcloud.ru/forcloud/app-autotest.git`
|
||||
|
||||
### Ключевые принципы
|
||||
|
||||
1. **Источник правды — Nubes API**. Все данные (инстансы, параметры, статусы) получаются
|
||||
только из API. Никаких локальных копий или кеша (кроме краткосрочного трекера).
|
||||
2. **Разделение CREATE и non-CREATE**. Это два принципиально разных потока.
|
||||
3. **Multi-worker safety**. Все общие ресурсы (/tmp файлы) защищены `fcntl.flock`.
|
||||
4. **Мелкие функции**. Каждое действие — отдельная функция.
|
||||
|
||||
---
|
||||
|
||||
## 2. Структура файлов
|
||||
|
||||
```
|
||||
app-autotest/
|
||||
├── site/ ← НЕ пакет (без __init__.py)
|
||||
│ ├── app.py ← Flask(__name__), VERSION, blueprints, /health
|
||||
│ ├── runner.py ← [LEGACY] Раннер тестов
|
||||
│ ├── config.yaml ← [LEGACY] Конфиг раннера
|
||||
│ ├── api/
|
||||
│ │ └── http_client.py ← HttpClient, автостенд
|
||||
│ ├── operations/
|
||||
│ │ ├── get_services.py ← get_services(), get_service_detail()
|
||||
│ │ ├── get_instances.py ← get_instances() (пагинация), get_organization()
|
||||
│ │ ├── get_params.py ← get_params_with_current_values() — слияние state.params + шаблон
|
||||
│ │ └── tracker.py ← /tmp/instances-{clientId}-{stand}.json + flock
|
||||
│ ├── routes/
|
||||
│ │ ├── main.py ← GET/POST /, /api/operations/<svc_id>
|
||||
│ │ ├── api.py ← [LEGACY] /api/run, /api/status, /api/config
|
||||
│ │ └── api_test.py ← /api/test, /api/params, /api/log
|
||||
│ ├── templates/
|
||||
│ │ └── index.html ← Весь UI (Jinja2 + ванильный JS)
|
||||
│ └── static/
|
||||
│ ├── style.css
|
||||
│ ├── logo.svg
|
||||
│ └── favicon.svg
|
||||
└── secrets/
|
||||
├── dev.token
|
||||
└── test.token
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Бэкенд: HTTP-клиент и автостенд
|
||||
|
||||
### `http_client.py`
|
||||
|
||||
- `HttpClient(endpoint, token)` — обёртка над `requests.Session`:
|
||||
- Заголовки: `Authorization: Bearer`, `User-Agent: Mozilla/5.0` (DDoS-Guard)
|
||||
- `get(path)` → `raise_for_status()` → `.json()`
|
||||
- `post(path, data)` → проверка `r.ok`, парсинг JSON, извлечение `Location`
|
||||
- `detect_endpoint(token)` — пробует dev→test стенды, возвращает рабочий URL
|
||||
- `stand_name(endpoint)` — "dev"/"test" по URL
|
||||
- `create_client(token, fallback)` — HttpClient + endpoint
|
||||
|
||||
### `get_instances.py`
|
||||
|
||||
- `get_instances(client)` — `GET /instances` с пагинацией (pageSize=200). Остановка по `len(batch) < pageSize`.
|
||||
- `get_organization(client)` — первый инстанс с `serviceId == 19`
|
||||
|
||||
### `get_services.py`
|
||||
|
||||
- `get_services(client)` → `GET /services` → список
|
||||
- `get_service_detail(client, svc_id)` → `GET /services/{id}` → детали + операции
|
||||
|
||||
### `get_params.py` — КЛЮЧЕВОЙ модуль
|
||||
|
||||
`get_params_with_current_values(client, op_id, instance_uid)`:
|
||||
1. `GET /instances/{uid}` → `state.params` (текущие значения: `{"whereFail":"1",...}`)
|
||||
2. `GET /instanceOperations/default/{opId}` → шаблон (коды, типы, valueList, dataDescriptor)
|
||||
3. Слияние: `defaultValue = state.params["код"] ?? template.defaultValue`
|
||||
4. Приведение типов: `bool` → `"true"/"false"`, `dict` → `json.dumps()`
|
||||
|
||||
### `tracker.py`
|
||||
|
||||
- Файл: `/tmp/instances-{clientId}-{stand}.json` (изолирован по пользователю и стенду)
|
||||
- Блокировка: `fcntl.flock(LOCK_EX | LOCK_NB)` + retry 2s
|
||||
- `add(client_id, stand, uid, svc_id, name)`, `remove(...)`, `list_all(...)`
|
||||
- Нужен ТОЛЬКО как fallback — когда инстанс только создан и ещё не в `/instances`
|
||||
|
||||
---
|
||||
|
||||
## 4. Бэкенд: Роуты
|
||||
|
||||
### `main.py`
|
||||
|
||||
- **`GET/POST /`** — главная: организация, инфраструктура (serviceId 2,12,19,21,22,25,26,29,110,150), сервисы
|
||||
- `action=save` → сохранить токен в cookie
|
||||
- `action=clear` → удалить cookie
|
||||
- **`GET /api/operations/<svc_id>`** — операции + autotest-инстансы:
|
||||
- Cloud-first: фильтр по префиксу `autotest-`
|
||||
- Tracker-fallback: статус "creating" для ещё невидимых
|
||||
|
||||
### `api_test.py` — основной модуль операций
|
||||
|
||||
- **`GET /api/params/<op_id>[?instanceUid=xxx]`**:
|
||||
- Без `instanceUid` → шаблонные `defaultValue`
|
||||
- С `instanceUid` → `get_params_with_current_values()`
|
||||
- **`POST /api/test`** — запуск операции:
|
||||
- **CREATE**: `POST /instances` → `instanceUid` → `POST /instanceOperations` → `opUid` → `tracker_add` → params → `/run`
|
||||
- **non-CREATE**: `POST /instanceOperations` → params → `/run` (redeploy: сразу `/run`)
|
||||
- `displayName` = `_get_instance_display_name()` (из API, не UUID!)
|
||||
- Фоновый поток: `_finish_op()` — поллинг до `dtFinish` (300s таймаут)
|
||||
- **`GET /api/test/status/<op_uid>`** — поллинг: in-memory `_op_results` → прямой API
|
||||
- **`GET /api/log`** — последние 200 строк из `/tmp/app-autotest.log`
|
||||
|
||||
### Логирование
|
||||
|
||||
`_log(msg)` в `api_test.py`:
|
||||
- stdout (gunicorn) + `/tmp/app-autotest.log` (flock, общий для воркеров)
|
||||
- Ротация при 512 КБ
|
||||
- UI: скрытая панель, кнопка `log` (правый нижний угол)
|
||||
|
||||
---
|
||||
|
||||
## 5. Фронтенд
|
||||
|
||||
### Структура
|
||||
|
||||
Один HTML-файл, три колонки:
|
||||
- Левая (280px): инфраструктура
|
||||
- Средняя (240px): сервисы
|
||||
- Правая (flex): инстансы + параметры + кнопка + этапы
|
||||
|
||||
### Глобальное состояние (JS)
|
||||
|
||||
```
|
||||
svcInstances — кеш инстансов (из /api/operations)
|
||||
selectedInst — UID выбранного инстанса
|
||||
selectedOp — {opId, opName, svcId}
|
||||
pollTimer — таймер поллинга
|
||||
SVC_ID = 1 — фиксированный сервис (Болванка)
|
||||
```
|
||||
|
||||
### Потоки операций
|
||||
|
||||
**CREATE**: `startCreate()` → `showParams(18,'create')` → форма → `executeOp(pp)` → POST /api/test → поллинг → refreshInstances
|
||||
|
||||
**MODIFY**: `toggleInstance()` → кнопки → `runOp('modify',opId)` → `showParams(opId,'modify')` → GET /api/params?instanceUid= → форма с ТЕКУЩИМИ значениями → валидация map → `executeOp(pp)` → POST /api/test → поллинг
|
||||
|
||||
**Без параметров** (suspend/delete/resume/redeploy): `runOp()` → confirm → `executeOp({})` → POST /api/test → поллинг
|
||||
|
||||
### Валидация JSON
|
||||
|
||||
- `_esc(s)` — HTML-экранирование (`"` → `"`, `&` → `&`, `<` → `<`)
|
||||
- `validateJson(el, quiet)` — `JSON.parse()` на `onblur` (красная рамка + текст)
|
||||
- Batch-проверка перед отправкой — ошибка → запрос не уходит
|
||||
|
||||
---
|
||||
|
||||
## 6. Nubes API — используемые endpoint'ы
|
||||
|
||||
| Метод | Путь | Назначение |
|
||||
|-------|------|-----------|
|
||||
| GET | `/instances?pageSize=200&page=N` | Все инстансы (пагинация) |
|
||||
| GET | `/instances/{uid}` | Детали + state.params |
|
||||
| GET | `/services` | Список сервисов |
|
||||
| GET | `/services/{id}` | Детали + операции |
|
||||
| GET | `/instanceOperations/default/{opId}` | Шаблон параметров |
|
||||
| POST | `/instances` | Создать инстанс |
|
||||
| POST | `/instanceOperations` | Создать операцию |
|
||||
| POST | `/instanceOperationCfsParams` | Установить параметр |
|
||||
| POST | `/instanceOperations/{opUid}/run` | Запустить операцию |
|
||||
| GET | `/instanceOperations/{opUid}?fields=...` | Поллинг статуса |
|
||||
|
||||
---
|
||||
|
||||
## 7. Ограничения платформы
|
||||
|
||||
- `site/` — НЕ пакет (без `__init__.py`), импорты без префикса `site.`
|
||||
- `app.run(host="0.0.0.0", port=5000)`
|
||||
- Gunicorn multi-worker → общие данные через файлы + flock
|
||||
- `/tmp/` теряется при редеплое
|
||||
- User-Agent: `Mozilla/5.0` обязателен (DDoS-Guard)
|
||||
Reference in New Issue
Block a user