Alerting: List V2 - Improve error handling (#106282)

* Add error badge to the DataSourceSection component

* Fix lint

* Update translations

* Fix error propagation from featureDiscoveryApi

* Pass errors to GMA loader
titolins/update-alerting-prom-am
Konrad Lalik 3 weeks ago committed by GitHub
parent 1837f32d76
commit 7d5a26a4e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 36
      public/app/features/alerting/unified/api/featureDiscoveryApi.ts
  2. 6
      public/app/features/alerting/unified/rule-list/GroupedView.tsx
  3. 4
      public/app/features/alerting/unified/rule-list/PaginatedDataSourceLoader.tsx
  4. 10
      public/app/features/alerting/unified/rule-list/PaginatedGrafanaLoader.tsx
  5. 19
      public/app/features/alerting/unified/rule-list/components/DataSourceSection.tsx
  6. 1
      public/app/features/alerting/unified/rule-list/hooks/useLazyLoadPrometheusGroups.tsx
  7. 4
      public/locales/en-US/grafana.json

@ -59,24 +59,28 @@ export const featureDiscoveryApi = alertingApi.injectEndpoints({
return { error: new Error(`Missing data source configuration for ${rulesSourceIdentifier}`) };
}
const features = await discoverFeaturesByUid(dataSourceSettings.uid);
try {
const features = await discoverFeaturesByUid(dataSourceSettings.uid);
const rulerConfig = features.features.rulerApiEnabled
? ({
dataSourceName: dataSourceSettings.name,
dataSourceUid: dataSourceSettings.uid,
apiVersion: features.application === PromApplication.Cortex ? 'legacy' : 'config',
} satisfies RulerDataSourceConfig)
: undefined;
const rulerConfig = features.features.rulerApiEnabled
? ({
dataSourceName: dataSourceSettings.name,
dataSourceUid: dataSourceSettings.uid,
apiVersion: features.application === PromApplication.Cortex ? 'legacy' : 'config',
} satisfies RulerDataSourceConfig)
: undefined;
return {
data: {
name: dataSourceSettings.name,
uid: dataSourceSettings.uid,
application: features.application,
rulerConfig,
} satisfies RulesSourceFeatures,
};
return {
data: {
name: dataSourceSettings.name,
uid: dataSourceSettings.uid,
application: features.application,
rulerConfig,
} satisfies RulesSourceFeatures,
};
} catch (error) {
return { error: error };
}
},
}),
}),

