From 516afbea6768af89b8e2d4f32bc8d8690ad9602e Mon Sep 17 00:00:00 2001 From: Sahil Rasaikar Date: Sun, 17 Aug 2025 16:18:02 +0530 Subject: [PATCH] Init Commit: fix for issue #4510 Signed-off-by: Sahil Rasaikar --- rules/alerting.go | 11 ++++++++- .../mantine-ui/src/api/responseTypes/rules.ts | 2 +- web/ui/mantine-ui/src/pages/AlertsPage.tsx | 23 ++++++++++++++++--- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/rules/alerting.go b/rules/alerting.go index 9a6ff0a113..6af3a46171 100644 --- a/rules/alerting.go +++ b/rules/alerting.go @@ -58,6 +58,8 @@ const ( // StateFiring is the state of an alert that has been active for longer than // the configured threshold duration. StateFiring + // StateUnknown is the state of an alert that has not yet been evaluated. + StateUnknown ) func (s AlertState) String() string { @@ -68,6 +70,8 @@ func (s AlertState) String() string { return "pending" case StateFiring: return "firing" + case StateUnknown: + return "unknown" } panic(fmt.Errorf("unknown alert state: %d", s)) } @@ -530,8 +534,13 @@ func (r *AlertingRule) Eval(ctx context.Context, queryOffset time.Duration, ts t } // State returns the maximum state of alert instances for this rule. -// StateFiring > StatePending > StateInactive. +// StateFiring > StatePending > StateInactive > StateUnknown. func (r *AlertingRule) State() AlertState { + // If the rule has never been evaluated, return StateUnknown + if r.GetEvaluationTimestamp().IsZero() { + return StateUnknown + } + r.activeMtx.Lock() defer r.activeMtx.Unlock() diff --git a/web/ui/mantine-ui/src/api/responseTypes/rules.ts b/web/ui/mantine-ui/src/api/responseTypes/rules.ts index ff04ad2cc9..89ce0f4acc 100644 --- a/web/ui/mantine-ui/src/api/responseTypes/rules.ts +++ b/web/ui/mantine-ui/src/api/responseTypes/rules.ts @@ -1,4 +1,4 @@ -type RuleState = "pending" | "firing" | "inactive"; +type RuleState = "pending" | "firing" | "inactive" | "unknown"; export interface Alert { labels: Record; diff --git a/web/ui/mantine-ui/src/pages/AlertsPage.tsx b/web/ui/mantine-ui/src/pages/AlertsPage.tsx index 00e6595fd4..2608117c8f 100644 --- a/web/ui/mantine-ui/src/pages/AlertsPage.tsx +++ b/web/ui/mantine-ui/src/pages/AlertsPage.tsx @@ -45,6 +45,7 @@ type AlertsPageData = { inactive: number; pending: number; firing: number; + unknown: number; }; groups: { name: string; @@ -55,6 +56,7 @@ type AlertsPageData = { inactive: number; pending: number; firing: number; + unknown: number; }; rules: { rule: AlertingRule; @@ -82,6 +84,7 @@ const buildAlertsPageData = ( inactive: 0, pending: 0, firing: 0, + unknown: 0, }, groups: [], }; @@ -92,6 +95,7 @@ const buildAlertsPageData = ( inactive: 0, pending: 0, firing: 0, + unknown: 0, }; for (const r of group.rules) { @@ -109,6 +113,10 @@ const buildAlertsPageData = ( pageData.globalCounts.pending++; groupCounts.pending++; break; + case "unknown": + pageData.globalCounts.unknown++; + groupCounts.unknown++; + break; default: throw new Error(`Unknown rule state: ${r.state}`); } @@ -239,6 +247,11 @@ export default function AlertsPage() { pending ({g.counts.pending}) )} + {g.counts.unknown > 0 && ( + + unknown ({g.counts.unknown}) + + )} {g.counts.inactive > 0 && ( inactive ({g.counts.inactive}) @@ -285,7 +298,9 @@ export default function AlertsPage() { ? panelClasses.panelHealthErr : r.counts.pending > 0 ? panelClasses.panelHealthWarn - : panelClasses.panelHealthOk + : r.rule.state === "unknown" + ? panelClasses.panelHealthUnknown + : panelClasses.panelHealthOk } > o === "inactive" ? badgeClasses.healthOk : o === "pending" ? badgeClasses.healthWarn - : badgeClasses.healthErr + : o === "firing" + ? badgeClasses.healthErr + : badgeClasses.healthUnknown } optionCount={(o) => alertsPageData.globalCounts[