diff --git a/site/app.py b/site/app.py index 64d17fa..e60515a 100644 --- a/site/app.py +++ b/site/app.py @@ -19,7 +19,7 @@ from routes.api_scenario_run import bp_run as api_scenario_run_bp from routes.api_scenario_defs import bp_defs as api_scenario_defs_bp # Версия — меняется при КАЖДОМ изменении кода. Показывается в топбаре UI. -VERSION = "1.2.0" +VERSION = "1.2.1" app = Flask(__name__, template_folder="templates", static_folder="static") app.config["NUBES_API_ENDPOINT"] = os.getenv("NUBES_API_ENDPOINT", "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc") diff --git a/site/operations/executor.py b/site/operations/executor.py index 2d99953..938799f 100644 --- a/site/operations/executor.py +++ b/site/operations/executor.py @@ -2,15 +2,18 @@ Единый executor операции Nubes. Делает всё до /run включительно. НЕ поллит. +Вызывает tracker_add для create сразу после получения instance_uid (защита от сирот). Используется api_test.py (ручной) и scenario.py (сценарный). """ from api.utils import find_uid, uid_from_location from operations.terraform import send_params_terraform +from operations.tracker import add as tracker_add def execute_operation(client, service_id, operation, instance_uid, params, - svc_op_id=None, display_name=None): + svc_op_id=None, display_name=None, descr=None, + client_id="", stand=""): """Запустить операцию и вернуть результат. Args: @@ -21,6 +24,9 @@ def execute_operation(client, service_id, operation, instance_uid, params, params: dict — {numeric_param_id: value} svc_op_id: int|None — svcOperationId (игнорируется для create) display_name: str|None — displayName (только для create) + descr: str|None — описание инстанса + client_id: str — для tracker_add + stand: str — для tracker_add Returns: dict {ok, error, failed_step, instance_uid, op_uid, display_name} @@ -32,7 +38,8 @@ def execute_operation(client, service_id, operation, instance_uid, params, if is_create: if not display_name: display_name = f"autotest-{service_id}" - descr = f"created by autotest" + if not descr: + descr = "created by autotest" payload = {"serviceId": service_id, "displayName": display_name, "descr": descr} try: resp = client.post("/instances", payload) @@ -44,6 +51,12 @@ def execute_operation(client, service_id, operation, instance_uid, params, return {"ok": False, "error": "No instanceUid in response", "failed_step": "instances", "instance_uid": None, "op_uid": None, "display_name": display_name} + # tracker_add СРАЗУ после instance_uid, до params/run — защита от сирот + try: + tracker_add(client_id, stand, instance_uid, service_id, display_name) + except Exception: + pass # не ронять операцию из-за трекера + # --- Шаг 2: POST /instanceOperations --- if is_create: op_payload = {"instanceUid": instance_uid, "operation": operation} diff --git a/site/operations/scenario.py b/site/operations/scenario.py index c931dc2..d5ad2aa 100644 --- a/site/operations/scenario.py +++ b/site/operations/scenario.py @@ -128,16 +128,19 @@ def run_scenario(client, steps, client_id, stand, user_email, app_version, scena if is_create: instance_uid = None display_name = f"{AUTOTEST_PREFIX}{scenario_name}-{uuid.uuid4().hex[:6]}" + descr = f"scenario {scenario_name} step {step_num}" else: instance_uid = _resolve_instance_uid(step, bindings, instance_map) if not instance_uid: raise ValueError(f"No instance for step {step_num} — need CREATE before non-create operations") display_name = None + descr = None # 4. Запуск через executor result = execute_operation( client, svc_id, op_name, instance_uid, resolved_params, - svc_op_id=svc_op_id, display_name=display_name + svc_op_id=svc_op_id, display_name=display_name, descr=descr, + client_id=client_id, stand=stand ) if not result["ok"]: raise RuntimeError(f"{op_name}: {result['error']} (step: {result['failed_step']})") diff --git a/site/routes/api_test.py b/site/routes/api_test.py index 86ba5c3..26aab87 100644 --- a/site/routes/api_test.py +++ b/site/routes/api_test.py @@ -193,9 +193,11 @@ def api_test(): if not display_name: display_name = f"autotest-{svc_id}" display_name = _unique_display_name(client, display_name) + descr = f"created by autotest v{current_app.config.get('VERSION', '')}" result = execute_operation( client, svc_id, op_name, None, params, - svc_op_id=svc_op_id, display_name=display_name + svc_op_id=svc_op_id, display_name=display_name, descr=descr, + client_id=get_client_id(), stand=get_stand() ) if not result["ok"]: return jsonify({"status": "FAIL", "error": result["error"]}), 500 @@ -203,14 +205,6 @@ def api_test(): op_uid = result["op_uid"] display_name = result["display_name"] - # Трекер - try: - tracker_add(get_client_id(), get_stand(), instance_uid, svc_id, display_name) - except Exception as e: - import traceback - print(f"[TRACKER ERROR] add failed: {e}", flush=True) - traceback.print_exc() - # Фоновый поллинг user_email = get_token_info().get("email", "") app_version = current_app.config.get("VERSION", "") diff --git a/site/static/js/params-render.js b/site/static/js/params-render.js index 3f62d60..e826248 100644 --- a/site/static/js/params-render.js +++ b/site/static/js/params-render.js @@ -31,6 +31,7 @@ function renderParamRow(p, allInst) { function renderMapFixedRow(p, dfl) { const dd = p.dataDescriptor; + const labelCls = p.isRequired ? (dfl ? 'req' : 'req-nodfl') : ''; let subHtml = ''; let dflObj = {}; try { dflObj = JSON.parse(dfl || '{}'); } catch (e) { }