Alerting: Limit GMA alerts on the new list page (#105657)
This commit is contained in:
@@ -28,10 +28,11 @@ type PromRulesOptions = WithNotificationOptions<{
|
||||
groupNextToken?: string;
|
||||
}>;
|
||||
|
||||
type GrafanaPromRulesOptions = Omit<PromRulesOptions, 'ruleSource' | 'namespace'> & {
|
||||
type GrafanaPromRulesOptions = Omit<PromRulesOptions, 'ruleSource' | 'namespace' | 'excludeAlerts'> & {
|
||||
folderUid?: string;
|
||||
dashboardUid?: string;
|
||||
panelId?: number;
|
||||
limitAlerts?: number;
|
||||
};
|
||||
|
||||
export const prometheusApi = alertingApi.injectEndpoints({
|
||||
@@ -71,13 +72,13 @@ export const prometheusApi = alertingApi.injectEndpoints({
|
||||
},
|
||||
}),
|
||||
getGrafanaGroups: build.query<PromRulesResponse<GrafanaPromRuleGroupDTO>, GrafanaPromRulesOptions>({
|
||||
query: ({ folderUid, groupName, ruleName, groupLimit, excludeAlerts, groupNextToken }) => ({
|
||||
query: ({ folderUid, groupName, ruleName, groupLimit, limitAlerts, groupNextToken }) => ({
|
||||
url: `api/prometheus/grafana/api/v1/rules`,
|
||||
params: {
|
||||
folder_uid: folderUid,
|
||||
rule_group: groupName,
|
||||
rule_name: ruleName,
|
||||
exclude_alerts: excludeAlerts?.toString(),
|
||||
limit_alerts: limitAlerts,
|
||||
group_limit: groupLimit?.toFixed(0),
|
||||
group_next_token: groupNextToken,
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isUndefined, omitBy, pick, sum } from 'lodash';
|
||||
import { isUndefined, omitBy } from 'lodash';
|
||||
import pluralize from 'pluralize';
|
||||
import * as React from 'react';
|
||||
import { Fragment, useDeferredValue, useMemo } from 'react';
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
} from 'app/types/unified-alerting';
|
||||
import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { totalFromStats } from '../../utils/ruleStats';
|
||||
|
||||
interface Props {
|
||||
namespaces: CombinedRuleNamespace[];
|
||||
}
|
||||
@@ -80,15 +82,6 @@ function statsFromNamespaces(namespaces: CombinedRuleNamespace[]): AlertGroupTot
|
||||
return stats;
|
||||
}
|
||||
|
||||
export function totalFromStats(stats: AlertGroupTotals): number {
|
||||
// countable stats will pick only the states that indicate a single rule – health indicators like "error" and "nodata" should
|
||||
// not be counted because they are already counted by their state
|
||||
const countableStats = pick(stats, ['alerting', 'pending', 'inactive', 'recording', 'recovering']);
|
||||
const total = sum(Object.values(countableStats));
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
export const RuleGroupStats = ({ group }: RuleGroupStatsProps) => {
|
||||
const stats = group.totals;
|
||||
const evaluationInterval = group?.interval;
|
||||
|
||||
@@ -232,6 +232,8 @@ export const mockGrafanaPromAlertingRule = (
|
||||
uid: 'mock-rule-uid-123',
|
||||
folderUid: 'NAMESPACE_UID',
|
||||
isPaused: false,
|
||||
totals: { alerting: 1 },
|
||||
totalsFiltered: { alerting: 1 },
|
||||
...partial,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -168,6 +168,8 @@ function rulerRuleToPromRule(rule: RulerGrafanaRuleDTO): GrafanaPromRuleDTO {
|
||||
health: 'ok',
|
||||
state: PromAlertingRuleState.Inactive,
|
||||
type: rulerRuleType.grafana.alertingRule(rule) ? PromRuleType.Alerting : PromRuleType.Recording,
|
||||
totals: {},
|
||||
totalsFiltered: {},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ export function GrafanaGroupLoader({
|
||||
{
|
||||
folderUid: groupIdentifier.namespace.uid,
|
||||
groupName: groupIdentifier.groupName,
|
||||
limitAlerts: 0,
|
||||
},
|
||||
{ pollingInterval: RULE_LIST_POLL_INTERVAL_MS }
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { GrafanaPromRuleDTO, PromRuleType, RulerGrafanaRuleDTO } from 'app/types
|
||||
import { alertRuleApi } from '../api/alertRuleApi';
|
||||
import { prometheusApi } from '../api/prometheusApi';
|
||||
import { GrafanaRulesSource } from '../utils/datasource';
|
||||
import { totalFromStats } from '../utils/ruleStats';
|
||||
import { rulerRuleType } from '../utils/rules';
|
||||
import { createRelativeUrl } from '../utils/url';
|
||||
|
||||
@@ -123,13 +124,14 @@ export function GrafanaRuleListItem({
|
||||
|
||||
if (rulerRuleType.grafana.alertingRule(rulerRule)) {
|
||||
const promAlertingRule = rule && rule.type === PromRuleType.Alerting ? rule : undefined;
|
||||
const instancesCount = totalFromStats(promAlertingRule?.totals ?? {});
|
||||
|
||||
return (
|
||||
<AlertRuleListItem
|
||||
{...commonProps}
|
||||
summary={annotations.summary}
|
||||
state={promAlertingRule?.state}
|
||||
instancesCount={promAlertingRule?.alerts?.length}
|
||||
instancesCount={instancesCount}
|
||||
operation={operation}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ import { useLazyLoadPrometheusGroups } from './hooks/useLazyLoadPrometheusGroups
|
||||
export const GRAFANA_GROUP_PAGE_SIZE = 40;
|
||||
|
||||
export function PaginatedGrafanaLoader() {
|
||||
const grafanaGroupsGenerator = useGrafanaGroupsGenerator({ populateCache: true });
|
||||
const grafanaGroupsGenerator = useGrafanaGroupsGenerator({ populateCache: true, limitAlerts: 0 });
|
||||
|
||||
const groupsGenerator = useRef(toIndividualRuleGroups(grafanaGroupsGenerator(GRAFANA_GROUP_PAGE_SIZE)));
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const { useLazyGetGroupsQuery, useLazyGetGrafanaGroupsQuery } = prometheusApi;
|
||||
|
||||
interface UseGeneratorHookOptions {
|
||||
populateCache?: boolean;
|
||||
limitAlerts?: number;
|
||||
}
|
||||
|
||||
interface FetchGroupsOptions {
|
||||
@@ -58,7 +59,7 @@ export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions =
|
||||
|
||||
const getGroupsAndProvideCache = useCallback(
|
||||
async (fetchOptions: FetchGroupsOptions) => {
|
||||
const response = await getGrafanaGroups(fetchOptions).unwrap();
|
||||
const response = await getGrafanaGroups({ ...fetchOptions, limitAlerts: hookOptions.limitAlerts }).unwrap();
|
||||
|
||||
// This is not mandatory to preload ruler rules, but it improves the UX
|
||||
// Because the user waits a bit longer for the initial load but doesn't need to wait for each group to be loaded
|
||||
@@ -74,7 +75,7 @@ export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions =
|
||||
await dispatch(
|
||||
prometheusApi.util.upsertQueryData(
|
||||
'getGrafanaGroups',
|
||||
{ folderUid: group.folderUid, groupName: group.name },
|
||||
{ folderUid: group.folderUid, groupName: group.name, limitAlerts: hookOptions.limitAlerts },
|
||||
{ data: { groups: [group] }, status: 'success' }
|
||||
)
|
||||
);
|
||||
@@ -85,7 +86,7 @@ export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions =
|
||||
|
||||
return response;
|
||||
},
|
||||
[getGrafanaGroups, dispatch, hookOptions.populateCache]
|
||||
[getGrafanaGroups, dispatch, hookOptions.populateCache, hookOptions.limitAlerts]
|
||||
);
|
||||
|
||||
return useCallback(
|
||||
|
||||
@@ -56,7 +56,7 @@ export function useFilteredRulesIteratorProvider() {
|
||||
const allExternalRulesSources = getExternalRulesSources();
|
||||
|
||||
const prometheusGroupsGenerator = usePrometheusGroupsGenerator();
|
||||
const grafanaGroupsGenerator = useGrafanaGroupsGenerator();
|
||||
const grafanaGroupsGenerator = useGrafanaGroupsGenerator({ limitAlerts: 0 });
|
||||
|
||||
const getFilteredRulesIterable = (filterState: RulesFilter, groupLimit: number): GetIteratorResult => {
|
||||
/* this is the abort controller that allows us to stop an AsyncIterable */
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { totalFromStats } from './RuleStats';
|
||||
import { totalFromStats } from './ruleStats';
|
||||
|
||||
describe('RuleStats', () => {
|
||||
describe('totalFromStats', () => {
|
||||
it('should count 0', () => {
|
||||
expect(
|
||||
totalFromStats({
|
||||
@@ -0,0 +1,12 @@
|
||||
import { pick, sum } from 'lodash';
|
||||
|
||||
import { AlertGroupTotals } from 'app/types/unified-alerting';
|
||||
|
||||
export function totalFromStats(stats: AlertGroupTotals): number {
|
||||
// countable stats will pick only the states that indicate a single rule – health indicators like "error" and "nodata" should
|
||||
// not be counted because they are already counted by their state
|
||||
const countableStats = pick(stats, ['alerting', 'pending', 'inactive', 'recording', 'recovering']);
|
||||
const total = sum(Object.values(countableStats));
|
||||
|
||||
return total;
|
||||
}
|
||||
@@ -87,6 +87,8 @@ export function getPrometheusRulesResponse(
|
||||
evaluationTime: 0,
|
||||
uid: rule_uid,
|
||||
folderUid: folderUid,
|
||||
totals: {},
|
||||
totalsFiltered: {},
|
||||
},
|
||||
],
|
||||
interval: 60,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { DataQuery, RelativeTimeRange } from '@grafana/data';
|
||||
import { ExpressionQuery } from 'app/features/expressions/types';
|
||||
|
||||
import { AlertGroupTotals } from './unified-alerting';
|
||||
import { AlertGroupTotals, AlertInstanceTotals } from './unified-alerting';
|
||||
|
||||
export type Labels = Record<string, string>;
|
||||
export type Annotations = Record<string, string>;
|
||||
@@ -170,7 +170,10 @@ export interface PromRuleGroupDTO<TRule = PromRuleDTO> {
|
||||
lastEvaluation?: string;
|
||||
}
|
||||
|
||||
export interface GrafanaPromAlertingRuleDTO extends GrafanaPromRuleDTOBase, PromAlertingRuleDTO {}
|
||||
export interface GrafanaPromAlertingRuleDTO extends GrafanaPromRuleDTOBase, PromAlertingRuleDTO {
|
||||
totals: AlertInstanceTotals;
|
||||
totalsFiltered: AlertInstanceTotals;
|
||||
}
|
||||
|
||||
export interface GrafanaPromRecordingRuleDTO extends GrafanaPromRuleDTOBase, PromRecordingRuleDTO {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user