diff --git a/pkg/services/alerting/notifier.go b/pkg/services/alerting/notifier.go index 32bece31820..c4c4dc45f50 100644 --- a/pkg/services/alerting/notifier.go +++ b/pkg/services/alerting/notifier.go @@ -65,7 +65,7 @@ func (n *notificationService) sendNotifications(context *EvalContext, notifiers for _, notifier := range notifiers { not := notifier //avoid updating scope variable in go routine - n.log.Info("Sending notification", "type", not.GetType(), "id", not.GetNotifierId(), "isDefault", not.GetIsDefault()) + n.log.Debug("Sending notification", "type", not.GetType(), "id", not.GetNotifierId(), "isDefault", not.GetIsDefault()) metrics.M_Alerting_Notification_Sent.WithLabelValues(not.GetType()).Inc() g.Go(func() error { return not.Notify(context) }) } diff --git a/pkg/services/alerting/notifiers/alertmanager.go b/pkg/services/alerting/notifiers/alertmanager.go new file mode 100644 index 00000000000..08f8e8be29c --- /dev/null +++ b/pkg/services/alerting/notifiers/alertmanager.go @@ -0,0 +1,96 @@ +package notifiers + +import ( + "time" + + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/log" + m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/alerting" +) + +func init() { + alerting.RegisterNotifier(&alerting.NotifierPlugin{ + Type: "prometheus-alertmanager", + Name: "Prometheus Alertmanager", + Description: "Sends alert to Prometheus Alertmanager", + Factory: NewAlertmanagerNotifier, + OptionsTemplate: ` +

Alertmanager settings

+
+ Url + +
+ `, + }) +} + +func NewAlertmanagerNotifier(model *m.AlertNotification) (alerting.Notifier, error) { + url := model.Settings.Get("url").MustString() + if url == "" { + return nil, alerting.ValidationError{Reason: "Could not find url property in settings"} + } + + return &AlertmanagerNotifier{ + NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings), + Url: url, + log: log.New("alerting.notifier.prometheus-alertmanager"), + }, nil +} + +type AlertmanagerNotifier struct { + NotifierBase + Url string + log log.Logger +} + +func (this *AlertmanagerNotifier) ShouldNotify(evalContext *alerting.EvalContext) bool { + return evalContext.Rule.State == m.AlertStateAlerting +} + +func (this *AlertmanagerNotifier) Notify(evalContext *alerting.EvalContext) error { + + alerts := make([]interface{}, 0) + for _, match := range evalContext.EvalMatches { + alertJSON := simplejson.New() + alertJSON.Set("startsAt", evalContext.StartTime.UTC().Format(time.RFC3339)) + + if ruleUrl, err := evalContext.GetRuleUrl(); err == nil { + alertJSON.Set("generatorURL", ruleUrl) + } + + if evalContext.Rule.Message != "" { + alertJSON.SetPath([]string{"annotations", "description"}, evalContext.Rule.Message) + } + + tags := make(map[string]string) + if len(match.Tags) == 0 { + tags["metric"] = match.Metric + } else { + for k, v := range match.Tags { + tags[k] = v + } + } + tags["alertname"] = evalContext.Rule.Name + alertJSON.Set("labels", tags) + + alerts = append(alerts, alertJSON) + } + + bodyJSON := simplejson.NewFromAny(alerts) + body, _ := bodyJSON.MarshalJSON() + + cmd := &m.SendWebhookSync{ + Url: this.Url + "/api/v1/alerts", + HttpMethod: "POST", + Body: string(body), + } + + if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { + this.log.Error("Failed to send alertmanager", "error", err, "alertmanager", this.Name) + return err + } + + return nil +} diff --git a/pkg/services/alerting/notifiers/alertmanager_test.go b/pkg/services/alerting/notifiers/alertmanager_test.go new file mode 100644 index 00000000000..3549b536e48 --- /dev/null +++ b/pkg/services/alerting/notifiers/alertmanager_test.go @@ -0,0 +1,47 @@ +package notifiers + +import ( + "testing" + + "github.com/grafana/grafana/pkg/components/simplejson" + m "github.com/grafana/grafana/pkg/models" + . "github.com/smartystreets/goconvey/convey" +) + +func TestAlertmanagerNotifier(t *testing.T) { + Convey("Alertmanager 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: "alertmanager", + Type: "alertmanager", + Settings: settingsJSON, + } + + _, err := NewAlertmanagerNotifier(model) + So(err, ShouldNotBeNil) + }) + + Convey("from settings", func() { + json := `{ "url": "http://127.0.0.1:9093/" }` + + settingsJSON, _ := simplejson.NewJson([]byte(json)) + model := &m.AlertNotification{ + Name: "alertmanager", + Type: "alertmanager", + Settings: settingsJSON, + } + + not, err := NewAlertmanagerNotifier(model) + alertmanagerNotifier := not.(*AlertmanagerNotifier) + + So(err, ShouldBeNil) + So(alertmanagerNotifier.Url, ShouldEqual, "http://127.0.0.1:9093/") + }) + }) + }) +} diff --git a/pkg/services/alerting/notifiers/base.go b/pkg/services/alerting/notifiers/base.go index 2308a5943d6..601f8fc24b1 100644 --- a/pkg/services/alerting/notifiers/base.go +++ b/pkg/services/alerting/notifiers/base.go @@ -15,7 +15,7 @@ type NotifierBase struct { } func NewNotifierBase(id int64, isDefault bool, name, notifierType string, model *simplejson.Json) NotifierBase { - uploadImage := model.Get("uploadImage").MustBool(true) + uploadImage := model.Get("uploadImage").MustBool(false) return NotifierBase{ Id: id, diff --git a/pkg/services/alerting/notifiers/dingding_test.go b/pkg/services/alerting/notifiers/dingding_test.go index b21815f95af..f89bf6382ce 100644 --- a/pkg/services/alerting/notifiers/dingding_test.go +++ b/pkg/services/alerting/notifiers/dingding_test.go @@ -25,10 +25,8 @@ func TestDingDingNotifier(t *testing.T) { }) Convey("settings should trigger incident", func() { - json := ` - { - "url": "https://www.google.com" - }` + json := `{ "url": "https://www.google.com" }` + settingsJSON, _ := simplejson.NewJson([]byte(json)) model := &m.AlertNotification{ Name: "dingding_testing",