mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-24 20:22:53 +00:00
fix(alerts): resolve intent overrides for agents merged with Proxmox nodes
A pulse-agent installed on a Proxmox node merges into one canonical
resource whose primary ID is node:{sourceID}, so the agent:{hostID}
reference the alert evaluator uses (hostResourceID) matched nothing in
the registry. Per-resource grace overrides and operator-state lookups
then silently resolved to factory policy: alerts fired with no grace
while the policy preview, which receives the canonical ID directly,
showed the override working. Index the prefixed agent ref as a
canonical identity alias so runtime resolution lands on the same
resource the override was saved against.
Refs #1497
Contract-Neutral: behavioral fix #1497: index agent: ref as canonical alias, no public contract shape change
This commit is contained in:
@@ -103,6 +103,13 @@ func canonicalAliases(resource Resource, primaryID, platformID, hostname string)
|
|||||||
strings.TrimSpace(resource.ID),
|
strings.TrimSpace(resource.ID),
|
||||||
)
|
)
|
||||||
values = append(values, resource.Identity.Hostnames...)
|
values = append(values, resource.Identity.Hostnames...)
|
||||||
|
// Alert evaluation references host-agent resources as "agent:{hostID}"
|
||||||
|
// (see alerts.hostResourceID). A merged PVE-node + pulse-agent resource
|
||||||
|
// takes "node:..." as its primary ID, so the prefixed agent ref must be
|
||||||
|
// indexed explicitly or per-resource policy lookups miss (#1497).
|
||||||
|
if agentID := strings.TrimSpace(canonicalAgentID(resource)); agentID != "" {
|
||||||
|
values = append(values, "agent:"+agentID)
|
||||||
|
}
|
||||||
if resource.Proxmox != nil {
|
if resource.Proxmox != nil {
|
||||||
values = append(
|
values = append(
|
||||||
values,
|
values,
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ func TestRefreshCanonicalIdentityPrefersTargetsAndCanonicalHostData(t *testing.T
|
|||||||
"tower.local",
|
"tower.local",
|
||||||
"machine-1",
|
"machine-1",
|
||||||
"agent-1",
|
"agent-1",
|
||||||
|
"agent:host-1",
|
||||||
}
|
}
|
||||||
if len(resource.Canonical.Aliases) != len(wantAliases) {
|
if len(resource.Canonical.Aliases) != len(wantAliases) {
|
||||||
t.Fatalf("aliases len = %d, want %d (%v)", len(resource.Canonical.Aliases), len(wantAliases), resource.Canonical.Aliases)
|
t.Fatalf("aliases len = %d, want %d (%v)", len(resource.Canonical.Aliases), len(wantAliases), resource.Canonical.Aliases)
|
||||||
@@ -435,6 +436,7 @@ func TestRefreshCanonicalIdentityCanonicalizesTargetResourceTypeAliases(t *testi
|
|||||||
"docker-runtime-1",
|
"docker-runtime-1",
|
||||||
"Tower",
|
"Tower",
|
||||||
"agent-1",
|
"agent-1",
|
||||||
|
"agent:host-1",
|
||||||
}
|
}
|
||||||
if len(resource.Canonical.Aliases) != len(wantAliases) {
|
if len(resource.Canonical.Aliases) != len(wantAliases) {
|
||||||
t.Fatalf("aliases len = %d, want %d (%v)", len(resource.Canonical.Aliases), len(wantAliases), resource.Canonical.Aliases)
|
t.Fatalf("aliases len = %d, want %d (%v)", len(resource.Canonical.Aliases), len(wantAliases), resource.Canonical.Aliases)
|
||||||
|
|||||||
@@ -194,6 +194,53 @@ func TestResourceRegistry_GetByReferenceResolvesSourceIDAndCanonicalAlias(t *tes
|
|||||||
if resolvedID != "agent-host-1" || resource.ID != "agent-host-1" {
|
if resolvedID != "agent-host-1" || resource.ID != "agent-host-1" {
|
||||||
t.Fatalf("alias reference resolved to id=%q resource=%q, want agent-host-1", resolvedID, resource.ID)
|
t.Fatalf("alias reference resolved to id=%q resource=%q, want agent-host-1", resolvedID, resource.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resource, resolvedID, ok = rr.GetByReference("agent:machine-1")
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("expected prefixed agent reference to resolve")
|
||||||
|
}
|
||||||
|
if resolvedID != "agent-host-1" || resource.ID != "agent-host-1" {
|
||||||
|
t.Fatalf("agent reference resolved to id=%q resource=%q, want agent-host-1", resolvedID, resource.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A pulse-agent installed on a Proxmox node merges into one canonical
|
||||||
|
// resource whose primary ID is "node:{sourceID}", not "agent:{agentID}".
|
||||||
|
// Alert evaluation still references the host as "agent:{hostID}"
|
||||||
|
// (alerts.hostResourceID), and per-resource intent policies are keyed by the
|
||||||
|
// canonical registry ID, so this reference must resolve or grace overrides
|
||||||
|
// silently fall back to factory (#1497).
|
||||||
|
func TestResourceRegistry_GetByReferenceResolvesAgentRefOnMergedProxmoxHost(t *testing.T) {
|
||||||
|
rr := NewRegistry(nil)
|
||||||
|
rr.IngestResources([]Resource{{
|
||||||
|
ID: "agent-abcdef123456",
|
||||||
|
Type: ResourceTypeAgent,
|
||||||
|
Name: "proxmox2",
|
||||||
|
Agent: &AgentData{
|
||||||
|
AgentID: "machine-2",
|
||||||
|
Hostname: "proxmox2.local",
|
||||||
|
},
|
||||||
|
Proxmox: &ProxmoxData{
|
||||||
|
SourceID: "node/proxmox2",
|
||||||
|
NodeName: "proxmox2",
|
||||||
|
},
|
||||||
|
}})
|
||||||
|
|
||||||
|
merged, ok := rr.Get("agent-abcdef123456")
|
||||||
|
if !ok || merged.Canonical == nil {
|
||||||
|
t.Fatalf("merged host missing canonical identity: ok=%v resource=%+v", ok, merged)
|
||||||
|
}
|
||||||
|
if merged.Canonical.PrimaryID != "node:node/proxmox2" {
|
||||||
|
t.Fatalf("merged host primary ID = %q, want node:node/proxmox2", merged.Canonical.PrimaryID)
|
||||||
|
}
|
||||||
|
|
||||||
|
resource, resolvedID, ok := rr.GetByReference("agent:machine-2")
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("expected agent reference to resolve on merged Proxmox host")
|
||||||
|
}
|
||||||
|
if resolvedID != "agent-abcdef123456" || resource.ID != "agent-abcdef123456" {
|
||||||
|
t.Fatalf("agent reference resolved to id=%q resource=%q, want agent-abcdef123456", resolvedID, resource.ID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMonitorAdapterKeepsSameNamedProxmoxProvidersDistinct(t *testing.T) {
|
func TestMonitorAdapterKeepsSameNamedProxmoxProvidersDistinct(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user