v1.0.50: fix showStages icon (dtFinish); fix api_operations (add tracked orphans); drop not_created filter
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
# Sonnet — почему ты пропустил эти баги?
|
||||||
|
|
||||||
|
Отправлено: 27.07.2026, после v1.0.49 (твой анализ выполнен, НЕ ПОМОГЛО)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ты предложил 4 правки — мы сделали. Инстанс ВСЁ РАВНО не появляется в списке. Плюс новый баг: ❌ вместо ⏳ для этапов в процессе.
|
||||||
|
|
||||||
|
## Ты НЕ заметил баг №1: showStages() — ❌ для выполняющихся этапов
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function showStages(stages){
|
||||||
|
stages.forEach(s=>{
|
||||||
|
const ok=s.isSuccessful;
|
||||||
|
const icon=ok===true?'✅':ok===false?'❌':'⏳'; // ← БАГ
|
||||||
|
```
|
||||||
|
|
||||||
|
API Nubes для этапа в процессе возвращает `isSuccessful: false` (или `null`).
|
||||||
|
Код интерпретирует `false === false` → ❌ (авария).
|
||||||
|
Но этап просто **ещё не завершился** — должен быть ⏳.
|
||||||
|
|
||||||
|
**Правильная логика:** проверять `dtFinish` этапа. Если `dtFinish` нет → ⏳.
|
||||||
|
Если есть и `isSuccessful === true` → ✅. Если есть и `false` → ❌.
|
||||||
|
|
||||||
|
Почему ты это пропустил? Код `showStages()` был в твоём обзоре.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ты НЕ заметил баг №2: api_operations() теряет инстансы
|
||||||
|
|
||||||
|
```python
|
||||||
|
def api_operations(svc_id):
|
||||||
|
tracked = tracker_list() # ← 5 UID (включая новый)
|
||||||
|
tracked_uids = {t["instanceUid"] for t in tracked if t["svcId"] == svc_id}
|
||||||
|
instances = get_instances(_client()) # ← Nubes API: только 4 старых
|
||||||
|
svc_instances = [i for i in instances
|
||||||
|
if i.get("instanceUid") in tracked_uids # ← новый UID НЕ НАЙДЕН
|
||||||
|
and i.get("explainedStatus") not in ("deleted", "not created")]
|
||||||
|
```
|
||||||
|
|
||||||
|
`tracker_add` работает (in-memory, мгновенно). Но `get_instances()` из Nubes API **не сразу** возвращает только что созданный инстанс.
|
||||||
|
Фильтр требует чтобы инстанс был В ОБОИХ источниках. Новый UID есть в `tracked_uids` но отсутствует в `instances` → выпадает.
|
||||||
|
|
||||||
|
Результат: список всегда показывает только старые инстансы, новый — никогда.
|
||||||
|
|
||||||
|
**Правильная логика:** инстансы из трекера, отсутствующие в `get_instances()`, добавлять напрямую со статусом `"creating"`.
|
||||||
|
|
||||||
|
Почему ты это пропустил? Код `api_operations()` был в твоём обзоре, полностью.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что мы имеем после твоих рекомендаций
|
||||||
|
|
||||||
|
| Версия | Что делали | Результат |
|
||||||
|
|--------|-----------|-----------|
|
||||||
|
| v1.0.48 | tracker_add синхронно, except:pass | ❌ инстанса нет |
|
||||||
|
| v1.0.49 | in-memory dict (твоя рекомендация) | ❌ инстанса нет |
|
||||||
|
| v1.0.49 | refreshInstances вместо selectService | ❌ stages ушли, но инстанса нет |
|
||||||
|
|
||||||
|
Трекер РАБОТАЕТ — in-memory dict, добавление мгновенное.
|
||||||
|
НО фильтр `api_operations()` ВЫБРАСЫВАЕТ новый инстанс потому что Nubes API его ещё не проиндексировал.
|
||||||
|
|
||||||
|
## Вопрос
|
||||||
|
|
||||||
|
Ты проанализировал ВЕСЬ код, включая `api_operations()` и `showStages()`.
|
||||||
|
Как ты мог пропустить оба этих бага, которые видны при простой трассировке CREATE-потока?
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Sonnet — ответ 27.07.2026 (v1.0.49, раунд 2)
|
||||||
|
|
||||||
|
## Найденные баги
|
||||||
|
|
||||||
|
### Баг #1: showStages() — ❌ для этапов в процессе
|
||||||
|
Файл: `site/templates/index.html`
|
||||||
|
Nubes возвращает `isSuccessful: false` для этапов «в процессе». Код:
|
||||||
|
```javascript
|
||||||
|
const icon=ok===true?'✅':ok===false?'❌':'⏳';
|
||||||
|
```
|
||||||
|
`false === false` → ❌. Должно быть ⏳.
|
||||||
|
|
||||||
|
Исправление: проверять `s.dtFinish`. Если нет → ⏳, если есть и `true` → ✅, если есть и `false` → ❌.
|
||||||
|
|
||||||
|
### Баг #2: api_operations() — фильтр теряет новые инстансы
|
||||||
|
Файлы: `site/routes/api_test.py` + `site/routes/main.py`
|
||||||
|
После `tracker_add` (мгновенно in-memory) инстанс есть в трекере, но Nubes API ещё не обновил `explainedStatus` — инстанс имеет статус `"not created"`. Фильтр:
|
||||||
|
```python
|
||||||
|
and i.get("explainedStatus") not in ("deleted", "not created")
|
||||||
|
```
|
||||||
|
отбрасывает его. Инстанс есть в трекере, есть в ответе API — но статус "not created" → фильтр удаляет.
|
||||||
|
|
||||||
|
Исправление: для tracked UID не применять фильтр `"not created"`. Добавлять отсутствующие из трекера напрямую.
|
||||||
|
|
||||||
|
### Баг #3: Дублирующийся route
|
||||||
|
`main_bp` зарегистрирован ПЕРВЫМ, `api_test_bp` — ВТОРЫМ. Оба определяют `GET /api/operations/<int:svc_id>`. Flask использует первое совпадение → `main.api_operations` вызывается, `api_test.api_operations` — никогда.
|
||||||
|
|
||||||
|
### Потенциальная проблема #4: Multi-worker gunicorn
|
||||||
|
In-memory dict НЕ работает при >1 gunicorn worker. Один worker добавляет в свой `_data`, другой читает из своего. Если pythonk8s запускает >1 worker — ни одно решение не сработает.
|
||||||
|
|
||||||
|
Нужно проверить логи пода на `Booting worker with pid`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Исправления (v1.0.50)
|
||||||
|
|
||||||
|
1. `showStages()`: использовать `dtFinish` этапа
|
||||||
|
2. `api_operations()`: не фильтровать "not created" для tracked UID, добавлять сирот из трекера
|
||||||
|
3. Убрать дублирующийся route
|
||||||
+1
-1
@@ -6,7 +6,7 @@ from routes.main import bp as main_bp
|
|||||||
from routes.api import bp as api_bp
|
from routes.api import bp as api_bp
|
||||||
from routes.api_test import bp as api_test_bp
|
from routes.api_test import bp as api_test_bp
|
||||||
|
|
||||||
VERSION = "1.0.49"
|
VERSION = "1.0.50"
|
||||||
|
|
||||||
app = Flask(__name__, template_folder="templates", static_folder="static")
|
app = Flask(__name__, template_folder="templates", static_folder="static")
|
||||||
app.config["NUBES_API_ENDPOINT"] = os.getenv("NUBES_API_ENDPOINT", "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc")
|
app.config["NUBES_API_ENDPOINT"] = os.getenv("NUBES_API_ENDPOINT", "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc")
|
||||||
|
|||||||
+14
-2
@@ -42,11 +42,23 @@ def api_operations(svc_id):
|
|||||||
ops = detail.get("operations", [])
|
ops = detail.get("operations", [])
|
||||||
# только отслеживаемые инстансы этого сервиса
|
# только отслеживаемые инстансы этого сервиса
|
||||||
tracked = tracker_list()
|
tracked = tracker_list()
|
||||||
tracked_uids = {t["instanceUid"] for t in tracked if t["svcId"] == svc_id}
|
tracked_by_uid = {t["instanceUid"]: t for t in tracked if t["svcId"] == svc_id}
|
||||||
|
tracked_uids = set(tracked_by_uid.keys())
|
||||||
instances = get_instances(_client())
|
instances = get_instances(_client())
|
||||||
|
nubes_uids = {i["instanceUid"] for i in instances}
|
||||||
svc_instances = [i for i in instances
|
svc_instances = [i for i in instances
|
||||||
if i.get("instanceUid") in tracked_uids
|
if i.get("instanceUid") in tracked_uids
|
||||||
and i.get("explainedStatus") not in ("deleted", "not created")]
|
and i.get("explainedStatus") not in ("deleted",)]
|
||||||
|
# Добавить tracked инстансы, которых Nubes ещё не отдаёт (creating)
|
||||||
|
for uid, t in tracked_by_uid.items():
|
||||||
|
if uid not in nubes_uids:
|
||||||
|
svc_instances.append({
|
||||||
|
"instanceUid": uid,
|
||||||
|
"displayName": t["displayName"],
|
||||||
|
"serviceId": svc_id,
|
||||||
|
"svc": detail.get("svc", ""),
|
||||||
|
"explainedStatus": "creating",
|
||||||
|
})
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"svc": detail.get("svc", ""),
|
"svc": detail.get("svc", ""),
|
||||||
"operations": [{"svcOperationId": o["svcOperationId"], "operation": o["operation"]} for o in ops],
|
"operations": [{"svcOperationId": o["svcOperationId"], "operation": o["operation"]} for o in ops],
|
||||||
|
|||||||
@@ -263,8 +263,8 @@ function showStages(stages){
|
|||||||
if(!stages||!stages.length) return;
|
if(!stages||!stages.length) return;
|
||||||
let html='<div style="font-size:11px;font-weight:600;color:var(--muted);margin-top:4px;">Этапы</div>';
|
let html='<div style="font-size:11px;font-weight:600;color:var(--muted);margin-top:4px;">Этапы</div>';
|
||||||
stages.forEach(s=>{
|
stages.forEach(s=>{
|
||||||
const ok=s.isSuccessful;
|
const done=!!s.dtFinish;
|
||||||
const icon=ok===true?'✅':ok===false?'❌':'⏳';
|
const icon=done?(s.isSuccessful?'✅':'❌'):'⏳';
|
||||||
html+=`<div style="font-size:11px;padding:2px 0;">${icon} ${s.stage} — ${(s.duration||0).toFixed(1)}s</div>`;
|
html+=`<div style="font-size:11px;padding:2px 0;">${icon} ${s.stage} — ${(s.duration||0).toFixed(1)}s</div>`;
|
||||||
});
|
});
|
||||||
document.getElementById('stages-box').innerHTML=html;
|
document.getElementById('stages-box').innerHTML=html;
|
||||||
|
|||||||
Reference in New Issue
Block a user