mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-11-01 16:29:50 +00:00
Keep socket-free LXC discovery truthful at its inventory limit
Inspect the remaining bounded config listing after finding 128 running guests. A 129th running guest or an unestablished later guest must fall back and remain degraded if pct cannot establish a complete list, rather than silently erase guests behind a complete status. Retain the existing PrivateNetwork and probe bounds, with 127/128/129 and stopped/unknown-tail provider regressions. Change-source: pulse-maintainer
This commit is contained in:
@@ -615,7 +615,14 @@ least-privilege installs using that helper reported `provider_unavailable`
|
||||
on every report (#2511). Discovery hands the whole node back to `pct list`
|
||||
whenever it cannot establish a guest's state (no cgroup v2, an unreadable
|
||||
listing, config, cgroup or process, a search limit, or cancellation); only a
|
||||
guest without a cgroup counts as stopped. The fixed `lxc-info -n <vmid> -p`
|
||||
guest without a cgroup counts as stopped. Exactly 128 running guests remain
|
||||
admissible, but discovery must inspect the rest of the bounded config listing:
|
||||
a 129th running guest or an unestablished later guest makes discovery unavailable,
|
||||
not a truncated complete inventory. The existing bounded `pct list` fallback
|
||||
then either establishes the full node or remains degraded under PrivateNetwork.
|
||||
`TestSocketFreeProxmoxLXCDiscoveryCannotTruncateCompleteInventory` covers 127/128/129
|
||||
running guests and stopped/unknown guests after the limit through discovery and
|
||||
collection. The fixed `lxc-info -n <vmid> -p`
|
||||
PID lookup remains for that path, and `pct df` remains the per-container
|
||||
fallback, including through the `--grant-pct` wrapper.
|
||||
`TestHelperProxmoxLXCFilesystemsNeedNoPctOrLXCSockets` drives the collector
|
||||
|
||||
@@ -966,7 +966,14 @@ opt-in owned tmpfs collector fixture. Installed-agent and model qualification
|
||||
are tracked separately in PATROL_ASSISTANT_CUSTOMER_JOURNEY.md.
|
||||
|
||||
The same observer reads Proxmox LXC guests for the host agent and its typed
|
||||
helper (#2511). An `lxc` request names the decimal VMID; the pinned process
|
||||
helper (#2511). Its socket-free producer must not label the first 128 guests as
|
||||
a complete inventory without inspecting later configs. A 129th running guest or
|
||||
an unknown guest after the limit leaves collection unavailable/degraded through
|
||||
the existing bounded fallback; a stopped later guest does not invalidate 128
|
||||
established guests. This preserves the consumer's distinction between complete
|
||||
inventory replacement and explicit partial omissions. The producer boundary is
|
||||
covered by `TestSocketFreeProxmoxLXCDiscoveryCannotTruncateCompleteInventory`;
|
||||
partial-wire, omitted-reading and complete-empty cache tests remain authoritative. An `lxc` request names the decimal VMID; the pinned process
|
||||
must sit in that guest's exact `lxc/<vmid>` cgroup and be its init (`NSpid` is
|
||||
exactly `<pid> 1`, so the PID 1 of a namespace nested inside the guest, such
|
||||
as Docker in LXC, cannot stand in for it), checked before and after the read.
|
||||
|
||||
@@ -609,3 +609,53 @@ func TestHelperProxmoxLXCFilesystemsNeedNoPctOrLXCSockets(t *testing.T) {
|
||||
t.Fatalf("126 disks = %+v, want rootfs and the mp0 mount with its own usage", got[0].Disks)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSocketFreeProxmoxLXCDiscoveryCannotTruncateCompleteInventory(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
running int
|
||||
tail string
|
||||
complete bool
|
||||
}{
|
||||
{"below limit", proxmoxLXCMaxContainers - 1, "", true},
|
||||
{"exact limit", proxmoxLXCMaxContainers, "", true},
|
||||
{"exact limit plus stopped", proxmoxLXCMaxContainers, "stopped", true},
|
||||
{"over limit", proxmoxLXCMaxContainers + 1, "", false},
|
||||
{"unknown after limit", proxmoxLXCMaxContainers, "unknown", false},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h := newFakeProxmoxLXCHost()
|
||||
for n := 0; n < tc.running; n++ {
|
||||
vmid, pid := 100+n, 1000+n
|
||||
h.config(vmid, fmt.Sprintf("hostname: ct%d\nrootfs: local:vm-%d-disk-0,size=1G\n", vmid, vmid))
|
||||
h.cgroup(strconv.Itoa(vmid)+"/ns", pid)
|
||||
h.proc(pid, fmt.Sprintf("%d\t1", pid), fmt.Sprintf("/lxc/%d/ns", vmid))
|
||||
h.observations[pid] = map[string]lxcObservation{"/": lxcUsage(4096, 1024, 3072)}
|
||||
}
|
||||
if tc.tail != "" {
|
||||
vmid := 100 + tc.running
|
||||
h.config(vmid, "hostname: tail\nrootfs: local:tail,size=1G\n")
|
||||
if tc.tail == "unknown" {
|
||||
h.cgroup(strconv.Itoa(vmid) + "/ns") // exists, but has no established init
|
||||
}
|
||||
}
|
||||
collector, commands := h.collector(t)
|
||||
agent := &Agent{logger: zerolog.Nop(), collector: collector}
|
||||
rows, ok := agent.discoverRunningProxmoxLXCContainers(t.Context())
|
||||
if ok != tc.complete || (ok && len(rows) != tc.running) || (!ok && rows != nil) || *commands != 0 {
|
||||
t.Fatalf("socket-free discovery: complete=%v rows=%d commands=%d", ok, len(rows), *commands)
|
||||
}
|
||||
result := agent.collectProxmoxLXCFilesystemsResult(t.Context())
|
||||
if tc.complete {
|
||||
if !result.Applicable || result.Degraded || result.Inventory == nil || result.Inventory.Status != "complete" || len(result.Inventory.Containers) != tc.running || *commands != 0 {
|
||||
t.Fatalf("complete boundary inventory=%+v commands=%d", result, *commands)
|
||||
}
|
||||
if last := result.Inventory.Containers[tc.running-1]; last.VMID != 99+tc.running || len(last.Disks) != 1 || last.Disks[0].UsedBytes != 1024 {
|
||||
t.Fatalf("boundary guest reading lost: %+v", last)
|
||||
}
|
||||
} else if !result.Applicable || !result.Degraded || result.Inventory != nil || *commands != 1 {
|
||||
t.Fatalf("unestablished node appeared complete instead of one failed pct fallback: %+v commands=%d", result, *commands)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,8 +130,11 @@ func (a *Agent) discoverRunningProxmoxLXCContainers(ctx context.Context) ([]prox
|
||||
return nil, false
|
||||
}
|
||||
running = append(running, proxmoxLXCRunningContainer{VMID: vmid, Name: proxmoxLXCConfigName(string(raw), vmid), PID: pid})
|
||||
if len(running) == proxmoxLXCMaxContainers {
|
||||
break
|
||||
if len(running) > proxmoxLXCMaxContainers {
|
||||
// A truncated list cannot authoritatively declare the node complete.
|
||||
// Keep the same bound as pct parsing and let the caller report a
|
||||
// degraded fallback, rather than erase uninspected running guests.
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return running, true
|
||||
|
||||
Reference in New Issue
Block a user