package alerting import ( "io/ioutil" "testing" "time" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/stretchr/testify/require" ) func TestAlertRuleExtraction(t *testing.T) { RegisterCondition("query", func(model *simplejson.Json, index int) (Condition, error) { return &FakeCondition{}, nil }) // mock data defaultDs := &models.DataSource{Id: 12, OrgId: 1, Name: "I am default", IsDefault: true} graphite2Ds := &models.DataSource{Id: 15, OrgId: 1, Name: "graphite2"} influxDBDs := &models.DataSource{Id: 16, OrgId: 1, Name: "InfluxDB"} prom := &models.DataSource{Id: 17, OrgId: 1, Name: "Prometheus"} bus.AddHandler("test", func(query *models.GetDefaultDataSourceQuery) error { query.Result = defaultDs return nil }) bus.AddHandler("test", func(query *models.GetDataSourceQuery) 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("./testdata/graphite-alert.json") require.Nil(t, err) t.Run("Parsing alert rules from dashboard json", func(t *testing.T) { dashJSON, err := simplejson.NewJson(json) require.Nil(t, err) dash := models.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() } require.Equal(t, getTarget(dashJSON), "") extractor := NewDashAlertExtractor(dash, 1, nil) _, _ = extractor.GetAlerts() require.Equal(t, getTarget(dashJSON), "") }) t.Run("Parsing and validating dashboard containing graphite alerts", func(t *testing.T) { dashJSON, err := simplejson.NewJson(json) require.Nil(t, err) dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) alerts, err := extractor.GetAlerts() require.Nil(t, err) require.Len(t, alerts, 2) for _, v := range alerts { require.EqualValues(t, v.DashboardId, 57) require.NotEmpty(t, v.Name) require.NotEmpty(t, v.Message) settings := simplejson.NewFromAny(v.Settings) require.Equal(t, settings.Get("interval").MustString(""), "") } require.EqualValues(t, alerts[0].Handler, 1) require.EqualValues(t, alerts[1].Handler, 0) require.EqualValues(t, alerts[0].Frequency, 60) require.EqualValues(t, alerts[1].Frequency, 60) require.EqualValues(t, alerts[0].PanelId, 3) require.EqualValues(t, alerts[1].PanelId, 4) require.Equal(t, alerts[0].For, time.Minute*2) require.Equal(t, alerts[1].For, time.Duration(0)) require.Equal(t, alerts[0].Name, "name1") require.Equal(t, alerts[0].Message, "desc1") require.Equal(t, alerts[1].Name, "name2") require.Equal(t, alerts[1].Message, "desc2") condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0]) query := condition.Get("query") require.EqualValues(t, query.Get("datasourceId").MustInt64(), 12) condition = simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0]) model := condition.Get("query").Get("model") require.Equal(t, model.Get("target").MustString(), "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)") }) t.Run("Panels missing id should return error", func(t *testing.T) { panelWithoutID, err := ioutil.ReadFile("./testdata/panels-missing-id.json") require.Nil(t, err) dashJSON, err := simplejson.NewJson(panelWithoutID) require.Nil(t, err) dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) _, err = extractor.GetAlerts() require.NotNil(t, err) }) t.Run("Panels missing id should return error", func(t *testing.T) { panelWithIDZero, err := ioutil.ReadFile("./testdata/panel-with-id-0.json") require.Nil(t, err) dashJSON, err := simplejson.NewJson(panelWithIDZero) require.Nil(t, err) dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) _, err = extractor.GetAlerts() require.NotNil(t, err) }) t.Run("Panel does not have datasource configured, use the default datasource", func(t *testing.T) { panelWithoutSpecifiedDatasource, err := ioutil.ReadFile("./testdata/panel-without-specified-datasource.json") require.Nil(t, err) dashJSON, err := simplejson.NewJson(panelWithoutSpecifiedDatasource) require.Nil(t, err) dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) alerts, err := extractor.GetAlerts() require.Nil(t, err) condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0]) query := condition.Get("query") require.EqualValues(t, query.Get("datasourceId").MustInt64(), 12) }) t.Run("Parse alerts from dashboard without rows", func(t *testing.T) { json, err := ioutil.ReadFile("./testdata/v5-dashboard.json") require.Nil(t, err) dashJSON, err := simplejson.NewJson(json) require.Nil(t, err) dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) alerts, err := extractor.GetAlerts() require.Nil(t, err) require.Len(t, alerts, 2) }) t.Run("Alert notifications are in DB", func(t *testing.T) { sqlstore.InitTestDB(t) firstNotification := models.CreateAlertNotificationCommand{Uid: "notifier1", OrgId: 1, Name: "1"} err = sqlstore.CreateAlertNotificationCommand(&firstNotification) require.Nil(t, err) secondNotification := models.CreateAlertNotificationCommand{Uid: "notifier2", OrgId: 1, Name: "2"} err = sqlstore.CreateAlertNotificationCommand(&secondNotification) require.Nil(t, err) json, err := ioutil.ReadFile("./testdata/influxdb-alert.json") require.Nil(t, err) dashJSON, err := simplejson.NewJson(json) require.Nil(t, err) dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) alerts, err := extractor.GetAlerts() require.Nil(t, err) require.Len(t, alerts, 1) for _, alert := range alerts { require.EqualValues(t, alert.DashboardId, 4) conditions := alert.Settings.Get("conditions").MustArray() cond := simplejson.NewFromAny(conditions[0]) require.Equal(t, cond.Get("query").Get("model").Get("interval").MustString(), ">10s") } }) t.Run("Should be able to extract collapsed panels", func(t *testing.T) { json, err := ioutil.ReadFile("./testdata/collapsed-panels.json") require.Nil(t, err) dashJSON, err := simplejson.NewJson(json) require.Nil(t, err) dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) alerts, err := extractor.GetAlerts() require.Nil(t, err) require.Len(t, alerts, 4) }) t.Run("Parse and validate dashboard without id and containing an alert", func(t *testing.T) { json, err := ioutil.ReadFile("./testdata/dash-without-id.json") require.Nil(t, err) dashJSON, err := simplejson.NewJson(json) require.Nil(t, err) dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) err = extractor.ValidateAlerts() require.Nil(t, err) _, err = extractor.GetAlerts() require.Equal(t, err.Error(), "alert validation error: Panel id is not correct, alertName=Influxdb, panelId=1") }) }