From a06a147c4c0b380c6afdee31bc11d8e570ea7d10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Mon, 27 Jul 2026 12:43:07 +0400 Subject: [PATCH] v1.0.71: finished status shows instance name --- DOCS/ARCHITECTURE.md | 2 +- DOCS/HISTORY.md | 20 +++++++++++++++++++- site/app.py | 2 +- site/routes/main.py | 19 ++++++++++++++++--- site/templates/index.html | 8 +++++--- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/DOCS/ARCHITECTURE.md b/DOCS/ARCHITECTURE.md index 67de0dc..ff24fe1 100644 --- a/DOCS/ARCHITECTURE.md +++ b/DOCS/ARCHITECTURE.md @@ -1,6 +1,6 @@ # Архитектура app-autotest — полный документ -v1.0.69, 27.07.2026 +v1.0.71, 27.07.2026 --- diff --git a/DOCS/HISTORY.md b/DOCS/HISTORY.md index 99cc710..f6d84a6 100644 --- a/DOCS/HISTORY.md +++ b/DOCS/HISTORY.md @@ -1,6 +1,24 @@ # История разработки app-autotest -# v1.0.69 (27.07.2026) — autotest prefix fixed in CREATE field +# v1.0.71 (27.07.2026) — finished status shows instance name + +### Что изменилось +- В `site/templates/index.html` финальная строка после `OK/FAIL` теперь показывает имя инстанса. +- Имя операции и длительность остаются рядом, отдельно от имени инстанса. + +### Что это чинит +- Ситуацию, когда после завершения delete/modify было видно только название операции, но не ясно, над каким инстансом она выполнялась. + +# v1.0.70 (27.07.2026) — dedupe autotest instances by displayName + +### Что изменилось +- В `site/routes/main.py` сервисный список теперь склеивает записи по `displayName`. +- Если cloud уже отдал `running`, он выигрывает у tracker-fallback `creating`. + +### Что это чинит +- Дубли одной и той же autotest-записи после CREATE, когда в списке появлялись и `running`, и `creating`. + +## v1.0.69 (27.07.2026) — autotest prefix fixed in CREATE field ### Что изменилось - В `site/templates/index.html` префикс `autotest-` отображается отдельным фиксированным текстом. diff --git a/site/app.py b/site/app.py index 5092d78..cee0b82 100644 --- a/site/app.py +++ b/site/app.py @@ -6,7 +6,7 @@ from routes.main import bp as main_bp from routes.api import bp as api_bp from routes.api_test import bp as api_test_bp -VERSION = "1.0.69" +VERSION = "1.0.71" 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") diff --git a/site/routes/main.py b/site/routes/main.py index e1941e5..c3fb6d8 100644 --- a/site/routes/main.py +++ b/site/routes/main.py @@ -156,7 +156,19 @@ def api_operations(svc_id): tracked_by_uid = {t["instanceUid"]: t for t in tracked if t["svcId"] == svc_id} instances = get_instances(client) nubes_uids = {i["instanceUid"] for i in instances} - svc_instances = [] + svc_instances_by_name = {} + + def _store_instance(item): + display_name = str(item.get("displayName", "") or "") + if not display_name.startswith(AUTOTEST_PREFIX): + return + existing = svc_instances_by_name.get(display_name) + if existing is None: + svc_instances_by_name[display_name] = item + return + if existing.get("status") == "creating" and item.get("status") != "creating": + svc_instances_by_name[display_name] = item + for i in instances: if not str(i.get("displayName", "")).startswith(AUTOTEST_PREFIX): continue @@ -164,11 +176,11 @@ def api_operations(svc_id): continue item = dict(i) item["status"] = _resolve_instance_status(item, tracked_by_uid.get(i.get("instanceUid"))) - svc_instances.append(item) + _store_instance(item) # Если Nubes ещё не отдаёт только что созданный autotest-инстанс, добавляем его из трекера. for uid, t in tracked_by_uid.items(): if uid not in nubes_uids: - svc_instances.append({ + _store_instance({ "instanceUid": uid, "displayName": t["displayName"], "serviceId": svc_id, @@ -176,6 +188,7 @@ def api_operations(svc_id): "explainedStatus": "", "status": "creating", }) + svc_instances = list(svc_instances_by_name.values()) return jsonify({"svc": detail.get("svc", ""), "operations": ops, "instances": svc_instances}) except Exception as e: return jsonify({"error": str(e)}), 500 diff --git a/site/templates/index.html b/site/templates/index.html index 975c927..bac3aa4 100644 --- a/site/templates/index.html +++ b/site/templates/index.html @@ -242,7 +242,7 @@ function showParams(opId,opName){ }); } -function setFinishedState(statusText, statusClass, statusError, statusDuration){ +function setFinishedState(statusText, statusClass, statusError, statusDuration, instanceName){ const btn=document.getElementById('btn-test'); btn.disabled=false; btn.textContent='Готово'; @@ -250,7 +250,8 @@ function setFinishedState(statusText, statusClass, statusError, statusDuration){ const opName=selectedOp?.opName||'операция'; const durationText=statusDuration!==undefined&&statusDuration!==null?`${statusDuration}s`:'0s'; const extra=statusError||''; - document.getElementById('test-status').innerHTML=` ${statusText} ${opName} ${durationText} ${extra}`; + const instName=instanceName||findInstName()||selectedInst||''; + document.getElementById('test-status').innerHTML=` ${statusText} ${opName} ${durationText} ${instName} ${extra}`; } function setRunningState(opName, displayName){ @@ -289,13 +290,14 @@ async function executeOp(params){ if(d.status==='FAIL'){document.getElementById('test-status').innerHTML=` FAIL ${d.error||''}`;btn.disabled=false;return;} // start polling const opUid=d.opUid; + const instanceName=d.displayName||displayName; pollTimer=setInterval(async()=>{ const sr=await fetch('/api/test/status/'+opUid); const sd=await sr.json(); showStages(sd.stages||[]); if(sd.status!=='RUNNING'){ stopPoll(); - setFinishedState(sd.status, sd.status==='OK'?'badge-success':'badge', sd.error, sd.duration); + setFinishedState(sd.status, sd.status==='OK'?'badge-success':'badge', sd.error, sd.duration, instanceName); if(sd.status==='OK'){ await refreshInstances(); }