@ -17,6 +17,7 @@ import (
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/datasources"
dsfakes "github.com/grafana/grafana/pkg/services/datasources/fakes"
dsfakes "github.com/grafana/grafana/pkg/services/datasources/fakes"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
acfakes "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol/fakes"
acfakes "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol/fakes"
@ -48,6 +49,13 @@ func TestRouteConvertPrometheusPostRuleGroup(t *testing.T) {
"severity" : "critical" ,
"severity" : "critical" ,
} ,
} ,
} ,
} ,
{
Record : "recorded-metric" ,
Expr : "vector(1)" ,
Labels : map [ string ] string {
"severity" : "warning" ,
} ,
} ,
} ,
} ,
}
}
@ -104,21 +112,39 @@ func TestRouteConvertPrometheusPostRuleGroup(t *testing.T) {
response := srv . RouteConvertPrometheusPostRuleGroup ( rc , fldr . Title , simpleGroup )
response := srv . RouteConvertPrometheusPostRuleGroup ( rc , fldr . Title , simpleGroup )
require . Equal ( t , http . StatusAccepted , response . Status ( ) )
require . Equal ( t , http . StatusAccepted , response . Status ( ) )
// Get the updated rule
// Get the rules
remaining , err := ruleStore . ListAlertRules ( context . Background ( ) , & models . ListAlertRulesQuery {
remaining , err := ruleStore . ListAlertRules ( context . Background ( ) , & models . ListAlertRulesQuery {
OrgID : 1 ,
OrgID : 1 ,
} )
} )
require . NoError ( t , err )
require . NoError ( t , err )
require . Len ( t , remaining , 1 )
require . Len ( t , remaining , 2 )
require . Equal ( t , simpleGroup . Name , remaining [ 0 ] . RuleGroup )
// Create a map of rule titles to their expected definitions
require . Equal ( t , fmt . Sprintf ( "[%s] %s" , simpleGroup . Name , simpleGroup . Rules [ 0 ] . Alert ) , remaining [ 0 ] . Title )
expectedRules := make ( map [ string ] string )
promRuleYAML , err := yaml . Marshal ( simpleGroup . Rules [ 0 ] )
for _ , rule := range simpleGroup . Rules {
if rule . Alert != "" {
title := fmt . Sprintf ( "[%s] %s" , simpleGroup . Name , rule . Alert )
promRuleYAML , err := yaml . Marshal ( rule )
require . NoError ( t , err )
require . NoError ( t , err )
expectedRules [ title ] = string ( promRuleYAML )
} else if rule . Record != "" {
title := fmt . Sprintf ( "[%s] %s" , simpleGroup . Name , rule . Record )
promRuleYAML , err := yaml . Marshal ( rule )
require . NoError ( t , err )
expectedRules [ title ] = string ( promRuleYAML )
}
}
promDefinition , err := remaining [ 0 ] . PrometheusRuleDefinition ( )
// Verify each rule matches its expected definition
for _ , r := range remaining {
require . Equal ( t , simpleGroup . Name , r . RuleGroup )
expectedDef , exists := expectedRules [ r . Title ]
require . True ( t , exists , "unexpected rule title: %s" , r . Title )
promDefinition , err := r . PrometheusRuleDefinition ( )
require . NoError ( t , err )
require . NoError ( t , err )
require . Equal ( t , string ( promRuleYAML ) , promDefinition )
require . Equal ( t , expectedDef , promDefinition )
}
} )
} )
t . Run ( "should fail to replace a provisioned rule group" , func ( t * testing . T ) {
t . Run ( "should fail to replace a provisioned rule group" , func ( t * testing . T ) {
@ -263,6 +289,58 @@ func TestRouteConvertPrometheusPostRuleGroup(t *testing.T) {
response := srv . RouteConvertPrometheusPostRuleGroup ( rc , "test" , simpleGroup )
response := srv . RouteConvertPrometheusPostRuleGroup ( rc , "test" , simpleGroup )
require . Equal ( t , http . StatusAccepted , response . Status ( ) )
require . Equal ( t , http . StatusAccepted , response . Status ( ) )
} )
} )
t . Run ( "with disabled recording rules" , func ( t * testing . T ) {
testCases := [ ] struct {
name string
recordingRules bool
recordingRulesTargetDS bool
expectedStatus int
} {
{
name : "when recording rules are enabled" ,
recordingRules : true ,
recordingRulesTargetDS : true ,
expectedStatus : http . StatusAccepted ,
} ,
{
name : "when recording rules are disabled" ,
recordingRules : false ,
recordingRulesTargetDS : true ,
expectedStatus : http . StatusBadRequest ,
} ,
{
name : "when target datasources for recording rules are disabled" ,
recordingRules : true ,
recordingRulesTargetDS : false ,
expectedStatus : http . StatusBadRequest ,
} ,
{
name : "when both recording rules and target datasources are disabled" ,
recordingRules : false ,
recordingRulesTargetDS : false ,
expectedStatus : http . StatusBadRequest ,
} ,
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
var features featuremgmt . FeatureToggles
if tc . recordingRulesTargetDS {
features = featuremgmt . WithFeatures ( featuremgmt . FlagGrafanaManagedRecordingRulesDatasources )
} else {
features = featuremgmt . WithFeatures ( )
}
srv , _ , _ , _ := createConvertPrometheusSrv ( t , withFeatureToggles ( features ) )
srv . cfg . RecordingRules . Enabled = tc . recordingRules
rc := createRequestCtx ( )
response := srv . RouteConvertPrometheusPostRuleGroup ( rc , "test" , simpleGroup )
require . Equal ( t , tc . expectedStatus , response . Status ( ) )
} )
}
} )
}
}
func TestRouteConvertPrometheusGetRuleGroup ( t * testing . T ) {
func TestRouteConvertPrometheusGetRuleGroup ( t * testing . T ) {
@ -783,6 +861,7 @@ type convertPrometheusSrvOptions struct {
provenanceStore provisioning . ProvisioningStore
provenanceStore provisioning . ProvisioningStore
fakeAccessControlRuleService * acfakes . FakeRuleService
fakeAccessControlRuleService * acfakes . FakeRuleService
quotaChecker * provisioning . MockQuotaChecker
quotaChecker * provisioning . MockQuotaChecker
featureToggles featuremgmt . FeatureToggles
}
}
type convertPrometheusSrvOptionsFunc func ( * convertPrometheusSrvOptions )
type convertPrometheusSrvOptionsFunc func ( * convertPrometheusSrvOptions )
@ -805,6 +884,12 @@ func withQuotaChecker(checker *provisioning.MockQuotaChecker) convertPrometheusS
}
}
}
}
func withFeatureToggles ( toggles featuremgmt . FeatureToggles ) convertPrometheusSrvOptionsFunc {
return func ( opts * convertPrometheusSrvOptions ) {
opts . featureToggles = toggles
}
}
func createConvertPrometheusSrv ( t * testing . T , opts ... convertPrometheusSrvOptionsFunc ) ( * ConvertPrometheusSrv , datasources . CacheService , * fakes . RuleStore , * foldertest . FakeService ) {
func createConvertPrometheusSrv ( t * testing . T , opts ... convertPrometheusSrvOptionsFunc ) ( * ConvertPrometheusSrv , datasources . CacheService , * fakes . RuleStore , * foldertest . FakeService ) {
t . Helper ( )
t . Helper ( )
@ -816,6 +901,7 @@ func createConvertPrometheusSrv(t *testing.T, opts ...convertPrometheusSrvOption
provenanceStore : fakes . NewFakeProvisioningStore ( ) ,
provenanceStore : fakes . NewFakeProvisioningStore ( ) ,
fakeAccessControlRuleService : & acfakes . FakeRuleService { } ,
fakeAccessControlRuleService : & acfakes . FakeRuleService { } ,
quotaChecker : quotas ,
quotaChecker : quotas ,
featureToggles : featuremgmt . WithFeatures ( featuremgmt . FlagGrafanaManagedRecordingRulesDatasources ) ,
}
}
for _ , opt := range opts {
for _ , opt := range opts {
@ -851,9 +937,12 @@ func createConvertPrometheusSrv(t *testing.T, opts ...convertPrometheusSrvOption
cfg := & setting . UnifiedAlertingSettings {
cfg := & setting . UnifiedAlertingSettings {
DefaultRuleEvaluationInterval : 1 * time . Minute ,
DefaultRuleEvaluationInterval : 1 * time . Minute ,
RecordingRules : setting . RecordingRuleSettings {
Enabled : true ,
} ,
}
}
srv := NewConvertPrometheusSrv ( cfg , log . NewNopLogger ( ) , ruleStore , dsCache , alertRuleService )
srv := NewConvertPrometheusSrv ( cfg , log . NewNopLogger ( ) , ruleStore , dsCache , alertRuleService , options . featureToggles )
return srv , dsCache , ruleStore , folderService
return srv , dsCache , ruleStore , folderService
}
}