mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Fix alert panel instance-based rules filtering (#52583)
This commit is contained in:
parent
b4c6efa07b
commit
0507b3564f
@ -8645,12 +8645,6 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||||
],
|
],
|
||||||
"public/app/plugins/panel/alertlist/UnifiedAlertList.tsx:5381": [
|
|
||||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
|
||||||
],
|
|
||||||
"public/app/plugins/panel/alertlist/unified-alerting/UngroupedView.tsx:5381": [
|
|
||||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
|
||||||
],
|
|
||||||
"public/app/plugins/panel/annolist/AnnoListPanel.test.tsx:5381": [
|
"public/app/plugins/panel/annolist/AnnoListPanel.test.tsx:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { isArray, reduce } from 'lodash';
|
import { isArray, reduce } from 'lodash';
|
||||||
|
|
||||||
|
import { IconName } from '@grafana/ui';
|
||||||
import { QueryPartDef, QueryPart } from 'app/features/alerting/state/query_part';
|
import { QueryPartDef, QueryPart } from 'app/features/alerting/state/query_part';
|
||||||
|
|
||||||
const alertQueryDef = new QueryPartDef({
|
const alertQueryDef = new QueryPartDef({
|
||||||
@ -87,7 +88,13 @@ function normalizeAlertState(state: string) {
|
|||||||
return state.toLowerCase().replace(/_/g, '').split(' ')[0];
|
return state.toLowerCase().replace(/_/g, '').split(' ')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStateDisplayModel(state: string) {
|
interface AlertStateDisplayModel {
|
||||||
|
text: string;
|
||||||
|
iconClass: IconName;
|
||||||
|
stateClass: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStateDisplayModel(state: string): AlertStateDisplayModel {
|
||||||
const normalizedState = normalizeAlertState(state);
|
const normalizedState = normalizeAlertState(state);
|
||||||
|
|
||||||
switch (normalizedState) {
|
switch (normalizedState) {
|
||||||
|
@ -29,6 +29,8 @@ export const AlertInstances: FC<Props> = ({ alerts, options }) => {
|
|||||||
setDisplayInstances((display) => !display);
|
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(
|
const filteredAlerts = useMemo(
|
||||||
(): Alert[] => filterAlerts(options, sortAlerts(options.sortOrder, alerts)) ?? [],
|
(): Alert[] => filterAlerts(options, sortAlerts(options.sortOrder, alerts)) ?? [],
|
||||||
[alerts, options]
|
[alerts, options]
|
||||||
|
@ -25,6 +25,7 @@ import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
|
|||||||
import { GroupMode, SortOrder, UnifiedAlertListOptions } from './types';
|
import { GroupMode, SortOrder, UnifiedAlertListOptions } from './types';
|
||||||
import GroupedModeView from './unified-alerting/GroupedView';
|
import GroupedModeView from './unified-alerting/GroupedView';
|
||||||
import UngroupedModeView from './unified-alerting/UngroupedView';
|
import UngroupedModeView from './unified-alerting/UngroupedView';
|
||||||
|
import { filterAlerts } from './util';
|
||||||
|
|
||||||
export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -143,14 +144,15 @@ function filterRules(props: PanelProps<UnifiedAlertListOptions>, rules: PromRule
|
|||||||
const replacedLabelFilter = replaceVariables(options.alertInstanceLabelFilter);
|
const replacedLabelFilter = replaceVariables(options.alertInstanceLabelFilter);
|
||||||
const matchers = parseMatchers(replacedLabelFilter);
|
const matchers = parseMatchers(replacedLabelFilter);
|
||||||
// Reduce rules and instances to only those that match
|
// 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));
|
const filteredAlerts = (rule.rule.alerts ?? []).filter(({ labels }) => labelsMatchMatchers(labels, matchers));
|
||||||
if (filteredAlerts.length) {
|
if (filteredAlerts.length) {
|
||||||
rules.push({ ...rule, rule: { ...rule.rule, alerts: filteredAlerts } });
|
rules.push({ ...rule, rule: { ...rule.rule, alerts: filteredAlerts } });
|
||||||
}
|
}
|
||||||
return rules;
|
return rules;
|
||||||
}, [] as PromRuleWithLocation[]);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.folder) {
|
if (options.folder) {
|
||||||
filteredRules = filteredRules.filter((rule) => {
|
filteredRules = filteredRules.filter((rule) => {
|
||||||
return rule.namespaceName === options.folder.title;
|
return rule.namespaceName === options.folder.title;
|
||||||
@ -166,6 +168,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;
|
return filteredRules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,12 @@ import React, { FC, useMemo } from 'react';
|
|||||||
|
|
||||||
import { useStyles2 } from '@grafana/ui';
|
import { useStyles2 } from '@grafana/ui';
|
||||||
import { AlertLabel } from 'app/features/alerting/unified/components/AlertLabel';
|
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 { AlertInstances } from '../AlertInstances';
|
||||||
import { getStyles } from '../UnifiedAlertList';
|
import { getStyles } from '../UnifiedAlertList';
|
||||||
import { GroupedRules, UnifiedAlertListOptions } from '../types';
|
import { GroupedRules, UnifiedAlertListOptions } from '../types';
|
||||||
|
import { filterAlerts } from '../util';
|
||||||
|
|
||||||
type GroupedModeProps = {
|
type GroupedModeProps = {
|
||||||
rules: PromRuleWithLocation[];
|
rules: PromRuleWithLocation[];
|
||||||
@ -19,7 +20,7 @@ const GroupedModeView: FC<GroupedModeProps> = ({ rules, options }) => {
|
|||||||
const groupBy = options.groupBy;
|
const groupBy = options.groupBy;
|
||||||
|
|
||||||
const groupedRules = useMemo<GroupedRules>(() => {
|
const groupedRules = useMemo<GroupedRules>(() => {
|
||||||
const groupedRules = new Map();
|
const groupedRules = new Map<string, Alert[]>();
|
||||||
|
|
||||||
const hasInstancesWithMatchingLabels = (rule: PromRuleWithLocation) =>
|
const hasInstancesWithMatchingLabels = (rule: PromRuleWithLocation) =>
|
||||||
groupBy ? alertHasEveryLabel(rule, groupBy) : true;
|
groupBy ? alertHasEveryLabel(rule, groupBy) : true;
|
||||||
@ -33,8 +34,19 @@ const GroupedModeView: FC<GroupedModeProps> = ({ rules, options }) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return groupedRules;
|
// Remove groups having no instances
|
||||||
}, [groupBy, rules]);
|
// 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 (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -2,7 +2,7 @@ import { css } from '@emotion/css';
|
|||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2, intervalToAbbreviatedDurationString } from '@grafana/data';
|
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 alertDef from 'app/features/alerting/state/alertDef';
|
||||||
import { alertStateToReadable, alertStateToState, getFirstActiveAt } from 'app/features/alerting/unified/utils/rules';
|
import { alertStateToReadable, alertStateToState, getFirstActiveAt } from 'app/features/alerting/unified/utils/rules';
|
||||||
import { PromRuleWithLocation } from 'app/types/unified-alerting';
|
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}`}>
|
<li className={styles.alertRuleItem} key={`alert-${namespaceName}-${groupName}-${rule.name}-${index}`}>
|
||||||
<div className={stateStyle.icon}>
|
<div className={stateStyle.icon}>
|
||||||
<Icon
|
<Icon
|
||||||
name={alertDef.getStateDisplayModel(rule.state).iconClass as IconName}
|
name={alertDef.getStateDisplayModel(rule.state).iconClass}
|
||||||
className={stateStyle[alertStateToState(rule.state)]}
|
className={stateStyle[alertStateToState(rule.state)]}
|
||||||
size={'lg'}
|
size={'lg'}
|
||||||
/>
|
/>
|
||||||
|
Loading…
Reference in New Issue
Block a user