mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Give mock mode Docker-in-LXC coverage
The Proxmox page's nested-container row cue, the drawer's nested Docker card, and the proxmox-pve platform scoping of nested workloads had no local reproduction at all: mock mode never generated a Docker host with the proxmox-lxc-docker: identity prefix, so the surface could only be seen against a live estate with node agents, exec-scoped tokens, and the server-side inventory opt-in. That is how it shipped invisible. The fixture now marks the first two running LXC guests as probed Docker hosts and appends a nested Docker host for each, following the production proxmoxGuestDockerAgentID convention of proxmox-lxc-docker:<instance>:<node>:<vmid>. One guest nests a single container and one nests several, so the row cue renders both its singular and plural counts. The nested hosts mirror the shape of the real pct exec inventory: guest-derived sizing and usage, one rootfs disk, and no native engine inventory, host I/O rates, temperature, machine ID, or Swarm membership.
This commit is contained in:
@@ -641,6 +641,7 @@ func buildFixtureState(config MockConfig) models.StateSnapshot {
|
||||
|
||||
ensureFreshFilesystemFixture(&data)
|
||||
ensureConfigOnlyMountFixture(&data)
|
||||
ensureMockDockerInLXCFixture(&data, config)
|
||||
|
||||
// Calculate stats
|
||||
data.Stats.StartTime = time.Now()
|
||||
@@ -2690,6 +2691,103 @@ func generateDockerHosts(config MockConfig) []models.DockerHost {
|
||||
return hosts
|
||||
}
|
||||
|
||||
// mockProxmoxLXCDockerHostSourcePrefix mirrors the production Docker host
|
||||
// identity prefix from monitoring.proxmoxGuestDockerAgentID (and the platform
|
||||
// scope match in unifiedresources): hosts whose source ID carries it are
|
||||
// treated as Docker runtimes discovered inside a Proxmox LXC guest.
|
||||
const mockProxmoxLXCDockerHostSourcePrefix = "proxmox-lxc-docker:"
|
||||
|
||||
// ensureMockDockerInLXCFixture adds Proxmox-side Docker-in-LXC discovery to
|
||||
// the fixture estate: the first running LXC guests are marked HasDocker (as
|
||||
// the node's pct exec socket probe would) and each gains a nested Docker host
|
||||
// whose identity follows the production proxmoxGuestDockerAgentID convention,
|
||||
// "proxmox-lxc-docker:" + the guest's instance:node:vmid coordinates. That
|
||||
// prefix is what grants the nested workloads the proxmox-pve platform scope
|
||||
// and lets the Proxmox page match them back to their parent guest for the
|
||||
// row cue and drawer card, so without this fixture the whole surface has no
|
||||
// local reproduction.
|
||||
func ensureMockDockerInLXCFixture(data *models.StateSnapshot, config MockConfig) {
|
||||
if data == nil || config.DockerHostCount <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// One single-container guest and one busier guest, so the row cue
|
||||
// renders both its singular and plural counts.
|
||||
nestedCounts := []int{1, 4}
|
||||
now := time.Now()
|
||||
nested := 0
|
||||
for i := range data.Containers {
|
||||
if nested >= len(nestedCounts) {
|
||||
break
|
||||
}
|
||||
ct := &data.Containers[i]
|
||||
if ct.Status != "running" || ct.IsOCI {
|
||||
continue
|
||||
}
|
||||
|
||||
ct.HasDocker = true
|
||||
ct.DockerCheckedAt = now
|
||||
|
||||
sourceID := fmt.Sprintf("%s%s:%s:%d", mockProxmoxLXCDockerHostSourcePrefix, ct.Instance, ct.Node, ct.VMID)
|
||||
containers := generateDockerContainers(ct.Name, i, config, false)
|
||||
if len(containers) > nestedCounts[nested] {
|
||||
containers = containers[:nestedCounts[nested]]
|
||||
}
|
||||
|
||||
version := dockerVersions[rand.Intn(len(dockerVersions))]
|
||||
kernelVersion := "6.8.12-4-pve"
|
||||
for _, node := range data.Nodes {
|
||||
if node.Name == ct.Node && node.KernelVersion != "" {
|
||||
kernelVersion = node.KernelVersion
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Host info mirrors what parseProxmoxGuestDockerInventory plus
|
||||
// enrichGuestDockerReportFromContainer report for a real guest: the
|
||||
// guest's own sizing and usage, one rootfs disk, no machine ID, no
|
||||
// agent version, no network interfaces.
|
||||
host := models.DockerHost{
|
||||
ID: sourceID,
|
||||
AgentID: sourceID,
|
||||
Hostname: ct.Name,
|
||||
DisplayName: ct.Name,
|
||||
OS: "linux",
|
||||
KernelVersion: kernelVersion,
|
||||
Architecture: "x86_64",
|
||||
Runtime: "docker",
|
||||
RuntimeVersion: version,
|
||||
DockerVersion: version,
|
||||
CPUs: ct.CPUs,
|
||||
TotalMemoryBytes: ct.Memory.Total,
|
||||
UptimeSeconds: ct.Uptime,
|
||||
CPUUsage: clampFloat(ct.CPU*100, 0, 100),
|
||||
Memory: models.Memory{
|
||||
Total: ct.Memory.Total,
|
||||
Used: ct.Memory.Used,
|
||||
Free: ct.Memory.Free,
|
||||
Usage: ct.Memory.Usage,
|
||||
},
|
||||
Disks: []models.Disk{{
|
||||
Total: ct.Disk.Total,
|
||||
Used: ct.Disk.Used,
|
||||
Free: ct.Disk.Free,
|
||||
Usage: ct.Disk.Usage,
|
||||
Mountpoint: "/",
|
||||
Type: "rootfs",
|
||||
}},
|
||||
Status: "online",
|
||||
LastSeen: now,
|
||||
IntervalSeconds: 30,
|
||||
Containers: containers,
|
||||
}
|
||||
|
||||
data.DockerHosts = append(data.DockerHosts, host)
|
||||
data.ConnectionHealth[dockerConnectionPrefix+host.ID] = true
|
||||
nested++
|
||||
}
|
||||
}
|
||||
|
||||
func ensureMockDockerNativeInventory(host *models.DockerHost, hostIndex int, now time.Time) {
|
||||
if host == nil {
|
||||
return
|
||||
|
||||
@@ -18,14 +18,18 @@ func TestBuildFixtureStateIncludesDockerHosts(t *testing.T) {
|
||||
|
||||
data := buildFixtureState(cfg)
|
||||
|
||||
if len(data.DockerHosts) != cfg.DockerHostCount {
|
||||
t.Fatalf("expected %d docker hosts, got %d", cfg.DockerHostCount, len(data.DockerHosts))
|
||||
}
|
||||
|
||||
// Nested Docker-in-LXC hosts are appended on top of the configured
|
||||
// agent-reported hosts and deliberately carry no native engine
|
||||
// inventory, so the native-host expectations exclude them.
|
||||
nativeHosts := 0
|
||||
for _, host := range data.DockerHosts {
|
||||
if host.ID == "" {
|
||||
t.Fatalf("docker host missing id: %+v", host)
|
||||
}
|
||||
if strings.HasPrefix(host.ID, mockProxmoxLXCDockerHostSourcePrefix) {
|
||||
continue
|
||||
}
|
||||
nativeHosts++
|
||||
if len(host.Containers) == 0 {
|
||||
t.Fatalf("docker host %s has no containers", host.Hostname)
|
||||
}
|
||||
@@ -42,6 +46,9 @@ func TestBuildFixtureStateIncludesDockerHosts(t *testing.T) {
|
||||
t.Fatalf("docker host %s has no engine storage usage inventory: %+v", host.Hostname, host.StorageUsage)
|
||||
}
|
||||
}
|
||||
if nativeHosts != cfg.DockerHostCount {
|
||||
t.Fatalf("expected %d native docker hosts, got %d", cfg.DockerHostCount, nativeHosts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeGuestCountsHandlesZeroBaselines(t *testing.T) {
|
||||
@@ -300,6 +307,11 @@ func TestBuildFixtureStatePopulatesDockerHostIORates(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, host := range data.DockerHosts {
|
||||
if strings.HasPrefix(host.ID, mockProxmoxLXCDockerHostSourcePrefix) {
|
||||
// Docker-in-LXC hosts mirror the production pct exec inventory,
|
||||
// which reports no host-level I/O rates or temperature.
|
||||
continue
|
||||
}
|
||||
if host.Status == "offline" {
|
||||
if host.NetInRate != 0 || host.NetOutRate != 0 || host.DiskReadRate != 0 || host.DiskWriteRate != 0 {
|
||||
t.Fatalf("offline docker host %s should have zero I/O rates", host.ID)
|
||||
@@ -893,3 +905,76 @@ func TestEnsureConfigOnlyMountFixtureSeedsUnknownUsageMount(t *testing.T) {
|
||||
t.Fatalf("config-only mount must keep its mp key and device, got %+v", archive)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFixtureStateIncludesDockerInLXCFixture(t *testing.T) {
|
||||
cfg := DefaultConfig
|
||||
cfg.StoppedPercent = 0 // keep every LXC running so the fixture always binds
|
||||
|
||||
data := buildFixtureState(cfg)
|
||||
|
||||
var nested []models.DockerHost
|
||||
for _, host := range data.DockerHosts {
|
||||
if strings.HasPrefix(host.ID, mockProxmoxLXCDockerHostSourcePrefix) {
|
||||
nested = append(nested, host)
|
||||
}
|
||||
}
|
||||
if len(nested) != 2 {
|
||||
t.Fatalf("expected 2 nested Docker-in-LXC hosts, got %d", len(nested))
|
||||
}
|
||||
|
||||
guestsBySourceID := make(map[string]models.Container, len(data.Containers))
|
||||
for _, ct := range data.Containers {
|
||||
key := fmt.Sprintf("%s%s:%s:%d", mockProxmoxLXCDockerHostSourcePrefix, ct.Instance, ct.Node, ct.VMID)
|
||||
guestsBySourceID[key] = ct
|
||||
}
|
||||
|
||||
containerCounts := make(map[int]bool)
|
||||
for _, host := range nested {
|
||||
guest, ok := guestsBySourceID[host.ID]
|
||||
if !ok {
|
||||
t.Fatalf("nested docker host %s has no matching LXC guest", host.ID)
|
||||
}
|
||||
if guest.Status != "running" {
|
||||
t.Fatalf("nested docker host %s bound to non-running guest %s", host.ID, guest.ID)
|
||||
}
|
||||
if !guest.HasDocker || guest.DockerCheckedAt.IsZero() {
|
||||
t.Fatalf("parent LXC %s not marked as probed Docker host", guest.ID)
|
||||
}
|
||||
if host.AgentID != host.ID {
|
||||
t.Fatalf("nested docker host agent id %q must equal its source id %q", host.AgentID, host.ID)
|
||||
}
|
||||
if host.Hostname != guest.Name {
|
||||
t.Fatalf("nested docker host hostname %q should be the guest name %q", host.Hostname, guest.Name)
|
||||
}
|
||||
if len(host.Containers) == 0 {
|
||||
t.Fatalf("nested docker host %s has no containers", host.ID)
|
||||
}
|
||||
containerCounts[len(host.Containers)] = true
|
||||
if len(host.Images) != 0 || len(host.Volumes) != 0 || len(host.Networks) != 0 || host.StorageUsage != nil {
|
||||
t.Fatalf("nested docker host %s must not fabricate native engine inventory (production pct exec inventory has none)", host.ID)
|
||||
}
|
||||
if host.Swarm != nil {
|
||||
t.Fatalf("nested docker host %s must not join the mock Swarm", host.ID)
|
||||
}
|
||||
if !data.ConnectionHealth[dockerConnectionPrefix+host.ID] {
|
||||
t.Fatalf("nested docker host %s missing connection health entry", host.ID)
|
||||
}
|
||||
}
|
||||
if !containerCounts[1] {
|
||||
t.Fatalf("expected one single-container nested host so the row cue renders its singular form, got counts %v", containerCounts)
|
||||
}
|
||||
|
||||
// Disabling Docker in the mock estate disables the nested fixture too.
|
||||
cfg.DockerHostCount = 0
|
||||
dataNoDocker := buildFixtureState(cfg)
|
||||
for _, host := range dataNoDocker.DockerHosts {
|
||||
if strings.HasPrefix(host.ID, mockProxmoxLXCDockerHostSourcePrefix) {
|
||||
t.Fatalf("nested docker host %s generated despite DockerHostCount=0", host.ID)
|
||||
}
|
||||
}
|
||||
for _, ct := range dataNoDocker.Containers {
|
||||
if ct.HasDocker {
|
||||
t.Fatalf("LXC %s marked HasDocker despite DockerHostCount=0", ct.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user