diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go index 8f79354898d..d78e84f6974 100644 --- a/pkg/services/alerting/extractor.go +++ b/pkg/services/alerting/extractor.go @@ -120,6 +120,10 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) { jsonQuery.SetPath([]string{"datasourceId"}, datasource.Id) } + if interval, err := panel.Get("interval").String(); err == nil { + panelQuery.Set("interval", interval) + } + jsonQuery.Set("model", panelQuery.Interface()) } diff --git a/pkg/services/alerting/extractor_test.go b/pkg/services/alerting/extractor_test.go index f82210f1b1a..e72e6d938d8 100644 --- a/pkg/services/alerting/extractor_test.go +++ b/pkg/services/alerting/extractor_test.go @@ -6,6 +6,7 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" . "github.com/smartystreets/goconvey/convey" ) @@ -17,8 +18,35 @@ func TestAlertRuleExtraction(t *testing.T) { return &FakeCondition{}, nil }) - Convey("Parsing and validating alerts from dashboards", func() { - json := `{ + setting.NewConfigContext(&setting.CommandLineArgs{ + HomePath: "../../../", + }) + + // 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"} + + 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 + } + return nil + }) + + json := ` + { "id": 57, "title": "Graphite 4", "originalTitle": "Graphite 4", @@ -80,32 +108,16 @@ func TestAlertRuleExtraction(t *testing.T) { ] } ] - }` + }` + + 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) - // mock data - defaultDs := &m.DataSource{Id: 12, OrgId: 2, Name: "I am default", IsDefault: true} - graphite2Ds := &m.DataSource{Id: 15, OrgId: 2, Name: "graphite2"} - - 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 - } - return nil - }) - alerts, err := extractor.GetAlerts() Convey("Get rules without error", func() { @@ -119,6 +131,9 @@ func TestAlertRuleExtraction(t *testing.T) { 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() { @@ -156,5 +171,317 @@ func TestAlertRuleExtraction(t *testing.T) { }) }) }) + + Convey("Parse and validate dashboard containing influxdb alert", func() { + + json2 := `{ + "id": 4, + "title": "Influxdb", + "tags": [ + "apa" + ], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "sharedCrosshair": false, + "rows": [ + { + "collapse": false, + "editable": true, + "height": "450px", + "panels": [ + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "frequency": "3s", + "handler": 1, + "name": "Influxdb", + "noDataState": "no_data", + "notifications": [ + { + "id": 6 + } + ] + }, + "alerting": {}, + "aliasColors": { + "logins.count.count": "#890F02" + }, + "bars": false, + "datasource": "InfluxDB", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "id": 1, + "interval": ">10s", + "isNew": true, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "datacenter" + ], + "type": "tag" + }, + { + "params": [ + "none" + ], + "type": "fill" + } + ], + "hide": false, + "measurement": "logins.count", + "policy": "default", + "query": "SELECT 8 * count(\"value\") FROM \"logins.count\" WHERE $timeFilter GROUP BY time($interval), \"datacenter\" fill(none)", + "rawQuery": true, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "count" + } + ] + ], + "tags": [] + }, + { + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "hide": true, + "measurement": "cpu", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ], + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [] + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Panel Title", + "tooltip": { + "msResolution": false, + "ordering": "alphabetical", + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "editable": true, + "error": false, + "id": 2, + "isNew": true, + "limit": 10, + "links": [], + "show": "current", + "span": 2, + "stateFilter": [ + "alerting" + ], + "title": "Alert status", + "type": "alertlist" + } + ], + "title": "Row" + } + ], + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "schemaVersion": 13, + "version": 120, + "links": [], + "gnetId": null + }` + + dashJson, err := simplejson.NewJson([]byte(json2)) + 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") + } + }) + }) }) }