diff --git a/pkg/services/ngalert/api/api_ruler_test.go b/pkg/services/ngalert/api/api_ruler_test.go index e1ae7815bb2..be5c677c0ff 100644 --- a/pkg/services/ngalert/api/api_ruler_test.go +++ b/pkg/services/ngalert/api/api_ruler_test.go @@ -653,7 +653,7 @@ func createService(store *fakes.RuleStore) *RulerSrv { authz: accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient())), amConfigStore: &fakeAMRefresher{}, amRefresher: &fakeAMRefresher{}, - featureManager: featuremgmt.WithFeatures(), + featureManager: featuremgmt.WithFeatures(featuremgmt.FlagGrafanaManagedRecordingRules), } } diff --git a/pkg/services/ngalert/api/compat.go b/pkg/services/ngalert/api/compat.go index 20a3fb3fcfe..0183ab65cf7 100644 --- a/pkg/services/ngalert/api/compat.go +++ b/pkg/services/ngalert/api/compat.go @@ -179,16 +179,32 @@ func AlertRuleExportFromAlertRule(rule models.AlertRule) (definitions.AlertRuleE data = append(data, query) } + cPtr := &rule.Condition + if rule.Condition == "" { + cPtr = nil + } + + noDataState := definitions.NoDataState(rule.NoDataState) + ndsPtr := &noDataState + if noDataState == "" { + ndsPtr = nil + } + execErrorState := definitions.ExecutionErrorState(rule.ExecErrState) + eesPtr := &execErrorState + if execErrorState == "" { + eesPtr = nil + } + result := definitions.AlertRuleExport{ UID: rule.UID, Title: rule.Title, For: model.Duration(rule.For), - Condition: rule.Condition, + Condition: cPtr, Data: data, DashboardUID: rule.DashboardUID, PanelID: rule.PanelID, - NoDataState: definitions.NoDataState(rule.NoDataState), - ExecErrState: definitions.ExecutionErrorState(rule.ExecErrState), + NoDataState: ndsPtr, + ExecErrState: eesPtr, IsPaused: rule.IsPaused, NotificationSettings: AlertRuleNotificationSettingsExportFromNotificationSettings(rule.NotificationSettings), Record: AlertRuleRecordExportFromRecord(rule.Record), diff --git a/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.hcl b/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.hcl index 025d519cd50..b50b0d3dde7 100644 --- a/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.hcl +++ b/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.hcl @@ -87,4 +87,48 @@ resource "grafana_rule_group" "rule_group_d3e8424bfbf66bc3" { mute_timings = ["test-mute"] } } + rule { + name = "recording rule" + + data { + ref_id = "query" + + relative_time_range { + from = 18000 + to = 10800 + } + + datasource_uid = "000000002" + model = "{\"expr\":\"http_request_duration_microseconds_count\",\"hide\":false,\"interval\":\"\",\"intervalMs\":1000,\"legendFormat\":\"\",\"maxDataPoints\":100,\"refId\":\"query\"}" + } + data { + ref_id = "reduced" + + relative_time_range { + from = 18000 + to = 10800 + } + + datasource_uid = "__expr__" + model = "{\"expression\":\"query\",\"hide\":false,\"intervalMs\":1000,\"maxDataPoints\":100,\"reducer\":\"mean\",\"refId\":\"reduced\",\"type\":\"reduce\"}" + } + data { + ref_id = "condition" + + relative_time_range { + from = 18000 + to = 10800 + } + + datasource_uid = "__expr__" + model = "{\"expression\":\"$reduced > 10\",\"hide\":false,\"intervalMs\":1000,\"maxDataPoints\":100,\"refId\":\"condition\",\"type\":\"math\"}" + } + + is_paused = false + + record { + metric = "test_metric" + from = "condition" + } + } } diff --git a/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.json b/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.json index 6af0194cb8a..9c814c758d7 100644 --- a/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.json +++ b/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.json @@ -108,7 +108,6 @@ ], "noDataState": "NoData", "execErrState": "Alerting", - "for": "0s", "isPaused": false, "notification_settings":{ "receiver":"Test-Receiver", @@ -118,6 +117,66 @@ "repeat_interval":"5m", "mute_time_intervals":["test-mute"] } + }, + { + "title": "recording rule", + "data": [ + { + "refId": "query", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "datasourceUid": "000000002", + "model": { + "expr": "http_request_duration_microseconds_count", + "hide": false, + "interval": "", + "intervalMs": 1000, + "legendFormat": "", + "maxDataPoints": 100, + "refId": "query" + } + }, + { + "refId": "reduced", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "datasourceUid": "__expr__", + "model": { + "expression": "query", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 100, + "reducer": "mean", + "refId": "reduced", + "type": "reduce" + } + }, + { + "refId": "condition", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "datasourceUid": "__expr__", + "model": { + "expression": "$reduced \u003e 10", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 100, + "refId": "condition", + "type": "math" + } + } + ], + "isPaused": false, + "record": { + "metric": "test_metric", + "from": "condition" + } } ] } diff --git a/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.yaml b/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.yaml index f91d13ec635..c4a1f9edf12 100644 --- a/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.yaml +++ b/pkg/services/ngalert/api/test-data/post-rulegroup-101-export.yaml @@ -81,7 +81,6 @@ groups: type: reduce noDataState: NoData execErrState: Alerting - for: 0s isPaused: false notification_settings: receiver: Test-Receiver @@ -94,3 +93,47 @@ groups: repeat_interval: 5m mute_time_intervals: - test-mute + - title: recording rule + data: + - refId: query + relativeTimeRange: + from: 18000 + to: 10800 + datasourceUid: "000000002" + model: + expr: http_request_duration_microseconds_count + hide: false + interval: "" + intervalMs: 1000 + legendFormat: "" + maxDataPoints: 100 + refId: query + - refId: reduced + relativeTimeRange: + from: 18000 + to: 10800 + datasourceUid: __expr__ + model: + expression: query + hide: false + intervalMs: 1000 + maxDataPoints: 100 + reducer: mean + refId: reduced + type: reduce + - refId: condition + relativeTimeRange: + from: 18000 + to: 10800 + datasourceUid: __expr__ + model: + expression: $reduced > 10 + hide: false + intervalMs: 1000 + maxDataPoints: 100 + refId: condition + type: math + isPaused: false + record: + metric: test_metric + from: condition diff --git a/pkg/services/ngalert/api/test-data/post-rulegroup-101.json b/pkg/services/ngalert/api/test-data/post-rulegroup-101.json index 2871b1f5787..a340dbd189c 100644 --- a/pkg/services/ngalert/api/test-data/post-rulegroup-101.json +++ b/pkg/services/ngalert/api/test-data/post-rulegroup-101.json @@ -119,6 +119,70 @@ "mute_time_intervals":["test-mute"] } } + }, + { + "grafana_alert": { + "title": "recording rule", + "data": [ + { + "refId": "query", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "datasourceUid": "000000002", + "model": { + "expr": "http_request_duration_microseconds_count", + "hide": false, + "interval": "", + "intervalMs": 1000, + "legendFormat": "", + "maxDataPoints": 100, + "refId": "query" + } + }, + { + "refId": "reduced", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "datasourceUid": "__expr__", + "model": { + "expression": "query", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 100, + "reducer": "mean", + "refId": "reduced", + "type": "reduce" + } + }, + { + "refId": "condition", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "datasourceUid": "__expr__", + "model": { + "expression": "$reduced > 10", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 100, + "refId": "condition", + "type": "math" + } + } + ], + "record": { + "metric": "test_metric", + "from": "condition" + } + } } ] } diff --git a/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go b/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go index 5681a376cef..ba47633dc97 100644 --- a/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go +++ b/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go @@ -258,15 +258,15 @@ type AlertRuleGroupExport struct { // AlertRuleExport is the provisioned file export of models.AlertRule. type AlertRuleExport struct { - UID string `json:"uid,omitempty" yaml:"uid,omitempty"` - Title string `json:"title" yaml:"title" hcl:"name"` - Condition string `json:"condition" yaml:"condition" hcl:"condition"` - Data []AlertQueryExport `json:"data" yaml:"data" hcl:"data,block"` - DashboardUID *string `json:"dashboardUid,omitempty" yaml:"dashboardUid,omitempty"` - PanelID *int64 `json:"panelId,omitempty" yaml:"panelId,omitempty"` - NoDataState NoDataState `json:"noDataState" yaml:"noDataState" hcl:"no_data_state"` - ExecErrState ExecutionErrorState `json:"execErrState" yaml:"execErrState" hcl:"exec_err_state"` - For model.Duration `json:"for" yaml:"for"` + UID string `json:"uid,omitempty" yaml:"uid,omitempty"` + Title string `json:"title" yaml:"title" hcl:"name"` + Condition *string `json:"condition,omitempty" yaml:"condition,omitempty" hcl:"condition"` + Data []AlertQueryExport `json:"data" yaml:"data" hcl:"data,block"` + DashboardUID *string `json:"dashboardUid,omitempty" yaml:"dashboardUid,omitempty"` + PanelID *int64 `json:"panelId,omitempty" yaml:"panelId,omitempty"` + NoDataState *NoDataState `json:"noDataState,omitempty" yaml:"noDataState,omitempty" hcl:"no_data_state"` + ExecErrState *ExecutionErrorState `json:"execErrState,omitempty" yaml:"execErrState,omitempty" hcl:"exec_err_state"` + For model.Duration `json:"for,omitempty" yaml:"for,omitempty"` // ForString is used to: // - Only export the for field for HCL if it is non-zero. // - Format the Prometheus model.Duration type properly for HCL. @@ -275,7 +275,7 @@ type AlertRuleExport struct { Labels *map[string]string `json:"labels,omitempty" yaml:"labels,omitempty" hcl:"labels"` IsPaused bool `json:"isPaused" yaml:"isPaused" hcl:"is_paused"` NotificationSettings *AlertRuleNotificationSettingsExport `json:"notification_settings,omitempty" yaml:"notification_settings,omitempty" hcl:"notification_settings,block"` - Record *AlertRuleRecordExport `json:"record,omitempty" yaml:"record,omitempty" hcl:"record"` + Record *AlertRuleRecordExport `json:"record,omitempty" yaml:"record,omitempty" hcl:"record,block"` } // AlertQueryExport is the provisioned export of models.AlertQuery.