mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-09 19:32:24 +00:00
Expose monitored system latest signal source
This commit is contained in:
parent
87a3ec8736
commit
148c7e8ab5
14 changed files with 207 additions and 62 deletions
|
|
@ -243,6 +243,10 @@ included grouped observation, with `last_seen` left only as a compatibility
|
|||
alias during rollout, rather than a promise that every grouped source is
|
||||
healthy at that moment. Lifecycle-adjacent consumers must not label it with
|
||||
generic single-source health wording.
|
||||
That same ledger read now also includes `latest_included_signal_source`, so
|
||||
lifecycle-adjacent consumers can attribute the freshest grouped observation to
|
||||
its canonical reporting source instead of inferring it from the broader grouped
|
||||
system source set.
|
||||
Lifecycle-adjacent workspace copy must also keep the same commercial framing:
|
||||
infrastructure operations may point operators to Pulse Pro for billing, but it
|
||||
must describe that boundary in monitored-system, plan-limit, and license-status
|
||||
|
|
|
|||
|
|
@ -257,6 +257,10 @@ That same timestamp is now canonically exposed as
|
|||
during rollout. It represents the freshest included grouped observation, not a
|
||||
claim that every grouped source reported successfully at that time, and API
|
||||
consumers must preserve that meaning in their labeling and presentation.
|
||||
That canonical signal contract now also includes
|
||||
`latest_included_signal_source`, so consumers can attribute the freshest
|
||||
included grouped observation to the source that produced it instead of
|
||||
guessing from the broader grouped `source` field.
|
||||
That client contract must also fail closed when older or partial payloads omit
|
||||
the nested explanation object: the frontend may normalize missing explanation
|
||||
fields to empty reasons/surfaces plus a safe default summary, but it must not
|
||||
|
|
|
|||
|
|
@ -225,6 +225,10 @@ signal timestamp by its real meaning. The canonical API field is now
|
|||
rollout; it represents the freshest included grouped observation, not a
|
||||
guarantee that every grouped source is healthy, so the UI must not present it
|
||||
with single-source `Last Seen` wording.
|
||||
That same cloud-paid surface should also show the canonical
|
||||
`latest_included_signal_source` attribution when present, so a customer can
|
||||
see which grouped source most recently reported instead of reading an
|
||||
unqualified aggregate timestamp.
|
||||
Frontend billing/admin surfaces must not synthesize `plan_version` from
|
||||
subscription lifecycle state. When a hosted billing record lacks a plan label,
|
||||
the UI must preserve that absence instead of fabricating values like `active`
|
||||
|
|
|
|||
|
|
@ -381,6 +381,10 @@ freshest grouped observation, with `last_seen` left only as a compatibility
|
|||
alias during rollout, rather than a universal health timestamp, so storage- or
|
||||
recovery-adjacent consumers must not present that field with bare single-
|
||||
source `Last Seen` wording that hides grouped stale/offline conditions.
|
||||
That same dependency now also includes `latest_included_signal_source`, so
|
||||
storage- or recovery-adjacent consumers can identify which grouped source most
|
||||
recently reported instead of deriving attribution from the broader grouped
|
||||
source field.
|
||||
That same shared `internal/api/` dependency now also assumes self-hosted
|
||||
commercial counting is canonical at the top-level monitored-system boundary:
|
||||
shared setup, deploy, entitlement, and API-backed monitoring helpers may not
|
||||
|
|
|
|||
|
|
@ -184,6 +184,11 @@ grouped source reported more recently than the degraded one, so consumers do
|
|||
not present a fresh `Last Seen` timestamp beside warning or offline state
|
||||
without the canonical explanation of which grouped source is still reporting
|
||||
and which one drifted stale or disconnected.
|
||||
That same monitored-system contract now also owns attribution for the freshest
|
||||
included grouped signal. Unified resources must expose not just the timestamp
|
||||
of that latest included observation but also the canonical source that
|
||||
produced it, so consumers can say which grouped source most recently reported
|
||||
instead of showing an unowned aggregate timestamp.
|
||||
|
||||
The unified-resource runtime now also owns the durable change timeline for the
|
||||
canonical resource view. `internal/unifiedresources/monitor_adapter.go` feeds
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ describe('MonitoredSystemLedgerAPI', () => {
|
|||
reasons: [],
|
||||
},
|
||||
latest_included_signal_at: '2026-01-01T00:00:00Z',
|
||||
latest_included_signal_source: 'agent',
|
||||
last_seen: '2026-01-01T00:00:00Z',
|
||||
source: 'agent',
|
||||
explanation: {
|
||||
|
|
@ -103,6 +104,7 @@ describe('MonitoredSystemLedgerAPI', () => {
|
|||
type: 'host',
|
||||
status: 'warning',
|
||||
latest_included_signal_at: '2026-01-01T00:00:00Z',
|
||||
latest_included_signal_source: 'agent',
|
||||
last_seen: '2026-01-01T00:00:00Z',
|
||||
source: 'agent',
|
||||
},
|
||||
|
|
@ -124,6 +126,7 @@ describe('MonitoredSystemLedgerAPI', () => {
|
|||
type: 'host',
|
||||
status: 'warning',
|
||||
latest_included_signal_at: '2026-03-23T11:59:50Z',
|
||||
latest_included_signal_source: 'docker',
|
||||
last_seen: '2026-03-23T11:59:50Z',
|
||||
source: 'multiple',
|
||||
},
|
||||
|
|
@ -135,6 +138,7 @@ describe('MonitoredSystemLedgerAPI', () => {
|
|||
const result = await MonitoredSystemLedgerAPI.getLedger();
|
||||
|
||||
expect(result.systems[0]?.latest_included_signal_at).toBe('2026-03-23T11:59:50Z');
|
||||
expect(result.systems[0]?.latest_included_signal_source).toBe('docker');
|
||||
});
|
||||
|
||||
it('falls back to the deprecated last_seen alias for older payloads', async () => {
|
||||
|
|
@ -155,6 +159,7 @@ describe('MonitoredSystemLedgerAPI', () => {
|
|||
const result = await MonitoredSystemLedgerAPI.getLedger();
|
||||
|
||||
expect(result.systems[0]?.latest_included_signal_at).toBe('2026-03-23T11:59:50Z');
|
||||
expect(result.systems[0]?.latest_included_signal_source).toBeUndefined();
|
||||
});
|
||||
|
||||
it('preserves canonical status explanation reasons from the API contract', async () => {
|
||||
|
|
@ -179,6 +184,7 @@ describe('MonitoredSystemLedgerAPI', () => {
|
|||
],
|
||||
},
|
||||
latest_included_signal_at: '2026-03-23T11:59:50Z',
|
||||
latest_included_signal_source: 'docker',
|
||||
last_seen: '2026-03-23T11:59:50Z',
|
||||
source: 'multiple',
|
||||
},
|
||||
|
|
@ -210,6 +216,7 @@ describe('MonitoredSystemLedgerAPI', () => {
|
|||
type: 'host',
|
||||
status: 'degraded',
|
||||
latest_included_signal_at: '2026-01-01T00:00:00Z',
|
||||
latest_included_signal_source: 'agent',
|
||||
last_seen: '2026-01-01T00:00:00Z',
|
||||
source: 'agent',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export interface MonitoredSystemLedgerEntry {
|
|||
status: MonitoredSystemLedgerStatus;
|
||||
status_explanation?: MonitoredSystemLedgerStatusExplanation;
|
||||
latest_included_signal_at: string; // freshest included observation, RFC3339 or empty
|
||||
latest_included_signal_source?: string;
|
||||
last_seen?: string; // deprecated compatibility alias
|
||||
source: string;
|
||||
explanation?: MonitoredSystemLedgerExplanation;
|
||||
|
|
@ -80,6 +81,9 @@ function normalizeMonitoredSystemLedgerEntry(
|
|||
status,
|
||||
latest_included_signal_at:
|
||||
entry.latest_included_signal_at?.trim() || entry.last_seen?.trim() || '',
|
||||
latest_included_signal_source: normalizeMonitoredSystemLedgerSource(
|
||||
entry.latest_included_signal_source ?? (entry.source !== 'multiple' ? entry.source : ''),
|
||||
),
|
||||
status_explanation: {
|
||||
summary: entry.status_explanation?.summary ?? defaultMonitoredSystemStatusExplanation(status),
|
||||
reasons: (entry.status_explanation?.reasons ?? []).map(normalizeMonitoredSystemLedgerStatusReason),
|
||||
|
|
@ -144,3 +148,20 @@ function normalizeMonitoredSystemLedgerStatusReasonStatus(
|
|||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeMonitoredSystemLedgerSource(
|
||||
source: string | null | undefined,
|
||||
): string | undefined {
|
||||
switch ((source ?? '').trim().toLowerCase()) {
|
||||
case 'agent':
|
||||
case 'docker':
|
||||
case 'kubernetes':
|
||||
case 'pbs':
|
||||
case 'pmg':
|
||||
case 'proxmox':
|
||||
case 'truenas':
|
||||
return source?.trim().toLowerCase();
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,39 @@ function systemStatusExplanation(system: MonitoredSystemLedgerEntry): MonitoredS
|
|||
};
|
||||
}
|
||||
|
||||
function monitoredSystemSourceLabel(source: string | undefined): string {
|
||||
switch ((source ?? '').trim().toLowerCase()) {
|
||||
case 'agent':
|
||||
return 'Agent';
|
||||
case 'docker':
|
||||
return 'Docker';
|
||||
case 'kubernetes':
|
||||
return 'Kubernetes';
|
||||
case 'pbs':
|
||||
return 'PBS';
|
||||
case 'pmg':
|
||||
return 'PMG';
|
||||
case 'proxmox':
|
||||
return 'Proxmox';
|
||||
case 'truenas':
|
||||
return 'TrueNAS';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function latestIncludedSignalLabel(system: MonitoredSystemLedgerEntry): string {
|
||||
if (!system.latest_included_signal_at) {
|
||||
return '—';
|
||||
}
|
||||
const relative = formatRelativeTime(system.latest_included_signal_at, { compact: true });
|
||||
const source = monitoredSystemSourceLabel(system.latest_included_signal_source);
|
||||
if (source === '') {
|
||||
return relative;
|
||||
}
|
||||
return `${relative} via ${source}`;
|
||||
}
|
||||
|
||||
export function MonitoredSystemLedgerPanel(props: MonitoredSystemLedgerPanelProps = {}) {
|
||||
const [ledger, { refetch }] = createResource(() => MonitoredSystemLedgerAPI.getLedger());
|
||||
const [expandedSystemKey, setExpandedSystemKey] = createSignal<string | null>(null);
|
||||
|
|
@ -224,9 +257,7 @@ export function MonitoredSystemLedgerPanel(props: MonitoredSystemLedgerPanelProp
|
|||
</TableCell>
|
||||
<TableCell>
|
||||
<span class="text-xs text-muted">
|
||||
{system.latest_included_signal_at
|
||||
? formatRelativeTime(system.latest_included_signal_at, { compact: true })
|
||||
: '—'}
|
||||
{latestIncludedSignalLabel(system)}
|
||||
</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ describe('MonitoredSystemLedgerPanel', () => {
|
|||
reasons: [],
|
||||
},
|
||||
latest_included_signal_at: '2026-01-01T00:00:00Z',
|
||||
latest_included_signal_source: 'agent',
|
||||
last_seen: '2026-01-01T00:00:00Z',
|
||||
source: 'agent',
|
||||
explanation: {
|
||||
|
|
@ -144,6 +145,7 @@ describe('MonitoredSystemLedgerPanel', () => {
|
|||
reasons: [],
|
||||
},
|
||||
latest_included_signal_at: '2026-01-01T00:00:00Z',
|
||||
latest_included_signal_source: 'agent',
|
||||
last_seen: '2026-01-01T00:00:00Z',
|
||||
source: 'agent',
|
||||
explanation: {
|
||||
|
|
@ -180,6 +182,7 @@ describe('MonitoredSystemLedgerPanel', () => {
|
|||
],
|
||||
},
|
||||
latest_included_signal_at: '2026-01-02T00:00:00Z',
|
||||
latest_included_signal_source: 'pbs',
|
||||
last_seen: '2026-01-02T00:00:00Z',
|
||||
source: 'pbs',
|
||||
explanation: {
|
||||
|
|
@ -211,6 +214,7 @@ describe('MonitoredSystemLedgerPanel', () => {
|
|||
|
||||
expect(screen.getByText('Monitored System Ledger')).toBeInTheDocument();
|
||||
expect(screen.getByText('Latest Included Signal')).toBeInTheDocument();
|
||||
expect(screen.getByText('2026-01-02T00:00:00Z via PBS')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Review the monitored systems currently counted against your Pulse Pro plan limit.',
|
||||
|
|
@ -265,6 +269,7 @@ describe('MonitoredSystemLedgerPanel', () => {
|
|||
reasons: [],
|
||||
},
|
||||
latest_included_signal_at: '2026-01-01T00:00:00Z',
|
||||
latest_included_signal_source: 'agent',
|
||||
last_seen: '2026-01-01T00:00:00Z',
|
||||
source: 'agent',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -594,9 +594,10 @@ func TestContract_MonitoredSystemLedgerJSONSnapshot(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
LatestIncludedSignalAt: "2026-03-18T17:30:00Z",
|
||||
LastSeen: "2026-03-18T17:30:00Z",
|
||||
Source: "agent",
|
||||
LatestIncludedSignalAt: "2026-03-18T17:30:00Z",
|
||||
LatestIncludedSignalSource: "agent",
|
||||
LastSeen: "2026-03-18T17:30:00Z",
|
||||
Source: "agent",
|
||||
Explanation: MonitoredSystemLedgerExplanation{
|
||||
Summary: "Counts as one monitored system because Pulse sees one top-level host view from agent.",
|
||||
Reasons: []MonitoredSystemLedgerExplanationReason{
|
||||
|
|
@ -646,6 +647,7 @@ func TestContract_MonitoredSystemLedgerJSONSnapshot(t *testing.T) {
|
|||
]
|
||||
},
|
||||
"latest_included_signal_at":"2026-03-18T17:30:00Z",
|
||||
"latest_included_signal_source":"agent",
|
||||
"last_seen":"2026-03-18T17:30:00Z",
|
||||
"source":"agent",
|
||||
"explanation":{
|
||||
|
|
|
|||
|
|
@ -13,14 +13,15 @@ import (
|
|||
// MonitoredSystemLedgerEntry represents a single counted top-level monitored
|
||||
// system.
|
||||
type MonitoredSystemLedgerEntry struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"` // "online", "warning", "offline", "unknown"
|
||||
StatusExplanation MonitoredSystemLedgerStatusExplanation `json:"status_explanation"`
|
||||
LatestIncludedSignalAt string `json:"latest_included_signal_at"` // freshest included observation, RFC3339 or empty
|
||||
LastSeen string `json:"last_seen,omitempty"` // deprecated compatibility alias for latest_included_signal_at
|
||||
Source string `json:"source"`
|
||||
Explanation MonitoredSystemLedgerExplanation `json:"explanation"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"` // "online", "warning", "offline", "unknown"
|
||||
StatusExplanation MonitoredSystemLedgerStatusExplanation `json:"status_explanation"`
|
||||
LatestIncludedSignalAt string `json:"latest_included_signal_at"` // freshest included observation, RFC3339 or empty
|
||||
LatestIncludedSignalSource string `json:"latest_included_signal_source,omitempty"`
|
||||
LastSeen string `json:"last_seen,omitempty"` // deprecated compatibility alias for latest_included_signal_at
|
||||
Source string `json:"source"`
|
||||
Explanation MonitoredSystemLedgerExplanation `json:"explanation"`
|
||||
}
|
||||
|
||||
type MonitoredSystemLedgerStatusExplanation struct {
|
||||
|
|
@ -119,14 +120,15 @@ func (r *Router) handleMonitoredSystemLedger(w http.ResponseWriter, req *http.Re
|
|||
for _, system := range systems {
|
||||
status := normalizeStatus(string(system.Status))
|
||||
entries = append(entries, MonitoredSystemLedgerEntry{
|
||||
Name: system.Name,
|
||||
Type: system.Type,
|
||||
Status: status,
|
||||
StatusExplanation: monitoredSystemLedgerStatusExplanation(system.StatusExplanation, status),
|
||||
LatestIncludedSignalAt: formatLastSeen(system.LastSeen),
|
||||
LastSeen: formatLastSeen(system.LastSeen),
|
||||
Source: system.Source,
|
||||
Explanation: monitoredSystemLedgerExplanation(system.Explanation),
|
||||
Name: system.Name,
|
||||
Type: system.Type,
|
||||
Status: status,
|
||||
StatusExplanation: monitoredSystemLedgerStatusExplanation(system.StatusExplanation, status),
|
||||
LatestIncludedSignalAt: formatLastSeen(system.LastSeen),
|
||||
LatestIncludedSignalSource: normalizeMonitoredSystemLedgerSource(system.LatestIncludedSignalSource),
|
||||
LastSeen: formatLastSeen(system.LastSeen),
|
||||
Source: system.Source,
|
||||
Explanation: monitoredSystemLedgerExplanation(system.Explanation),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -204,6 +206,15 @@ func normalizeMonitoredSystemLedgerReasonStatus(status string) string {
|
|||
}
|
||||
}
|
||||
|
||||
func normalizeMonitoredSystemLedgerSource(source string) string {
|
||||
switch source {
|
||||
case "agent", "docker", "kubernetes", "pbs", "pmg", "proxmox", "truenas":
|
||||
return source
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func formatLastSeen(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return ""
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@ func TestMonitoredSystemLedgerEntryTypes(t *testing.T) {
|
|||
Summary: "All included top-level collection paths currently report online status.",
|
||||
Reasons: []MonitoredSystemLedgerStatusReason{},
|
||||
},
|
||||
LatestIncludedSignalAt: "2025-01-01T00:00:00Z",
|
||||
LastSeen: "2025-01-01T00:00:00Z",
|
||||
Source: "agent",
|
||||
LatestIncludedSignalAt: "2025-01-01T00:00:00Z",
|
||||
LatestIncludedSignalSource: "agent",
|
||||
LastSeen: "2025-01-01T00:00:00Z",
|
||||
Source: "agent",
|
||||
Explanation: MonitoredSystemLedgerExplanation{
|
||||
Summary: "Counts as one monitored system because Pulse sees one top-level host view from agent.",
|
||||
Reasons: []MonitoredSystemLedgerExplanationReason{
|
||||
|
|
@ -52,6 +53,9 @@ func TestMonitoredSystemLedgerEntryTypes(t *testing.T) {
|
|||
if decoded.LatestIncludedSignalAt != "2025-01-01T00:00:00Z" {
|
||||
t.Errorf("latest included signal mismatch: %+v", decoded)
|
||||
}
|
||||
if decoded.LatestIncludedSignalSource != "agent" {
|
||||
t.Errorf("latest included signal source mismatch: %+v", decoded)
|
||||
}
|
||||
if decoded.Source != "agent" {
|
||||
t.Errorf("source mismatch: got %q", decoded.Source)
|
||||
}
|
||||
|
|
@ -194,9 +198,10 @@ func TestHandleMonitoredSystemLedgerHTTP(t *testing.T) {
|
|||
Summary: "All included top-level collection paths currently report online status.",
|
||||
Reasons: []MonitoredSystemLedgerStatusReason{},
|
||||
},
|
||||
LatestIncludedSignalAt: "2025-01-01T00:00:00Z",
|
||||
LastSeen: "2025-01-01T00:00:00Z",
|
||||
Source: "agent",
|
||||
LatestIncludedSignalAt: "2025-01-01T00:00:00Z",
|
||||
LatestIncludedSignalSource: "agent",
|
||||
LastSeen: "2025-01-01T00:00:00Z",
|
||||
Source: "agent",
|
||||
Explanation: MonitoredSystemLedgerExplanation{
|
||||
Summary: "Counts as one monitored system because Pulse sees one top-level host view from agent.",
|
||||
Reasons: []MonitoredSystemLedgerExplanationReason{
|
||||
|
|
@ -240,6 +245,9 @@ func TestHandleMonitoredSystemLedgerHTTP(t *testing.T) {
|
|||
if decoded.Systems[0].LatestIncludedSignalAt != "2025-01-01T00:00:00Z" {
|
||||
t.Errorf("expected latest included signal timestamp, got %+v", decoded.Systems[0])
|
||||
}
|
||||
if decoded.Systems[0].LatestIncludedSignalSource != "agent" {
|
||||
t.Errorf("expected latest included signal source, got %+v", decoded.Systems[0])
|
||||
}
|
||||
if decoded.Systems[0].Explanation.Summary == "" {
|
||||
t.Errorf("expected explanation summary, got %+v", decoded.Systems[0].Explanation)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,13 +64,14 @@ type MonitoredSystemStatusReason struct {
|
|||
// MonitoredSystemRecord describes a counted top-level monitored system after
|
||||
// canonical cross-view deduplication.
|
||||
type MonitoredSystemRecord struct {
|
||||
Name string
|
||||
Type string
|
||||
Status ResourceStatus
|
||||
StatusExplanation MonitoredSystemStatusExplanation
|
||||
LastSeen time.Time
|
||||
Source string
|
||||
Explanation MonitoredSystemGroupingExplanation
|
||||
Name string
|
||||
Type string
|
||||
Status ResourceStatus
|
||||
StatusExplanation MonitoredSystemStatusExplanation
|
||||
LastSeen time.Time
|
||||
LatestIncludedSignalSource string
|
||||
Source string
|
||||
Explanation MonitoredSystemGroupingExplanation
|
||||
}
|
||||
|
||||
// MonitoredSystemCount returns the number of top-level monitored systems after
|
||||
|
|
@ -173,14 +174,16 @@ func resolveMonitoredSystemTopLevelSystems(rs ReadState) TopLevelSystemResolver
|
|||
func monitoredSystemRecord(group monitoredSystemGroup) MonitoredSystemRecord {
|
||||
resource := preferredMonitoredSystemResource(group.resources)
|
||||
status := monitoredSystemStatus(group.resources)
|
||||
latestSignal := monitoredSystemLatestObservation(group.resources)
|
||||
record := MonitoredSystemRecord{
|
||||
Name: monitoredSystemDisplayName(group.resources, resource),
|
||||
Type: monitoredSystemType(resource),
|
||||
Status: status,
|
||||
StatusExplanation: monitoredSystemStatusExplanation(group.resources, status),
|
||||
LastSeen: monitoredSystemLastSeen(group.resources),
|
||||
Source: monitoredSystemSource(group.resources),
|
||||
Explanation: normalizeMonitoredSystemGroupingExplanation(group.explanation),
|
||||
Name: monitoredSystemDisplayName(group.resources, resource),
|
||||
Type: monitoredSystemType(resource),
|
||||
Status: status,
|
||||
StatusExplanation: monitoredSystemStatusExplanation(group.resources, status),
|
||||
LastSeen: latestSignal.LastSeen,
|
||||
LatestIncludedSignalSource: latestSignal.Source,
|
||||
Source: monitoredSystemSource(group.resources),
|
||||
Explanation: normalizeMonitoredSystemGroupingExplanation(group.explanation),
|
||||
}
|
||||
if record.Name == "" {
|
||||
record.Name = "Unnamed system"
|
||||
|
|
@ -198,6 +201,9 @@ func monitoredSystemRecord(group monitoredSystemGroup) MonitoredSystemRecord {
|
|||
if record.Source == "" {
|
||||
record.Source = "unknown"
|
||||
}
|
||||
if record.LatestIncludedSignalSource == "" && record.Source != "multiple" {
|
||||
record.LatestIncludedSignalSource = record.Source
|
||||
}
|
||||
if record.Explanation.Summary == "" {
|
||||
record.Explanation = monitoredSystemStandaloneExplanation(group.resources)
|
||||
}
|
||||
|
|
@ -597,7 +603,7 @@ func monitoredSystemStatusSummary(
|
|||
}
|
||||
}
|
||||
|
||||
type monitoredSystemLatestObservation struct {
|
||||
type monitoredSystemObservation struct {
|
||||
Name string
|
||||
Source string
|
||||
LastSeen time.Time
|
||||
|
|
@ -638,8 +644,23 @@ func monitoredSystemHasReasonStatus(reasons []MonitoredSystemStatusReason, statu
|
|||
return false
|
||||
}
|
||||
|
||||
func monitoredSystemLatestOnlineObservation(resources []*Resource) monitoredSystemLatestObservation {
|
||||
var latest monitoredSystemLatestObservation
|
||||
func monitoredSystemLatestOnlineObservation(resources []*Resource) monitoredSystemObservation {
|
||||
return monitoredSystemLatestObservationMatching(resources, func(status string) bool {
|
||||
return status == "online"
|
||||
})
|
||||
}
|
||||
|
||||
func monitoredSystemLatestObservation(resources []*Resource) monitoredSystemObservation {
|
||||
return monitoredSystemLatestObservationMatching(resources, func(status string) bool {
|
||||
return status != ""
|
||||
})
|
||||
}
|
||||
|
||||
func monitoredSystemLatestObservationMatching(
|
||||
resources []*Resource,
|
||||
include func(status string) bool,
|
||||
) monitoredSystemObservation {
|
||||
var latest monitoredSystemObservation
|
||||
for _, resource := range resources {
|
||||
if resource == nil {
|
||||
continue
|
||||
|
|
@ -660,11 +681,12 @@ func monitoredSystemLatestOnlineObservation(resources []*Resource) monitoredSyst
|
|||
})
|
||||
for _, source := range sourceKeys {
|
||||
sourceStatus := resource.SourceStatus[source]
|
||||
if normalizeMonitoredSystemSourceStatus(sourceStatus.Status) != "online" {
|
||||
normalizedStatus := normalizeMonitoredSystemSourceStatus(sourceStatus.Status)
|
||||
if !include(normalizedStatus) {
|
||||
continue
|
||||
}
|
||||
if latest.LastSeen.IsZero() || sourceStatus.LastSeen.After(latest.LastSeen) {
|
||||
latest = monitoredSystemLatestObservation{
|
||||
if monitoredSystemObservationIsLater(latest.LastSeen, latest.Source, sourceStatus.LastSeen, string(source)) {
|
||||
latest = monitoredSystemObservation{
|
||||
Name: name,
|
||||
Source: string(source),
|
||||
LastSeen: sourceStatus.LastSeen,
|
||||
|
|
@ -673,11 +695,13 @@ func monitoredSystemLatestOnlineObservation(resources []*Resource) monitoredSyst
|
|||
}
|
||||
}
|
||||
|
||||
if normalizeMonitoredSystemSourceStatus(string(resource.Status)) == "online" &&
|
||||
(latest.LastSeen.IsZero() || resource.LastSeen.After(latest.LastSeen)) {
|
||||
latest = monitoredSystemLatestObservation{
|
||||
normalizedStatus := normalizeMonitoredSystemSourceStatus(string(resource.Status))
|
||||
primarySource := monitoredSystemPrimarySource(resource)
|
||||
if include(normalizedStatus) &&
|
||||
monitoredSystemObservationIsLater(latest.LastSeen, latest.Source, resource.LastSeen, primarySource) {
|
||||
latest = monitoredSystemObservation{
|
||||
Name: name,
|
||||
Source: monitoredSystemPrimarySource(resource),
|
||||
Source: primarySource,
|
||||
LastSeen: resource.LastSeen,
|
||||
}
|
||||
}
|
||||
|
|
@ -685,6 +709,27 @@ func monitoredSystemLatestOnlineObservation(resources []*Resource) monitoredSyst
|
|||
return latest
|
||||
}
|
||||
|
||||
func monitoredSystemObservationIsLater(
|
||||
currentAt time.Time,
|
||||
currentSource string,
|
||||
candidateAt time.Time,
|
||||
candidateSource string,
|
||||
) bool {
|
||||
if candidateAt.IsZero() {
|
||||
return false
|
||||
}
|
||||
if currentAt.IsZero() {
|
||||
return true
|
||||
}
|
||||
if candidateAt.After(currentAt) {
|
||||
return true
|
||||
}
|
||||
if candidateAt.Equal(currentAt) && strings.TrimSpace(candidateSource) < strings.TrimSpace(currentSource) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func monitoredSystemStatusReasonPriority(reason MonitoredSystemStatusReason) int {
|
||||
switch reason.Status {
|
||||
case "offline":
|
||||
|
|
@ -805,16 +850,7 @@ func monitoredSystemSurfaceStatusReasonSummary(
|
|||
}
|
||||
|
||||
func monitoredSystemLastSeen(resources []*Resource) time.Time {
|
||||
var lastSeen time.Time
|
||||
for _, resource := range resources {
|
||||
if resource == nil || resource.LastSeen.IsZero() {
|
||||
continue
|
||||
}
|
||||
if lastSeen.IsZero() || resource.LastSeen.After(lastSeen) {
|
||||
lastSeen = resource.LastSeen
|
||||
}
|
||||
}
|
||||
return lastSeen
|
||||
return monitoredSystemLatestObservation(resources).LastSeen
|
||||
}
|
||||
|
||||
func monitoredSystemSource(resources []*Resource) string {
|
||||
|
|
|
|||
|
|
@ -300,6 +300,9 @@ func TestMonitoredSystemsExplainsStaleGroupedSourceWhileLastSeenStaysFresh(t *te
|
|||
if !system.LastSeen.Equal(dockerResource.LastSeen) {
|
||||
t.Fatalf("expected grouped last_seen %s, got %s", dockerResource.LastSeen, system.LastSeen)
|
||||
}
|
||||
if system.LatestIncludedSignalSource != string(SourceDocker) {
|
||||
t.Fatalf("expected latest included signal source docker, got %+v", system)
|
||||
}
|
||||
if system.StatusExplanation.Summary == "" {
|
||||
t.Fatal("expected grouped monitored system status explanation summary")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue