mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-02-18 23:41:48 +01:00
301 lines
8.6 KiB
Go
301 lines
8.6 KiB
Go
package monitoring
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/alerts"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
agentsdocker "github.com/rcourtman/pulse-go-rewrite/pkg/agents/docker"
|
|
)
|
|
|
|
func newTestMonitor(t *testing.T) *Monitor {
|
|
t.Helper()
|
|
|
|
m := &Monitor{
|
|
state: models.NewState(),
|
|
alertManager: alerts.NewManager(),
|
|
removedDockerHosts: make(map[string]time.Time),
|
|
rateTracker: NewRateTracker(),
|
|
dockerTokenBindings: make(map[string]string),
|
|
dockerMetadataStore: config.NewDockerMetadataStore(t.TempDir()),
|
|
}
|
|
t.Cleanup(func() { m.alertManager.Stop() })
|
|
return m
|
|
}
|
|
|
|
func TestApplyDockerReportGeneratesUniqueIDsForCollidingHosts(t *testing.T) {
|
|
monitor := newTestMonitor(t)
|
|
|
|
baseTimestamp := time.Now().UTC()
|
|
baseReport := agentsdocker.Report{
|
|
Agent: agentsdocker.AgentInfo{
|
|
Version: "1.0.0",
|
|
IntervalSeconds: 30,
|
|
},
|
|
Host: agentsdocker.HostInfo{
|
|
Hostname: "docker-host",
|
|
Name: "Docker Host",
|
|
MachineID: "machine-duplicate",
|
|
DockerVersion: "26.0.0",
|
|
TotalCPU: 4,
|
|
TotalMemoryBytes: 8 << 30,
|
|
UptimeSeconds: 120,
|
|
},
|
|
Containers: []agentsdocker.Container{
|
|
{ID: "container-1", Name: "nginx"},
|
|
},
|
|
Timestamp: baseTimestamp,
|
|
}
|
|
|
|
token1 := &config.APITokenRecord{ID: "token-host-1", Name: "Host 1"}
|
|
host1, err := monitor.ApplyDockerReport(baseReport, token1)
|
|
if err != nil {
|
|
t.Fatalf("ApplyDockerReport host1: %v", err)
|
|
}
|
|
if host1.ID == "" {
|
|
t.Fatalf("expected host1 to have an identifier")
|
|
}
|
|
|
|
hosts := monitor.state.GetDockerHosts()
|
|
if len(hosts) != 1 {
|
|
t.Fatalf("expected 1 host after first report, got %d", len(hosts))
|
|
}
|
|
|
|
secondReport := baseReport
|
|
secondReport.Host.Name = "Docker Host Clone"
|
|
secondReport.Timestamp = baseTimestamp.Add(45 * time.Second)
|
|
|
|
token2 := &config.APITokenRecord{ID: "token-host-2", Name: "Host 2"}
|
|
host2, err := monitor.ApplyDockerReport(secondReport, token2)
|
|
if err != nil {
|
|
t.Fatalf("ApplyDockerReport host2: %v", err)
|
|
}
|
|
if host2.ID == "" {
|
|
t.Fatalf("expected host2 to have an identifier")
|
|
}
|
|
if host2.ID == host1.ID {
|
|
t.Fatalf("expected unique identifiers, but both hosts share %q", host2.ID)
|
|
}
|
|
|
|
hosts = monitor.state.GetDockerHosts()
|
|
if len(hosts) != 2 {
|
|
t.Fatalf("expected 2 hosts after second report, got %d", len(hosts))
|
|
}
|
|
|
|
secondReport.Timestamp = secondReport.Timestamp.Add(45 * time.Second)
|
|
secondReport.Containers = append(secondReport.Containers, agentsdocker.Container{
|
|
ID: "container-2",
|
|
Name: "redis",
|
|
})
|
|
|
|
updatedHost2, err := monitor.ApplyDockerReport(secondReport, token2)
|
|
if err != nil {
|
|
t.Fatalf("ApplyDockerReport host2 update: %v", err)
|
|
}
|
|
if updatedHost2.ID != host2.ID {
|
|
t.Fatalf("expected host2 to retain identifier %q, got %q", host2.ID, updatedHost2.ID)
|
|
}
|
|
|
|
hosts = monitor.state.GetDockerHosts()
|
|
var found models.DockerHost
|
|
for _, h := range hosts {
|
|
if h.ID == host2.ID {
|
|
found = h
|
|
break
|
|
}
|
|
}
|
|
if found.ID == "" {
|
|
t.Fatalf("failed to locate host2 in state after update")
|
|
}
|
|
if len(found.Containers) != 2 {
|
|
t.Fatalf("expected host2 to have 2 containers after update, got %d", len(found.Containers))
|
|
}
|
|
}
|
|
|
|
func TestApplyDockerReportUsesTokenToDisambiguateAgentIDCollisions(t *testing.T) {
|
|
monitor := newTestMonitor(t)
|
|
|
|
baseReport := agentsdocker.Report{
|
|
Agent: agentsdocker.AgentInfo{
|
|
ID: "duplicate-agent",
|
|
Version: "1.0.0",
|
|
IntervalSeconds: 30,
|
|
},
|
|
Host: agentsdocker.HostInfo{
|
|
Hostname: "docker-one",
|
|
Name: "Docker One",
|
|
MachineID: "machine-A",
|
|
DockerVersion: "26.0.0",
|
|
TotalCPU: 4,
|
|
TotalMemoryBytes: 16 << 30,
|
|
UptimeSeconds: 120,
|
|
},
|
|
Containers: []agentsdocker.Container{
|
|
{ID: "container-a", Name: "api"},
|
|
},
|
|
Timestamp: time.Now().UTC(),
|
|
}
|
|
|
|
tokenOne := &config.APITokenRecord{ID: "token-one", Name: "Token One"}
|
|
hostOne, err := monitor.ApplyDockerReport(baseReport, tokenOne)
|
|
if err != nil {
|
|
t.Fatalf("ApplyDockerReport hostOne: %v", err)
|
|
}
|
|
if hostOne.ID == "" {
|
|
t.Fatal("expected hostOne to receive an identifier")
|
|
}
|
|
|
|
secondReport := baseReport
|
|
secondReport.Host.Hostname = "docker-two"
|
|
secondReport.Host.Name = "Docker Two"
|
|
secondReport.Host.MachineID = "machine-B"
|
|
secondReport.Containers = []agentsdocker.Container{
|
|
{ID: "container-b", Name: "db"},
|
|
}
|
|
secondReport.Timestamp = baseReport.Timestamp.Add(30 * time.Second)
|
|
|
|
tokenTwo := &config.APITokenRecord{ID: "token-two", Name: "Token Two"}
|
|
hostTwo, err := monitor.ApplyDockerReport(secondReport, tokenTwo)
|
|
if err != nil {
|
|
t.Fatalf("ApplyDockerReport hostTwo: %v", err)
|
|
}
|
|
|
|
if hostTwo.ID == "" {
|
|
t.Fatal("expected hostTwo to receive an identifier")
|
|
}
|
|
if hostOne.ID == hostTwo.ID {
|
|
t.Fatalf("expected different identifiers for hosts sharing an agent ID, got %q", hostOne.ID)
|
|
}
|
|
|
|
hosts := monitor.state.GetDockerHosts()
|
|
if len(hosts) != 2 {
|
|
t.Fatalf("expected 2 hosts after two reports, got %d", len(hosts))
|
|
}
|
|
|
|
updatedReport := baseReport
|
|
updatedReport.Timestamp = baseReport.Timestamp.Add(60 * time.Second)
|
|
updatedReport.Containers = append(updatedReport.Containers, agentsdocker.Container{
|
|
ID: "container-c",
|
|
Name: "cache",
|
|
})
|
|
|
|
updatedHostOne, err := monitor.ApplyDockerReport(updatedReport, tokenOne)
|
|
if err != nil {
|
|
t.Fatalf("ApplyDockerReport hostOne update: %v", err)
|
|
}
|
|
if updatedHostOne.ID != hostOne.ID {
|
|
t.Fatalf("expected hostOne to retain identifier %q, got %q", hostOne.ID, updatedHostOne.ID)
|
|
}
|
|
}
|
|
|
|
func TestApplyDockerReportIncludesContainerDiskDetails(t *testing.T) {
|
|
timestamp := time.Now().UTC()
|
|
report := agentsdocker.Report{
|
|
Agent: agentsdocker.AgentInfo{
|
|
ID: "agent-1",
|
|
Version: "1.2.3",
|
|
IntervalSeconds: 30,
|
|
},
|
|
Host: agentsdocker.HostInfo{
|
|
Hostname: "disk-host",
|
|
},
|
|
Containers: []agentsdocker.Container{
|
|
{
|
|
ID: "ctr-1",
|
|
Name: "app",
|
|
WritableLayerBytes: 512 * 1024 * 1024,
|
|
RootFilesystemBytes: 2 * 1024 * 1024 * 1024,
|
|
BlockIO: &agentsdocker.ContainerBlockIO{
|
|
ReadBytes: 123456,
|
|
WriteBytes: 654321,
|
|
},
|
|
Mounts: []agentsdocker.ContainerMount{
|
|
{
|
|
Type: "bind",
|
|
Source: "/srv/app/config",
|
|
Destination: "/config",
|
|
Mode: "rw",
|
|
RW: true,
|
|
Propagation: "rprivate",
|
|
Name: "",
|
|
Driver: "",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Timestamp: timestamp,
|
|
}
|
|
|
|
monitor := newTestMonitor(t)
|
|
host, err := monitor.ApplyDockerReport(report, nil)
|
|
if err != nil {
|
|
t.Fatalf("ApplyDockerReport returned error: %v", err)
|
|
}
|
|
|
|
if len(host.Containers) != 1 {
|
|
t.Fatalf("expected 1 container, got %d", len(host.Containers))
|
|
}
|
|
|
|
container := host.Containers[0]
|
|
if container.WritableLayerBytes != 512*1024*1024 {
|
|
t.Fatalf("expected writable layer bytes to match, got %d", container.WritableLayerBytes)
|
|
}
|
|
if container.RootFilesystemBytes != 2*1024*1024*1024 {
|
|
t.Fatalf("expected root filesystem bytes to match, got %d", container.RootFilesystemBytes)
|
|
}
|
|
|
|
if container.BlockIO == nil {
|
|
t.Fatalf("expected block IO stats to be populated")
|
|
}
|
|
if container.BlockIO.ReadBytes != 123456 || container.BlockIO.WriteBytes != 654321 {
|
|
t.Fatalf("unexpected block IO values: %+v", container.BlockIO)
|
|
}
|
|
if container.BlockIO.ReadRateBytesPerSecond != nil || container.BlockIO.WriteRateBytesPerSecond != nil {
|
|
t.Fatalf("expected block IO rates to be unset on first sample: %+v", container.BlockIO)
|
|
}
|
|
|
|
if len(container.Mounts) != 1 {
|
|
t.Fatalf("expected mounts to be preserved, got %d", len(container.Mounts))
|
|
}
|
|
mount := container.Mounts[0]
|
|
if mount.Source != "/srv/app/config" || mount.Destination != "/config" || !mount.RW {
|
|
t.Fatalf("unexpected mount payload: %+v", mount)
|
|
}
|
|
}
|
|
|
|
func TestApplyDockerReportPodmanRuntimeMetadata(t *testing.T) {
|
|
monitor := newTestMonitor(t)
|
|
|
|
report := agentsdocker.Report{
|
|
Agent: agentsdocker.AgentInfo{
|
|
ID: "agent-podman",
|
|
Version: "2.0.0",
|
|
IntervalSeconds: 60,
|
|
},
|
|
Host: agentsdocker.HostInfo{
|
|
Hostname: "podman-host",
|
|
Runtime: "podman",
|
|
RuntimeVersion: "4.9.3",
|
|
DockerVersion: "",
|
|
},
|
|
Timestamp: time.Now().UTC(),
|
|
}
|
|
|
|
host, err := monitor.ApplyDockerReport(report, nil)
|
|
if err != nil {
|
|
t.Fatalf("ApplyDockerReport returned error: %v", err)
|
|
}
|
|
|
|
if host.Runtime != "podman" {
|
|
t.Fatalf("expected runtime podman, got %q", host.Runtime)
|
|
}
|
|
if host.RuntimeVersion != "4.9.3" {
|
|
t.Fatalf("expected runtime version 4.9.3, got %q", host.RuntimeVersion)
|
|
}
|
|
if host.DockerVersion != "4.9.3" {
|
|
t.Fatalf("expected docker version fallback to runtime version, got %q", host.DockerVersion)
|
|
}
|
|
}
|