Alerting: Improve rule validation to check if rule uses backend datasources (#58986)
* validate if rule uses backend datasources * add backend datasource to test * fix tests * another forgotten import * remove unused var
This commit is contained in:
@@ -12,11 +12,13 @@ import (
|
||||
ptr "github.com/xorcare/pointer"
|
||||
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
fakes "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func TestEvaluateExecutionResult(t *testing.T) {
|
||||
@@ -351,15 +353,20 @@ func TestEvaluateExecutionResultsNoData(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
type services struct {
|
||||
cache *fakes.FakeCacheService
|
||||
pluginsStore *plugins.FakePluginStore
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
condition func(service *fakes.FakeCacheService) models.Condition
|
||||
condition func(services services) models.Condition
|
||||
error bool
|
||||
}{
|
||||
{
|
||||
name: "fail if no expressions",
|
||||
error: true,
|
||||
condition: func(service *fakes.FakeCacheService) models.Condition {
|
||||
condition: func(_ services) models.Condition {
|
||||
return models.Condition{
|
||||
Condition: "A",
|
||||
Data: []models.AlertQuery{},
|
||||
@@ -369,17 +376,25 @@ func TestValidate(t *testing.T) {
|
||||
{
|
||||
name: "fail if condition RefID does not exist",
|
||||
error: true,
|
||||
condition: func(service *fakes.FakeCacheService) models.Condition {
|
||||
ds := models.GenerateAlertQuery()
|
||||
service.DataSources = append(service.DataSources, &datasources.DataSource{
|
||||
Uid: ds.DatasourceUID,
|
||||
condition: func(services services) models.Condition {
|
||||
dsQuery := models.GenerateAlertQuery()
|
||||
ds := &datasources.DataSource{
|
||||
Uid: dsQuery.DatasourceUID,
|
||||
Type: util.GenerateShortUID(),
|
||||
}
|
||||
services.cache.DataSources = append(services.cache.DataSources, ds)
|
||||
services.pluginsStore.PluginList = append(services.pluginsStore.PluginList, plugins.PluginDTO{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: ds.Type,
|
||||
Backend: true,
|
||||
},
|
||||
})
|
||||
|
||||
return models.Condition{
|
||||
Condition: "C",
|
||||
Data: []models.AlertQuery{
|
||||
ds,
|
||||
models.CreateClassicConditionExpression("B", ds.RefID, "last", "gt", rand.Int()),
|
||||
dsQuery,
|
||||
models.CreateClassicConditionExpression("B", dsQuery.RefID, "last", "gt", rand.Int()),
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -387,17 +402,24 @@ func TestValidate(t *testing.T) {
|
||||
{
|
||||
name: "fail if condition RefID is empty",
|
||||
error: true,
|
||||
condition: func(service *fakes.FakeCacheService) models.Condition {
|
||||
ds := models.GenerateAlertQuery()
|
||||
service.DataSources = append(service.DataSources, &datasources.DataSource{
|
||||
Uid: ds.DatasourceUID,
|
||||
condition: func(services services) models.Condition {
|
||||
dsQuery := models.GenerateAlertQuery()
|
||||
ds := &datasources.DataSource{
|
||||
Uid: dsQuery.DatasourceUID,
|
||||
Type: util.GenerateShortUID(),
|
||||
}
|
||||
services.cache.DataSources = append(services.cache.DataSources, ds)
|
||||
services.pluginsStore.PluginList = append(services.pluginsStore.PluginList, plugins.PluginDTO{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: ds.Type,
|
||||
Backend: true,
|
||||
},
|
||||
})
|
||||
|
||||
return models.Condition{
|
||||
Condition: "",
|
||||
Data: []models.AlertQuery{
|
||||
ds,
|
||||
models.CreateClassicConditionExpression("B", ds.RefID, "last", "gt", rand.Int()),
|
||||
dsQuery,
|
||||
models.CreateClassicConditionExpression("B", dsQuery.RefID, "last", "gt", rand.Int()),
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -405,13 +427,68 @@ func TestValidate(t *testing.T) {
|
||||
{
|
||||
name: "fail if datasource with UID does not exists",
|
||||
error: true,
|
||||
condition: func(service *fakes.FakeCacheService) models.Condition {
|
||||
ds := models.GenerateAlertQuery()
|
||||
condition: func(services services) models.Condition {
|
||||
dsQuery := models.GenerateAlertQuery()
|
||||
// do not update the cache service
|
||||
return models.Condition{
|
||||
Condition: ds.RefID,
|
||||
Condition: dsQuery.RefID,
|
||||
Data: []models.AlertQuery{
|
||||
ds,
|
||||
dsQuery,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail if datasource cannot be found in plugin store",
|
||||
error: true,
|
||||
condition: func(services services) models.Condition {
|
||||
dsQuery := models.GenerateAlertQuery()
|
||||
ds := &datasources.DataSource{
|
||||
Uid: dsQuery.DatasourceUID,
|
||||
Type: util.GenerateShortUID(),
|
||||
}
|
||||
services.cache.DataSources = append(services.cache.DataSources, ds)
|
||||
// do not update the plugin store
|
||||
return models.Condition{
|
||||
Condition: dsQuery.RefID,
|
||||
Data: []models.AlertQuery{
|
||||
dsQuery,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail if datasource is not backend one",
|
||||
error: true,
|
||||
condition: func(services services) models.Condition {
|
||||
dsQuery1 := models.GenerateAlertQuery()
|
||||
dsQuery2 := models.GenerateAlertQuery()
|
||||
ds1 := &datasources.DataSource{
|
||||
Uid: dsQuery1.DatasourceUID,
|
||||
Type: util.GenerateShortUID(),
|
||||
}
|
||||
ds2 := &datasources.DataSource{
|
||||
Uid: dsQuery2.DatasourceUID,
|
||||
Type: util.GenerateShortUID(),
|
||||
}
|
||||
services.cache.DataSources = append(services.cache.DataSources, ds1, ds2)
|
||||
services.pluginsStore.PluginList = append(services.pluginsStore.PluginList, plugins.PluginDTO{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: ds1.Type,
|
||||
Backend: false,
|
||||
},
|
||||
}, plugins.PluginDTO{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: ds2.Type,
|
||||
Backend: true,
|
||||
},
|
||||
})
|
||||
// do not update the plugin store
|
||||
return models.Condition{
|
||||
Condition: dsQuery1.RefID,
|
||||
Data: []models.AlertQuery{
|
||||
dsQuery1,
|
||||
dsQuery2,
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -419,17 +496,25 @@ func TestValidate(t *testing.T) {
|
||||
{
|
||||
name: "pass if datasource exists and condition is correct",
|
||||
error: false,
|
||||
condition: func(service *fakes.FakeCacheService) models.Condition {
|
||||
ds := models.GenerateAlertQuery()
|
||||
service.DataSources = append(service.DataSources, &datasources.DataSource{
|
||||
Uid: ds.DatasourceUID,
|
||||
condition: func(services services) models.Condition {
|
||||
dsQuery := models.GenerateAlertQuery()
|
||||
ds := &datasources.DataSource{
|
||||
Uid: dsQuery.DatasourceUID,
|
||||
Type: util.GenerateShortUID(),
|
||||
}
|
||||
services.cache.DataSources = append(services.cache.DataSources, ds)
|
||||
services.pluginsStore.PluginList = append(services.pluginsStore.PluginList, plugins.PluginDTO{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: ds.Type,
|
||||
Backend: true,
|
||||
},
|
||||
})
|
||||
|
||||
return models.Condition{
|
||||
Condition: "B",
|
||||
Data: []models.AlertQuery{
|
||||
ds,
|
||||
models.CreateClassicConditionExpression("B", ds.RefID, "last", "gt", rand.Int()),
|
||||
dsQuery,
|
||||
models.CreateClassicConditionExpression("B", dsQuery.RefID, "last", "gt", rand.Int()),
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -441,12 +526,16 @@ func TestValidate(t *testing.T) {
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
cacheService := &fakes.FakeCacheService{}
|
||||
condition := testCase.condition(cacheService)
|
||||
store := &plugins.FakePluginStore{}
|
||||
condition := testCase.condition(services{
|
||||
cache: cacheService,
|
||||
pluginsStore: store,
|
||||
})
|
||||
|
||||
evaluator := NewEvaluatorFactory(setting.UnifiedAlertingSettings{}, cacheService, expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, nil, nil))
|
||||
evaluator := NewEvaluatorFactory(setting.UnifiedAlertingSettings{}, cacheService, expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, nil, nil), store)
|
||||
evalCtx := Context(context.Background(), u)
|
||||
|
||||
_, err := evaluator.Create(evalCtx, condition)
|
||||
err := evaluator.Validate(evalCtx, condition)
|
||||
if testCase.error {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user