func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error { metrics.M_Alerting_Notification_Sent_PagerDuty.Inc(1) if evalContext.Rule.State == m.AlertStateOK && !this.AutoResolve { this.log.Info("Not sending a trigger to Pagerduty", "state", evalContext.Rule.State, "auto resolve", this.AutoResolve) return nil } eventType := "trigger" if evalContext.Rule.State == m.AlertStateOK { eventType = "resolve" } this.log.Info("Notifying Pagerduty", "event_type", eventType) bodyJSON := simplejson.New() bodyJSON.Set("service_key", this.Key) bodyJSON.Set("description", evalContext.Rule.Name+" - "+evalContext.Rule.Message) bodyJSON.Set("client", "Grafana") bodyJSON.Set("event_type", eventType) bodyJSON.Set("incident_key", "alertId-"+strconv.FormatInt(evalContext.Rule.Id, 10)) ruleUrl, err := evalContext.GetRuleUrl() if err != nil { this.log.Error("Failed get rule link", "error", err) return err } bodyJSON.Set("client_url", ruleUrl) if evalContext.ImagePublicUrl != "" { contexts := make([]interface{}, 1) imageJSON := simplejson.New() imageJSON.Set("type", "image") imageJSON.Set("src", evalContext.ImagePublicUrl) contexts[0] = imageJSON bodyJSON.Set("contexts", contexts) } body, _ := bodyJSON.MarshalJSON() cmd := &m.SendWebhookSync{ Url: pagerdutyEventApiUrl, Body: string(body), HttpMethod: "POST", } if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { this.log.Error("Failed to send notification to Pagerduty", "error", err, "body", string(body)) return err } return nil }
func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error { this.log.Info("Sending alert notification to", "bot_token", this.BotToken) this.log.Info("Sending alert notification to", "chat_id", this.ChatID) metrics.M_Alerting_Notification_Sent_Telegram.Inc(1) bodyJSON := simplejson.New() bodyJSON.Set("chat_id", this.ChatID) bodyJSON.Set("parse_mode", "html") message := fmt.Sprintf("%s\nState: %s\nMessage: %s\n", evalContext.GetNotificationTitle(), evalContext.Rule.Name, evalContext.Rule.Message) ruleUrl, err := evalContext.GetRuleUrl() if err == nil { message = message + fmt.Sprintf("URL: %s\n", ruleUrl) } bodyJSON.Set("text", message) url := fmt.Sprintf(telegeramApiUrl, this.BotToken, "sendMessage") body, _ := bodyJSON.MarshalJSON() cmd := &m.SendWebhookSync{ Url: url, Body: string(body), HttpMethod: "POST", } if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { this.log.Error("Failed to send webhook", "error", err, "webhook", this.Name) return err } return nil }
func (this *WebhookNotifier) Notify(evalContext *alerting.EvalContext) error { this.log.Info("Sending webhook") metrics.M_Alerting_Notification_Sent_Webhook.Inc(1) bodyJSON := simplejson.New() bodyJSON.Set("title", evalContext.GetNotificationTitle()) bodyJSON.Set("ruleId", evalContext.Rule.Id) bodyJSON.Set("ruleName", evalContext.Rule.Name) bodyJSON.Set("state", evalContext.Rule.State) bodyJSON.Set("evalMatches", evalContext.EvalMatches) ruleUrl, err := evalContext.GetRuleUrl() if err == nil { bodyJSON.Set("rule_url", ruleUrl) } if evalContext.ImagePublicUrl != "" { bodyJSON.Set("image_url", evalContext.ImagePublicUrl) } body, _ := bodyJSON.MarshalJSON() cmd := &m.SendWebhookSync{ Url: this.Url, User: this.User, Password: this.Password, Body: string(body), } if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { this.log.Error("Failed to send webhook", "error", err, "webhook", this.Name) } return nil }
func (e *apiClient) createRequest(query string) (*http.Request, error) { u, err := url.Parse(e.Url) if err != nil { return nil, err } u.Path = path.Join(u.Path, "query") payload := simplejson.New() payload.Set("query", query) jsonPayload, err := payload.MarshalJSON() if err != nil { return nil, err } req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(string(jsonPayload))) if err != nil { return nil, err } req.Header.Set("User-Agent", "Grafana") req.Header.Set("Content-Type", "application/json") if e.BasicAuth { req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword) } return req, nil }
func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { executionError := "" annotationData := simplejson.New() evalContext.Rule.State = handler.GetStateFromEvaluation(evalContext) if evalContext.Error != nil { executionError = evalContext.Error.Error() annotationData.Set("errorMessage", executionError) } if evalContext.Firing { annotationData = simplejson.NewFromAny(evalContext.EvalMatches) } countStateResult(evalContext.Rule.State) if evalContext.ShouldUpdateAlertState() { handler.log.Info("New state change", "alertId", evalContext.Rule.Id, "newState", evalContext.Rule.State, "prev state", evalContext.PrevAlertState) cmd := &m.SetAlertStateCommand{ AlertId: evalContext.Rule.Id, OrgId: evalContext.Rule.OrgId, State: evalContext.Rule.State, Error: executionError, EvalData: annotationData, } if err := bus.Dispatch(cmd); err != nil { handler.log.Error("Failed to save state", "error", err) } // save annotation item := annotations.Item{ OrgId: evalContext.Rule.OrgId, DashboardId: evalContext.Rule.DashboardId, PanelId: evalContext.Rule.PanelId, Type: annotations.AlertType, AlertId: evalContext.Rule.Id, Title: evalContext.Rule.Name, Text: evalContext.GetStateModel().Text, NewState: string(evalContext.Rule.State), PrevState: string(evalContext.PrevAlertState), Epoch: time.Now().Unix(), Data: annotationData, } annotationRepo := annotations.GetRepository() if err := annotationRepo.Save(&item); err != nil { handler.log.Error("Failed to save annotation for new alert state", "error", err) } if evalContext.ShouldSendNotification() { handler.notifier.Notify(evalContext) } } return nil }
// NewDashboard creates a new dashboard func NewDashboard(title string) *Dashboard { dash := &Dashboard{} dash.Data = simplejson.New() dash.Data.Set("title", title) dash.Title = title dash.Created = time.Now() dash.Updated = time.Now() dash.UpdateSlug() return dash }
func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error { this.log.Info("Sending sensu result") metrics.M_Alerting_Notification_Sent_Sensu.Inc(1) bodyJSON := simplejson.New() bodyJSON.Set("ruleId", evalContext.Rule.Id) // Sensu alerts cannot have spaces in them bodyJSON.Set("name", strings.Replace(evalContext.Rule.Name, " ", "_", -1)) // Sensu alerts require a command // We set it to the grafana ruleID bodyJSON.Set("source", "grafana_rule_"+strconv.FormatInt(evalContext.Rule.Id, 10)) // Finally, sensu expects an output // We set it to a default output bodyJSON.Set("output", "Grafana Metric Condition Met") bodyJSON.Set("evalMatches", evalContext.EvalMatches) if evalContext.Rule.State == "alerting" { bodyJSON.Set("status", 2) } else if evalContext.Rule.State == "no_data" { bodyJSON.Set("status", 1) } else { bodyJSON.Set("status", 0) } ruleUrl, err := evalContext.GetRuleUrl() if err == nil { bodyJSON.Set("ruleUrl", ruleUrl) } if evalContext.ImagePublicUrl != "" { bodyJSON.Set("imageUrl", evalContext.ImagePublicUrl) } if evalContext.Rule.Message != "" { bodyJSON.Set("message", evalContext.Rule.Message) } body, _ := bodyJSON.MarshalJSON() cmd := &m.SendWebhookSync{ Url: this.Url, User: this.User, Password: this.Password, Body: string(body), HttpMethod: "POST", } if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { this.log.Error("Failed to send sensu event", "error", err, "sensu", this.Name) return err } return nil }
func TestPluginDashboards(t *testing.T) { Convey("When asking plugin dashboard info", t, func() { setting.Cfg = ini.Empty() sec, _ := setting.Cfg.NewSection("plugin.test-app") sec.NewKey("path", "../../tests/test-app") err := Init() So(err, ShouldBeNil) bus.AddHandler("test", func(query *m.GetDashboardQuery) error { if query.Slug == "nginx-connections" { dash := m.NewDashboard("Nginx Connections") dash.Data.Set("revision", "1.1") query.Result = dash return nil } return m.ErrDashboardNotFound }) bus.AddHandler("test", func(query *m.GetDashboardsByPluginIdQuery) error { var data = simplejson.New() data.Set("title", "Nginx Connections") data.Set("revision", 22) query.Result = []*m.Dashboard{ {Slug: "nginx-connections", Data: data}, } return nil }) dashboards, err := GetPluginDashboards(1, "test-app") So(err, ShouldBeNil) Convey("should return 2 dashboarrd", func() { So(len(dashboards), ShouldEqual, 2) }) Convey("should include installed version info", func() { So(dashboards[0].Title, ShouldEqual, "Nginx Connections") So(dashboards[0].Revision, ShouldEqual, 25) So(dashboards[0].ImportedRevision, ShouldEqual, 22) So(dashboards[0].ImportedUri, ShouldEqual, "db/nginx-connections") So(dashboards[1].Revision, ShouldEqual, 2) So(dashboards[1].ImportedRevision, ShouldEqual, 0) }) }) }
func (this *OpsGenieNotifier) createAlert(evalContext *alerting.EvalContext) error { this.log.Info("Creating OpsGenie alert", "ruleId", evalContext.Rule.Id, "notification", this.Name) ruleUrl, err := evalContext.GetRuleUrl() if err != nil { this.log.Error("Failed get rule link", "error", err) return err } bodyJSON := simplejson.New() bodyJSON.Set("apiKey", this.ApiKey) bodyJSON.Set("message", evalContext.Rule.Name) bodyJSON.Set("source", "Grafana") bodyJSON.Set("alias", "alertId-"+strconv.FormatInt(evalContext.Rule.Id, 10)) bodyJSON.Set("description", fmt.Sprintf("%s - %s\n%s", evalContext.Rule.Name, ruleUrl, evalContext.Rule.Message)) details := simplejson.New() details.Set("url", ruleUrl) if evalContext.ImagePublicUrl != "" { details.Set("image", evalContext.ImagePublicUrl) } bodyJSON.Set("details", details) body, _ := bodyJSON.MarshalJSON() cmd := &m.SendWebhookSync{ Url: opsgenieCreateAlertURL, Body: string(body), HttpMethod: "POST", } if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { this.log.Error("Failed to send notification to OpsGenie", "error", err, "body", string(body)) } return nil }
func TestTokenClient(t *testing.T) { SkipConvey("Token client", t, func() { dsInfo := &models.DataSource{ JsonData: simplejson.New(), Url: "", } client := NewTokenClient(dsInfo) body, err := client.RequestTokenData(context.TODO()) So(err, ShouldBeNil) //So(len(body.Functions), ShouldBeGreaterThan, 1) So(len(body.Metrics), ShouldBeGreaterThan, 1) }) }
func (this *OpsGenieNotifier) closeAlert(evalContext *alerting.EvalContext) error { this.log.Info("Closing OpsGenie alert", "ruleId", evalContext.Rule.Id, "notification", this.Name) bodyJSON := simplejson.New() bodyJSON.Set("apiKey", this.ApiKey) bodyJSON.Set("alias", "alertId-"+strconv.FormatInt(evalContext.Rule.Id, 10)) body, _ := bodyJSON.MarshalJSON() cmd := &m.SendWebhookSync{ Url: opsgenieCloseAlertURL, Body: string(body), HttpMethod: "POST", } if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { this.log.Error("Failed to send notification to OpsGenie", "error", err, "body", string(body)) } return nil }
func (this *DashTemplateEvaluator) Eval() (*simplejson.Json, error) { this.result = simplejson.New() this.variables = make(map[string]string) this.varRegex, _ = regexp.Compile(`(\$\{\w+\})`) // check that we have all inputs we need for _, inputDef := range this.template.Get("__inputs").MustArray() { inputDefJson := simplejson.NewFromAny(inputDef) inputName := inputDefJson.Get("name").MustString() inputType := inputDefJson.Get("type").MustString() input := this.findInput(inputName, inputType) if input == nil { return nil, &DashboardInputMissingError{VariableName: inputName} } this.variables["${"+inputName+"}"] = input.Value } return simplejson.NewFromAny(this.evalObject(this.template)), nil }
func TestDashboardModel(t *testing.T) { Convey("When generating slug", t, func() { dashboard := NewDashboard("Grafana Play Home") dashboard.UpdateSlug() So(dashboard.Slug, ShouldEqual, "grafana-play-home") }) Convey("Given a dashboard json", t, func() { json := simplejson.New() json.Set("title", "test dash") Convey("With tags as string value", func() { json.Set("tags", "") dash := NewDashboardFromJson(json) So(len(dash.GetTags()), ShouldEqual, 0) }) }) }
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) }) }) }
func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { oldState := evalContext.Rule.State executionError := "" annotationData := simplejson.New() if evalContext.Error != nil { handler.log.Error("Alert Rule Result Error", "ruleId", evalContext.Rule.Id, "error", evalContext.Error) evalContext.Rule.State = m.AlertStateExecError executionError = evalContext.Error.Error() annotationData.Set("errorMessage", executionError) } else if evalContext.Firing { evalContext.Rule.State = m.AlertStateAlerting annotationData = simplejson.NewFromAny(evalContext.EvalMatches) } else { if evalContext.NoDataFound { if evalContext.Rule.NoDataState != m.NoDataKeepState { evalContext.Rule.State = evalContext.Rule.NoDataState.ToAlertState() } } else { evalContext.Rule.State = m.AlertStateOK } } countStateResult(evalContext.Rule.State) if handler.shouldUpdateAlertState(evalContext, oldState) { handler.log.Info("New state change", "alertId", evalContext.Rule.Id, "newState", evalContext.Rule.State, "oldState", oldState) cmd := &m.SetAlertStateCommand{ AlertId: evalContext.Rule.Id, OrgId: evalContext.Rule.OrgId, State: evalContext.Rule.State, Error: executionError, EvalData: annotationData, } if err := bus.Dispatch(cmd); err != nil { handler.log.Error("Failed to save state", "error", err) } // save annotation item := annotations.Item{ OrgId: evalContext.Rule.OrgId, DashboardId: evalContext.Rule.DashboardId, PanelId: evalContext.Rule.PanelId, Type: annotations.AlertType, AlertId: evalContext.Rule.Id, Title: evalContext.Rule.Name, Text: evalContext.GetStateModel().Text, NewState: string(evalContext.Rule.State), PrevState: string(oldState), Epoch: time.Now().Unix(), Data: annotationData, } annotationRepo := annotations.GetRepository() if err := annotationRepo.Save(&item); err != nil { handler.log.Error("Failed to save annotation for new alert state", "error", err) } if (oldState == m.AlertStatePending) && (evalContext.Rule.State == m.AlertStateOK) { handler.log.Info("Notfication not sent", "oldState", oldState, "newState", evalContext.Rule.State) } else { handler.notifier.Notify(evalContext) } } return nil }
func TestInfluxdbQueryParser(t *testing.T) { Convey("Influxdb query parser", t, func() { parser := &InfluxdbQueryParser{} dsInfo := &tsdb.DataSourceInfo{ JsonData: simplejson.New(), } 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" ] } ] ], "alias": "serie alias", "tags": [ { "key": "datacenter", "operator": "=", "value": "America" }, { "condition": "OR", "key": "hostname", "operator": "=", "value": "server1" } ] } ` dsInfo.JsonData.Set("timeInterval", ">20s") modelJson, err := simplejson.NewJson([]byte(json)) So(err, ShouldBeNil) res, err := parser.Parse(modelJson, dsInfo) So(err, ShouldBeNil) So(len(res.GroupBy), ShouldEqual, 3) So(len(res.Selects), ShouldEqual, 3) So(len(res.Tags), ShouldEqual, 2) So(res.Interval, ShouldEqual, ">20s") So(res.Alias, ShouldEqual, "serie alias") }) Convey("can part raw query json model", func() { json := ` { "dsType": "influxdb", "groupBy": [ { "params": [ "$interval" ], "type": "time" }, { "params": [ "null" ], "type": "fill" } ], "interval": ">10s", "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, dsInfo) 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) So(res.Interval, ShouldEqual, ">10s") }) }) }
func TestDataSourceCache(t *testing.T) { Convey("When caching a datasource proxy", t, func() { clearCache() ds := DataSource{ Id: 1, Url: "http://k8s:8001", Type: "Kubernetes", } t1, err := ds.GetHttpTransport() So(err, ShouldBeNil) t2, err := ds.GetHttpTransport() So(err, ShouldBeNil) Convey("Should be using the cached proxy", func() { So(t2, ShouldEqual, t1) }) }) Convey("When getting kubernetes datasource proxy", t, func() { clearCache() setting.SecretKey = "password" json := simplejson.New() json.Set("tlsAuth", true) json.Set("tlsAuthWithCACert", true) t := time.Now() ds := DataSource{ Url: "http://k8s:8001", Type: "Kubernetes", Updated: t.Add(-2 * time.Minute), } transport, err := ds.GetHttpTransport() So(err, ShouldBeNil) Convey("Should have no cert", func() { So(transport.TLSClientConfig.InsecureSkipVerify, ShouldEqual, true) }) ds.JsonData = json ds.SecureJsonData = map[string][]byte{ "tlsCACert": util.Encrypt([]byte(caCert), "password"), "tlsClientCert": util.Encrypt([]byte(clientCert), "password"), "tlsClientKey": util.Encrypt([]byte(clientKey), "password"), } ds.Updated = t.Add(-1 * time.Minute) transport, err = ds.GetHttpTransport() So(err, ShouldBeNil) Convey("Should add cert", func() { So(transport.TLSClientConfig.InsecureSkipVerify, ShouldEqual, false) So(len(transport.TLSClientConfig.Certificates), ShouldEqual, 1) }) ds.JsonData = nil ds.SecureJsonData = map[string][]byte{} ds.Updated = t transport, err = ds.GetHttpTransport() So(err, ShouldBeNil) Convey("Should remove cert", func() { So(transport.TLSClientConfig.InsecureSkipVerify, ShouldEqual, true) So(len(transport.TLSClientConfig.Certificates), ShouldEqual, 0) }) }) }
func TestAlertNotificationSQLAccess(t *testing.T) { Convey("Testing Alert notification sql access", t, func() { InitTestDB(t) var err error Convey("Alert notifications should be empty", func() { cmd := &m.GetAlertNotificationsQuery{ OrgId: 2, Name: "email", } err := GetAlertNotifications(cmd) fmt.Printf("errror %v", err) So(err, ShouldBeNil) So(cmd.Result, ShouldBeNil) }) Convey("Can save Alert Notification", func() { cmd := &m.CreateAlertNotificationCommand{ Name: "ops", Type: "email", OrgId: 1, Settings: simplejson.New(), } err = CreateAlertNotificationCommand(cmd) So(err, ShouldBeNil) So(cmd.Result.Id, ShouldNotEqual, 0) So(cmd.Result.OrgId, ShouldNotEqual, 0) So(cmd.Result.Type, ShouldEqual, "email") Convey("Cannot save Alert Notification with the same name", func() { err = CreateAlertNotificationCommand(cmd) So(err, ShouldNotBeNil) }) Convey("Can update alert notification", func() { newCmd := &m.UpdateAlertNotificationCommand{ Name: "NewName", Type: "webhook", OrgId: cmd.Result.OrgId, Settings: simplejson.New(), Id: cmd.Result.Id, } err := UpdateAlertNotification(newCmd) So(err, ShouldBeNil) So(newCmd.Result.Name, ShouldEqual, "NewName") }) }) Convey("Can search using an array of ids", func() { cmd1 := m.CreateAlertNotificationCommand{Name: "nagios", Type: "webhook", OrgId: 1, Settings: simplejson.New()} cmd2 := m.CreateAlertNotificationCommand{Name: "slack", Type: "webhook", OrgId: 1, Settings: simplejson.New()} cmd3 := m.CreateAlertNotificationCommand{Name: "ops2", Type: "email", OrgId: 1, Settings: simplejson.New()} cmd4 := m.CreateAlertNotificationCommand{IsDefault: true, Name: "default", Type: "email", OrgId: 1, Settings: simplejson.New()} otherOrg := m.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, Settings: simplejson.New()} So(CreateAlertNotificationCommand(&cmd1), ShouldBeNil) So(CreateAlertNotificationCommand(&cmd2), ShouldBeNil) So(CreateAlertNotificationCommand(&cmd3), ShouldBeNil) So(CreateAlertNotificationCommand(&cmd4), ShouldBeNil) So(CreateAlertNotificationCommand(&otherOrg), ShouldBeNil) Convey("search", func() { query := &m.GetAlertNotificationsToSendQuery{ Ids: []int64{cmd1.Result.Id, cmd2.Result.Id, 112341231}, OrgId: 1, } err := GetAlertNotificationsToSend(query) So(err, ShouldBeNil) So(len(query.Result), ShouldEqual, 3) }) Convey("all", func() { query := &m.GetAllAlertNotificationsQuery{ OrgId: 1, } err := GetAllAlertNotifications(query) So(err, ShouldBeNil) So(len(query.Result), ShouldEqual, 4) }) }) }) }
func TestOpenTsdbExecutor(t *testing.T) { Convey("OpenTsdb query testing", t, func() { exec := &OpenTsdbExecutor{} Convey("Build metric with downsampling enabled", func() { query := &tsdb.Query{ Model: simplejson.New(), } query.Model.Set("metric", "cpu.average.percent") query.Model.Set("aggregator", "avg") query.Model.Set("disableDownsampling", false) query.Model.Set("downsampleInterval", "") query.Model.Set("downsampleAggregator", "avg") query.Model.Set("downsampleFillPolicy", "none") metric := exec.buildMetric(query) So(len(metric), ShouldEqual, 3) So(metric["metric"], ShouldEqual, "cpu.average.percent") So(metric["aggregator"], ShouldEqual, "avg") So(metric["downsample"], ShouldEqual, "1m-avg") }) Convey("Build metric with downsampling diabled", func() { query := &tsdb.Query{ Model: simplejson.New(), } query.Model.Set("metric", "cpu.average.percent") query.Model.Set("aggregator", "avg") query.Model.Set("disableDownsampling", true) query.Model.Set("downsampleInterval", "") query.Model.Set("downsampleAggregator", "avg") query.Model.Set("downsampleFillPolicy", "none") metric := exec.buildMetric(query) So(len(metric), ShouldEqual, 2) So(metric["metric"], ShouldEqual, "cpu.average.percent") So(metric["aggregator"], ShouldEqual, "avg") }) Convey("Build metric with downsampling enabled with params", func() { query := &tsdb.Query{ Model: simplejson.New(), } query.Model.Set("metric", "cpu.average.percent") query.Model.Set("aggregator", "avg") query.Model.Set("disableDownsampling", false) query.Model.Set("downsampleInterval", "5m") query.Model.Set("downsampleAggregator", "sum") query.Model.Set("downsampleFillPolicy", "null") metric := exec.buildMetric(query) So(len(metric), ShouldEqual, 3) So(metric["metric"], ShouldEqual, "cpu.average.percent") So(metric["aggregator"], ShouldEqual, "avg") So(metric["downsample"], ShouldEqual, "5m-sum-null") }) Convey("Build metric with tags with downsampling disabled", func() { query := &tsdb.Query{ Model: simplejson.New(), } query.Model.Set("metric", "cpu.average.percent") query.Model.Set("aggregator", "avg") query.Model.Set("disableDownsampling", true) query.Model.Set("downsampleInterval", "5m") query.Model.Set("downsampleAggregator", "sum") query.Model.Set("downsampleFillPolicy", "null") tags := simplejson.New() tags.Set("env", "prod") tags.Set("app", "grafana") query.Model.Set("tags", tags.MustMap()) metric := exec.buildMetric(query) So(len(metric), ShouldEqual, 3) So(metric["metric"], ShouldEqual, "cpu.average.percent") So(metric["aggregator"], ShouldEqual, "avg") So(metric["downsample"], ShouldEqual, nil) So(len(metric["tags"].(map[string]interface{})), ShouldEqual, 2) So(metric["tags"].(map[string]interface{})["env"], ShouldEqual, "prod") So(metric["tags"].(map[string]interface{})["app"], ShouldEqual, "grafana") So(metric["tags"].(map[string]interface{})["ip"], ShouldEqual, nil) }) Convey("Build metric with rate enabled but counter disabled", func() { query := &tsdb.Query{ Model: simplejson.New(), } query.Model.Set("metric", "cpu.average.percent") query.Model.Set("aggregator", "avg") query.Model.Set("disableDownsampling", true) query.Model.Set("shouldComputeRate", true) query.Model.Set("isCounter", false) tags := simplejson.New() tags.Set("env", "prod") tags.Set("app", "grafana") query.Model.Set("tags", tags.MustMap()) metric := exec.buildMetric(query) So(len(metric), ShouldEqual, 5) So(metric["metric"], ShouldEqual, "cpu.average.percent") So(metric["aggregator"], ShouldEqual, "avg") So(len(metric["tags"].(map[string]interface{})), ShouldEqual, 2) So(metric["tags"].(map[string]interface{})["env"], ShouldEqual, "prod") So(metric["tags"].(map[string]interface{})["app"], ShouldEqual, "grafana") So(metric["tags"].(map[string]interface{})["ip"], ShouldEqual, nil) So(metric["rate"], ShouldEqual, true) So(metric["rateOptions"].(map[string]interface{})["counter"], ShouldEqual, false) }) Convey("Build metric with rate and counter enabled", func() { query := &tsdb.Query{ Model: simplejson.New(), } query.Model.Set("metric", "cpu.average.percent") query.Model.Set("aggregator", "avg") query.Model.Set("disableDownsampling", true) query.Model.Set("shouldComputeRate", true) query.Model.Set("isCounter", true) query.Model.Set("counterMax", 45) query.Model.Set("counterResetValue", 60) tags := simplejson.New() tags.Set("env", "prod") tags.Set("app", "grafana") query.Model.Set("tags", tags.MustMap()) metric := exec.buildMetric(query) So(len(metric), ShouldEqual, 5) So(metric["metric"], ShouldEqual, "cpu.average.percent") So(metric["aggregator"], ShouldEqual, "avg") So(len(metric["tags"].(map[string]interface{})), ShouldEqual, 2) So(metric["tags"].(map[string]interface{})["env"], ShouldEqual, "prod") So(metric["tags"].(map[string]interface{})["app"], ShouldEqual, "grafana") So(metric["tags"].(map[string]interface{})["ip"], ShouldEqual, nil) So(metric["rate"], ShouldEqual, true) So(len(metric["rateOptions"].(map[string]interface{})), ShouldEqual, 3) So(metric["rateOptions"].(map[string]interface{})["counter"], ShouldEqual, true) So(metric["rateOptions"].(map[string]interface{})["counterMax"], ShouldEqual, 45) So(metric["rateOptions"].(map[string]interface{})["resetValue"], ShouldEqual, 60) }) }) }
func TestDataSourceProxy(t *testing.T) { Convey("When getting graphite datasource proxy", t, func() { ds := m.DataSource{Url: "htttp://graphite:8080", Type: m.DS_GRAPHITE} targetUrl, err := url.Parse(ds.Url) proxy := NewReverseProxy(&ds, "/render", targetUrl) proxy.Transport, err = DataProxyTransport(&ds) So(err, ShouldBeNil) transport, ok := proxy.Transport.(*http.Transport) So(ok, ShouldBeTrue) So(transport.TLSClientConfig.InsecureSkipVerify, ShouldBeTrue) requestUrl, _ := url.Parse("http://grafana.com/sub") req := http.Request{URL: requestUrl} proxy.Director(&req) Convey("Can translate request url and path", func() { So(req.URL.Host, ShouldEqual, "graphite:8080") So(req.URL.Path, ShouldEqual, "/render") }) }) Convey("When getting influxdb datasource proxy", t, func() { ds := m.DataSource{ Type: m.DS_INFLUXDB_08, Url: "http://influxdb:8083", Database: "site", User: "******", Password: "******", } targetUrl, _ := url.Parse(ds.Url) proxy := NewReverseProxy(&ds, "", targetUrl) requestUrl, _ := url.Parse("http://grafana.com/sub") req := http.Request{URL: requestUrl} proxy.Director(&req) Convey("Should add db to url", func() { So(req.URL.Path, ShouldEqual, "/db/site/") }) Convey("Should add username and password", func() { queryVals := req.URL.Query() So(queryVals["u"][0], ShouldEqual, "user") So(queryVals["p"][0], ShouldEqual, "password") }) }) Convey("When getting kubernetes datasource proxy", t, func() { setting.SecretKey = "password" json := simplejson.New() json.Set("tlsAuth", true) json.Set("tlsAuthWithCACert", true) ds := m.DataSource{ Url: "htttp://k8s:8001", Type: "Kubernetes", JsonData: json, SecureJsonData: map[string][]byte{ "tlsCACert": util.Encrypt([]byte(caCert), "password"), "tlsClientCert": util.Encrypt([]byte(clientCert), "password"), "tlsClientKey": util.Encrypt([]byte(clientKey), "password"), }, } targetUrl, err := url.Parse(ds.Url) proxy := NewReverseProxy(&ds, "", targetUrl) proxy.Transport, err = DataProxyTransport(&ds) So(err, ShouldBeNil) transport, ok := proxy.Transport.(*http.Transport) Convey("Should add cert", func() { So(ok, ShouldBeTrue) So(transport.TLSClientConfig.InsecureSkipVerify, ShouldEqual, false) So(len(transport.TLSClientConfig.Certificates), ShouldEqual, 1) }) }) }
func TestAlertingDataAccess(t *testing.T) { Convey("Testing Alerting data access", t, func() { InitTestDB(t) testDash := insertTestDashboard("dashboard with alerts", 1, "alert") items := []*m.Alert{ { PanelId: 1, DashboardId: testDash.Id, OrgId: testDash.OrgId, Name: "Alerting title", Message: "Alerting message", Settings: simplejson.New(), Frequency: 1, }, } cmd := m.SaveAlertsCommand{ Alerts: items, DashboardId: testDash.Id, OrgId: 1, UserId: 1, } err := SaveAlerts(&cmd) Convey("Can create one alert", func() { So(err, ShouldBeNil) }) Convey("Can read properties", func() { alertQuery := m.GetAlertsQuery{DashboardId: testDash.Id, PanelId: 1, OrgId: 1} err2 := HandleAlertsQuery(&alertQuery) alert := alertQuery.Result[0] So(err2, ShouldBeNil) So(alert.Name, ShouldEqual, "Alerting title") So(alert.Message, ShouldEqual, "Alerting message") So(alert.State, ShouldEqual, "no_data") So(alert.Frequency, ShouldEqual, 1) }) Convey("Alerts with same dashboard id and panel id should update", func() { modifiedItems := items modifiedItems[0].Name = "Name" modifiedCmd := m.SaveAlertsCommand{ DashboardId: testDash.Id, OrgId: 1, UserId: 1, Alerts: modifiedItems, } err := SaveAlerts(&modifiedCmd) Convey("Can save alerts with same dashboard and panel id", func() { So(err, ShouldBeNil) }) Convey("Alerts should be updated", func() { query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1} err2 := HandleAlertsQuery(&query) So(err2, ShouldBeNil) So(len(query.Result), ShouldEqual, 1) So(query.Result[0].Name, ShouldEqual, "Name") Convey("Alert state should not be updated", func() { So(query.Result[0].State, ShouldEqual, "no_data") }) }) Convey("Updates without changes should be ignored", func() { err3 := SaveAlerts(&modifiedCmd) So(err3, ShouldBeNil) }) }) Convey("Multiple alerts per dashboard", func() { multipleItems := []*m.Alert{ { DashboardId: testDash.Id, PanelId: 1, Name: "1", OrgId: 1, Settings: simplejson.New(), }, { DashboardId: testDash.Id, PanelId: 2, Name: "2", OrgId: 1, Settings: simplejson.New(), }, { DashboardId: testDash.Id, PanelId: 3, Name: "3", OrgId: 1, Settings: simplejson.New(), }, } cmd.Alerts = multipleItems err = SaveAlerts(&cmd) Convey("Should save 3 dashboards", func() { So(err, ShouldBeNil) queryForDashboard := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1} err2 := HandleAlertsQuery(&queryForDashboard) So(err2, ShouldBeNil) So(len(queryForDashboard.Result), ShouldEqual, 3) }) Convey("should updated two dashboards and delete one", func() { missingOneAlert := multipleItems[:2] cmd.Alerts = missingOneAlert err = SaveAlerts(&cmd) Convey("should delete the missing alert", func() { query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1} err2 := HandleAlertsQuery(&query) So(err2, ShouldBeNil) So(len(query.Result), ShouldEqual, 2) }) }) }) Convey("When dashboard is removed", func() { items := []*m.Alert{ { PanelId: 1, DashboardId: testDash.Id, Name: "Alerting title", Message: "Alerting message", }, } cmd := m.SaveAlertsCommand{ Alerts: items, DashboardId: testDash.Id, OrgId: 1, UserId: 1, } SaveAlerts(&cmd) err = DeleteDashboard(&m.DeleteDashboardCommand{ OrgId: 1, Slug: testDash.Slug, }) So(err, ShouldBeNil) Convey("Alerts should be removed", func() { query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1} err2 := HandleAlertsQuery(&query) So(testDash.Id, ShouldEqual, 1) So(err2, ShouldBeNil) So(len(query.Result), ShouldEqual, 0) }) }) }) }