feat(alerts): renames carry their alerts — monitor and host renames move rules, windows and history (#5065)
#5065 "Renames carry their alerts". Alert rules, maintenance-window scopes and metric history key a thing by (source, name), so a rename used to leave the rules watching a name nothing reported under, and split the history. - core/renames.carry_rename moves AlertRule.resource_name, MaintenanceWindow.scope_resource, plugin_metrics and plugin_metrics_hourly to the new name in the caller's transaction. A host rename also carries its parts: host_agent "<host>:…" and docker "<host>/…" (HOST_SOURCES). Matching is exact (a prefix compare, not LIKE or a collation range). History left under the new name by something deleted is dropped first rather than merged in. - Web monitor edit, MCP update_monitor and web host edit call it; MCP answers with `carried` (it used to return a warning naming stranded rules). - A name another monitor of the same type, or another host, already has is refused on create and rename: two would share one history. - The rename is audited (monitor.updated / host.renamed) with what moved. Tests: an integration test renames a firing monitor through the web route and finds its rule still firing and cleared by a sample on the new name, its window and raw+hourly history moved and an orphan's history dropped; a host rename moves agent and container parts but not a longer-named host's or another source's; a guard that every host_agent resource is "<host>" or "<host>:…". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -114,6 +114,20 @@ def monitor_fields(mtype: str, *, target: str, name: str = "", check_interval_se
|
||||
}
|
||||
|
||||
|
||||
async def check_name_free(db, mtype: str, name: str, exclude_id: str | None = None) -> None:
|
||||
"""ValueError when another monitor of this type already has this name.
|
||||
|
||||
A monitor's checks are recorded, and its alert rules watch it, by
|
||||
(type, name), so two monitors sharing both would share one history.
|
||||
"""
|
||||
q = select(Monitor.id).where(Monitor.type == mtype, Monitor.name == name)
|
||||
if exclude_id:
|
||||
q = q.where(Monitor.id != exclude_id)
|
||||
if (await db.execute(q.limit(1))).first():
|
||||
raise ValueError(f"another {mtype} monitor is already named {name!r}; its checks and "
|
||||
"alert rules go by that name, so pick a different one.")
|
||||
|
||||
|
||||
async def monitor_rows(db, range_key: str):
|
||||
"""Per-monitor state: latest result, heartbeat strip, uptime and TLS days.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user