Files
polygon/site/routes/run.py
T
naeel 58d54f2a95 v0.5.0: 3 стенда — изоляция состояния, WSGI middleware, OpenAPI server selector, POLYGON_STANDS env
- loader.py: STANDS dict, get_services/get_ops_index per stand, POLYGON_STANDS env
- mock_state.py: get_state(stand_id) — ленивая инициализация
- app.py: StandMiddleware — перезапись PATH_INFO до Flask-роутинга
- Все роуты: LocalProxy(state), LocalProxy(SERVICES/OPS_INDEX) через g.stand_id
- openapi.py: _build_servers() — по одному серверу на стенд
- services/stand{1,2,3}/ — 37 YAML на стенд из STANDS/dev и STANDS/test
- tests/test_api.py: +4 multi-stand теста (изоляция, fail-next, OpenAPI servers)
- Обратная совместимость: /api/v1/svc/... → стенд по умолчанию
2026-08-01 20:10:19 +04:00

82 lines
3.5 KiB
Python
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.
"""
routes/run.py — запуск операции.
Blueprint "run" с префиксом /api/v1/svc.
POST /instanceOperations/<uid>/run — выполнить операцию.
Вынесен в отдельный файл потому что логика run критична и специфична:
синхронный sleep, вызов state_machine.apply_effect, простановка dtFinish.
"""
import time
from flask import Blueprint, g, jsonify
from werkzeug.local import LocalProxy
import mock_state
import state_machine
import config.loader as _cfg
from utils.now import now as _now
state = LocalProxy(lambda: mock_state.get_state(g.stand_id))
SERVICES = LocalProxy(lambda: _cfg.get_services(g.stand_id))
bp = Blueprint("run", __name__, url_prefix="/api/v1/svc")
@bp.route("/instanceOperations/<uid>/run", methods=["POST"])
def run_operation(uid):
"""POST /instanceOperations/{uid}/run — выполнить операцию.
Алгоритм (синхронный, без потоков):
1. Проверить что операция существует (→ 404 если нет)
2. Записать dtStart = now()
3. sleep(DELAY) — эмуляция времени выполнения
4. Вызвать state_machine.apply_effect() — мутировать состояние инстанса
5. Записать dtFinish = now(), isSuccessful = True
После этого GET /instanceOperations/{uid} вернёт dtFinish,
и poll_until_done() в app-autotest завершит поллинг.
Возвращает {"ok": True} — формат как в реальном Nubes API.
Повторный вызов → 409 (operation already completed).
"""
op = state.get_operation(uid)
if not op:
return jsonify({"error": "operation not found"}), 404
# Защита от повторного запуска: если dtFinish уже проставлен —
# операция уже выполнена, повторный run может испортить данные.
if op.get("dtFinish") is not None:
return jsonify({"error": "operation already completed"}), 409
# Фиксируем время старта
op["dtStart"] = _now()
# Эмулируем задержку выполнения (по умолчанию 0.1с)
# ⛔ _cfg.DELAY — модульная переменная, может меняться через /_mock/delay
if _cfg.DELAY > 0:
time.sleep(_cfg.DELAY)
# Проверка /_mock/fail-next — симуляция падения операции
st = state
if st.fail_next:
st.fail_next = False # one-shot: сбросить после использования
op["dtFinish"] = _now()
op["isSuccessful"] = False
op["errorLog"] = "mock failure (requested by /_mock/fail-next)"
op["stages"] = [
{"stage": "mock_error", "status": "failed", "error": "mock failure"}
]
return jsonify({"ok": False, "error": op["errorLog"]})
# Применить эффект операции к состоянию инстанса
# (create → running+params, delete → удалить, modify → мерж params, ...)
state_machine.apply_effect(uid, st, _cfg.SERVICES)
# Фиксируем время завершения — операция выполнена успешно
op["dtFinish"] = _now()
op["isSuccessful"] = True
return jsonify({"ok": True})