mirror of
https://github.com/grafana/grafana.git
synced 2024-11-30 12:44:10 -06:00
68833fa978
This changes the logic for the alert validation in the extractor. Validation is executed twice, once before attempting to save the alerts and once after saving the dashboard while saving the alerts to the db. The first validation will not require that the alert has a dashboard id which allows new dashboards with alerts to be saved. Fixes #11247
266 lines
7.7 KiB
Go
266 lines
7.7 KiB
Go
package alerting
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"testing"
|
|
|
|
"github.com/grafana/grafana/pkg/bus"
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
m "github.com/grafana/grafana/pkg/models"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
)
|
|
|
|
func TestAlertRuleExtraction(t *testing.T) {
|
|
|
|
Convey("Parsing alert rules from dashboard json", t, func() {
|
|
|
|
RegisterCondition("query", func(model *simplejson.Json, index int) (Condition, error) {
|
|
return &FakeCondition{}, nil
|
|
})
|
|
|
|
// mock data
|
|
defaultDs := &m.DataSource{Id: 12, OrgId: 1, Name: "I am default", IsDefault: true}
|
|
graphite2Ds := &m.DataSource{Id: 15, OrgId: 1, Name: "graphite2"}
|
|
influxDBDs := &m.DataSource{Id: 16, OrgId: 1, Name: "InfluxDB"}
|
|
prom := &m.DataSource{Id: 17, OrgId: 1, Name: "Prometheus"}
|
|
|
|
bus.AddHandler("test", func(query *m.GetDataSourcesQuery) error {
|
|
query.Result = []*m.DataSource{defaultDs, graphite2Ds}
|
|
return nil
|
|
})
|
|
|
|
bus.AddHandler("test", func(query *m.GetDataSourceByNameQuery) error {
|
|
if query.Name == defaultDs.Name {
|
|
query.Result = defaultDs
|
|
}
|
|
if query.Name == graphite2Ds.Name {
|
|
query.Result = graphite2Ds
|
|
}
|
|
if query.Name == influxDBDs.Name {
|
|
query.Result = influxDBDs
|
|
}
|
|
if query.Name == prom.Name {
|
|
query.Result = prom
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
json, err := ioutil.ReadFile("./test-data/graphite-alert.json")
|
|
So(err, ShouldBeNil)
|
|
|
|
Convey("Extractor should not modify the original json", func() {
|
|
dashJson, err := simplejson.NewJson([]byte(json))
|
|
So(err, ShouldBeNil)
|
|
|
|
dash := m.NewDashboardFromJson(dashJson)
|
|
|
|
getTarget := func(j *simplejson.Json) string {
|
|
rowObj := j.Get("rows").MustArray()[0]
|
|
row := simplejson.NewFromAny(rowObj)
|
|
panelObj := row.Get("panels").MustArray()[0]
|
|
panel := simplejson.NewFromAny(panelObj)
|
|
conditionObj := panel.Get("alert").Get("conditions").MustArray()[0]
|
|
condition := simplejson.NewFromAny(conditionObj)
|
|
return condition.Get("query").Get("model").Get("target").MustString()
|
|
}
|
|
|
|
Convey("Dashboard json rows.panels.alert.query.model.target should be empty", func() {
|
|
So(getTarget(dashJson), ShouldEqual, "")
|
|
})
|
|
|
|
extractor := NewDashAlertExtractor(dash, 1)
|
|
_, _ = extractor.GetAlerts()
|
|
|
|
Convey("Dashboard json should not be updated after extracting rules", func() {
|
|
So(getTarget(dashJson), ShouldEqual, "")
|
|
})
|
|
})
|
|
|
|
Convey("Parsing and validating dashboard containing graphite alerts", func() {
|
|
|
|
dashJson, err := simplejson.NewJson([]byte(json))
|
|
So(err, ShouldBeNil)
|
|
|
|
dash := m.NewDashboardFromJson(dashJson)
|
|
extractor := NewDashAlertExtractor(dash, 1)
|
|
|
|
alerts, err := extractor.GetAlerts()
|
|
|
|
Convey("Get rules without error", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("all properties have been set", func() {
|
|
So(len(alerts), ShouldEqual, 2)
|
|
|
|
for _, v := range alerts {
|
|
So(v.DashboardId, ShouldEqual, 57)
|
|
So(v.Name, ShouldNotBeEmpty)
|
|
So(v.Message, ShouldNotBeEmpty)
|
|
|
|
settings := simplejson.NewFromAny(v.Settings)
|
|
So(settings.Get("interval").MustString(""), ShouldEqual, "")
|
|
}
|
|
|
|
Convey("should extract handler property", func() {
|
|
So(alerts[0].Handler, ShouldEqual, 1)
|
|
So(alerts[1].Handler, ShouldEqual, 0)
|
|
})
|
|
|
|
Convey("should extract frequency in seconds", func() {
|
|
So(alerts[0].Frequency, ShouldEqual, 60)
|
|
So(alerts[1].Frequency, ShouldEqual, 60)
|
|
})
|
|
|
|
Convey("should extract panel idc", func() {
|
|
So(alerts[0].PanelId, ShouldEqual, 3)
|
|
So(alerts[1].PanelId, ShouldEqual, 4)
|
|
})
|
|
|
|
Convey("should extract name and desc", func() {
|
|
So(alerts[0].Name, ShouldEqual, "name1")
|
|
So(alerts[0].Message, ShouldEqual, "desc1")
|
|
So(alerts[1].Name, ShouldEqual, "name2")
|
|
So(alerts[1].Message, ShouldEqual, "desc2")
|
|
})
|
|
|
|
Convey("should set datasourceId", func() {
|
|
condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0])
|
|
query := condition.Get("query")
|
|
So(query.Get("datasourceId").MustInt64(), ShouldEqual, 12)
|
|
})
|
|
|
|
Convey("should copy query model to condition", func() {
|
|
condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0])
|
|
model := condition.Get("query").Get("model")
|
|
So(model.Get("target").MustString(), ShouldEqual, "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)")
|
|
})
|
|
})
|
|
})
|
|
|
|
Convey("Panels missing id should return error", func() {
|
|
panelWithoutId, err := ioutil.ReadFile("./test-data/panels-missing-id.json")
|
|
So(err, ShouldBeNil)
|
|
|
|
dashJson, err := simplejson.NewJson([]byte(panelWithoutId))
|
|
So(err, ShouldBeNil)
|
|
dash := m.NewDashboardFromJson(dashJson)
|
|
extractor := NewDashAlertExtractor(dash, 1)
|
|
|
|
_, err = extractor.GetAlerts()
|
|
|
|
Convey("panels without Id should return error", func() {
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
})
|
|
|
|
Convey("Panel with id set to zero should return error", func() {
|
|
panelWithIdZero, err := ioutil.ReadFile("./test-data/panel-with-id-0.json")
|
|
So(err, ShouldBeNil)
|
|
|
|
dashJson, err := simplejson.NewJson([]byte(panelWithIdZero))
|
|
So(err, ShouldBeNil)
|
|
dash := m.NewDashboardFromJson(dashJson)
|
|
extractor := NewDashAlertExtractor(dash, 1)
|
|
|
|
_, err = extractor.GetAlerts()
|
|
|
|
Convey("panel with id 0 should return error", func() {
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
})
|
|
|
|
Convey("Parse alerts from dashboard without rows", func() {
|
|
json, err := ioutil.ReadFile("./test-data/v5-dashboard.json")
|
|
So(err, ShouldBeNil)
|
|
|
|
dashJson, err := simplejson.NewJson(json)
|
|
So(err, ShouldBeNil)
|
|
dash := m.NewDashboardFromJson(dashJson)
|
|
extractor := NewDashAlertExtractor(dash, 1)
|
|
|
|
alerts, err := extractor.GetAlerts()
|
|
|
|
Convey("Get rules without error", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Should have 2 alert rule", func() {
|
|
So(len(alerts), ShouldEqual, 2)
|
|
})
|
|
})
|
|
|
|
Convey("Parse and validate dashboard containing influxdb alert", func() {
|
|
json, err := ioutil.ReadFile("./test-data/influxdb-alert.json")
|
|
So(err, ShouldBeNil)
|
|
|
|
dashJson, err := simplejson.NewJson(json)
|
|
So(err, ShouldBeNil)
|
|
dash := m.NewDashboardFromJson(dashJson)
|
|
extractor := NewDashAlertExtractor(dash, 1)
|
|
|
|
alerts, err := extractor.GetAlerts()
|
|
|
|
Convey("Get rules without error", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("should be able to read interval", func() {
|
|
So(len(alerts), ShouldEqual, 1)
|
|
|
|
for _, alert := range alerts {
|
|
So(alert.DashboardId, ShouldEqual, 4)
|
|
|
|
conditions := alert.Settings.Get("conditions").MustArray()
|
|
cond := simplejson.NewFromAny(conditions[0])
|
|
|
|
So(cond.Get("query").Get("model").Get("interval").MustString(), ShouldEqual, ">10s")
|
|
}
|
|
})
|
|
})
|
|
|
|
Convey("Should be able to extract collapsed panels", func() {
|
|
json, err := ioutil.ReadFile("./test-data/collapsed-panels.json")
|
|
So(err, ShouldBeNil)
|
|
|
|
dashJson, err := simplejson.NewJson(json)
|
|
So(err, ShouldBeNil)
|
|
|
|
dash := m.NewDashboardFromJson(dashJson)
|
|
extractor := NewDashAlertExtractor(dash, 1)
|
|
|
|
alerts, err := extractor.GetAlerts()
|
|
|
|
Convey("Get rules without error", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("should be able to extract collapsed alerts", func() {
|
|
So(len(alerts), ShouldEqual, 4)
|
|
})
|
|
})
|
|
|
|
Convey("Parse and validate dashboard without id and containing an alert", func() {
|
|
json, err := ioutil.ReadFile("./test-data/dash-without-id.json")
|
|
So(err, ShouldBeNil)
|
|
|
|
dashJSON, err := simplejson.NewJson(json)
|
|
So(err, ShouldBeNil)
|
|
dash := m.NewDashboardFromJson(dashJSON)
|
|
extractor := NewDashAlertExtractor(dash, 1)
|
|
|
|
err = extractor.ValidateAlerts()
|
|
|
|
Convey("Should validate without error", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Should fail on save", func() {
|
|
_, err := extractor.GetAlerts()
|
|
So(err, ShouldEqual, m.ErrDashboardContainsInvalidAlertData)
|
|
})
|
|
})
|
|
})
|
|
}
|