mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: improve alerting default datasource search when extracting alerts (#29993)
* improve alerting search datasource * correct the xorm get usage * adding default datasource unittest
This commit is contained in:
parent
bf0d940e6d
commit
2a9c625c9f
@ -208,6 +208,12 @@ type GetDataSourcesQuery struct {
|
|||||||
Result []*DataSource
|
Result []*DataSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetDefaultDataSourceQuery struct {
|
||||||
|
OrgId int64
|
||||||
|
User *SignedInUser
|
||||||
|
Result *DataSource
|
||||||
|
}
|
||||||
|
|
||||||
type GetDataSourceByIdQuery struct {
|
type GetDataSourceByIdQuery struct {
|
||||||
Id int64
|
Id int64
|
||||||
OrgId int64
|
OrgId int64
|
||||||
|
@ -32,26 +32,19 @@ func NewDashAlertExtractor(dash *models.Dashboard, orgID int64, user *models.Sig
|
|||||||
|
|
||||||
func (e *DashAlertExtractor) lookupDatasourceID(dsName string) (*models.DataSource, error) {
|
func (e *DashAlertExtractor) lookupDatasourceID(dsName string) (*models.DataSource, error) {
|
||||||
if dsName == "" {
|
if dsName == "" {
|
||||||
query := &models.GetDataSourcesQuery{OrgId: e.OrgID}
|
query := &models.GetDefaultDataSourceQuery{OrgId: e.OrgID}
|
||||||
if err := bus.Dispatch(query); err != nil {
|
if err := bus.Dispatch(query); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return query.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, ds := range query.Result {
|
|
||||||
if ds.IsDefault {
|
|
||||||
return ds, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
query := &models.GetDataSourceByNameQuery{Name: dsName, OrgId: e.OrgID}
|
query := &models.GetDataSourceByNameQuery{Name: dsName, OrgId: e.OrgID}
|
||||||
if err := bus.Dispatch(query); err != nil {
|
if err := bus.Dispatch(query); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return query.Result, nil
|
return query.Result, nil
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("Could not find datasource id for " + dsName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func findPanelQueryByRefID(panel *simplejson.Json, refID string) *simplejson.Json {
|
func findPanelQueryByRefID(panel *simplejson.Json, refID string) *simplejson.Json {
|
||||||
|
@ -24,8 +24,8 @@ func TestAlertRuleExtraction(t *testing.T) {
|
|||||||
influxDBDs := &models.DataSource{Id: 16, OrgId: 1, Name: "InfluxDB"}
|
influxDBDs := &models.DataSource{Id: 16, OrgId: 1, Name: "InfluxDB"}
|
||||||
prom := &models.DataSource{Id: 17, OrgId: 1, Name: "Prometheus"}
|
prom := &models.DataSource{Id: 17, OrgId: 1, Name: "Prometheus"}
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.GetDataSourcesQuery) error {
|
bus.AddHandler("test", func(query *models.GetDefaultDataSourceQuery) error {
|
||||||
query.Result = []*models.DataSource{defaultDs, graphite2Ds}
|
query.Result = defaultDs
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -175,6 +175,28 @@ func TestAlertRuleExtraction(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Panel does not have datasource configured, use the default datasource", func() {
|
||||||
|
panelWithoutSpecifiedDatasource, err := ioutil.ReadFile("./testdata/panel-without-specified-datasource.json")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
dashJSON, err := simplejson.NewJson(panelWithoutSpecifiedDatasource)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
dash := models.NewDashboardFromJson(dashJSON)
|
||||||
|
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||||
|
|
||||||
|
alerts, err := extractor.GetAlerts()
|
||||||
|
|
||||||
|
Convey("Get rules without error", func() {
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Use default datasource", func() {
|
||||||
|
condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0])
|
||||||
|
query := condition.Get("query")
|
||||||
|
So(query.Get("datasourceId").MustInt64(), ShouldEqual, 12)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Convey("Parse alerts from dashboard without rows", func() {
|
Convey("Parse alerts from dashboard without rows", func() {
|
||||||
json, err := ioutil.ReadFile("./testdata/v5-dashboard.json")
|
json, err := ioutil.ReadFile("./testdata/v5-dashboard.json")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
185
pkg/services/alerting/testdata/panel-without-specified-datasource.json
vendored
Normal file
185
pkg/services/alerting/testdata/panel-without-specified-datasource.json
vendored
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": "-- Grafana --",
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"gnetId": null,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"id": 436,
|
||||||
|
"links": [],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"alert": {
|
||||||
|
"alertRuleTags": {},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"evaluator": {
|
||||||
|
"params": [
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"type": "gt"
|
||||||
|
},
|
||||||
|
"operator": {
|
||||||
|
"type": "and"
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"params": [
|
||||||
|
"A",
|
||||||
|
"15s",
|
||||||
|
"now"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"reducer": {
|
||||||
|
"params": [],
|
||||||
|
"type": "last"
|
||||||
|
},
|
||||||
|
"type": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"executionErrorState": "alerting",
|
||||||
|
"for": "0m",
|
||||||
|
"frequency": "10s",
|
||||||
|
"handler": 1,
|
||||||
|
"message": "A message",
|
||||||
|
"name": "Ok / Alerting Cycle test default datasource",
|
||||||
|
"noDataState": "no_data",
|
||||||
|
"notifications": []
|
||||||
|
},
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"custom": {},
|
||||||
|
"links": []
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 2,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.4.0-pre",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"pulseWave": {
|
||||||
|
"offCount": 6,
|
||||||
|
"offValue": "2",
|
||||||
|
"onCount": 6,
|
||||||
|
"onValue": "4",
|
||||||
|
"timeStep": 10
|
||||||
|
},
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "predictable_pulse",
|
||||||
|
"stringInput": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [
|
||||||
|
{
|
||||||
|
"colorMode": "critical",
|
||||||
|
"fill": true,
|
||||||
|
"line": true,
|
||||||
|
"op": "gt",
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Alerting / Ok",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:536",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:537",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schemaVersion": 27,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [],
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-6h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "extractor test default datasource",
|
||||||
|
"uid": "bqQguKaMz",
|
||||||
|
"version": 7
|
||||||
|
}
|
@ -24,6 +24,7 @@ func init() {
|
|||||||
bus.AddHandler("sql", UpdateDataSource)
|
bus.AddHandler("sql", UpdateDataSource)
|
||||||
bus.AddHandler("sql", GetDataSourceById)
|
bus.AddHandler("sql", GetDataSourceById)
|
||||||
bus.AddHandler("sql", GetDataSourceByName)
|
bus.AddHandler("sql", GetDataSourceByName)
|
||||||
|
bus.AddHandler("sql", GetDefaultDataSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDataSourceByID(id, orgID int64, engine *xorm.Engine) (*models.DataSource, error) {
|
func getDataSourceByID(id, orgID int64, engine *xorm.Engine) (*models.DataSource, error) {
|
||||||
@ -77,6 +78,20 @@ func GetDataSources(query *models.GetDataSourcesQuery) error {
|
|||||||
return sess.Find(&query.Result)
|
return sess.Find(&query.Result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDefaultDataSource is used to get the default datasource of organization
|
||||||
|
func GetDefaultDataSource(query *models.GetDefaultDataSourceQuery) error {
|
||||||
|
datasource := models.DataSource{}
|
||||||
|
|
||||||
|
exists, err := x.Where("org_id=? AND is_default=?", query.OrgId, true).Get(&datasource)
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return models.ErrDataSourceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
query.Result = &datasource
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteDataSourceById(cmd *models.DeleteDataSourceByIdCommand) error {
|
func DeleteDataSourceById(cmd *models.DeleteDataSourceByIdCommand) error {
|
||||||
return inTransaction(func(sess *DBSession) error {
|
return inTransaction(func(sess *DBSession) error {
|
||||||
var rawSQL = "DELETE FROM data_source WHERE id=? and org_id=?"
|
var rawSQL = "DELETE FROM data_source WHERE id=? and org_id=?"
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
package sqlstore
|
package sqlstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -287,3 +289,51 @@ func TestDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetDefaultDataSource(t *testing.T) {
|
||||||
|
InitTestDB(t)
|
||||||
|
|
||||||
|
t.Run("should return error if there is no default datasource", func(t *testing.T) {
|
||||||
|
cmd := models.AddDataSourceCommand{
|
||||||
|
OrgId: 10,
|
||||||
|
Name: "nisse",
|
||||||
|
Type: models.DS_GRAPHITE,
|
||||||
|
Access: models.DS_ACCESS_DIRECT,
|
||||||
|
Url: "http://test",
|
||||||
|
}
|
||||||
|
|
||||||
|
err := AddDataSource(&cmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
query := models.GetDefaultDataSourceQuery{OrgId: 10}
|
||||||
|
err = GetDefaultDataSource(&query)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.True(t, errors.Is(err, models.ErrDataSourceNotFound))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should return default datasource if exists", func(t *testing.T) {
|
||||||
|
cmd := models.AddDataSourceCommand{
|
||||||
|
OrgId: 10,
|
||||||
|
Name: "default datasource",
|
||||||
|
Type: models.DS_GRAPHITE,
|
||||||
|
Access: models.DS_ACCESS_DIRECT,
|
||||||
|
Url: "http://test",
|
||||||
|
IsDefault: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := AddDataSource(&cmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
query := models.GetDefaultDataSourceQuery{OrgId: 10}
|
||||||
|
err = GetDefaultDataSource(&query)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "default datasource", query.Result.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not return default datasource of other organisation", func(t *testing.T) {
|
||||||
|
query := models.GetDefaultDataSourceQuery{OrgId: 1}
|
||||||
|
err := GetDefaultDataSource(&query)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.True(t, errors.Is(err, models.ErrDataSourceNotFound))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -151,4 +151,7 @@ func addDataSourceMigration(mg *Migrator) {
|
|||||||
mg.AddMigration("Add unique index datasource_org_id_uid", NewAddIndexMigration(tableV2, &Index{
|
mg.AddMigration("Add unique index datasource_org_id_uid", NewAddIndexMigration(tableV2, &Index{
|
||||||
Cols: []string{"org_id", "uid"}, Type: UniqueIndex,
|
Cols: []string{"org_id", "uid"}, Type: UniqueIndex,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
mg.AddMigration("add unique index datasource_org_id_is_default", NewAddIndexMigration(tableV2, &Index{
|
||||||
|
Cols: []string{"org_id", "is_default"}}))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user