Alerting: Use default_datasource_uid as the default target for recording rules in UI (#106415)
* Alerting: Use default_datasource_uid as the default target for recording rules * Add tests --------- Co-authored-by: Konrad Lalik <konradlalik@gmail.com>
This commit is contained in:
co-authored by
Konrad Lalik
parent
a29e24c5b4
commit
7c3f7b9e8b
@@ -73,6 +73,8 @@ export interface UnifiedAlertingConfig {
|
||||
// will be undefined if implementation is not "multiple"
|
||||
alertStateHistoryPrimary?: string;
|
||||
recordingRulesEnabled?: boolean;
|
||||
// will be undefined if no default datasource is configured
|
||||
defaultRecordingRulesTargetDatasourceUID?: string;
|
||||
}
|
||||
|
||||
/** Supported OAuth services
|
||||
|
||||
@@ -159,6 +159,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
|
||||
alertStateHistoryBackend: undefined,
|
||||
alertStateHistoryPrimary: undefined,
|
||||
recordingRulesEnabled: false,
|
||||
defaultRecordingRulesTargetDatasourceUID: undefined,
|
||||
};
|
||||
applicationInsightsConnectionString?: string;
|
||||
applicationInsightsEndpointUrl?: string;
|
||||
|
||||
@@ -94,10 +94,11 @@ type FrontendSettingsAnalyticsDTO struct {
|
||||
}
|
||||
|
||||
type FrontendSettingsUnifiedAlertingDTO struct {
|
||||
MinInterval string `json:"minInterval"`
|
||||
AlertStateHistoryBackend string `json:"alertStateHistoryBackend,omitempty"`
|
||||
AlertStateHistoryPrimary string `json:"alertStateHistoryPrimary,omitempty"`
|
||||
RecordingRulesEnabled bool `json:"recordingRulesEnabled"`
|
||||
MinInterval string `json:"minInterval"`
|
||||
AlertStateHistoryBackend string `json:"alertStateHistoryBackend,omitempty"`
|
||||
AlertStateHistoryPrimary string `json:"alertStateHistoryPrimary,omitempty"`
|
||||
RecordingRulesEnabled bool `json:"recordingRulesEnabled"`
|
||||
DefaultRecordingRulesTargetDatasourceUID string `json:"defaultRecordingRulesTargetDatasourceUID,omitempty"`
|
||||
}
|
||||
|
||||
// Enterprise-only
|
||||
|
||||
@@ -351,6 +351,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
|
||||
}
|
||||
|
||||
frontendSettings.UnifiedAlerting.RecordingRulesEnabled = hs.Cfg.UnifiedAlerting.RecordingRules.Enabled
|
||||
frontendSettings.UnifiedAlerting.DefaultRecordingRulesTargetDatasourceUID = hs.Cfg.UnifiedAlerting.RecordingRules.DefaultDatasourceUID
|
||||
|
||||
if hs.Cfg.UnifiedAlerting.Enabled != nil {
|
||||
frontendSettings.UnifiedAlertingEnabled = *hs.Cfg.UnifiedAlerting.Enabled
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { GrafanaConfig } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { mockAlertQuery, mockDataSource, mockReduceExpression, mockThresholdExpression } from '../mocks';
|
||||
import { testWithFeatureToggles } from '../test/test-utils';
|
||||
import { RuleFormType } from '../types/rule-form';
|
||||
@@ -191,3 +194,27 @@ describe('getDefaultManualRouting', () => {
|
||||
expect(getDefautManualRouting()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDefaultFormValues', () => {
|
||||
// This is for Typescript. GrafanaBootConfig returns narrower types than GrafanaConfig
|
||||
const grafanaConfig: GrafanaConfig = config;
|
||||
const uaConfig = grafanaConfig.unifiedAlerting;
|
||||
|
||||
afterEach(() => {
|
||||
uaConfig.defaultRecordingRulesTargetDatasourceUID = undefined;
|
||||
});
|
||||
|
||||
it('should set targetDatasourceUid from config when defaultRecordingRulesTargetDatasourceUID is provided', () => {
|
||||
const expectedDatasourceUid = 'test-datasource-uid';
|
||||
uaConfig.defaultRecordingRulesTargetDatasourceUID = expectedDatasourceUid;
|
||||
|
||||
const result = getDefaultFormValues();
|
||||
|
||||
expect(result.targetDatasourceUid).toBe(expectedDatasourceUid);
|
||||
});
|
||||
|
||||
it('should set targetDatasourceUid to undefined when defaultRecordingRulesTargetDatasourceUID is not provided', () => {
|
||||
const result = getDefaultFormValues();
|
||||
expect(result.targetDatasourceUid).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -71,6 +71,7 @@ export const getDefaultFormValues = (): RuleFormValues => {
|
||||
overrideTimings: false,
|
||||
muteTimeIntervals: [],
|
||||
editorSettings: getDefaultEditorSettings(),
|
||||
targetDatasourceUid: config.unifiedAlerting?.defaultRecordingRulesTargetDatasourceUID,
|
||||
|
||||
// cortex / loki
|
||||
namespace: '',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { PromQuery } from '@grafana/prometheus';
|
||||
import { RuleWithLocation } from 'app/types/unified-alerting';
|
||||
import {
|
||||
AlertDataQuery,
|
||||
AlertQuery,
|
||||
@@ -7,7 +8,7 @@ import {
|
||||
RulerAlertingRuleDTO,
|
||||
} from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { mockDataSource } from '../mocks';
|
||||
import { mockDataSource, mockRuleWithLocation, mockRulerGrafanaRecordingRule } from '../mocks';
|
||||
import { getDefaultFormValues } from '../rule-editor/formDefaults';
|
||||
import { setupDataSources } from '../testSetup/datasources';
|
||||
import { AlertManagerManualRouting, RuleFormType, RuleFormValues } from '../types/rule-form';
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
getContactPointsFromDTO,
|
||||
getInstantFromDataQuery,
|
||||
getNotificationSettingsForDTO,
|
||||
rulerRuleToFormValues,
|
||||
} from './rule-form';
|
||||
|
||||
describe('formValuesToRulerGrafanaRuleDTO', () => {
|
||||
@@ -111,6 +113,85 @@ describe('formValuesToRulerGrafanaRuleDTO', () => {
|
||||
expect(alertingRulerRuleToRuleForm(rule)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('rulerRuleToFormValues', () => {
|
||||
it('should convert grafana recording rule to form values', () => {
|
||||
const mockRecordingRule = mockRulerGrafanaRecordingRule({
|
||||
grafana_alert: {
|
||||
uid: 'recording-rule-uid',
|
||||
title: 'My Recording Rule',
|
||||
namespace_uid: 'folder-uid',
|
||||
rule_group: 'recording-group',
|
||||
condition: 'A',
|
||||
record: {
|
||||
metric: 'my_metric',
|
||||
from: 'A',
|
||||
target_datasource_uid: 'target-ds-uid',
|
||||
},
|
||||
data: [
|
||||
{
|
||||
datasourceUid: 'prom-uid',
|
||||
refId: 'A',
|
||||
queryType: '',
|
||||
model: { refId: 'A' },
|
||||
},
|
||||
],
|
||||
is_paused: false,
|
||||
},
|
||||
annotations: {
|
||||
description: 'This is a recording rule',
|
||||
summary: 'Recording rule summary',
|
||||
},
|
||||
labels: {
|
||||
team: 'platform',
|
||||
env: 'production',
|
||||
},
|
||||
});
|
||||
|
||||
const ruleWithLocation: RuleWithLocation = mockRuleWithLocation(mockRecordingRule, {
|
||||
ruleSourceName: GRAFANA_RULES_SOURCE_NAME,
|
||||
namespace: 'Test Folder',
|
||||
group: {
|
||||
name: 'recording-group',
|
||||
interval: '1m',
|
||||
rules: [mockRecordingRule],
|
||||
},
|
||||
});
|
||||
|
||||
const result = rulerRuleToFormValues(ruleWithLocation);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
name: 'My Recording Rule',
|
||||
type: RuleFormType.grafanaRecording,
|
||||
group: 'recording-group',
|
||||
evaluateEvery: '1m',
|
||||
queries: [
|
||||
{
|
||||
datasourceUid: 'prom-uid',
|
||||
refId: 'A',
|
||||
queryType: '',
|
||||
model: { refId: 'A' },
|
||||
},
|
||||
],
|
||||
condition: 'A',
|
||||
annotations: [
|
||||
{ key: 'summary', value: 'Recording rule summary' },
|
||||
{ key: 'description', value: 'This is a recording rule' },
|
||||
{ key: 'runbook_url', value: '' },
|
||||
],
|
||||
labels: [
|
||||
{ key: 'team', value: 'platform' },
|
||||
{ key: 'env', value: 'production' },
|
||||
{ key: '', value: '' }, // empty row added for form editing
|
||||
],
|
||||
folder: { title: 'Test Folder', uid: 'folder-uid' },
|
||||
isPaused: false,
|
||||
metric: 'my_metric',
|
||||
targetDatasourceUid: 'target-ds-uid',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getContactPointsFromDTO', () => {
|
||||
it('should return undefined if notification_settings is not defined', () => {
|
||||
const ga: GrafanaRuleDefinition = {
|
||||
|
||||
@@ -297,7 +297,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,
|
||||
targetDatasourceUid: ga.record?.target_datasource_uid || defaultFormValues.targetDatasourceUid,
|
||||
};
|
||||
} else if (rulerRuleType.grafana.rule(rule)) {
|
||||
// grafana alerting rule
|
||||
|
||||
Reference in New Issue
Block a user