fix: remote job stuck after SSH update completes (v0.5.4)

Treat agent_update_state as source of truth for active job; poll log thread no longer overwrites finished status.
This commit is contained in:
2026-07-08 12:30:14 +10:00
parent 253b80c500
commit 1ad01534f1
3 changed files with 44 additions and 4 deletions
+19 -3
View File
@@ -126,7 +126,10 @@ def _poll_ssh_update_log_loop(
session = SessionLocal() session = SessionLocal()
try: try:
row = session.get(Host, host_id) row = session.get(Host, host_id)
if row is None or row.agent_update_state != "running": if row is None or (row.agent_update_state or "").strip().lower() != "running":
continue
payload = dict(row.remote_action or {})
if (payload.get("status") or "").strip().lower() != "running":
continue continue
cfg = get_effective_linux_admin_config(session) cfg = get_effective_linux_admin_config(session)
if not cfg.configured: if not cfg.configured:
@@ -148,7 +151,12 @@ def _poll_ssh_update_log_loop(
continue continue
if tail: if tail:
break break
session.refresh(row)
if (row.agent_update_state or "").strip().lower() != "running":
continue
payload = dict(row.remote_action or {}) payload = dict(row.remote_action or {})
if (payload.get("status") or "").strip().lower() != "running":
continue
if tail: if tail:
payload["output"] = tail payload["output"] = tail
payload["message"] = "Выполняется обновление… (лог с хоста)" payload["message"] = "Выполняется обновление… (лог с хоста)"
@@ -339,8 +347,16 @@ def get_remote_action_status(host: Host) -> dict[str, Any]:
payload = dict(host.remote_action or {}) payload = dict(host.remote_action or {})
if not payload: if not payload:
return {"active": False, "host_id": host.id} return {"active": False, "host_id": host.id}
status = payload.get("status") or host.agent_update_state or "unknown" agent_state = (host.agent_update_state or "").strip().lower()
active = status == "running" or host.agent_update_state == "running" if agent_state == "running":
active = True
status = payload.get("status") or "running"
elif agent_state in ("success", "failed"):
active = False
status = payload.get("status") or agent_state
else:
status = payload.get("status") or host.agent_update_state or "unknown"
active = status == "running"
return { return {
"active": active, "active": active,
"host_id": host.id, "host_id": host.id,
+1 -1
View File
@@ -1,5 +1,5 @@
"""Единый источник версии SAC (API, health, логи, OpenAPI).""" """Единый источник версии SAC (API, health, логи, OpenAPI)."""
APP_NAME = "Security Alert Center" APP_NAME = "Security Alert Center"
APP_VERSION = "0.5.3" APP_VERSION = "0.5.4"
APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}" APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}"
+24
View File
@@ -247,6 +247,30 @@ def test_clear_stale_running_remote_actions(db_session):
assert host.remote_action["ok"] is False assert host.remote_action["ok"] is False
def test_get_remote_action_status_ignores_stale_running_payload(db_session):
from app.services.host_remote_actions import get_remote_action_status
host = Host(
hostname="done-linux",
os_family="linux",
product="ssh-monitor",
agent_update_state="success",
remote_action={
"status": "running",
"message": "Выполняется обновление… (лог с хоста)",
"output": "=== Script update completed successfully ===",
"ok": True,
"finished_at": "2026-07-08T02:21:11+00:00",
},
)
db_session.add(host)
db_session.commit()
status = get_remote_action_status(host)
assert status["active"] is False
assert status["agent_update_state"] == "success"
def test_cancel_host_remote_job_api(jwt_headers, client, db_session): def test_cancel_host_remote_job_api(jwt_headers, client, db_session):
host = Host( host = Host(
hostname="cancel-me", hostname="cancel-me",