@ -38,7 +38,7 @@ export function GrafanaDataSourceLoader() {
}
function DataSourceLoader({ rulesSourceIdentifier }: DataSourceLoaderProps) {
const { data: dataSourceInfo, isLoading } = useDiscoverDsFeaturesQuery({ uid: rulesSourceIdentifier.uid });
const { data: dataSourceInfo, isLoading, error } = useDiscoverDsFeaturesQuery({ uid: rulesSourceIdentifier.uid });
const { uid, name } = rulesSourceIdentifier;
@ -46,6 +46,10 @@ function DataSourceLoader({ rulesSourceIdentifier }: DataSourceLoaderProps) {
return <DataSourceSection loader={<Skeleton width={250} height={16} />} uid={uid} name={name} />;
}
if (error) {
return <DataSourceSection error={error} uid={uid} name={name} />;
}
// 2. grab prometheus rule groups with max_groups if supported
if (dataSourceInfo) {
return (

@ -41,7 +41,7 @@ export function PaginatedDataSourceLoader({ rulesSourceIdentifier, application }
};
}, [groupsGenerator]);
const { isLoading, groups, hasMoreGroups, fetchMoreGroups } = useLazyLoadPrometheusGroups(
const { isLoading, groups, hasMoreGroups, fetchMoreGroups, error } = useLazyLoadPrometheusGroups(
groupsGenerator.current,
DATA_SOURCE_GROUP_PAGE_SIZE
);
@ -50,7 +50,7 @@ export function PaginatedDataSourceLoader({ rulesSourceIdentifier, application }
const groupsByNamespace = useMemo(() => groupBy(groups, 'file'), [groups]);
return (
<DataSourceSection name={name} application={application} uid={uid} isLoading={isLoading}>
<DataSourceSection name={name} application={application} uid={uid} isLoading={isLoading} error={error}>
<Stack direction="column" gap={0}>
{Object.entries(groupsByNamespace).map(([namespace, groups]) => (
<ListSection

@ -33,7 +33,7 @@ export function PaginatedGrafanaLoader() {
};
}, []);
const { isLoading, groups, hasMoreGroups, fetchMoreGroups } = useLazyLoadPrometheusGroups(
const { isLoading, groups, hasMoreGroups, fetchMoreGroups, error } = useLazyLoadPrometheusGroups(
groupsGenerator.current,
GRAFANA_GROUP_PAGE_SIZE
);
@ -42,7 +42,13 @@ export function PaginatedGrafanaLoader() {
const hasNoRules = isEmpty(groups) && !isLoading;
return (
<DataSourceSection name="Grafana" application="grafana" uid={GrafanaRulesSourceSymbol} isLoading={isLoading}>
<DataSourceSection
name="Grafana"
application="grafana"
uid={GrafanaRulesSourceSymbol}
isLoading={isLoading}
error={error}
>
<Stack direction="column" gap={0}>
{Object.entries(groupsByFolder).map(([folderUid, groups]) => {
// Groups are grouped by folder, so we can use the first group to get the folder name

@ -4,13 +4,13 @@ import { useToggle } from 'react-use';
import { GrafanaTheme2 } from '@grafana/data';
import { Trans, useTranslate } from '@grafana/i18n';
import { IconButton, LinkButton, Stack, Text, useStyles2 } from '@grafana/ui';
import { Button, IconButton, LinkButton, Stack, Text, Toggletip, useStyles2 } from '@grafana/ui';
import { GrafanaRulesSourceSymbol, RulesSourceIdentifier } from 'app/types/unified-alerting';
import { RulesSourceApplication } from 'app/types/unified-alerting-dto';
import { Spacer } from '../../components/Spacer';
import { WithReturnButton } from '../../components/WithReturnButton';
import { isAdmin } from '../../utils/misc';
import { isAdmin, stringifyErrorLike } from '../../utils/misc';
import { DataSourceIcon } from './Namespace';
import { LoadingIndicator } from './RuleGroup';
@ -22,6 +22,7 @@ export interface DataSourceSectionProps extends PropsWithChildren {
application?: RulesSourceApplication;
isLoading?: boolean;
description?: ReactNode;
error?: unknown;
}
export const DataSourceSection = ({
@ -30,6 +31,7 @@ export const DataSourceSection = ({
application,
children,
loader,
error,
isLoading = false,
description = null,
}: DataSourceSectionProps) => {
@ -60,6 +62,7 @@ export const DataSourceSection = ({
name={isCollapsed ? 'angle-right' : 'angle-down'}
onClick={toggleCollapsed}
aria-label={t('common.collapse', 'Collapse')}
disabled={Boolean(error)}
/>
{application && <DataSourceIcon application={application} />}
@ -71,6 +74,18 @@ export const DataSourceSection = ({
{'·'} {description}
</Text>
)}
{Boolean(error) && (
<Toggletip
title={t('alerting.rule-list.ds-error.title', 'Cannot load rules for this datasource')}
content={<div>{stringifyErrorLike(error)}</div>}
>
<Button variant="destructive" fill="outline" size="sm" icon="exclamation-circle">
<Trans i18nKey="alerting.rule-list.error-button">Error</Trans>
</Button>
</Toggletip>
)}
<Spacer />
{configureLink && (
<WithReturnButton

@ -51,6 +51,7 @@ export function useLazyLoadPrometheusGroups<TGroup extends PromRuleGroupDTO>(
return {
isLoading,
error: groupsRequestState.error,
groups,
hasMoreGroups: !isLoading && hasMoreGroups,
fetchMoreGroups,

@ -2364,11 +2364,15 @@
"cannot-load-rule-details-for": "Cannot load rule details for UID {{uid}}",
"configure-datasource": "Configure",
"draft-new-rule": "Draft a new rule",
"ds-error": {
"title": "Cannot load rules for this datasource"
},
"ds-error-boundary": {
"description": "Check the data source configuration. Does the data source support Prometheus API?",
"title": "Unable to load rules from this data source"
},
"empty-data-source": "No rules found",
"error-button": "Error",
"filter-view": {
"cancel-search": "Cancel search",
"no-more-results": "No more results – found {{numberOfRules}} rules",

Loading…
Cancel
Save