mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 20:52:51 +00:00
f4ddeeb0e5
Follow-up to #2620, which documented both conflations below as residuals. ## Problem Two Docker alert producers wrote names into identity slots: - A container reported without an ID alerted under its Docker host's own reference, `docker:<host>`. Its alerts showed on the host, joined the host's resource history (which #2620 binds to the host), were never cleared when the container left the report (cleanup matches `docker:<host>/`), and Patrol registered `docker:<host>` on both the host and the container, so the host's scope alias was ambiguous. - A Swarm service without an ID put its normalized name in the `/service/<ID>` slot, so a name equal to another service's Swarm ID shared that service's alerts and history. #2620's resolver had to treat every service in such a cluster as a conflict. Pulse's own agent always sends Docker and Swarm IDs, so this needs a malformed or third-party report. The registry still shows those containers and services. ## Change - `internal/alerts`: `DockerHostResourceID`, `DockerContainerResourceID(host, id, name)` and `DockerServiceResourceID` are the only builders. An ID-less container alerts under `docker:<host>/name:<name>` and an ID-less service under `docker:<host>/service/name:<normalized name>`. Docker and Swarm IDs never contain a colon, so neither can equal a host or ID reference. One with neither an ID nor a name raises no alert. - The container and service policy switches classify by the first path segment after the host, so a container name containing `/service/` stays a container. - Report cleanup, host removal and the host's offline alert also clear a pre-upgrade container alert left on exactly `docker:<host>` (recorded host must match, container ID empty). A pending image update recorded there keeps its age when the first report still shows it pending. - Canonical state IDs were split at the first `::`, but colliding Docker hosts get IDs like `<base>::<suffix>`. Every container alert of such a host was rewritten to `docker:<base>`, the other host's reference: it joined that host's history and missed its own host's cleanup. `splitCanonicalStateID` now splits before the alert's recorded spec ID, else after its resource ID, so alerts saved the old way also recover on restore. - Patrol registers the same builders' output as scope aliases, with the container's name. - History: a service slot containing `:` is a name reference (never binds, never retried), and the cluster-wide conflict for ID-less services is gone. A real service ID reference now binds even when its cluster also has an ID-less service. - Contract notes in alerts, ai-runtime, monitoring and unified-resources. Remaining legacy boundary (in the unified-resources contract): rows journaled before this change keep their old reference. An ID-less container's old rows stay in its host's history, and an ID-less service's old rows bind only to a service whose ID equals its normalized name. ## Proof - `TestDockerAlertsWithoutIDsUseTheirOwnReferences` (real `CheckDockerHost`: fired and active references, removal cleanup, pre-upgrade leftover on report and offline, config-save container/service switches). - `TestDockerHostIDWithSeparatorKeepsItsAlertReferences`, `TestDockerAlertsRestoredWithCutShortReferencesRecover` (writes `active-alerts.json`, restores through `LoadActiveAlerts`) and `TestDockerContainerWithoutIDKeepsPendingUpdateAgeAcrossUpgrade`. - `TestOwnerAlertTimelinesUseCanonicalHistoryIdentity`: real producers into the SQLite history. The ID-less container and the service named like `web`'s Swarm ID stay out of the host's and `web`'s histories. - `TestResolvePatrolScopeResolvesDockerAlertResourceIDs`: the host's alias stays unambiguous, and the ID-less container's name reference resolves to the container in the unified read state. - `TestHistoryIdentityMonitorAdapterResolvesDockerHostAndServiceReferences`: the ID reference binds before and after the service leaves inventory, and name references never bind or retry. - Mutation check: putting back each old behavior fails at least one of these tests. Covered: host fallback, unmarked service name, no skip for unidentifiable containers or services, each legacy clause and its host check, the update-age carry-over, first-`::` splitting with and without the spec rule, substring classification, the Patrol alias without the name, and the resolver accepting a `name:` slot.