mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Fix filtering when panel variables are in use (#66977)
Fix alert instances filtering when panel variables are in use
This commit is contained in:
parent
f3dbb7b34a
commit
9f1fe51edc
@ -106,6 +106,13 @@ export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
||||
);
|
||||
}
|
||||
|
||||
const { options, replaceVariables } = props;
|
||||
const parsedOptions: UnifiedAlertListOptions = {
|
||||
...props.options,
|
||||
alertName: replaceVariables(options.alertName),
|
||||
alertInstanceLabelFilter: replaceVariables(options.alertInstanceLabelFilter),
|
||||
};
|
||||
|
||||
return (
|
||||
<CustomScrollbar autoHeightMin="100%" autoHeightMax="100%">
|
||||
<div className={styles.container}>
|
||||
@ -124,10 +131,10 @@ export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
||||
/>
|
||||
)}
|
||||
{props.options.viewMode === ViewMode.List && props.options.groupMode === GroupMode.Custom && haveResults && (
|
||||
<GroupedModeView rules={rules} options={props.options} />
|
||||
<GroupedModeView rules={rules} options={parsedOptions} />
|
||||
)}
|
||||
{props.options.viewMode === ViewMode.List && props.options.groupMode === GroupMode.Default && haveResults && (
|
||||
<UngroupedModeView rules={rules} options={props.options} />
|
||||
<UngroupedModeView rules={rules} options={parsedOptions} />
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
@ -209,7 +216,15 @@ function filterRules(props: PanelProps<UnifiedAlertListOptions>, rules: Combined
|
||||
// when we display a rule with 0 instances
|
||||
filteredRules = filteredRules.reduce<CombinedRuleWithLocation[]>((rules, rule) => {
|
||||
const alertingRule = getAlertingRule(rule);
|
||||
const filteredAlerts = alertingRule ? filterAlerts(options, alertingRule.alerts ?? []) : [];
|
||||
const filteredAlerts = alertingRule
|
||||
? filterAlerts(
|
||||
{
|
||||
stateFilter: options.stateFilter,
|
||||
alertInstanceLabelFilter: replaceVariables(options.alertInstanceLabelFilter),
|
||||
},
|
||||
alertingRule.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)
|
||||
|
@ -1,14 +1,26 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { byRole, byText } from 'testing-library-selector';
|
||||
|
||||
import { getDefaultTimeRange, LoadingState, PanelProps, FieldConfigSource } from '@grafana/data';
|
||||
import { TimeRangeUpdatedEvent } from '@grafana/runtime';
|
||||
import { DashboardSrv, setDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
|
||||
import { contextSrv } from '../../../core/services/context_srv';
|
||||
import {
|
||||
mockPromAlert,
|
||||
mockPromAlertingRule,
|
||||
mockPromRuleGroup,
|
||||
mockPromRuleNamespace,
|
||||
mockUnifiedAlertingStore,
|
||||
} from '../../../features/alerting/unified/mocks';
|
||||
import { GRAFANA_RULES_SOURCE_NAME } from '../../../features/alerting/unified/utils/datasource';
|
||||
|
||||
import { UnifiedAlertList } from './UnifiedAlertList';
|
||||
import { UnifiedAlertListOptions, SortOrder, GroupMode, ViewMode } from './types';
|
||||
import * as utils from './util';
|
||||
|
||||
jest.mock('app/features/alerting/unified/api/alertmanager');
|
||||
|
||||
@ -60,14 +72,36 @@ const dashboard = {
|
||||
},
|
||||
};
|
||||
|
||||
const renderPanel = (options: UnifiedAlertListOptions = defaultOptions) => {
|
||||
const store = configureStore();
|
||||
const renderPanel = (options: Partial<UnifiedAlertListOptions> = defaultOptions) => {
|
||||
const store = mockUnifiedAlertingStore({
|
||||
promRules: {
|
||||
grafana: {
|
||||
loading: false,
|
||||
dispatched: true,
|
||||
result: [
|
||||
mockPromRuleNamespace({
|
||||
name: 'ns1',
|
||||
groups: [
|
||||
mockPromRuleGroup({
|
||||
name: 'group1',
|
||||
rules: [
|
||||
mockPromAlertingRule({
|
||||
name: 'rule1',
|
||||
alerts: [mockPromAlert({ labels: { severity: 'critical' } })],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const dashSrv: unknown = { getCurrent: () => dashboard };
|
||||
setDashboardSrv(dashSrv as DashboardSrv);
|
||||
|
||||
defaultProps.options = options;
|
||||
const props = { ...defaultProps };
|
||||
const props = { ...defaultProps, options: { ...defaultOptions, ...options } };
|
||||
|
||||
return render(
|
||||
<Provider store={store}>
|
||||
@ -82,4 +116,38 @@ describe('UnifiedAlertList', () => {
|
||||
expect(dashboard.events.subscribe).toHaveBeenCalledTimes(1);
|
||||
expect(dashboard.events.subscribe.mock.calls[0][0]).toEqual(TimeRangeUpdatedEvent);
|
||||
});
|
||||
|
||||
it('should replace option variables before filtering', async () => {
|
||||
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
|
||||
const filterAlertsSpy = jest.spyOn(utils, 'filterAlerts');
|
||||
|
||||
const replaceVarsSpy = jest.spyOn(defaultProps, 'replaceVariables').mockReturnValue('severity=critical');
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderPanel({
|
||||
alertInstanceLabelFilter: '$label',
|
||||
dashboardAlerts: false,
|
||||
alertName: '',
|
||||
datasource: GRAFANA_RULES_SOURCE_NAME,
|
||||
folder: undefined,
|
||||
});
|
||||
|
||||
expect(byText('rule1').get()).toBeInTheDocument();
|
||||
|
||||
const expandElement = byText('1 instance').get();
|
||||
|
||||
await user.click(expandElement);
|
||||
|
||||
const tagsElement = await byRole('list', { name: 'Tags' }).find();
|
||||
expect(await byRole('listitem').find(tagsElement)).toHaveTextContent('severity=critical');
|
||||
|
||||
expect(replaceVarsSpy).toHaveBeenLastCalledWith('$label');
|
||||
expect(filterAlertsSpy).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
alertInstanceLabelFilter: 'severity=critical',
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -1,20 +1,21 @@
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import { Labels, PanelProps } from '@grafana/data';
|
||||
import { Labels } from '@grafana/data';
|
||||
import { labelsMatchMatchers, parseMatchers } from 'app/features/alerting/unified/utils/alertmanager';
|
||||
import { replaceVariables } from 'app/plugins/datasource/prometheus/querybuilder/shared/parsingUtils';
|
||||
import { Alert, hasAlertState } from 'app/types/unified-alerting';
|
||||
import { GrafanaAlertState, PromAlertingRuleState } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { UnifiedAlertListOptions } from './types';
|
||||
|
||||
function hasLabelFilter(alertInstanceLabelFilter: string, labels: Labels) {
|
||||
const replacedLabelFilter = replaceVariables(alertInstanceLabelFilter);
|
||||
const matchers = parseMatchers(replacedLabelFilter);
|
||||
const matchers = parseMatchers(alertInstanceLabelFilter);
|
||||
return labelsMatchMatchers(labels, matchers);
|
||||
}
|
||||
|
||||
export function filterAlerts(options: PanelProps<UnifiedAlertListOptions>['options'], alerts: Alert[]): Alert[] {
|
||||
export function filterAlerts(
|
||||
options: Pick<UnifiedAlertListOptions, 'stateFilter' | 'alertInstanceLabelFilter'>,
|
||||
alerts: Alert[]
|
||||
): Alert[] {
|
||||
const { stateFilter, alertInstanceLabelFilter } = options;
|
||||
|
||||
if (isEmpty(stateFilter)) {
|
||||
@ -31,8 +32,7 @@ export function filterAlerts(options: PanelProps<UnifiedAlertListOptions>['optio
|
||||
(stateFilter.normal && hasAlertState(alert, GrafanaAlertState.Normal)) ||
|
||||
(stateFilter.error && hasAlertState(alert, GrafanaAlertState.Error)) ||
|
||||
(stateFilter.inactive && hasAlertState(alert, PromAlertingRuleState.Inactive))) &&
|
||||
((alertInstanceLabelFilter && hasLabelFilter(options.alertInstanceLabelFilter, alert.labels)) ||
|
||||
!alertInstanceLabelFilter)
|
||||
(alertInstanceLabelFilter ? hasLabelFilter(options.alertInstanceLabelFilter, alert.labels) : true)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user