From f6e330b3d7ea8682af586605ebed28bb2723ecc1 Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Tue, 17 Jun 2025 22:55:45 +0200 Subject: [PATCH] Alerting: Allow disabling recording rules write for a data source in the UI (#106664) Adds a new "Allow as recording rules target" toggle to Prometheus datasource configuration that controls whether the datasource can be selected as a target for writing recording rules. --------- Co-authored-by: ismail simsek Co-authored-by: Konrad Lalik --- .betterer.results | 2 + conf/defaults.ini | 4 ++ conf/sample.ini | 4 ++ .../prometheus/configure/_index.md | 13 ++++ .../setup-grafana/configure-grafana/_index.md | 4 ++ packages/grafana-data/src/types/datasource.ts | 1 + .../src/selectors/components.ts | 3 + .../AlertingSettingsOverhaul.test.tsx | 59 ++++++++++++++++--- .../AlertingSettingsOverhaul.tsx | 36 +++++++++++ .../src/locales/en-US/grafana-prometheus.json | 4 +- packages/grafana-prometheus/src/types.ts | 1 + packages/grafana-runtime/src/config.ts | 1 + pkg/api/dtos/frontend_settings.go | 3 +- pkg/api/frontendsettings.go | 3 +- pkg/setting/setting.go | 4 ++ .../import-to-gma/ImportToGMARules.tsx | 14 +++-- .../rule-editor/AlertRuleNameInput.tsx | 6 +- .../alerting/unified/utils/datasource.test.ts | 51 +++++++++++++++- .../alerting/unified/utils/datasource.ts | 8 +++ 19 files changed, 201 insertions(+), 20 deletions(-) diff --git a/.betterer.results b/.betterer.results index c766d6f60e8..94ece6c91c3 100644 --- a/.betterer.results +++ b/.betterer.results @@ -4406,6 +4406,8 @@ exports[`no gf-form usage`] = { [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"] ], "packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.tsx:5381": [ + [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], + [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"] diff --git a/conf/defaults.ini b/conf/defaults.ini index 1a4e1e828b6..5bf75ead579 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -489,6 +489,10 @@ concurrent_query_count = 10 # It only works if the data source's `jsonData.manageAlerts` prop does not contain a previously configured value. default_manage_alerts_ui_toggle = true +# Default behavior for the "Allow as recording rules target" toggle when configuring a data source. +# It only works if the data source's `jsonData.allowAsRecordingRulesTarget` prop does not contain a previously configured value. +default_allow_recording_rules_target_alerts_ui_toggle = true + ################################### SQL Data Sources ##################### [sql_datasources] # Default maximum number of open connections maintained in the connection pool diff --git a/conf/sample.ini b/conf/sample.ini index 868bc750d73..14bd91da46b 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -487,6 +487,10 @@ # It only works if the data source's `jsonData.manageAlerts` prop does not contain a previously configured value. ;default_manage_alerts_ui_toggle = true +# Default behavior for the "Allow as recording rules target" toggle when configuring a data source. +# It only works if the data source's `jsonData.allowAsRecordingRulesTarget` prop does not contain a previously configured value. +;default_allow_recording_rules_target_alerts_ui_toggle = true + ################################### SQL Data Sources ##################### [sql_datasources] # Default maximum number of open connections maintained in the connection pool diff --git a/docs/sources/datasources/prometheus/configure/_index.md b/docs/sources/datasources/prometheus/configure/_index.md index a1b3b641061..7616e329bb6 100644 --- a/docs/sources/datasources/prometheus/configure/_index.md +++ b/docs/sources/datasources/prometheus/configure/_index.md @@ -61,6 +61,11 @@ refs: destination: /docs/grafana//setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle - pattern: /docs/grafana-cloud/ destination: /docs/grafana//setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle + manage-recording-rules-toggle: + - pattern: /docs/grafana/ + destination: /docs/grafana//setup-grafana/configure-grafana/#default_allow_recording_rules_target_alerts_ui_toggle + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//setup-grafana/configure-grafana/#default_allow_recording_rules_target_alerts_ui_toggle private-data-source-connect: - pattern: /docs/grafana/ destination: docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/ @@ -81,6 +86,11 @@ refs: destination: /docs/grafana//setup-grafana/configure-grafana/#configuration-file-location - pattern: /docs/grafana-cloud/ destination: /docs/grafana//setup-grafana/configure-grafana/#configuration-file-location + grafana-managed-recording-rules: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/ --- # Configure the Prometheus data source @@ -179,6 +189,8 @@ Following are optional configuration settings you can configure for more control - **Manage alerts via Alerting UI** -Toggled on by default. This enables [data source-managed rules in Grafana Alerting](ref:alerting-alert-rules) for this data source. For `Mimir`, it enables managing data source-managed rules and alerts. For `Prometheus`, it only supports viewing existing rules and alerts, which are displayed as data source-managed. Change this by setting the [`default_manage_alerts_ui_toggle`](ref:manage-alerts-toggle) option in the `grafana.ini` configuration file. +- **Allow as recording rules target** - Toggled on by default. This allows the data source to be selected as a target destination for writing [Grafana-managed recording rules](ref:grafana-managed-recording-rules). When enabled, this data source will appear in the target data source list when creating or importing recording rules. When disabled, the data source will be filtered out from recording rules target selection. Change this by setting the [`default_allow_recording_rules_target_alerts_ui_toggle`](ref:manage-recording-rules-toggle) option in the `grafana.ini` configuration file. + **Interval behavior:** - **Scrape interval** - Sets the standard scrape and evaluation interval in Prometheus. The default is `15s`. This interval determines how often Prometheus scrapes targets. Set it to match the typical scrape and evaluation interval in your Prometheus configuration file. If you set a higher value than your Prometheus configuration, Grafana will evaluate data at this interval, resulting in fewer data points. @@ -254,6 +266,7 @@ After you have provisioned a data source you cannot edit it. jsonData: httpMethod: POST manageAlerts: true + allowAsRecordingRulesTarget: true prometheusType: Prometheus prometheusVersion: 3.3.0 cacheLevel: 'High' diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index f5f109fc18c..14e4e77cfc2 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -888,6 +888,10 @@ Increasing this value allows processing more dashboards in each cleanup cycle bu Default behavior for the "Manage alerts via Alerting UI" toggle when configuring a data source. It only works if the data source's `jsonData.manageAlerts` prop does not contain a previously configured value. +#### `default_allow_recording_rules_target_alerts_ui_toggle` + +Default behavior for the "Allow as recording rules target" toggle when configuring a data source. It only works if the data source's `jsonData.allowAsRecordingRulesTarget` prop does not contain a previously configured value. + ### `[sql_datasources]` #### `max_open_conns_default` diff --git a/packages/grafana-data/src/types/datasource.ts b/packages/grafana-data/src/types/datasource.ts index d75d3f0bec4..03e086dffdb 100644 --- a/packages/grafana-data/src/types/datasource.ts +++ b/packages/grafana-data/src/types/datasource.ts @@ -642,6 +642,7 @@ export interface DataSourceJsonData { defaultRegion?: string; profile?: string; manageAlerts?: boolean; + allowAsRecordingRulesTarget?: boolean; alertmanagerUid?: string; disableGrafanaCache?: boolean; } diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index f0e51f64da6..952da3cb8e2 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -179,6 +179,9 @@ export const versionedComponents = { manageAlerts: { '10.4.0': 'prometheus-alerts-manager', // id for switch component }, + allowAsRecordingRulesTarget: { + '12.1.0': 'prometheus-recording-rules-target', + }, scrapeInterval: { '10.4.0': 'data-testid scrape interval', }, diff --git a/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.test.tsx b/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.test.tsx index 8c3d94d4c99..6cbf7715552 100644 --- a/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.test.tsx +++ b/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.test.tsx @@ -1,5 +1,6 @@ import { render } from '@testing-library/react'; +import { selectors } from '@grafana/e2e-selectors'; import { config } from '@grafana/runtime'; import { createDefaultConfigOptions } from '../test/__mocks__/datasource'; @@ -7,7 +8,7 @@ import { createDefaultConfigOptions } from '../test/__mocks__/datasource'; import { AlertingSettingsOverhaul } from './AlertingSettingsOverhaul'; describe(AlertingSettingsOverhaul.name, () => { - describe('Switch checked behavior', () => { + describe('Manage Alerts toggle', () => { describe('when options.jsonData.manageAlerts is unset', () => { it('uses the config default `true`', () => { const options = createDefaultConfigOptions(); @@ -15,9 +16,12 @@ describe(AlertingSettingsOverhaul.name, () => { config.defaultDatasourceManageAlertsUiToggle = true; - const { getByRole } = render( {}} options={options} />); + const { container } = render( {}} options={options} />); - expect(getByRole('switch')).toBeChecked(); + const manageAlertsToggle = container.querySelector( + `#${selectors.components.DataSource.Prometheus.configPage.manageAlerts}` + ); + expect(manageAlertsToggle).toBeChecked(); }); it('uses the config default `false`', () => { @@ -26,9 +30,12 @@ describe(AlertingSettingsOverhaul.name, () => { config.defaultDatasourceManageAlertsUiToggle = false; - const { getByRole } = render( {}} options={options} />); + const { container } = render( {}} options={options} />); - expect(getByRole('switch')).not.toBeChecked(); + const manageAlertsToggle = container.querySelector( + `#${selectors.components.DataSource.Prometheus.configPage.manageAlerts}` + ); + expect(manageAlertsToggle).not.toBeChecked(); }); }); @@ -39,9 +46,47 @@ describe(AlertingSettingsOverhaul.name, () => { config.defaultDatasourceManageAlertsUiToggle = configDefault; - const { getByRole } = render( {}} options={options} />); + const { container } = render( {}} options={options} />); - expect(getByRole('switch')).toBeChecked(); + const manageAlertsToggle = container.querySelector( + `#${selectors.components.DataSource.Prometheus.configPage.manageAlerts}` + ); + expect(manageAlertsToggle).toBeChecked(); + }); + }); + }); + + describe('Recording Rules Target toggle', () => { + describe('when options.jsonData.allowAsRecordingRulesTarget is unset', () => { + it('defaults to `true` (enabled)', () => { + const options = createDefaultConfigOptions(); + options.jsonData.allowAsRecordingRulesTarget = undefined; + + const { container } = render( {}} options={options} />); + + const recordingRulesTargetToggle = container.querySelector( + `#${selectors.components.DataSource.Prometheus.configPage.allowAsRecordingRulesTarget}` + ); + expect(recordingRulesTargetToggle).toBeChecked(); + }); + }); + + describe('when options.jsonData.allowAsRecordingRulesTarget is set', () => { + it.each([true, false])('uses the allowAsRecordingRulesTarget value %s', (value) => { + const options = createDefaultConfigOptions(); + options.jsonData.allowAsRecordingRulesTarget = value; + + const { container } = render( {}} options={options} />); + + const recordingRulesTargetToggle = container.querySelector( + `#${selectors.components.DataSource.Prometheus.configPage.allowAsRecordingRulesTarget}` + ); + + if (value) { + expect(recordingRulesTargetToggle).toBeChecked(); + } else { + expect(recordingRulesTargetToggle).not.toBeChecked(); + } }); }); }); diff --git a/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.tsx b/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.tsx index c1c6f6df635..13ee0ac5485 100644 --- a/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.tsx +++ b/packages/grafana-prometheus/src/configuration/AlertingSettingsOverhaul.tsx @@ -15,6 +15,7 @@ interface Props interface AlertingConfig extends DataSourceJsonData { manageAlerts?: boolean; + allowAsRecordingRulesTarget?: boolean; } export function AlertingSettingsOverhaul({ @@ -66,6 +67,41 @@ export function AlertingSettingsOverhaul({ +
+
+ + + Allow this data source to be selected as a target for writing recording rules. + {' '} + {docsTip()} + + } + interactive={true} + className={styles.switchField} + > + + onOptionsChange({ + ...options, + jsonData: { ...options.jsonData, allowAsRecordingRulesTarget: event!.currentTarget.checked }, + }) + } + id={selectors.components.DataSource.Prometheus.configPage.allowAsRecordingRulesTarget} + /> + +
+
); diff --git a/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json index 2e226d9114c..a7a4e3c6705 100644 --- a/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json @@ -111,8 +111,10 @@ "configuration": { "alerting-settings-overhaul": { "label-manage-alerts-via-alerting-ui": "Manage alerts via Alerting UI", + "label-allow-as-recording-rules-target": "Allow as recording rules target", "title-alerting": "Alerting", - "tooltip-manage-alerts-via-alerting-ui": "Manage alert rules for this data source. To manage other alerting resources, add an Alertmanager data source." + "tooltip-manage-alerts-via-alerting-ui": "Manage alert rules for this data source. To manage other alerting resources, add an Alertmanager data source.", + "tooltip-allow-as-recording-rules-target": "Allow this data source to be selected as a target for writing recording rules." }, "config-editor": { "browser-access-mode-error": "Browser access mode in the Prometheus data source is no longer available. Switch to server access mode.", diff --git a/packages/grafana-prometheus/src/types.ts b/packages/grafana-prometheus/src/types.ts index 25887e7d8eb..709d81af22a 100644 --- a/packages/grafana-prometheus/src/types.ts +++ b/packages/grafana-prometheus/src/types.ts @@ -51,6 +51,7 @@ export interface PromOptions extends DataSourceJsonData { incrementalQuerying?: boolean; incrementalQueryOverlapWindow?: string; disableRecordingRules?: boolean; + allowAsRecordingRulesTarget?: boolean; sigV4Auth?: boolean; oauthPassThru?: boolean; codeModeMetricNamesSuggestionLimit?: number; diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index ace5b34c6b4..d39d1526b5a 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -193,6 +193,7 @@ export class GrafanaBootConfig implements GrafanaConfig { connMaxLifetime: 14400, }; defaultDatasourceManageAlertsUiToggle = true; + defaultAllowRecordingRulesTargetAlertsUiToggle = true; tokenExpirationDayLimit: undefined; enableFrontendSandboxForPlugins: string[] = []; diff --git a/pkg/api/dtos/frontend_settings.go b/pkg/api/dtos/frontend_settings.go index 2c4406f41c9..2efeb995191 100644 --- a/pkg/api/dtos/frontend_settings.go +++ b/pkg/api/dtos/frontend_settings.go @@ -249,7 +249,8 @@ type FrontendSettingsDTO struct { Azure FrontendSettingsAzureDTO `json:"azure"` - DefaultDatasourceManageAlertsUIToggle bool `json:"defaultDatasourceManageAlertsUiToggle"` + DefaultDatasourceManageAlertsUIToggle bool `json:"defaultDatasourceManageAlertsUiToggle"` + DefaultAllowRecordingRulesTargetAlertsUIToggle bool `json:"defaultAllowRecordingRulesTargetAlertsUiToggle"` Caching FrontendSettingsCachingDTO `json:"caching"` RecordedQueries FrontendSettingsRecordedQueriesDTO `json:"recordedQueries"` diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 3a062fedad4..7e65ffb38ce 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -261,7 +261,8 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro ExploreDefaultTimeOffset: hs.Cfg.ExploreDefaultTimeOffset, ExploreHideLogsDownload: hs.Cfg.ExploreHideLogsDownload, - DefaultDatasourceManageAlertsUIToggle: hs.Cfg.DefaultDatasourceManageAlertsUIToggle, + DefaultDatasourceManageAlertsUIToggle: hs.Cfg.DefaultDatasourceManageAlertsUIToggle, + DefaultAllowRecordingRulesTargetAlertsUIToggle: hs.Cfg.DefaultAllowRecordingRulesTargetAlertsUIToggle, BuildInfo: dtos.FrontendSettingsBuildInfoDTO{ HideVersion: hideVersion, diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index a45780dfdab..3dc8efce449 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -341,6 +341,9 @@ type Cfg struct { // Default behavior for the "Manage alerts via Alerting UI" toggle when configuring a data source. // It only works if the data source's `jsonData.manageAlerts` prop does not contain a previously configured value. DefaultDatasourceManageAlertsUIToggle bool + // Default behavior for the "Allow as recording rules target" toggle when configuring a data source. + // It only works if the data source's `jsonData.allowAsRecordingRulesTarget` prop does not contain a previously configured value. + DefaultAllowRecordingRulesTargetAlertsUIToggle bool // IP range access control IPRangeACEnabled bool @@ -1969,6 +1972,7 @@ func (cfg *Cfg) readDataSourcesSettings() { cfg.DataSourceLimit = datasources.Key("datasource_limit").MustInt(5000) cfg.ConcurrentQueryCount = datasources.Key("concurrent_query_count").MustInt(10) cfg.DefaultDatasourceManageAlertsUIToggle = datasources.Key("default_manage_alerts_ui_toggle").MustBool(true) + cfg.DefaultAllowRecordingRulesTargetAlertsUIToggle = datasources.Key("default_allow_recording_rules_target_alerts_ui_toggle").MustBool(true) } func (cfg *Cfg) readDataSourceSecuritySettings() { diff --git a/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.tsx b/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.tsx index 29480b7f1cb..bfdb5b3303c 100644 --- a/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.tsx +++ b/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.tsx @@ -25,7 +25,11 @@ import { NestedFolderPicker } from 'app/core/components/NestedFolderPicker/Neste import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; import { Folder } from '../../types/rule-form'; -import { DataSourceType } from '../../utils/datasource'; +import { + DataSourceType, + isSupportedExternalPrometheusFlavoredRulesSourceType, + isValidRecordingRulesTarget, +} from '../../utils/datasource'; import { stringifyErrorLike } from '../../utils/misc'; import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertingPageWrapper } from '../AlertingPageWrapper'; @@ -319,11 +323,11 @@ function YamlTargetDataSourceField() { noDefault inputId="yaml-target-data-source" alerting - filter={(ds: DataSourceInstanceSettings) => ds.type === 'prometheus'} + filter={(ds: DataSourceInstanceSettings) => isSupportedExternalPrometheusFlavoredRulesSourceType(ds.type)} onChange={(ds: DataSourceInstanceSettings) => { setValue('yamlImportTargetDatasourceUID', ds.uid); const recordingRulesTargetDs = getValues('targetDatasourceUID'); - if (!recordingRulesTargetDs) { + if (!recordingRulesTargetDs && isValidRecordingRulesTarget(ds)) { setValue('targetDatasourceUID', ds.uid); } }} @@ -367,7 +371,7 @@ function TargetDataSourceForRecordingRulesField() { current={field.value} inputId="recording-rules-target-data-source" noDefault - filter={(ds: DataSourceInstanceSettings) => ds.type === 'prometheus'} + filter={isValidRecordingRulesTarget} onChange={(ds: DataSourceInstanceSettings) => { setValue('targetDatasourceUID', ds.uid); }} @@ -479,7 +483,7 @@ function DataSourceField() { // If we've chosen a Prometheus data source, we can set the recording rules target data source to the same as the source const recordingRulesTargetDs = getValues('targetDatasourceUID'); if (!recordingRulesTargetDs) { - const targetDataSourceUID = ds.type === DataSourceType.Prometheus ? ds.uid : undefined; + const targetDataSourceUID = isValidRecordingRulesTarget(ds) ? ds.uid : undefined; setValue('targetDatasourceUID', targetDataSourceUID); } }} 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 1664c24c03c..a3e5497e689 100644 --- a/public/app/features/alerting/unified/components/rule-editor/AlertRuleNameInput.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/AlertRuleNameInput.tsx @@ -7,7 +7,7 @@ import { Field, Input, Stack, Text } from '@grafana/ui'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; import { RuleFormType, RuleFormValues } from '../../types/rule-form'; -import { isSupportedExternalPrometheusFlavoredRulesSourceType } from '../../utils/datasource'; +import { isValidRecordingRulesTarget } from '../../utils/datasource'; import { isCloudRecordingRuleByType, isGrafanaRecordingRuleByType, isRecordingRuleByType } from '../../utils/rules'; import { RuleEditorSection } from './RuleEditorSection'; @@ -128,9 +128,7 @@ export const AlertRuleNameAndMetric = () => { current={field.value} noDefault // Filter with `filter` prop instead of `type` prop to avoid showing the `-- Grafana --` data source - filter={(ds: DataSourceInstanceSettings) => - isSupportedExternalPrometheusFlavoredRulesSourceType(ds.type) - } + filter={isValidRecordingRulesTarget} onChange={(ds: DataSourceInstanceSettings) => { setValue('targetDatasourceUid', ds.uid); }} diff --git a/public/app/features/alerting/unified/utils/datasource.test.ts b/public/app/features/alerting/unified/utils/datasource.test.ts index 3c10151be9f..3ce38a54521 100644 --- a/public/app/features/alerting/unified/utils/datasource.test.ts +++ b/public/app/features/alerting/unified/utils/datasource.test.ts @@ -1,6 +1,10 @@ import { mockDataSource } from '../mocks'; -import { isDataSourceManagingAlerts } from './datasource'; +import { + SUPPORTED_EXTERNAL_PROMETHEUS_FLAVORED_RULE_SOURCE_TYPES, + isDataSourceManagingAlerts, + isValidRecordingRulesTarget, +} from './datasource'; describe('isDataSourceManagingAlerts', () => { it('should return true when the prop is set as true', () => { @@ -37,3 +41,48 @@ it('should return false when the prop is set as false', () => { ) ).toBe(false); }); + +describe('isValidRecordingRulesTarget', () => { + it.each(SUPPORTED_EXTERNAL_PROMETHEUS_FLAVORED_RULE_SOURCE_TYPES)( + 'should return true for %s datasource with manageRecordingRulesTarget enabled', + (type) => { + expect( + isValidRecordingRulesTarget( + mockDataSource({ + type, + jsonData: { + allowAsRecordingRulesTarget: true, + }, + }) + ) + ).toBe(true); + } + ); + + it.each(SUPPORTED_EXTERNAL_PROMETHEUS_FLAVORED_RULE_SOURCE_TYPES)( + 'should return true for %s datasource when manageRecordingRulesTarget is undefined (defaults to true)', + (type) => { + expect( + isValidRecordingRulesTarget( + mockDataSource({ + type, + jsonData: {}, + }) + ) + ).toBe(true); + } + ); + + it('should return false for loki datasource (unsupported type)', () => { + expect( + isValidRecordingRulesTarget( + mockDataSource({ + type: 'loki', + jsonData: { + allowAsRecordingRulesTarget: true, + }, + }) + ) + ).toBe(false); + }); +}); diff --git a/public/app/features/alerting/unified/utils/datasource.ts b/public/app/features/alerting/unified/utils/datasource.ts index d54efeadcb7..a50eddcfa50 100644 --- a/public/app/features/alerting/unified/utils/datasource.ts +++ b/public/app/features/alerting/unified/utils/datasource.ts @@ -338,6 +338,10 @@ export function isDataSourceManagingAlerts(ds: DataSourceInstanceSettings) { + return ds.jsonData.allowAsRecordingRulesTarget !== false; // if this prop is undefined it defaults to true +} + export function ruleIdentifierToRuleSourceIdentifier(ruleIdentifier: RuleIdentifier): RulesSourceIdentifier { if (isGrafanaRuleIdentifier(ruleIdentifier)) { return { uid: GrafanaRulesSourceSymbol, name: GRAFANA_RULES_SOURCE_NAME, ruleSourceType: 'grafana' }; @@ -389,3 +393,7 @@ export const SUPPORTED_RULE_SOURCE_TYPES = [ GRAFANA_RULES_SOURCE_NAME, ...SUPPORTED_EXTERNAL_RULE_SOURCE_TYPES, ] as const satisfies string[]; + +export function isValidRecordingRulesTarget(ds: DataSourceInstanceSettings): boolean { + return isSupportedExternalPrometheusFlavoredRulesSourceType(ds.type) && isDataSourceAllowedAsRecordingRulesTarget(ds); +}