[v8.5.x] Alerting: Fix alert panel instance-based rules filtering (#52583) (#52653)

This commit is contained in:
Konrad Lalik
2022-07-26 10:02:18 +02:00
committed by GitHub
parent 393c63e479
commit 804a52b059
5 changed files with 45 additions and 9 deletions
@@ -1,5 +1,6 @@
import { isArray, reduce } from 'lodash';
import { IconName } from '@grafana/ui';
import { QueryPartDef, QueryPart } from 'app/features/alerting/state/query_part';
const alertQueryDef = new QueryPartDef({
@@ -81,7 +82,13 @@ function createReducerPart(model: any) {
return new QueryPart(model, def);
}
function getStateDisplayModel(state: string) {
interface AlertStateDisplayModel {
text: string;
iconClass: IconName;
stateClass: string;
}
function getStateDisplayModel(state: string): AlertStateDisplayModel {
const normalizedState = state.toLowerCase().replace(/_/g, '');
switch (normalizedState) {
@@ -27,6 +27,8 @@ export const AlertInstances: FC<Props> = ({ alerts, options }) => {
setDisplayInstances((display) => !display);
}, []);
// TODO Filtering instances here has some implications
// If a rule has 0 instances after filtering there is no way not to show that rule
const filteredAlerts = useMemo(
(): Alert[] => filterAlerts(options, sortAlerts(options.sortOrder, alerts)) ?? [],
[alerts, options]
@@ -25,6 +25,7 @@ import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
import { GroupMode, SortOrder, UnifiedAlertListOptions } from './types';
import GroupedModeView from './unified-alerting/GroupedView';
import UngroupedModeView from './unified-alerting/UngroupedView';
import { filterAlerts } from './util';
export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
const dispatch = useDispatch();
@@ -135,14 +136,15 @@ function filterRules(props: PanelProps<UnifiedAlertListOptions>, rules: PromRule
const replacedLabelFilter = replaceVariables(options.alertInstanceLabelFilter);
const matchers = parseMatchers(replacedLabelFilter);
// Reduce rules and instances to only those that match
filteredRules = filteredRules.reduce((rules, rule) => {
filteredRules = filteredRules.reduce<PromRuleWithLocation[]>((rules, rule) => {
const filteredAlerts = (rule.rule.alerts ?? []).filter(({ labels }) => labelsMatchMatchers(labels, matchers));
if (filteredAlerts.length) {
rules.push({ ...rule, rule: { ...rule.rule, alerts: filteredAlerts } });
}
return rules;
}, [] as PromRuleWithLocation[]);
}, []);
}
if (options.folder) {
filteredRules = filteredRules.filter((rule) => {
return rule.namespaceName === options.folder.title;
@@ -158,6 +160,19 @@ function filterRules(props: PanelProps<UnifiedAlertListOptions>, rules: PromRule
);
}
// Remove rules having 0 instances
// AlertInstances filters instances and we need to prevent situation
// when we display a rule with 0 instances
filteredRules = filteredRules.reduce<PromRuleWithLocation[]>((rules, rule) => {
const filteredAlerts = filterAlerts(options, rule.rule.alerts ?? []);
if (filteredAlerts.length) {
// We intentionally don't set alerts to filteredAlerts
// because later we couldn't display that some alerts are hidden (ref AlertInstances filtering)
rules.push(rule);
}
return rules;
}, []);
return filteredRules;
}
@@ -2,11 +2,12 @@ import React, { FC, useMemo } from 'react';
import { useStyles2 } from '@grafana/ui';
import { AlertLabel } from 'app/features/alerting/unified/components/AlertLabel';
import { PromRuleWithLocation } from 'app/types/unified-alerting';
import { Alert, PromRuleWithLocation } from 'app/types/unified-alerting';
import { AlertInstances } from '../AlertInstances';
import { getStyles } from '../UnifiedAlertList';
import { GroupedRules, UnifiedAlertListOptions } from '../types';
import { filterAlerts } from '../util';
type GroupedModeProps = {
rules: PromRuleWithLocation[];
@@ -19,7 +20,7 @@ const GroupedModeView: FC<GroupedModeProps> = ({ rules, options }) => {
const groupBy = options.groupBy;
const groupedRules = useMemo<GroupedRules>(() => {
const groupedRules = new Map();
const groupedRules = new Map<string, Alert[]>();
const hasInstancesWithMatchingLabels = (rule: PromRuleWithLocation) =>
groupBy ? alertHasEveryLabel(rule, groupBy) : true;
@@ -33,8 +34,19 @@ const GroupedModeView: FC<GroupedModeProps> = ({ rules, options }) => {
});
});
return groupedRules;
}, [groupBy, rules]);
// Remove groups having no instances
// This is different from filtering Rules without instances that we do in UnifiedAlertList
const filteredGroupedRules = Array.from(groupedRules.entries()).reduce((acc, [groupKey, groupAlerts]) => {
const filteredAlerts = filterAlerts(options, groupAlerts);
if (filteredAlerts.length > 0) {
acc.set(groupKey, filteredAlerts);
}
return acc;
}, new Map<string, Alert[]>());
return filteredGroupedRules;
}, [groupBy, rules, options]);
return (
<>
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
import React, { FC } from 'react';
import { GrafanaTheme2, intervalToAbbreviatedDurationString } from '@grafana/data';
import { Icon, IconName, useStyles2 } from '@grafana/ui';
import { Icon, useStyles2 } from '@grafana/ui';
import alertDef from 'app/features/alerting/state/alertDef';
import { alertStateToState, getFirstActiveAt } from 'app/features/alerting/unified/utils/rules';
import { PromRuleWithLocation } from 'app/types/unified-alerting';
@@ -33,7 +33,7 @@ const UngroupedModeView: FC<UngroupedModeProps> = ({ rules, options }) => {
<li className={styles.alertRuleItem} key={`alert-${namespaceName}-${groupName}-${rule.name}-${index}`}>
<div className={stateStyle.icon}>
<Icon
name={alertDef.getStateDisplayModel(rule.state).iconClass as IconName}
name={alertDef.getStateDisplayModel(rule.state).iconClass}
className={stateStyle[alertStateToState[rule.state]]}
size={'lg'}
/>