diff --git a/public/app/features/alerting/unified/api/featureDiscoveryApi.ts b/public/app/features/alerting/unified/api/featureDiscoveryApi.ts
index e5f90c2baf6..b60290ad481 100644
--- a/public/app/features/alerting/unified/api/featureDiscoveryApi.ts
+++ b/public/app/features/alerting/unified/api/featureDiscoveryApi.ts
@@ -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 };
+ }
},
}),
}),
diff --git a/public/app/features/alerting/unified/rule-list/GroupedView.tsx b/public/app/features/alerting/unified/rule-list/GroupedView.tsx
index da79a9e6d5a..3458ad6822c 100644
--- a/public/app/features/alerting/unified/rule-list/GroupedView.tsx
+++ b/public/app/features/alerting/unified/rule-list/GroupedView.tsx
@@ -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 } uid={uid} name={name} />;
}
+ if (error) {
+ return ;
+ }
+
// 2. grab prometheus rule groups with max_groups if supported
if (dataSourceInfo) {
return (
diff --git a/public/app/features/alerting/unified/rule-list/PaginatedDataSourceLoader.tsx b/public/app/features/alerting/unified/rule-list/PaginatedDataSourceLoader.tsx
index 950c460d1e2..161437c7528 100644
--- a/public/app/features/alerting/unified/rule-list/PaginatedDataSourceLoader.tsx
+++ b/public/app/features/alerting/unified/rule-list/PaginatedDataSourceLoader.tsx
@@ -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 (
-
+
{Object.entries(groupsByNamespace).map(([namespace, groups]) => (
+
{Object.entries(groupsByFolder).map(([folderUid, groups]) => {
// Groups are grouped by folder, so we can use the first group to get the folder name
diff --git a/public/app/features/alerting/unified/rule-list/components/DataSourceSection.tsx b/public/app/features/alerting/unified/rule-list/components/DataSourceSection.tsx
index c75a9ff9efb..424dfcc3c3f 100644
--- a/public/app/features/alerting/unified/rule-list/components/DataSourceSection.tsx
+++ b/public/app/features/alerting/unified/rule-list/components/DataSourceSection.tsx
@@ -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 && }
@@ -71,6 +74,18 @@ export const DataSourceSection = ({
{'·'} {description}
)}
+
+ {Boolean(error) && (
+ {stringifyErrorLike(error)}}
+ >
+
+
+ )}
+
{configureLink && (
(
return {
isLoading,
+ error: groupsRequestState.error,
groups,
hasMoreGroups: !isLoading && hasMoreGroups,
fetchMoreGroups,
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index 38eff47641d..cddd86ac0d9 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -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",