Pulse/internal/mock/platform_fixtures.go
rcourtman faefe6edc8 Remove 198 unreachable Go functions
Dead-code sweep. Functions flagged unreachable by golang.org/x/tools/cmd/deadcode
and confirmed unused across pulse, pulse-enterprise, pulse-pro and pulse-mobile by
adversarial cross-repo verification. Cross-module reachability was checked
explicitly (only pkg/ exported symbols are importable by other modules; internal/
packages and _test.go files are not). go build, go vet and test-compile all pass.
2026-06-03 12:29:37 +01:00

651 lines
22 KiB
Go

package mock
import (
"math"
"strings"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/truenas"
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
"github.com/rcourtman/pulse-go-rewrite/internal/vmware"
)
const DefaultPlatformPollIntervalSeconds = 60
type PlatformFixtures struct {
TrueNAS truenas.FixtureSnapshot
VMware vmware.InventorySnapshot
}
type TrueNASConnectionFixture struct {
ID string
Name string
Host string
Port int
APIKey string
UseHTTPS bool
Enabled bool
PollIntervalSeconds int
CollectedAt time.Time
ResourceID string
Systems int
StoragePools int
Datasets int
Apps int
Disks int
RecoveryArtifacts int
}
type VMwareConnectionFixture struct {
ID string
Name string
Host string
Port int
Username string
Password string
Enabled bool
PollIntervalSeconds int
CollectedAt time.Time
Hosts int
VMs int
Datastores int
Networks int
VIRelease string
}
func defaultPlatformFixtures() PlatformFixtures {
return PlatformFixtures{
TrueNAS: truenas.DefaultFixtures(),
VMware: vmware.DefaultFixtures(),
}
}
func rebasePlatformFixtures(fixtures PlatformFixtures, now time.Time) PlatformFixtures {
target := now.UTC()
return PlatformFixtures{
TrueNAS: rebaseTrueNASPlatformFixture(refreshTrueNASPlatformFixture(fixtures.TrueNAS, target), target),
VMware: rebaseVMwarePlatformFixture(refreshVMwarePlatformFixture(fixtures.VMware, target), target),
}
}
func DefaultTrueNASConnectionFixture() TrueNASConnectionFixture {
return defaultTrueNASConnectionFixture(currentOrDefaultPlatformFixtures())
}
func DefaultVMwareConnectionFixture() VMwareConnectionFixture {
fixtures := currentOrDefaultPlatformFixtures().VMware
return VMwareConnectionFixture{
ID: strings.TrimSpace(fixtures.ConnectionID),
Name: strings.TrimSpace(fixtures.ConnectionName),
Host: strings.TrimSpace(fixtures.VCenterHost),
Port: 443,
Username: "administrator@vsphere.local",
Password: "mock-vcenter-password",
Enabled: true,
PollIntervalSeconds: DefaultPlatformPollIntervalSeconds,
CollectedAt: fixtures.CollectedAt,
Hosts: len(fixtures.Hosts),
VMs: len(fixtures.VMs),
Datastores: len(fixtures.Datastores),
Networks: len(fixtures.Networks),
VIRelease: strings.TrimSpace(fixtures.VIRelease),
}
}
func defaultTrueNASConnectionFixture(fixtures PlatformFixtures) TrueNASConnectionFixture {
snapshot := fixtures.TrueNAS
collectedAt := trueNASCollectedAt(snapshot)
host := strings.TrimSpace(snapshot.System.Hostname)
return TrueNASConnectionFixture{
ID: "truenas-mock-1",
Name: "Archive NAS",
Host: host,
Port: 443,
APIKey: "mock-truenas-api-key",
UseHTTPS: true,
Enabled: true,
PollIntervalSeconds: DefaultPlatformPollIntervalSeconds,
CollectedAt: collectedAt,
ResourceID: host,
Systems: 1,
StoragePools: len(snapshot.Pools),
Datasets: len(snapshot.Datasets),
Apps: len(snapshot.Apps),
Disks: len(snapshot.Disks),
RecoveryArtifacts: len(snapshot.ZFSSnapshots) + len(snapshot.ReplicationTasks),
}
}
func SupplementalRecords(source unifiedresources.DataSource) []unifiedresources.IngestRecord {
if IsMockEnabled() {
return CurrentFixtureGraph().SupplementalRecords(source)
}
platformFixtures := defaultPlatformFixtures()
now := time.Now().UTC()
switch normalizeSupplementalSource(source) {
case unifiedresources.SourceTrueNAS:
return truenas.FixtureRecords(platformFixtures.TrueNAS)
case unifiedresources.SourceVMware:
return vmware.FixtureRecords(platformFixtures.VMware)
case unifiedresources.SourceAvailability:
return availabilityFixtureRecords(defaultAvailabilityFixtures(now), now)
default:
return nil
}
}
func SupplementalChanges(source unifiedresources.DataSource) []unifiedresources.ResourceChange {
if IsMockEnabled() {
return CurrentFixtureGraph().SupplementalChanges(source)
}
platformFixtures := defaultPlatformFixtures()
switch normalizeSupplementalSource(source) {
case unifiedresources.SourceVMware:
return vmware.FixtureActivityChanges(platformFixtures.VMware)
default:
return nil
}
}
func SupplementalOwnedSources() []unifiedresources.DataSource {
return []unifiedresources.DataSource{
unifiedresources.SourceTrueNAS,
unifiedresources.SourceVMware,
unifiedresources.SourceAvailability,
}
}
func UnifiedResourceSnapshot() ([]unifiedresources.Resource, time.Time) {
if !IsMockEnabled() {
return nil, time.Time{}
}
return CurrentFixtureGraph().UnifiedResourceSnapshot()
}
func (g FixtureGraph) UnifiedResourceSnapshot() ([]unifiedresources.Resource, time.Time) {
registry := unifiedresources.NewRegistry(nil)
registry.IngestSnapshot(unifiedresources.SnapshotWithoutSources(g.State, SupplementalOwnedSources()))
for _, source := range SupplementalOwnedSources() {
records := g.SupplementalRecords(source)
if len(records) == 0 {
continue
}
registry.IngestRecords(source, records)
}
freshness := g.State.LastUpdate
for _, candidate := range []time.Time{
trueNASCollectedAt(g.PlatformFixtures.TrueNAS),
g.PlatformFixtures.VMware.CollectedAt,
availabilityFixturesFreshness(g.AvailabilityFixtures),
} {
if candidate.IsZero() {
continue
}
if freshness.IsZero() || candidate.After(freshness) {
freshness = candidate
}
}
resources := registry.List()
for i := range resources {
if target := registry.MetricsTarget(resources[i].ID); target != nil {
resources[i].MetricsTarget = target
}
}
return resources, freshness
}
func (g FixtureGraph) SupplementalRecords(source unifiedresources.DataSource) []unifiedresources.IngestRecord {
switch normalizeSupplementalSource(source) {
case unifiedresources.SourceTrueNAS:
return truenas.FixtureRecords(g.PlatformFixtures.TrueNAS)
case unifiedresources.SourceVMware:
return vmware.FixtureRecords(g.PlatformFixtures.VMware)
case unifiedresources.SourceAvailability:
return availabilityFixtureRecords(g.AvailabilityFixtures, availabilityFixturesFreshness(g.AvailabilityFixtures))
default:
return nil
}
}
func (g FixtureGraph) SupplementalChanges(source unifiedresources.DataSource) []unifiedresources.ResourceChange {
switch normalizeSupplementalSource(source) {
case unifiedresources.SourceVMware:
return vmware.FixtureActivityChanges(g.PlatformFixtures.VMware)
default:
return nil
}
}
func trueNASCollectedAt(fixtures truenas.FixtureSnapshot) time.Time {
if !fixtures.CollectedAt.IsZero() {
return fixtures.CollectedAt
}
return fixtures.System.CollectedAt
}
func rebaseTrueNASPlatformFixture(snapshot truenas.FixtureSnapshot, target time.Time) truenas.FixtureSnapshot {
out := cloneTrueNASFixtureSnapshot(snapshot)
anchor := trueNASCollectedAt(snapshot)
if anchor.IsZero() {
anchor = target
}
shift := target.Sub(anchor)
out.CollectedAt = target
out.System.CollectedAt = shiftTime(snapshot.System.CollectedAt, shift, target)
for i := range out.Alerts {
out.Alerts[i].Datetime = shiftTime(snapshot.Alerts[i].Datetime, shift, target)
}
for i := range out.Apps {
if out.Apps[i].Stats != nil {
out.Apps[i].Stats.CollectedAt = shiftTime(snapshot.Apps[i].Stats.CollectedAt, shift, target)
}
}
for i := range out.ZFSSnapshots {
if snapshot.ZFSSnapshots[i].CreatedAt != nil {
rebased := shiftTime(*snapshot.ZFSSnapshots[i].CreatedAt, shift, target)
out.ZFSSnapshots[i].CreatedAt = &rebased
}
}
for i := range out.ReplicationTasks {
if snapshot.ReplicationTasks[i].LastRun != nil {
rebased := shiftTime(*snapshot.ReplicationTasks[i].LastRun, shift, target)
out.ReplicationTasks[i].LastRun = &rebased
}
}
return out
}
func rebaseVMwarePlatformFixture(snapshot vmware.InventorySnapshot, target time.Time) vmware.InventorySnapshot {
out := cloneVMwareInventorySnapshot(snapshot)
anchor := snapshot.CollectedAt
if anchor.IsZero() {
anchor = target
}
shift := target.Sub(anchor)
out.CollectedAt = target
for i := range out.Hosts {
rebaseVMwareAlarms(out.Hosts[i].TriggeredAlarms, snapshot.Hosts[i].TriggeredAlarms, shift, target)
rebaseVMwareTasks(out.Hosts[i].RecentTasks, snapshot.Hosts[i].RecentTasks, shift, target)
rebaseVMwareEvents(out.Hosts[i].RecentEvents, snapshot.Hosts[i].RecentEvents, shift, target)
}
for i := range out.VMs {
rebaseVMwareAlarms(out.VMs[i].TriggeredAlarms, snapshot.VMs[i].TriggeredAlarms, shift, target)
rebaseVMwareTasks(out.VMs[i].RecentTasks, snapshot.VMs[i].RecentTasks, shift, target)
rebaseVMwareEvents(out.VMs[i].RecentEvents, snapshot.VMs[i].RecentEvents, shift, target)
}
for i := range out.Datastores {
rebaseVMwareAlarms(out.Datastores[i].TriggeredAlarms, snapshot.Datastores[i].TriggeredAlarms, shift, target)
rebaseVMwareTasks(out.Datastores[i].RecentTasks, snapshot.Datastores[i].RecentTasks, shift, target)
rebaseVMwareEvents(out.Datastores[i].RecentEvents, snapshot.Datastores[i].RecentEvents, shift, target)
}
for i := range out.Networks {
rebaseVMwareAlarms(out.Networks[i].TriggeredAlarms, snapshot.Networks[i].TriggeredAlarms, shift, target)
rebaseVMwareTasks(out.Networks[i].RecentTasks, snapshot.Networks[i].RecentTasks, shift, target)
rebaseVMwareEvents(out.Networks[i].RecentEvents, snapshot.Networks[i].RecentEvents, shift, target)
}
return out
}
func refreshTrueNASPlatformFixture(snapshot truenas.FixtureSnapshot, at time.Time) truenas.FixtureSnapshot {
out := cloneTrueNASFixtureSnapshot(snapshot)
hostname := strings.TrimSpace(out.System.Hostname)
if hostname != "" {
out.System.CPUPercent = SampleMetric("agent", hostname, "cpu", at)
out.System.DiskReadRate = SampleMetric("agent", hostname, "diskread", at)
out.System.DiskWriteRate = SampleMetric("agent", hostname, "diskwrite", at)
out.System.NetInRate = SampleMetric("agent", hostname, "netin", at)
out.System.NetOutRate = SampleMetric("agent", hostname, "netout", at)
if out.System.MemoryTotalBytes > 0 {
memoryPercent := SampleMetric("agent", hostname, "memory", at)
usedBytes := bytesFromPercent(out.System.MemoryTotalBytes, memoryPercent)
out.System.MemoryAvailableBytes = out.System.MemoryTotalBytes - usedBytes
if out.System.MemoryAvailableBytes < 0 {
out.System.MemoryAvailableBytes = 0
}
}
}
for i := range out.Pools {
usage := SampleMetric("storage", TrueNASPoolMetricID(out.System.Hostname, out.Pools[i].Name), "usage", at)
applyTrueNASCapacityUsage(&out.Pools[i].UsedBytes, &out.Pools[i].FreeBytes, out.Pools[i].TotalBytes, usage)
}
for i := range out.Datasets {
totalBytes := out.Datasets[i].UsedBytes + out.Datasets[i].AvailBytes
usage := SampleMetric("storage", TrueNASDatasetMetricID(out.System.Hostname, out.Datasets[i].Name), "usage", at)
usedBytes := bytesFromPercent(totalBytes, usage)
out.Datasets[i].UsedBytes = usedBytes
out.Datasets[i].AvailBytes = totalBytes - usedBytes
if out.Datasets[i].AvailBytes < 0 {
out.Datasets[i].AvailBytes = 0
}
}
for i := range out.Disks {
resourceID := trueNASDiskMetricID(out.Disks[i])
if resourceID == "" {
continue
}
out.Disks[i].Temperature = int(math.Round(SampleMetric("disk", resourceID, "smart_temp", at)))
refreshTrueNASDiskAggregate(&out.Disks[i].TemperatureAggregate, out.Disks[i].Temperature)
}
for i := range out.Apps {
stats := out.Apps[i].Stats
if stats == nil {
continue
}
appID := TrueNASAppMetricID(out.System.Hostname, out.Apps[i])
if appID == "" {
continue
}
stats.CollectedAt = at
stopped := strings.EqualFold(strings.TrimSpace(out.Apps[i].State), "stopped")
if stopped {
stats.CPUPercent = 0
stats.NetInRate = 0
stats.NetOutRate = 0
stats.DiskReadRate = 0
stats.DiskWriteRate = 0
stats.BlockReadBytes = 0
stats.BlockWriteBytes = 0
if out.System.MemoryTotalBytes > 0 {
idlePercent := clampFloat(SampleMetric("dockerContainer", appID, "memory", at)*0.12, 0.5, 8)
stats.MemoryBytes = bytesFromPercent(out.System.MemoryTotalBytes, idlePercent)
}
} else {
stats.CPUPercent = SampleMetric("dockerContainer", appID, "cpu", at)
stats.NetInRate = SampleMetric("dockerContainer", appID, "netin", at)
stats.NetOutRate = SampleMetric("dockerContainer", appID, "netout", at)
stats.DiskReadRate = SampleMetric("dockerContainer", appID, "diskread", at)
stats.DiskWriteRate = SampleMetric("dockerContainer", appID, "diskwrite", at)
stats.BlockReadBytes = int64(math.Round(stats.DiskReadRate * math.Max(1, float64(stats.IntervalSeconds))))
stats.BlockWriteBytes = int64(math.Round(stats.DiskWriteRate * math.Max(1, float64(stats.IntervalSeconds))))
if out.System.MemoryTotalBytes > 0 {
memoryPercent := SampleMetric("dockerContainer", appID, "memory", at)
stats.MemoryBytes = bytesFromPercent(out.System.MemoryTotalBytes, memoryPercent)
}
}
refreshTrueNASAppInterfaces(stats)
}
return out
}
func refreshVMwarePlatformFixture(snapshot vmware.InventorySnapshot, at time.Time) vmware.InventorySnapshot {
out := cloneVMwareInventorySnapshot(snapshot)
for i := range out.Hosts {
refreshVMwareInventoryMetrics(out.Hosts[i].Metrics, "agent", vmware.SourceID(out.ConnectionID, "host", out.Hosts[i].Host), at)
}
for i := range out.VMs {
resourceID := vmware.SourceID(out.ConnectionID, "vm", out.VMs[i].VM)
refreshVMwareInventoryMetrics(out.VMs[i].Metrics, "vm", resourceID, at)
if strings.EqualFold(strings.TrimSpace(out.VMs[i].PowerState), "powered_off") && out.VMs[i].Metrics != nil {
*out.VMs[i].Metrics.CPUPercent = 0
*out.VMs[i].Metrics.NetInBytesPerSecond = 0
*out.VMs[i].Metrics.NetOutBytesPerSecond = 0
*out.VMs[i].Metrics.DiskReadBytesPerSecond = 0
*out.VMs[i].Metrics.DiskWriteBytesPerSecond = 0
// A powered-off VM has no current uptime, and VMware Tools is
// obviously not reporting guest filesystem usage. Drop the
// pointers entirely so the frontend renders "—" rather than 0,
// matching how Pulse signals "no data" for an offline guest.
out.VMs[i].Metrics.UptimeSeconds = nil
out.VMs[i].Metrics.DiskUsedBytes = nil
out.VMs[i].Metrics.DiskTotalBytes = nil
out.VMs[i].Metrics.DiskPercent = nil
}
}
for i := range out.Datastores {
total := out.Datastores[i].Capacity
if total <= 0 {
continue
}
usage := SampleMetric("storage", vmware.SourceID(out.ConnectionID, "datastore", out.Datastores[i].Datastore), "usage", at)
used := bytesFromPercent(total, usage)
out.Datastores[i].FreeSpace = total - used
if out.Datastores[i].FreeSpace < 0 {
out.Datastores[i].FreeSpace = 0
}
}
return out
}
func applyTrueNASCapacityUsage(usedBytes *int64, freeBytes *int64, totalBytes int64, usagePercent float64) {
if usedBytes == nil || freeBytes == nil || totalBytes <= 0 {
return
}
used := bytesFromPercent(totalBytes, usagePercent)
*usedBytes = used
*freeBytes = totalBytes - used
if *freeBytes < 0 {
*freeBytes = 0
}
}
func bytesFromPercent(totalBytes int64, usagePercent float64) int64 {
if totalBytes <= 0 {
return 0
}
usage := clampFloat(usagePercent, 0, 100)
used := int64(math.Round((float64(totalBytes) * usage) / 100.0))
if used < 0 {
return 0
}
if used > totalBytes {
return totalBytes
}
return used
}
func refreshTrueNASDiskAggregate(aggregate *truenas.DiskTemperatureAggregate, current int) {
if aggregate == nil {
return
}
currentFloat := float64(current)
if aggregate.WindowDays <= 0 {
aggregate.WindowDays = 7
}
if aggregate.MinCelsius <= 0 || aggregate.MinCelsius > currentFloat {
aggregate.MinCelsius = math.Max(25, currentFloat-4)
}
if aggregate.MaxCelsius <= 0 || aggregate.MaxCelsius < currentFloat {
aggregate.MaxCelsius = math.Min(95, currentFloat+5)
}
aggregate.AvgCelsius = clampFloat(currentFloat-0.8, aggregate.MinCelsius, aggregate.MaxCelsius)
}
func trueNASDiskMetricID(disk truenas.Disk) string {
resourceID := strings.TrimSpace(disk.Serial)
if resourceID == "" {
resourceID = strings.TrimSpace(disk.ID)
}
if resourceID == "" {
resourceID = strings.TrimSpace(disk.Name)
}
return resourceID
}
func refreshTrueNASAppInterfaces(stats *truenas.AppStats) {
if stats == nil || len(stats.Interfaces) == 0 {
return
}
var totalRx float64
var totalTx float64
for _, iface := range stats.Interfaces {
totalRx += iface.RxBytesPS
totalTx += iface.TxBytesPS
}
for i := range stats.Interfaces {
rxShare := 1 / float64(len(stats.Interfaces))
txShare := 1 / float64(len(stats.Interfaces))
if totalRx > 0 {
rxShare = stats.Interfaces[i].RxBytesPS / totalRx
}
if totalTx > 0 {
txShare = stats.Interfaces[i].TxBytesPS / totalTx
}
stats.Interfaces[i].RxBytesPS = stats.NetInRate * rxShare
stats.Interfaces[i].TxBytesPS = stats.NetOutRate * txShare
}
}
func refreshVMwareInventoryMetrics(metrics *vmware.InventoryMetrics, resourceClass, resourceID string, at time.Time) {
if metrics == nil || strings.TrimSpace(resourceID) == "" {
return
}
memoryTotal := int64(0)
if metrics.MemoryTotalBytes != nil {
memoryTotal = *metrics.MemoryTotalBytes
}
*ensureFloat64Ptr(&metrics.CPUPercent) = SampleMetric(resourceClass, resourceID, "cpu", at)
*ensureFloat64Ptr(&metrics.MemoryPercent) = SampleMetric(resourceClass, resourceID, "memory", at)
*ensureFloat64Ptr(&metrics.NetInBytesPerSecond) = SampleMetric(resourceClass, resourceID, "netin", at)
*ensureFloat64Ptr(&metrics.NetOutBytesPerSecond) = SampleMetric(resourceClass, resourceID, "netout", at)
*ensureFloat64Ptr(&metrics.DiskReadBytesPerSecond) = SampleMetric(resourceClass, resourceID, "diskread", at)
*ensureFloat64Ptr(&metrics.DiskWriteBytesPerSecond) = SampleMetric(resourceClass, resourceID, "diskwrite", at)
if memoryTotal > 0 {
*ensureInt64Ptr(&metrics.MemoryUsedBytes) = bytesFromPercent(memoryTotal, *metrics.MemoryPercent)
}
// Uptime: a per-resource stable base age (1-30 days) that climbs forward
// with `at`. Matches what a real vCenter would report from
// sys.uptime.latest (and sys.osUptime.latest for VMs with Tools running).
*ensureInt64Ptr(&metrics.UptimeSeconds) = mockUptimeSeconds(resourceClass, resourceID, at)
// Guest filesystem usage: only meaningful for VMs (hosts don't have a
// `guest` shape in vSphere). For VMs we synthesize a stable total
// capacity and let SampleMetric oscillate usage naturally so the table
// renders realistic-looking bars instead of always-empty cells.
if resourceClass == "vm" {
total := mockGuestDiskTotalBytes(resourceID)
usage := clampFloat(SampleMetric(resourceClass, resourceID, "diskusage", at), 0, 100)
*ensureInt64Ptr(&metrics.DiskTotalBytes) = total
*ensureInt64Ptr(&metrics.DiskUsedBytes) = bytesFromPercent(total, usage)
*ensureFloat64Ptr(&metrics.DiskPercent) = usage
}
}
// mockUptimeSeconds returns a stable, slowly-incrementing uptime for a
// vSphere mock resource. The base age is derived from the resource ID so
// each host / VM has its own multi-day uptime, then `at` shifts it forward
// so the column ticks naturally between snapshots.
func mockUptimeSeconds(resourceClass, resourceID string, at time.Time) int64 {
seed := fnv64a(resourceClass + "|" + resourceID + "|uptime")
// Base age between 1 hour and 30 days
const minSeconds = uint64(3600)
const maxSeconds = uint64(30 * 24 * 60 * 60)
span := maxSeconds - minSeconds
baseSeconds := int64(minSeconds + (seed % span))
if at.IsZero() {
return baseSeconds
}
// Anchor the climb at a stable epoch so successive refreshes ramp up
// without resetting; use 2026-01-01 UTC.
anchor := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
if at.Before(anchor) {
return baseSeconds
}
return baseSeconds + int64(at.Sub(anchor)/time.Second)
}
// mockGuestDiskTotalBytes returns a per-VM stable guest filesystem total
// (between 32 GiB and 256 GiB) so the same VM always appears at the same
// size across refreshes.
func mockGuestDiskTotalBytes(resourceID string) int64 {
const (
minGiB = int64(32)
maxGiB = int64(256)
)
seed := fnv64a(resourceID + "|disktotal")
gib := minGiB + int64(seed%uint64(maxGiB-minGiB+1))
return gib * 1024 * 1024 * 1024
}
// fnv64a is a tiny deterministic hash so mock helpers can derive stable
// per-resource seeds without pulling in a math/rand source.
func fnv64a(input string) uint64 {
const (
offset uint64 = 14695981039346656037
prime uint64 = 1099511628211
)
hash := offset
for i := 0; i < len(input); i++ {
hash ^= uint64(input[i])
hash *= prime
}
return hash
}
func ensureFloat64Ptr(target **float64) *float64 {
if *target == nil {
*target = new(float64)
}
return *target
}
func ensureInt64Ptr(target **int64) *int64 {
if *target == nil {
*target = new(int64)
}
return *target
}
func rebaseVMwareAlarms(out []vmware.InventoryAlarm, in []vmware.InventoryAlarm, shift time.Duration, target time.Time) {
for i := range out {
out[i].TriggeredAt = shiftTime(in[i].TriggeredAt, shift, target)
}
}
func rebaseVMwareTasks(out []vmware.InventoryTask, in []vmware.InventoryTask, shift time.Duration, target time.Time) {
for i := range out {
out[i].StartedAt = shiftTime(in[i].StartedAt, shift, target)
out[i].CompletedAt = shiftTime(in[i].CompletedAt, shift, target)
}
}
func rebaseVMwareEvents(out []vmware.InventoryEvent, in []vmware.InventoryEvent, shift time.Duration, target time.Time) {
for i := range out {
out[i].CreatedAt = shiftTime(in[i].CreatedAt, shift, target)
}
}
func shiftTime(value time.Time, shift time.Duration, fallback time.Time) time.Time {
if value.IsZero() {
return fallback
}
return value.Add(shift)
}
func normalizeSupplementalSource(source unifiedresources.DataSource) unifiedresources.DataSource {
switch strings.ToLower(strings.TrimSpace(string(source))) {
case "truenas":
return unifiedresources.SourceTrueNAS
case "vmware", "vmware-vsphere":
return unifiedresources.SourceVMware
case "availability", "network-endpoint", "network-endpoints":
return unifiedresources.SourceAvailability
default:
return ""
}
}