Files
autotest/DOCS/step-by-step-audit-v1.0.50.md
T

134 lines
6.4 KiB
Markdown
Raw 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.
# ⚠️ LEGACY — НЕАКТУАЛЬНО. Исторический документ.
# Пошаговый аудит кода — 27.07.2026 (v1.0.50)
## Методология
Полная трассировка CREATE-потока: UI → api_test() → _finish_op() → api_test_status() → refreshInstances() → api_operations(). Каждый if, try, except, присваивание.
---
## Результат: найдено 3 бага
### 🔴 Баг #1 (КРИТИЧЕСКИЙ): Multi-worker gunicorn — in-memory dict не работает
**Файл:** `site/operations/tracker.py`
```python
_data = {
"408b7f96-...": {...}, # 4 initial items
}
def add(instance_uid, svc_id, display_name):
with _LOCK:
_data[instance_uid] = {...}
```
`_data` — module-level dict. У каждого gunicorn worker своя копия модуля → свой `_data`.
**Трассировка:**
1. POST /api/test → попадает на **воркер A**
2. `tracker_add(...)``_data` воркера A = 5 элементов ✅
3. GET /api/operations/1 → попадает на **воркер B**
4. `tracker_list()``_data` воркера B = 4 элемента ❌
**Почему это объясняет ВСЕ симптомы:**
- После F5 — рандомный воркер → опять 4
- v1.0.46-1.0.49 — ни одно решение не помогало (in-memory принципиально не跨-process)
- Инстанс в Nubes есть, в трекере нет — разные воркеры
**Решение:** вернуть файловый трекер (`/tmp/instances.json`) с межпроцессной блокировкой (`fcntl.flock` вместо `threading.Lock`).
---
### 🔴 Баг #2 (КРИТИЧЕСКИЙ): _finish_op() умирает молча на не-словаре
**Файл:** `site/routes/api_test.py`, строки 176-179
```python
def _finish_op(...):
while time.time() < deadline:
try:
data = client.get(...) # ← except ловит ТОЛЬКО это
except Exception:
time.sleep(5)
continue
op = data.get("instanceOperation", {}) # ← если data не dict → AttributeError!
```
Если Nubes API возвращает `list`, `str`, `None` или любой не-словарь — `data.get()`**AttributeError**. Этот except НЕ покрывает строку 179. Поток умирает молча. Python daemon-потоки не пишут traceback.
**Решение:** обернуть ВСЁ тело цикла (строки 166-195) в `try/except Exception: print(traceback)`.
---
### 🟡 Баг #3 (НЕКРИТИЧНЫЙ): _op_results — та же multi-worker проблема
**Файл:** `site/routes/api_test.py`, строка 152
```python
_op_results = {} # module-level
# В _finish_op (воркер A):
_op_results[op_uid] = {"status": "OK", ...}
# В api_test_status (воркер B):
if op_uid in _op_results: # ← False! (другой воркер)
return jsonify(_op_results[op_uid])
# fallback → прямой запрос в Nubes API
```
**Некритично** потому что есть fallback: если `_op_results` не содержит op_uid, `api_test_status()` делает прямой GET в Nubes API и возвращает статус. UI получает stages напрямую из Nubes, не из `_op_results`.
**НО:** fallback не обновляет `tracker_remove` для delete. При multi-worker delete не удалит инстанс из трекера.
---
## Полная трассировка CREATE (все шаги)
| Шаг | Код | Результат | Статус |
|-----|-----|-----------|--------|
| 1 | `data = request.get_json()` | `svc_id=1, op_name="create", display_name="autotest-1-xxx"` | ✅ |
| 2 | `client.post("/instances", ...)` | `instance_uid = "f192b10d-..."` | ✅ |
| 3 | `client.post("/instanceOperations", ...)` | `op_uid = "d489348e-..."` | ✅ |
| 4 | `for pid,pval: client.post("/instanceOperationCfsParams", ...)` | Параметры установлены | ✅ |
| 5 | `client.post("/instanceOperations/{op_uid}/run")` | Операция запущена | ✅ |
| 6 | `tracker_add(instance_uid, svc_id, display_name)` | `_data[uid] = {...}` (in-memory) | ✅ |
| 7 | `threading.Thread(target=_finish_op, ...)` | Поток запущен | ✅ |
| 8 | `return {status:"RUNNING", opUid, instanceUid}` | Ответ UI | ✅ |
| 9 | UI poll `/api/test/status/<opUid>` | `_op_results[opUid]` или fallback API | ⚠️ разн. воркеры |
| 10 | `_finish_op` детектит `dtFinish` | `_op_results[opUid] = {status:"OK"}` | ⚠️ воркер A |
| 11 | UI: `refreshInstances()``/api/operations/1` | `tracker_list()` → 4 элемента | ❌ воркер B |
| 12 | `api_operations()` возвращает 4 инстанса | Нового нет | ❌ |
| 13 | F5 → `selectService(1)``/api/operations/1` | Опять 4 | ❌ |
---
## Дополнительные находки (не критические)
### ⚠️ params loop — orphaned resources при ошибке
Если `client.post("/instanceOperationCfsParams", ...)` падает на mid-param:
- Инстанс УЖЕ создан в Nubes (сирота)
- Операция УЖЕ создана (сирота)
- `tracker_add` НЕ вызван (он после цикла)
- Ответ: FAIL
### ⚠️ `data["svcOperationId"]` — KeyError если поле отсутствует
Вызов API без `svcOperationId` в JSON → KeyError → FAIL. Обработано внешним try/except.
### ⚠️ `int(pid)` — ValueError если param ID не число
`int("abc")` → ValueError → FAIL. Обработано внешним try/except.
### ⚠️ `_find_uid()` итерация по ВСЕМ значениям
Может случайно найти uid во вложенном объекте. Низкий риск, но нечисто.
---
## План исправлений для v1.0.51
| # | Что | Как |
|---|-----|-----|
| 1 | Файловый трекер с fcntl.flock | Вернуть `/tmp/instances.json`, заменить `threading.Lock` на `fcntl.flock` |
| 2 | _finish_op: try/except на всё тело | Обернуть строки 166-195 в `try/except: print(traceback)` |
| 3 | (опционально) `svc_id = int(data["serviceId"])` | Защита от строкового "1" в JSON |