mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-10 00:14:38 +00:00
Clears the six dupl pairs in internal/monitoring: - poll_providers.go: PVE/PBS/PMG listInstances / describeInstances / connectionStatus closures collapse into generic sortedClientNames, describeProviderInstances, and providerConnectionStatuses helpers; the PBS/PMG adapters are now built by newPrefixedPollProvider from a prefixedPollProviderSpec (prefix drives status keys, health keys, and the fallback instance name). - truenas_poller.go / vmware_poller.go: the Start scheduling loop (double-start guard, stopped-channel handshake, sync+poll cadence) moves to startPollerLoop, and the active-connection config policy (enabled instances keyed by trimmed connection ID, defaults applied) moves to loadActiveInstanceConfigs, both in the new platform_poller_shared.go. - monitor_polling_vm.go / monitor_polling_containers.go: traditional polling now records guest series via the existing canonical recordGuestMetric helper (io/network sentinels preserve the historical cpu/memory/disk-only behavior on this path); the source-shape guardrails in canonical_guardrails_test.go and memory_source_catalog_test.go pin the new delegation. - mock_metrics_history.go: native and TrueNAS disk seeding share one seedDiskTelemetry closure. Proof: new TestTrueNASPollerDoubleStartKeepsSingleLoop pins the shared lifecycle loop; new TestSeedMockMetricsHistory_DiskTelemetryParityAcrossNativeAndTrueNAS pins four-series parity for both disk sources. Contract Extension Points name the shared wiring layer and poller scaffold. Full internal/monitoring test suite passes.
115 lines
2.6 KiB
Go
115 lines
2.6 KiB
Go
package monitoring
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
)
|
|
|
|
// startPollerLoop runs the scheduling scaffold shared by the periodic
|
|
// platform pollers (TrueNAS, VMware): guard against double-start under mu,
|
|
// then sync + poll immediately and again on every nextWaitDuration tick until
|
|
// the context is cancelled. cancelSlot and stoppedSlot point at the poller's
|
|
// lifecycle fields so Stop keeps working against the same state.
|
|
func startPollerLoop(
|
|
ctx context.Context,
|
|
mu *sync.Mutex,
|
|
cancelSlot *context.CancelFunc,
|
|
stoppedSlot *chan struct{},
|
|
syncConnections func(),
|
|
pollAll func(context.Context),
|
|
nextWaitDuration func(time.Time) time.Duration,
|
|
) {
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
|
|
mu.Lock()
|
|
if *cancelSlot != nil {
|
|
mu.Unlock()
|
|
return
|
|
}
|
|
|
|
runCtx, cancel := context.WithCancel(ctx)
|
|
*cancelSlot = cancel
|
|
*stoppedSlot = make(chan struct{})
|
|
stopped := *stoppedSlot
|
|
mu.Unlock()
|
|
|
|
go func() {
|
|
defer close(stopped)
|
|
defer func() {
|
|
mu.Lock()
|
|
if *stoppedSlot == stopped {
|
|
*cancelSlot = nil
|
|
}
|
|
mu.Unlock()
|
|
}()
|
|
|
|
syncConnections()
|
|
pollAll(runCtx)
|
|
for {
|
|
wait := nextWaitDuration(time.Now())
|
|
timer := time.NewTimer(wait)
|
|
select {
|
|
case <-runCtx.Done():
|
|
if !timer.Stop() {
|
|
select {
|
|
case <-timer.C:
|
|
default:
|
|
}
|
|
}
|
|
return
|
|
case <-timer.C:
|
|
syncConnections()
|
|
pollAll(runCtx)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// loadActiveInstanceConfigs resolves the active platform connection configs
|
|
// for an org: prefer the poller's cached map, otherwise load from per-org
|
|
// persistence and keep enabled instances keyed by trimmed connection ID with
|
|
// defaults applied. Shared by the TrueNAS and VMware pollers so the "what
|
|
// counts as an active connection" policy stays single-sourced.
|
|
func loadActiveInstanceConfigs[T any](
|
|
cached map[string]T,
|
|
multiTenant *config.MultiTenantPersistence,
|
|
orgID string,
|
|
load func(*config.ConfigPersistence) ([]T, error),
|
|
applyDefaults func(*T),
|
|
enabled func(T) bool,
|
|
connID func(T) string,
|
|
) map[string]T {
|
|
if len(cached) > 0 || multiTenant == nil {
|
|
return cached
|
|
}
|
|
|
|
persistence, err := multiTenant.GetPersistence(orgID)
|
|
if err != nil || persistence == nil {
|
|
return cached
|
|
}
|
|
instances, err := load(persistence)
|
|
if err != nil {
|
|
return cached
|
|
}
|
|
|
|
active := make(map[string]T)
|
|
for i := range instances {
|
|
instance := instances[i]
|
|
applyDefaults(&instance)
|
|
if !enabled(instance) {
|
|
continue
|
|
}
|
|
id := strings.TrimSpace(connID(instance))
|
|
if id == "" {
|
|
continue
|
|
}
|
|
active[id] = instance
|
|
}
|
|
return active
|
|
}
|