mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 14:00:29 +00:00
802b3aac49
Two real bugs that surfaced once the 20m test timeout let the internal/api and internal/monitoring packages run to completion. cloneVMwareInventoryMetrics omitted four fields: Commit23ea4e487(Surface vSphere VM uptime and guest disk usage) added UptimeSeconds, DiskUsedBytes, DiskTotalBytes, and DiskPercent to vmware.InventoryMetrics but did not extend cloneVMwareInventoryMetrics. The clone left those pointer fields aliased to the source struct, so the mock fixture refresh path (refreshVMwareInventoryMetrics writing through metrics.UptimeSeconds via ensureInt64Ptr) and the snapshot read path (inventoryUptimeSeconds dereferencing metrics.UptimeSeconds) raced on the same heap-allocated int64. TestMonitorBuildBroadcastFrontendStateUsesCanonicalMockUnifiedResources exemption: The test asserts broadcast state does not publish the lowercase- hyphenated legacy docker host label so canonical docker hosts surface their human-readable DisplayName. Commit89abed099(2026-05-24) added the docker-swarm-node resource type whose Name is the swarm node hostname (matching how Docker Swarm identifies node members), which collided with the legacy-label rejection. Refine the assertion to apply only to host-type resources (docker-host, agent, node).
365 lines
11 KiB
Go
365 lines
11 KiB
Go
package mock
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/alerts"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/truenas"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/vmware"
|
|
)
|
|
|
|
// FixtureGraph is the canonical mock runtime owner for snapshot-backed and
|
|
// provider-backed fixtures. All mock projections should derive from this graph
|
|
// rather than mixing independent snapshot and provider helpers.
|
|
type FixtureGraph struct {
|
|
State models.StateSnapshot
|
|
AlertHistory []models.Alert
|
|
PlatformFixtures PlatformFixtures
|
|
AvailabilityFixtures []AvailabilityFixture
|
|
DiscoveryFixtures []*DiscoveryFixture
|
|
}
|
|
|
|
func emptyFixtureGraph() FixtureGraph {
|
|
return FixtureGraph{
|
|
State: models.EmptyStateSnapshot(),
|
|
}
|
|
}
|
|
|
|
func buildFixtureGraph(cfg MockConfig, now time.Time) FixtureGraph {
|
|
setMockUpdateInterval(cfg.UpdateInterval)
|
|
graph := FixtureGraph{
|
|
State: buildFixtureState(cfg),
|
|
PlatformFixtures: defaultPlatformFixtures(),
|
|
AvailabilityFixtures: defaultAvailabilityFixtures(now),
|
|
}
|
|
applyDemoScenarioGraph(&graph, now)
|
|
syncMetricRoleRegistryFromGraph(graph)
|
|
graph.UpdateMetrics(cfg, now)
|
|
graph.AlertHistory = buildAlertHistory(graph.State.Nodes, graph.State.VMs, graph.State.Containers)
|
|
syncMetricRoleRegistryFromGraph(graph)
|
|
return graph
|
|
}
|
|
|
|
func cloneFixtureGraph(in FixtureGraph) FixtureGraph {
|
|
return FixtureGraph{
|
|
State: cloneState(in.State),
|
|
AlertHistory: append([]models.Alert(nil), in.AlertHistory...),
|
|
PlatformFixtures: clonePlatformFixtures(in.PlatformFixtures),
|
|
AvailabilityFixtures: cloneAvailabilityFixtures(in.AvailabilityFixtures),
|
|
DiscoveryFixtures: cloneDiscoveryFixtures(in.DiscoveryFixtures),
|
|
}
|
|
}
|
|
|
|
func (g *FixtureGraph) UpdateMetrics(cfg MockConfig, now time.Time) {
|
|
if g == nil {
|
|
return
|
|
}
|
|
|
|
setMockUpdateInterval(cfg.UpdateInterval)
|
|
applyDemoScenarioGraph(g, now)
|
|
syncMetricRoleRegistryFromGraph(*g)
|
|
updateFixtureStateMetricsAt(&g.State, cfg, now)
|
|
g.PlatformFixtures = rebasePlatformFixtures(g.PlatformFixtures, now)
|
|
g.AvailabilityFixtures = rebaseAvailabilityFixtures(g.AvailabilityFixtures, now)
|
|
applyDemoScenarioGraph(g, now)
|
|
g.DiscoveryFixtures = buildDiscoveryFixtures(g.State, now)
|
|
syncMetricRoleRegistryFromGraph(*g)
|
|
}
|
|
|
|
func (g *FixtureGraph) UpdateAlertSnapshots(active []alerts.Alert, resolved []models.ResolvedAlert) {
|
|
if g == nil {
|
|
return
|
|
}
|
|
|
|
converted := make([]models.Alert, 0, len(active))
|
|
for _, alert := range active {
|
|
converted = append(converted, models.Alert{
|
|
ID: alert.ID,
|
|
Type: alert.Type,
|
|
Level: string(alert.Level),
|
|
ResourceID: alert.ResourceID,
|
|
ResourceName: alert.ResourceName,
|
|
Node: alert.Node,
|
|
Instance: alert.Instance,
|
|
Message: alert.Message,
|
|
Value: alert.Value,
|
|
Threshold: alert.Threshold,
|
|
StartTime: alert.StartTime,
|
|
Acknowledged: alert.Acknowledged,
|
|
})
|
|
}
|
|
|
|
g.State.ActiveAlerts = converted
|
|
g.State.RecentlyResolved = append([]models.ResolvedAlert(nil), resolved...)
|
|
}
|
|
|
|
func CurrentFixtureGraph() FixtureGraph {
|
|
if !IsMockEnabled() {
|
|
return emptyFixtureGraph()
|
|
}
|
|
|
|
dataMu.RLock()
|
|
defer dataMu.RUnlock()
|
|
|
|
return cloneFixtureGraph(mockGraph)
|
|
}
|
|
|
|
func currentOrDefaultPlatformFixtures() PlatformFixtures {
|
|
if !IsMockEnabled() {
|
|
return defaultPlatformFixtures()
|
|
}
|
|
|
|
return CurrentFixtureGraph().PlatformFixtures
|
|
}
|
|
|
|
func clonePlatformFixtures(in PlatformFixtures) PlatformFixtures {
|
|
return PlatformFixtures{
|
|
TrueNAS: cloneTrueNASFixtureSnapshot(in.TrueNAS),
|
|
VMware: cloneVMwareInventorySnapshot(in.VMware),
|
|
}
|
|
}
|
|
|
|
func cloneTrueNASFixtureSnapshot(in truenas.FixtureSnapshot) truenas.FixtureSnapshot {
|
|
out := in
|
|
out.System = cloneTrueNASSystemInfo(in.System)
|
|
out.Pools = append([]truenas.Pool(nil), in.Pools...)
|
|
out.Datasets = append([]truenas.Dataset(nil), in.Datasets...)
|
|
out.Disks = append([]truenas.Disk(nil), in.Disks...)
|
|
out.Alerts = append([]truenas.Alert(nil), in.Alerts...)
|
|
out.Apps = cloneTrueNASApps(in.Apps)
|
|
out.ZFSSnapshots = cloneTrueNASZFSSnapshots(in.ZFSSnapshots)
|
|
out.ReplicationTasks = cloneTrueNASReplicationTasks(in.ReplicationTasks)
|
|
return out
|
|
}
|
|
|
|
func cloneTrueNASSystemInfo(in truenas.SystemInfo) truenas.SystemInfo {
|
|
out := in
|
|
if len(in.TemperatureCelsius) > 0 {
|
|
out.TemperatureCelsius = make(map[string]float64, len(in.TemperatureCelsius))
|
|
for key, value := range in.TemperatureCelsius {
|
|
out.TemperatureCelsius[key] = value
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func cloneTrueNASApps(in []truenas.App) []truenas.App {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := make([]truenas.App, len(in))
|
|
for i := range in {
|
|
out[i] = in[i]
|
|
out[i].UsedHostIPs = append([]string(nil), in[i].UsedHostIPs...)
|
|
out[i].UsedPorts = cloneTrueNASAppPorts(in[i].UsedPorts)
|
|
out[i].Containers = cloneTrueNASAppContainers(in[i].Containers)
|
|
out[i].Volumes = append([]truenas.AppVolume(nil), in[i].Volumes...)
|
|
out[i].Images = append([]string(nil), in[i].Images...)
|
|
out[i].Networks = cloneTrueNASAppNetworks(in[i].Networks)
|
|
out[i].Stats = cloneTrueNASAppStats(in[i].Stats)
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func cloneTrueNASAppPorts(in []truenas.AppPort) []truenas.AppPort {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := make([]truenas.AppPort, len(in))
|
|
for i := range in {
|
|
out[i] = in[i]
|
|
out[i].HostPorts = append([]truenas.AppHostPort(nil), in[i].HostPorts...)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func cloneTrueNASAppContainers(in []truenas.AppContainer) []truenas.AppContainer {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := make([]truenas.AppContainer, len(in))
|
|
for i := range in {
|
|
out[i] = in[i]
|
|
out[i].PortConfig = cloneTrueNASAppPorts(in[i].PortConfig)
|
|
out[i].VolumeMounts = append([]truenas.AppVolume(nil), in[i].VolumeMounts...)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func cloneTrueNASAppNetworks(in []truenas.AppNetwork) []truenas.AppNetwork {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := make([]truenas.AppNetwork, len(in))
|
|
for i := range in {
|
|
out[i] = in[i]
|
|
out[i].Labels = cloneStringMap(in[i].Labels)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func cloneTrueNASAppStats(in *truenas.AppStats) *truenas.AppStats {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := *in
|
|
out.Interfaces = append([]truenas.AppInterfaceStats(nil), in.Interfaces...)
|
|
return &out
|
|
}
|
|
|
|
func cloneTrueNASZFSSnapshots(in []truenas.ZFSSnapshot) []truenas.ZFSSnapshot {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := make([]truenas.ZFSSnapshot, len(in))
|
|
for i := range in {
|
|
out[i] = in[i]
|
|
out[i].CreatedAt = cloneTimePtr(in[i].CreatedAt)
|
|
out[i].UsedBytes = cloneInt64Ptr(in[i].UsedBytes)
|
|
out[i].Referenced = cloneInt64Ptr(in[i].Referenced)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func cloneTrueNASReplicationTasks(in []truenas.ReplicationTask) []truenas.ReplicationTask {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := make([]truenas.ReplicationTask, len(in))
|
|
for i := range in {
|
|
out[i] = in[i]
|
|
out[i].SourceDatasets = append([]string(nil), in[i].SourceDatasets...)
|
|
out[i].LastRun = cloneTimePtr(in[i].LastRun)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func cloneVMwareInventorySnapshot(in vmware.InventorySnapshot) vmware.InventorySnapshot {
|
|
out := in
|
|
out.Hosts = cloneVMwareInventoryHosts(in.Hosts)
|
|
out.VMs = cloneVMwareInventoryVMs(in.VMs)
|
|
out.Datastores = cloneVMwareInventoryDatastores(in.Datastores)
|
|
out.Networks = cloneVMwareInventoryNetworks(in.Networks)
|
|
out.EnrichmentIssues = append([]vmware.InventoryEnrichmentIssue(nil), in.EnrichmentIssues...)
|
|
return out
|
|
}
|
|
|
|
func cloneVMwareInventoryHosts(in []vmware.InventoryHost) []vmware.InventoryHost {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := make([]vmware.InventoryHost, len(in))
|
|
for i := range in {
|
|
out[i] = in[i]
|
|
out[i].DatastoreIDs = append([]string(nil), in[i].DatastoreIDs...)
|
|
out[i].DatastoreNames = append([]string(nil), in[i].DatastoreNames...)
|
|
out[i].TriggeredAlarms = append([]vmware.InventoryAlarm(nil), in[i].TriggeredAlarms...)
|
|
out[i].RecentTasks = append([]vmware.InventoryTask(nil), in[i].RecentTasks...)
|
|
out[i].RecentEvents = append([]vmware.InventoryEvent(nil), in[i].RecentEvents...)
|
|
out[i].Metrics = cloneVMwareInventoryMetrics(in[i].Metrics)
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func cloneVMwareInventoryVMs(in []vmware.InventoryVM) []vmware.InventoryVM {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := make([]vmware.InventoryVM, len(in))
|
|
for i := range in {
|
|
out[i] = in[i]
|
|
out[i].DatastoreIDs = append([]string(nil), in[i].DatastoreIDs...)
|
|
out[i].DatastoreNames = append([]string(nil), in[i].DatastoreNames...)
|
|
out[i].GuestIPAddresses = append([]string(nil), in[i].GuestIPAddresses...)
|
|
out[i].TriggeredAlarms = append([]vmware.InventoryAlarm(nil), in[i].TriggeredAlarms...)
|
|
out[i].RecentTasks = append([]vmware.InventoryTask(nil), in[i].RecentTasks...)
|
|
out[i].RecentEvents = append([]vmware.InventoryEvent(nil), in[i].RecentEvents...)
|
|
out[i].Metrics = cloneVMwareInventoryMetrics(in[i].Metrics)
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func cloneVMwareInventoryDatastores(in []vmware.InventoryDatastore) []vmware.InventoryDatastore {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := make([]vmware.InventoryDatastore, len(in))
|
|
for i := range in {
|
|
out[i] = in[i]
|
|
out[i].HostIDs = append([]string(nil), in[i].HostIDs...)
|
|
out[i].HostNames = append([]string(nil), in[i].HostNames...)
|
|
out[i].VMIDs = append([]string(nil), in[i].VMIDs...)
|
|
out[i].VMNames = append([]string(nil), in[i].VMNames...)
|
|
out[i].Accessible = cloneBoolPtr(in[i].Accessible)
|
|
out[i].MultipleHostAccess = cloneBoolPtr(in[i].MultipleHostAccess)
|
|
out[i].TriggeredAlarms = append([]vmware.InventoryAlarm(nil), in[i].TriggeredAlarms...)
|
|
out[i].RecentTasks = append([]vmware.InventoryTask(nil), in[i].RecentTasks...)
|
|
out[i].RecentEvents = append([]vmware.InventoryEvent(nil), in[i].RecentEvents...)
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func cloneVMwareInventoryNetworks(in []vmware.InventoryNetwork) []vmware.InventoryNetwork {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := make([]vmware.InventoryNetwork, len(in))
|
|
for i := range in {
|
|
out[i] = in[i]
|
|
out[i].HostIDs = append([]string(nil), in[i].HostIDs...)
|
|
out[i].HostNames = append([]string(nil), in[i].HostNames...)
|
|
out[i].VMIDs = append([]string(nil), in[i].VMIDs...)
|
|
out[i].VMNames = append([]string(nil), in[i].VMNames...)
|
|
out[i].TriggeredAlarms = append([]vmware.InventoryAlarm(nil), in[i].TriggeredAlarms...)
|
|
out[i].RecentTasks = append([]vmware.InventoryTask(nil), in[i].RecentTasks...)
|
|
out[i].RecentEvents = append([]vmware.InventoryEvent(nil), in[i].RecentEvents...)
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func cloneVMwareInventoryMetrics(in *vmware.InventoryMetrics) *vmware.InventoryMetrics {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
|
|
out := *in
|
|
out.CPUPercent = cloneFloat64Ptr(in.CPUPercent)
|
|
out.MemoryPercent = cloneFloat64Ptr(in.MemoryPercent)
|
|
out.MemoryUsedBytes = cloneInt64Ptr(in.MemoryUsedBytes)
|
|
out.MemoryTotalBytes = cloneInt64Ptr(in.MemoryTotalBytes)
|
|
out.NetInBytesPerSecond = cloneFloat64Ptr(in.NetInBytesPerSecond)
|
|
out.NetOutBytesPerSecond = cloneFloat64Ptr(in.NetOutBytesPerSecond)
|
|
out.DiskReadBytesPerSecond = cloneFloat64Ptr(in.DiskReadBytesPerSecond)
|
|
out.DiskWriteBytesPerSecond = cloneFloat64Ptr(in.DiskWriteBytesPerSecond)
|
|
out.UptimeSeconds = cloneInt64Ptr(in.UptimeSeconds)
|
|
out.DiskUsedBytes = cloneInt64Ptr(in.DiskUsedBytes)
|
|
out.DiskTotalBytes = cloneInt64Ptr(in.DiskTotalBytes)
|
|
out.DiskPercent = cloneFloat64Ptr(in.DiskPercent)
|
|
return &out
|
|
}
|
|
|
|
func cloneFloat64Ptr(n *float64) *float64 {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
value := *n
|
|
return &value
|
|
}
|