func TestAlertingModelTest(t *testing.T) { Convey("Testing Alerting model", t, func() { json1, _ := simplejson.NewJson([]byte(`{ "field": "value" }`)) json2, _ := simplejson.NewJson([]byte(`{ "field": "value" }`)) rule1 := &Alert{ Settings: json1, Name: "Namn", Message: "Message", } rule2 := &Alert{ Settings: json2, Name: "Namn", Message: "Message", } Convey("Testing AlertRule equals", func() { So(rule1.ContainsUpdates(rule2), ShouldBeFalse) }) Convey("Changing the expression should contain update", func() { json2, _ := simplejson.NewJson([]byte(`{ "field": "newValue" }`)) rule1.Settings = json2 So(rule1.ContainsUpdates(rule2), ShouldBeTrue) }) }) }
func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error) { jsonModel, err := simplejson.NewJson([]byte(`{ "type": "query", "query": { "params": ["A", "5m", "now"], "datasourceId": 1, "model": {"target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"} }, "reducer":` + ctx.reducer + `, "evaluator":` + ctx.evaluator + ` }`)) So(err, ShouldBeNil) condition, err := NewQueryCondition(jsonModel, 0) So(err, ShouldBeNil) ctx.condition = condition condition.HandleRequest = func(context context.Context, req *tsdb.Request) (*tsdb.Response, error) { return &tsdb.Response{ Results: map[string]*tsdb.QueryResult{ "A": {Series: ctx.series}, }, }, nil } return condition.Eval(ctx.result) }
func copyJson(in *simplejson.Json) (*simplejson.Json, error) { rawJson, err := in.MarshalJSON() if err != nil { return nil, err } return simplejson.NewJson(rawJson) }
func TestTelegramNotifier(t *testing.T) { Convey("Telegram notifier tests", t, func() { Convey("Parsing alert notification from settings", func() { Convey("empty settings should return error", func() { json := `{ }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "telegram_testing", Type: "telegram", Settings: settingsJSON, } _, err := NewTelegramNotifier(model) So(err, ShouldNotBeNil) }) Convey("settings should trigger incident", func() { json := ` { "bottoken": "abcdefgh0123456789", "chatid": "-1234567890" }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "telegram_testing", Type: "telegram", Settings: settingsJSON, } not, err := NewTelegramNotifier(model) telegramNotifier := not.(*TelegramNotifier) So(err, ShouldBeNil) So(telegramNotifier.Name, ShouldEqual, "telegram_testing") So(telegramNotifier.Type, ShouldEqual, "telegram") So(telegramNotifier.BotToken, ShouldEqual, "abcdefgh0123456789") So(telegramNotifier.ChatID, ShouldEqual, "-1234567890") }) }) }) }
func evalutorScenario(json string, reducedValue float64, datapoints ...float64) bool { jsonModel, err := simplejson.NewJson([]byte(json)) So(err, ShouldBeNil) evaluator, err := NewAlertEvaluator(jsonModel) So(err, ShouldBeNil) return evaluator.Eval(null.FloatFrom(reducedValue)) }
func TestPagerdutyNotifier(t *testing.T) { Convey("Pagerduty notifier tests", t, func() { Convey("Parsing alert notification from settings", func() { Convey("empty settings should return error", func() { json := `{ }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "pageduty_testing", Type: "pagerduty", Settings: settingsJSON, } _, err := NewPagerdutyNotifier(model) So(err, ShouldNotBeNil) }) Convey("settings should trigger incident", func() { json := ` { "integrationKey": "abcdefgh0123456789" }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "pagerduty_testing", Type: "pagerduty", Settings: settingsJSON, } not, err := NewPagerdutyNotifier(model) pagerdutyNotifier := not.(*PagerdutyNotifier) So(err, ShouldBeNil) So(pagerdutyNotifier.Name, ShouldEqual, "pagerduty_testing") So(pagerdutyNotifier.Type, ShouldEqual, "pagerduty") So(pagerdutyNotifier.Key, ShouldEqual, "abcdefgh0123456789") }) }) }) }
func TestSensuNotifier(t *testing.T) { Convey("Sensu notifier tests", t, func() { Convey("Parsing alert notification from settings", func() { Convey("empty settings should return error", func() { json := `{ }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "sensu", Type: "sensu", Settings: settingsJSON, } _, err := NewSensuNotifier(model) So(err, ShouldNotBeNil) }) Convey("from settings", func() { json := ` { "url": "http://sensu-api.example.com:4567/results" }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "sensu", Type: "sensu", Settings: settingsJSON, } not, err := NewSensuNotifier(model) sensuNotifier := not.(*SensuNotifier) So(err, ShouldBeNil) So(sensuNotifier.Name, ShouldEqual, "sensu") So(sensuNotifier.Type, ShouldEqual, "sensu") So(sensuNotifier.Url, ShouldEqual, "http://sensu-api.example.com:4567/results") }) }) }) }
func TestOpsGenieNotifier(t *testing.T) { Convey("OpsGenie notifier tests", t, func() { Convey("Parsing alert notification from settings", func() { Convey("empty settings should return error", func() { json := `{ }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "opsgenie_testing", Type: "opsgenie", Settings: settingsJSON, } _, err := NewOpsGenieNotifier(model) So(err, ShouldNotBeNil) }) Convey("settings should trigger incident", func() { json := ` { "apiKey": "abcdefgh0123456789" }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "opsgenie_testing", Type: "opsgenie", Settings: settingsJSON, } not, err := NewOpsGenieNotifier(model) opsgenieNotifier := not.(*OpsGenieNotifier) So(err, ShouldBeNil) So(opsgenieNotifier.Name, ShouldEqual, "opsgenie_testing") So(opsgenieNotifier.Type, ShouldEqual, "opsgenie") So(opsgenieNotifier.ApiKey, ShouldEqual, "abcdefgh0123456789") }) }) }) }
func TestVictoropsNotifier(t *testing.T) { Convey("Victorops notifier tests", t, func() { Convey("Parsing alert notification from settings", func() { Convey("empty settings should return error", func() { json := `{ }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "victorops_testing", Type: "victorops", Settings: settingsJSON, } _, err := NewVictoropsNotifier(model) So(err, ShouldNotBeNil) }) Convey("from settings", func() { json := ` { "url": "http://google.com" }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "victorops_testing", Type: "victorops", Settings: settingsJSON, } not, err := NewVictoropsNotifier(model) victoropsNotifier := not.(*VictoropsNotifier) So(err, ShouldBeNil) So(victoropsNotifier.Name, ShouldEqual, "victorops_testing") So(victoropsNotifier.Type, ShouldEqual, "victorops") So(victoropsNotifier.URL, ShouldEqual, "http://google.com") }) }) }) }
func TestEmailNotifier(t *testing.T) { Convey("Email notifier tests", t, func() { Convey("Parsing alert notification from settings", func() { Convey("empty settings should return error", func() { json := `{ }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "ops", Type: "email", Settings: settingsJSON, } _, err := NewEmailNotifier(model) So(err, ShouldNotBeNil) }) Convey("from settings", func() { json := ` { "addresses": "*****@*****.**" }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "ops", Type: "email", Settings: settingsJSON, } not, err := NewEmailNotifier(model) emailNotifier := not.(*EmailNotifier) So(err, ShouldBeNil) So(emailNotifier.Name, ShouldEqual, "ops") So(emailNotifier.Type, ShouldEqual, "email") So(emailNotifier.Addresses[0], ShouldEqual, "*****@*****.**") }) }) }) }
func TestWebhookNotifier(t *testing.T) { Convey("Webhook notifier tests", t, func() { Convey("Parsing alert notification from settings", func() { Convey("empty settings should return error", func() { json := `{ }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "ops", Type: "email", Settings: settingsJSON, } _, err := NewWebHookNotifier(model) So(err, ShouldNotBeNil) }) Convey("from settings", func() { json := ` { "url": "http://google.com" }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "ops", Type: "email", Settings: settingsJSON, } not, err := NewWebHookNotifier(model) emailNotifier := not.(*WebhookNotifier) So(err, ShouldBeNil) So(emailNotifier.Name, ShouldEqual, "ops") So(emailNotifier.Type, ShouldEqual, "email") So(emailNotifier.Url, ShouldEqual, "http://google.com") }) }) }) }
func TestLineNotifier(t *testing.T) { Convey("Line notifier tests", t, func() { Convey("empty settings should return error", func() { json := `{ }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "line_testing", Type: "line", Settings: settingsJSON, } _, err := NewLINENotifier(model) So(err, ShouldNotBeNil) }) Convey("settings should trigger incident", func() { json := ` { "token": "abcdefgh0123456789" }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "line_testing", Type: "line", Settings: settingsJSON, } not, err := NewLINENotifier(model) lineNotifier := not.(*LineNotifier) So(err, ShouldBeNil) So(lineNotifier.Name, ShouldEqual, "line_testing") So(lineNotifier.Type, ShouldEqual, "line") So(lineNotifier.Token, ShouldEqual, "abcdefgh0123456789") }) }) }
func TestEvalutors(t *testing.T) { Convey("greater then", t, func() { So(evalutorScenario(`{"type": "gt", "params": [1] }`, 3), ShouldBeTrue) So(evalutorScenario(`{"type": "gt", "params": [3] }`, 1), ShouldBeFalse) }) Convey("less then", t, func() { So(evalutorScenario(`{"type": "lt", "params": [1] }`, 3), ShouldBeFalse) So(evalutorScenario(`{"type": "lt", "params": [3] }`, 1), ShouldBeTrue) }) Convey("within_range", t, func() { So(evalutorScenario(`{"type": "within_range", "params": [1, 100] }`, 3), ShouldBeTrue) So(evalutorScenario(`{"type": "within_range", "params": [1, 100] }`, 300), ShouldBeFalse) So(evalutorScenario(`{"type": "within_range", "params": [100, 1] }`, 3), ShouldBeTrue) So(evalutorScenario(`{"type": "within_range", "params": [100, 1] }`, 300), ShouldBeFalse) }) Convey("outside_range", t, func() { So(evalutorScenario(`{"type": "outside_range", "params": [1, 100] }`, 1000), ShouldBeTrue) So(evalutorScenario(`{"type": "outside_range", "params": [1, 100] }`, 50), ShouldBeFalse) So(evalutorScenario(`{"type": "outside_range", "params": [100, 1] }`, 1000), ShouldBeTrue) So(evalutorScenario(`{"type": "outside_range", "params": [100, 1] }`, 50), ShouldBeFalse) }) Convey("no_value", t, func() { Convey("should be false if serie have values", func() { So(evalutorScenario(`{"type": "no_value", "params": [] }`, 50), ShouldBeFalse) }) Convey("should be true when the serie have no value", func() { jsonModel, err := simplejson.NewJson([]byte(`{"type": "no_value", "params": [] }`)) So(err, ShouldBeNil) evaluator, err := NewAlertEvaluator(jsonModel) So(err, ShouldBeNil) So(evaluator.Eval(null.FloatFromPtr(nil)), ShouldBeTrue) }) }) }
func (c *connection) handleMessage(message []byte) { json, err := simplejson.NewJson(message) if err != nil { log.Error(3, "Unreadable message on websocket channel:", err) } msgType := json.Get("action").MustString() streamName := json.Get("stream").MustString() if len(streamName) == 0 { log.Error(3, "Not allowed to subscribe to empty stream name") return } switch msgType { case "subscribe": h.subChannel <- &streamSubscription{name: streamName, conn: c} case "unsubscribe": h.subChannel <- &streamSubscription{name: streamName, conn: c, remove: true} } }
func TestEvalutors(t *testing.T) { Convey("greater then", t, func() { So(evalutorScenario(`{"type": "gt", "params": [1] }`, 3), ShouldBeTrue) So(evalutorScenario(`{"type": "gt", "params": [3] }`, 1), ShouldBeFalse) }) Convey("less then", t, func() { So(evalutorScenario(`{"type": "lt", "params": [1] }`, 3), ShouldBeFalse) So(evalutorScenario(`{"type": "lt", "params": [3] }`, 1), ShouldBeTrue) }) Convey("within_range", t, func() { So(evalutorScenario(`{"type": "within_range", "params": [1, 100] }`, 3), ShouldBeTrue) So(evalutorScenario(`{"type": "within_range", "params": [1, 100] }`, 300), ShouldBeFalse) So(evalutorScenario(`{"type": "within_range", "params": [100, 1] }`, 3), ShouldBeTrue) So(evalutorScenario(`{"type": "within_range", "params": [100, 1] }`, 300), ShouldBeFalse) }) Convey("outside_range", t, func() { So(evalutorScenario(`{"type": "outside_range", "params": [1, 100] }`, 1000), ShouldBeTrue) So(evalutorScenario(`{"type": "outside_range", "params": [1, 100] }`, 50), ShouldBeFalse) So(evalutorScenario(`{"type": "outside_range", "params": [100, 1] }`, 1000), ShouldBeTrue) So(evalutorScenario(`{"type": "outside_range", "params": [100, 1] }`, 50), ShouldBeFalse) }) Convey("no_data", t, func() { So(evalutorScenario(`{"type": "no_data", "params": [] }`, 50), ShouldBeFalse) jsonModel, err := simplejson.NewJson([]byte(`{"type": "no_data", "params": [] }`)) So(err, ShouldBeNil) evaluator, err := NewAlertEvaluator(jsonModel) So(err, ShouldBeNil) So(evaluator.Eval(nil), ShouldBeTrue) }) }
func TestAlertRuleModel(t *testing.T) { Convey("Testing alert rule", t, func() { RegisterCondition("test", func(model *simplejson.Json, index int) (Condition, error) { return &FakeCondition{}, nil }) Convey("Can parse seconds", func() { seconds, _ := getTimeDurationStringToSeconds("10s") So(seconds, ShouldEqual, 10) }) Convey("Can parse minutes", func() { seconds, _ := getTimeDurationStringToSeconds("10m") So(seconds, ShouldEqual, 600) }) Convey("Can parse hours", func() { seconds, _ := getTimeDurationStringToSeconds("1h") So(seconds, ShouldEqual, 3600) }) Convey("defaults to seconds", func() { seconds, _ := getTimeDurationStringToSeconds("1o") So(seconds, ShouldEqual, 1) }) Convey("should return err for empty string", func() { _, err := getTimeDurationStringToSeconds("") So(err, ShouldNotBeNil) }) Convey("can construct alert rule model", func() { json := ` { "name": "name2", "description": "desc2", "handler": 0, "noDataMode": "critical", "enabled": true, "frequency": "60s", "conditions": [ { "type": "test", "prop": 123 } ], "notifications": [ {"id": 1134}, {"id": 22} ] } ` alertJSON, jsonErr := simplejson.NewJson([]byte(json)) So(jsonErr, ShouldBeNil) alert := &m.Alert{ Id: 1, OrgId: 1, DashboardId: 1, PanelId: 1, Settings: alertJSON, } alertRule, err := NewRuleFromDBAlert(alert) So(err, ShouldBeNil) So(len(alertRule.Conditions), ShouldEqual, 1) Convey("Can read notifications", func() { So(len(alertRule.Notifications), ShouldEqual, 2) }) }) }) }
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 }) 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", "tags": ["graphite"], "rows": [ { "panels": [ { "title": "Active desktop users", "editable": true, "type": "graph", "id": 3, "targets": [ { "refId": "A", "target": "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)" } ], "datasource": null, "alert": { "name": "name1", "message": "desc1", "handler": 1, "frequency": "60s", "conditions": [ { "type": "query", "query": {"params": ["A", "5m", "now"]}, "reducer": {"type": "avg", "params": []}, "evaluator": {"type": ">", "params": [100]} } ] } }, { "title": "Active mobile users", "id": 4, "targets": [ {"refId": "A", "target": ""}, {"refId": "B", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"} ], "datasource": "graphite2", "alert": { "name": "name2", "message": "desc2", "handler": 0, "frequency": "60s", "severity": "warning", "conditions": [ { "type": "query", "query": {"params": ["B", "5m", "now"]}, "reducer": {"type": "avg", "params": []}, "evaluator": {"type": ">", "params": [100]} } ] } } ] } ] }` 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("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") } }) }) }) }
func TestInfluxdbQueryParser(t *testing.T) { Convey("Influxdb query parser", t, func() { parser := &InfluxdbQueryParser{} Convey("can parse influxdb json model", func() { json := ` { "dsType": "influxdb", "groupBy": [ { "params": [ "$interval" ], "type": "time" }, { "params": [ "datacenter" ], "type": "tag" }, { "params": [ "none" ], "type": "fill" } ], "measurement": "logins.count", "policy": "default", "refId": "B", "resultFormat": "time_series", "select": [ [ { "type": "field", "params": [ "value" ] }, { "type": "count", "params": [] } ], [ { "type": "field", "params": [ "value" ] }, { "type": "bottom", "params": [ 3 ] } ], [ { "type": "field", "params": [ "value" ] }, { "type": "mean", "params": [] }, { "type": "math", "params": [ " / 100" ] } ] ], "tags": [ { "key": "datacenter", "operator": "=", "value": "America" }, { "condition": "OR", "key": "hostname", "operator": "=", "value": "server1" } ] } ` modelJson, err := simplejson.NewJson([]byte(json)) So(err, ShouldBeNil) res, err := parser.Parse(modelJson) So(err, ShouldBeNil) So(len(res.GroupBy), ShouldEqual, 3) So(len(res.Selects), ShouldEqual, 3) So(len(res.Tags), ShouldEqual, 2) }) Convey("can part raw query json model", func() { json := ` { "dsType": "influxdb", "groupBy": [ { "params": [ "$interval" ], "type": "time" }, { "params": [ "null" ], "type": "fill" } ], "policy": "default", "query": "RawDummieQuery", "rawQuery": true, "refId": "A", "resultFormat": "time_series", "select": [ [ { "params": [ "value" ], "type": "field" }, { "params": [ ], "type": "mean" } ] ], "tags": [ ] } ` modelJson, err := simplejson.NewJson([]byte(json)) So(err, ShouldBeNil) res, err := parser.Parse(modelJson) So(err, ShouldBeNil) So(res.RawQuery, ShouldEqual, "RawDummieQuery") So(len(res.GroupBy), ShouldEqual, 2) So(len(res.Selects), ShouldEqual, 1) So(len(res.Tags), ShouldEqual, 0) }) }) }
func TestSlackNotifier(t *testing.T) { Convey("Slack notifier tests", t, func() { Convey("Parsing alert notification from settings", func() { Convey("empty settings should return error", func() { json := `{ }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "ops", Type: "slack", Settings: settingsJSON, } _, err := NewSlackNotifier(model) So(err, ShouldNotBeNil) }) Convey("from settings", func() { json := ` { "url": "http://google.com" }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "ops", Type: "slack", Settings: settingsJSON, } not, err := NewSlackNotifier(model) slackNotifier := not.(*SlackNotifier) So(err, ShouldBeNil) So(slackNotifier.Name, ShouldEqual, "ops") So(slackNotifier.Type, ShouldEqual, "slack") So(slackNotifier.Url, ShouldEqual, "http://google.com") So(slackNotifier.Recipient, ShouldEqual, "") So(slackNotifier.Mention, ShouldEqual, "") }) Convey("from settings with Recipient and Mention", func() { json := ` { "url": "http://google.com", "recipient": "#ds-opentsdb", "mention": "@carl" }` settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "ops", Type: "slack", Settings: settingsJSON, } not, err := NewSlackNotifier(model) slackNotifier := not.(*SlackNotifier) So(err, ShouldBeNil) So(slackNotifier.Name, ShouldEqual, "ops") So(slackNotifier.Type, ShouldEqual, "slack") So(slackNotifier.Url, ShouldEqual, "http://google.com") So(slackNotifier.Recipient, ShouldEqual, "#ds-opentsdb") So(slackNotifier.Mention, ShouldEqual, "@carl") }) }) }) }
func TestDashboardImport(t *testing.T) { Convey("When importing plugin dashboard", t, func() { setting.Cfg = ini.Empty() sec, _ := setting.Cfg.NewSection("plugin.test-app") sec.NewKey("path", "../../tests/test-app") err := Init() So(err, ShouldBeNil) var importedDash *m.Dashboard bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error { importedDash = cmd.GetDashboardModel() cmd.Result = importedDash return nil }) cmd := ImportDashboardCommand{ PluginId: "test-app", Path: "dashboards/connections.json", OrgId: 1, UserId: 1, Inputs: []ImportDashboardInput{ {Name: "*", Type: "datasource", Value: "graphite"}, }, } err = ImportDashboard(&cmd) So(err, ShouldBeNil) Convey("should install dashboard", func() { So(importedDash, ShouldNotBeNil) resultStr, _ := importedDash.Data.EncodePretty() expectedBytes, _ := ioutil.ReadFile("../../tests/test-app/dashboards/connections_result.json") expectedJson, _ := simplejson.NewJson(expectedBytes) expectedStr, _ := expectedJson.EncodePretty() So(string(resultStr), ShouldEqual, string(expectedStr)) panel := importedDash.Data.Get("rows").GetIndex(0).Get("panels").GetIndex(0) So(panel.Get("datasource").MustString(), ShouldEqual, "graphite") }) }) Convey("When evaling dashboard template", t, func() { template, _ := simplejson.NewJson([]byte(`{ "__inputs": [ { "name": "DS_NAME", "type": "datasource" } ], "test": { "prop": "${DS_NAME}" } }`)) evaluator := &DashTemplateEvaluator{ template: template, inputs: []ImportDashboardInput{ {Name: "*", Type: "datasource", Value: "my-server"}, }, } res, err := evaluator.Eval() So(err, ShouldBeNil) Convey("should render template", func() { So(res.GetPath("test", "prop").MustString(), ShouldEqual, "my-server") }) Convey("should not include inputs in output", func() { inputs := res.Get("__inputs") So(inputs.Interface(), ShouldBeNil) }) }) }
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 }) Convey("Parsing and validating alerts from dashboards", func() { json := `{ "id": 57, "title": "Graphite 4", "originalTitle": "Graphite 4", "tags": ["graphite"], "rows": [ { "panels": [ { "title": "Active desktop users", "editable": true, "type": "graph", "id": 3, "targets": [ { "refId": "A", "target": "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)" } ], "datasource": null, "alert": { "name": "name1", "message": "desc1", "handler": 1, "frequency": "60s", "conditions": [ { "type": "query", "query": {"params": ["A", "5m", "now"]}, "reducer": {"type": "avg", "params": []}, "evaluator": {"type": ">", "params": [100]} } ] } }, { "title": "Active mobile users", "id": 4, "targets": [ {"refId": "A", "target": ""}, {"refId": "B", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"} ], "datasource": "graphite2", "alert": { "name": "name2", "message": "desc2", "handler": 0, "frequency": "60s", "severity": "warning", "conditions": [ { "type": "query", "query": {"params": ["B", "5m", "now"]}, "reducer": {"type": "avg", "params": []}, "evaluator": {"type": ">", "params": [100]} } ] } } ] } ] }` 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() { 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) } 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)") }) }) }) }) }
func TestMQEQueryParser(t *testing.T) { Convey("MQE query parser", t, func() { parser := &QueryParser{} dsInfo := &models.DataSource{JsonData: simplejson.New()} queryContext := &tsdb.QueryContext{} Convey("can parse simple mqe model", func() { json := ` { "cluster": [], "hosts": [ "staples-lab-1" ], "metrics": [ { "metric": "os.cpu.all*" } ], "rawQuery": "", "refId": "A" } ` modelJson, err := simplejson.NewJson([]byte(json)) So(err, ShouldBeNil) query, err := parser.Parse(modelJson, dsInfo, queryContext) So(err, ShouldBeNil) So(query.UseRawQuery, ShouldBeFalse) So(len(query.Cluster), ShouldEqual, 0) So(query.Hosts[0], ShouldEqual, "staples-lab-1") So(query.Metrics[0].Metric, ShouldEqual, "os.cpu.all*") }) Convey("can parse multi serie mqe model", func() { json := ` { "cluster": [ "demoapp" ], "hosts": [ "staples-lab-1" ], "metrics": [ { "metric": "os.cpu.all.active_percentage" }, { "metric": "os.disk.sda.io_time" } ], "functionList": [ { "func": "aggregate.min" }, { "func": "aggregate.max" } ], "rawQuery": "", "refId": "A", "addClusterToAlias": true, "addHostToAlias": true } ` modelJson, err := simplejson.NewJson([]byte(json)) So(err, ShouldBeNil) query, err := parser.Parse(modelJson, dsInfo, queryContext) So(err, ShouldBeNil) So(query.UseRawQuery, ShouldBeFalse) So(query.Cluster[0], ShouldEqual, "demoapp") So(query.Metrics[0].Metric, ShouldEqual, "os.cpu.all.active_percentage") So(query.Metrics[1].Metric, ShouldEqual, "os.disk.sda.io_time") So(query.FunctionList[0].Func, ShouldEqual, "aggregate.min") So(query.FunctionList[1].Func, ShouldEqual, "aggregate.max") }) Convey("can parse raw query", func() { json := ` { "addClusterToAlias": true, "addHostToAlias": true, "cluster": [], "hosts": [ "staples-lab-1" ], "metrics": [ { "alias": "cpu active", "metric": "os.cpu.all.active_percentage" }, { "alias": "disk sda time", "metric": "os.disk.sda.io_time" } ], "rawQuery": true, "query": "raw-query", "refId": "A" } ` modelJson, err := simplejson.NewJson([]byte(json)) So(err, ShouldBeNil) query, err := parser.Parse(modelJson, dsInfo, queryContext) So(err, ShouldBeNil) So(query.UseRawQuery, ShouldBeTrue) So(query.RawQuery, ShouldEqual, "raw-query") So(query.AddClusterToAlias, ShouldBeTrue) So(query.AddHostToAlias, ShouldBeTrue) }) }) }