diff --git a/public/app/features/alerting/unified/components/rule-editor/AlertRuleNameInput.tsx b/public/app/features/alerting/unified/components/rule-editor/AlertRuleNameInput.tsx index 5aaf8a6f09e..87b680f616b 100644 --- a/public/app/features/alerting/unified/components/rule-editor/AlertRuleNameInput.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/AlertRuleNameInput.tsx @@ -1,7 +1,11 @@ -import { useFormContext } from 'react-hook-form'; +import { Controller, useFormContext } from 'react-hook-form'; +import { DataSourceInstanceSettings } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; +import { config } from '@grafana/runtime'; import { Field, Input, Stack, Text } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; +import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; import { RuleFormType, RuleFormValues } from '../../types/rule-form'; import { isCloudRecordingRuleByType, isGrafanaRecordingRuleByType, isRecordingRuleByType } from '../../utils/rules'; @@ -21,9 +25,11 @@ const recordingRuleNameValidationPattern = (type: RuleFormType) => ({ */ export const AlertRuleNameAndMetric = () => { const { + control, register, watch, formState: { errors }, + setValue, } = useFormContext(); const ruleFormType = watch('type'); @@ -76,6 +82,39 @@ export const AlertRuleNameAndMetric = () => { /> )} + + {isGrafanaRecordingRule && config.featureToggles.grafanaManagedRecordingRulesDatasources && ( + + ( + ds.type === 'prometheus'} + onChange={(ds: DataSourceInstanceSettings) => { + setValue('targetDatasourceUid', ds.uid); + }} + /> + )} + name="targetDatasourceUid" + control={control} + rules={{ + required: { value: true, message: 'Please select a data source' }, + }} + /> + + )} ); diff --git a/public/app/features/alerting/unified/components/rule-viewer/tabs/Details.tsx b/public/app/features/alerting/unified/components/rule-viewer/tabs/Details.tsx index 8adb1023776..59d4d2d30d1 100644 --- a/public/app/features/alerting/unified/components/rule-viewer/tabs/Details.tsx +++ b/public/app/features/alerting/unified/components/rule-viewer/tabs/Details.tsx @@ -2,8 +2,10 @@ import { css } from '@emotion/css'; import { formatDistanceToNowStrict } from 'date-fns'; import { GrafanaTheme2, dateTimeFormat, dateTimeFormatTimeAgo } from '@grafana/data'; -import { Icon, Stack, Text, TextLink, useStyles2 } from '@grafana/ui'; +import { config } from '@grafana/runtime'; +import { Icon, Link, Stack, Text, TextLink, useStyles2 } from '@grafana/ui'; import { Trans, t } from 'app/core/internationalization'; +import { useDatasource } from 'app/features/datasources/hooks'; import { CombinedRule } from 'app/types/unified-alerting'; import { usePendingPeriod } from '../../../hooks/rules/usePendingPeriod'; @@ -53,6 +55,17 @@ export const Details = ({ rule }: DetailsProps) => { determinedRuleType = RuleType.CloudRecordingRule; } + const targetDatasourceUid = rulerRuleType.grafana.recordingRule(rule.rulerRule) + ? rule.rulerRule.grafana_alert.record?.target_datasource_uid + : null; + + const datasource = useDatasource(targetDatasourceUid); + + const showTargetDatasource = + config.featureToggles.grafanaManagedRecordingRulesDatasources && + targetDatasourceUid && + targetDatasourceUid !== 'grafana'; + const evaluationDuration = rule.promRule?.evaluationTime; const evaluationTimestamp = rule.promRule?.lastEvaluation; @@ -101,6 +114,20 @@ export const Details = ({ rule }: DetailsProps) => { )} )} + {showTargetDatasource && ( + + + datasource logo + {datasource?.name} + + + } + /> + )} diff --git a/public/app/features/alerting/unified/types/rule-form.ts b/public/app/features/alerting/unified/types/rule-form.ts index 07547e9c509..a4cc2dc2da7 100644 --- a/public/app/features/alerting/unified/types/rule-form.ts +++ b/public/app/features/alerting/unified/types/rule-form.ts @@ -54,7 +54,7 @@ export interface RuleFormValues { contactPoints?: AlertManagerManualRouting; editorSettings?: SimplifiedEditor; metric?: string; - + targetDatasourceUid?: string; // cortex / loki rules namespace: string; forTime: number; diff --git a/public/app/features/alerting/unified/utils/__snapshots__/rule-form.test.ts.snap b/public/app/features/alerting/unified/utils/__snapshots__/rule-form.test.ts.snap index 71e08950c88..d3d728fd59f 100644 --- a/public/app/features/alerting/unified/utils/__snapshots__/rule-form.test.ts.snap +++ b/public/app/features/alerting/unified/utils/__snapshots__/rule-form.test.ts.snap @@ -28,6 +28,7 @@ exports[`formValuesToRulerGrafanaRuleDTO should correctly convert rule form valu "record": { "from": "A", "metric": "", + "target_datasource_uid": undefined, }, "title": "", }, diff --git a/public/app/features/alerting/unified/utils/rule-form.ts b/public/app/features/alerting/unified/utils/rule-form.ts index d9e1130736e..7a95be75c1a 100644 --- a/public/app/features/alerting/unified/utils/rule-form.ts +++ b/public/app/features/alerting/unified/utils/rule-form.ts @@ -148,6 +148,7 @@ export function formValuesToRulerGrafanaRuleDTO(values: RuleFormValues): Postabl manualRouting, type, metric, + targetDatasourceUid, } = values; if (!condition) { throw new Error('You cannot create an alert rule without specifying the alert condition'); @@ -196,6 +197,7 @@ export function formValuesToRulerGrafanaRuleDTO(values: RuleFormValues): Postabl record: { metric: metric ?? name, from: condition, + target_datasource_uid: targetDatasourceUid, }, }, annotations, @@ -284,6 +286,7 @@ export function rulerRuleToFormValues(ruleWithLocation: RuleWithLocation): RuleF folder: { title: namespace, uid: ga.namespace_uid }, isPaused: ga.is_paused, metric: ga.record?.metric, + targetDatasourceUid: ga.record?.target_datasource_uid, }; } else if (rulerRuleType.grafana.rule(rule)) { // grafana alerting rule diff --git a/public/app/types/unified-alerting-dto.ts b/public/app/types/unified-alerting-dto.ts index bf112911756..6669d4fded9 100644 --- a/public/app/types/unified-alerting-dto.ts +++ b/public/app/types/unified-alerting-dto.ts @@ -262,6 +262,7 @@ export interface PostableGrafanaRuleDefinition { record?: { metric: string; from: string; + target_datasource_uid?: string; }; intervalSeconds?: number; } diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 43f96108203..a2dd8f62e0d 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -184,7 +184,8 @@ "rule-identifier": "Rule identifier", "rule-type": "Rule type", "state-error-timeout": "Alert state if execution error or timeout", - "state-no-data": "Alert state if no data or all values are null" + "state-no-data": "Alert state if no data or all values are null", + "target-datasource-uid": "Target data source" }, "alert-recording-rule-form": { "evaluation-behaviour": { @@ -504,6 +505,10 @@ "preview": "Preview", "previewCondition": "Preview alert rule condition" }, + "recording-rules": { + "description-target-data-source": "The Prometheus data source to store the recording rule in", + "label-target-data-source": "Target data source" + }, "rule-form": { "evaluation": { "evaluation-group-and-interval": "Evaluation group and interval",