deployment_query.py
4.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"""U-043: read-side controllers for `v_deployment_status` -- no operation_id
for these exists anywhere in 03-C/the MVCP JSON (this whole view is a PH5
addition with no 02-V spec to begin with, see development-plan.md's own
U-043 note), same "view's own filter/display needs directly justify a new
read endpoint" precedent as `c_task_list`/`c_artifact_list` before it.
`deployment` has no `project_id` column (01-M 1.15) -- there's exactly one
graphlemon self-target, not one per project, so these never take a
project_id filter the way task/artifact listing does.
"""
_STATUSES = (
"staged", "candidate_starting", "health_check_running", "health_check_passed",
"health_check_failed", "cutover_in_progress", "live", "monitoring",
"anomaly_detected", "rollback_in_progress", "rolled_back", "rollback_failed",
"blocked_pending_human", "monitoring_window_clean", "finalized",
)
_TERMINAL_STATUSES = {"rolled_back", "rollback_failed", "finalized", "blocked_pending_human"}
class DeploymentNotFoundError(Exception):
"""NOT_FOUND -- c_deployment_get on a nonexistent deployment_id."""
def _deployment_row_to_dict(r):
return {
"deployment_id": r[0], "git_commit_hash": r[1], "previous_commit_hash": r[2], "status": r[3],
"candidate_port": r[4], "candidate_instance_dir": r[5],
"started_at": r[6], "cutover_at": r[7], "monitoring_closed_at": r[8],
"rollback_at": r[9], "rollback_reason": r[10], "previous_instance_dir": r[11],
"is_active": r[3] not in _TERMINAL_STATUSES,
}
_DEPLOYMENT_COLUMNS = (
"deployment_id, git_commit_hash, previous_commit_hash, status, candidate_port, candidate_instance_dir, "
"started_at, cutover_at, monitoring_closed_at, rollback_at, rollback_reason, previous_instance_dir"
)
def list_deployments(conn, limit=20):
"""Newest-first (`rowid DESC` -- `deployment` has no created_at column
of its own, `started_at` is nullable until c_deploy_supervisor_stage
actually sets it, same ordering convention U-016/U-037 already use
elsewhere for "most recent" when a table lacks a dedicated timestamp)."""
rows = conn.execute(
"SELECT {} FROM deployment ORDER BY rowid DESC LIMIT ?".format(_DEPLOYMENT_COLUMNS), (limit,)
).fetchall()
return [_deployment_row_to_dict(r) for r in rows]
def get_current_deployment(conn):
"""The single non-terminal deployment, if any (01-M 1.15's own
`ux_deployment_active` constraint guarantees at most one) -- what
`v_deployment_status` actually wants front and center: "当前是否有
deployment 在进行中". Returns None when nothing is active (the common
case -- most of the time nothing is deploying)."""
row = conn.execute(
"SELECT {} FROM deployment WHERE status NOT IN ({}) ORDER BY rowid DESC LIMIT 1".format(
_DEPLOYMENT_COLUMNS, ", ".join("'{}'".format(s) for s in _TERMINAL_STATUSES)
)
).fetchone()
return _deployment_row_to_dict(row) if row else None
def get_deployment(conn, deployment_id):
"""Includes recent `deploy_health_signal` rows (newest first) -- "最近
一次健康信号采样结果" per this Unit's own Done line, but the full recent
history (not just the latest) is what actually lets a human tell "one
blip" from "a real pattern" at a glance, same reasoning U-026's
`v_graph_visualizer` tooltip already applies to per-node detail."""
row = conn.execute(
"SELECT {} FROM deployment WHERE deployment_id = ?".format(_DEPLOYMENT_COLUMNS), (deployment_id,)
).fetchone()
if row is None:
raise DeploymentNotFoundError(deployment_id)
result = _deployment_row_to_dict(row)
result["health_signals"] = _recent_signals(conn, deployment_id)
return result
def _recent_signals(conn, deployment_id, limit=50):
rows = conn.execute(
"SELECT signal_id, sampled_at, signal_type, status, detail FROM deploy_health_signal "
"WHERE deployment_id = ? ORDER BY rowid DESC LIMIT ?",
(deployment_id, limit),
).fetchall()
return [
{"signal_id": r[0], "sampled_at": r[1], "signal_type": r[2], "status": r[3], "detail": r[4]}
for r in rows
]