|
@@ -1,8 +1,11 @@
|
|
|
package notifiers
|
|
package notifiers
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
|
+ "bytes"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
- "net/url"
|
|
|
|
|
|
|
+ "io"
|
|
|
|
|
+ "mime/multipart"
|
|
|
|
|
+ "os"
|
|
|
"strconv"
|
|
"strconv"
|
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/bus"
|
|
"github.com/grafana/grafana/pkg/bus"
|
|
@@ -91,6 +94,7 @@ func NewPushoverNotifier(model *m.AlertNotification) (alerting.Notifier, error)
|
|
|
retry, _ := strconv.Atoi(model.Settings.Get("retry").MustString())
|
|
retry, _ := strconv.Atoi(model.Settings.Get("retry").MustString())
|
|
|
expire, _ := strconv.Atoi(model.Settings.Get("expire").MustString())
|
|
expire, _ := strconv.Atoi(model.Settings.Get("expire").MustString())
|
|
|
sound := model.Settings.Get("sound").MustString()
|
|
sound := model.Settings.Get("sound").MustString()
|
|
|
|
|
+ uploadImage := model.Settings.Get("uploadImage").MustBool(true)
|
|
|
|
|
|
|
|
if userKey == "" {
|
|
if userKey == "" {
|
|
|
return nil, alerting.ValidationError{Reason: "User key not given"}
|
|
return nil, alerting.ValidationError{Reason: "User key not given"}
|
|
@@ -107,6 +111,7 @@ func NewPushoverNotifier(model *m.AlertNotification) (alerting.Notifier, error)
|
|
|
Expire: expire,
|
|
Expire: expire,
|
|
|
Device: device,
|
|
Device: device,
|
|
|
Sound: sound,
|
|
Sound: sound,
|
|
|
|
|
+ Upload: uploadImage,
|
|
|
log: log.New("alerting.notifier.pushover"),
|
|
log: log.New("alerting.notifier.pushover"),
|
|
|
}, nil
|
|
}, nil
|
|
|
}
|
|
}
|
|
@@ -120,6 +125,7 @@ type PushoverNotifier struct {
|
|
|
Expire int
|
|
Expire int
|
|
|
Device string
|
|
Device string
|
|
|
Sound string
|
|
Sound string
|
|
|
|
|
+ Upload bool
|
|
|
log log.Logger
|
|
log log.Logger
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -140,38 +146,22 @@ func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
|
|
|
if evalContext.Error != nil {
|
|
if evalContext.Error != nil {
|
|
|
message += fmt.Sprintf("\n<b>Error message:</b> %s", evalContext.Error.Error())
|
|
message += fmt.Sprintf("\n<b>Error message:</b> %s", evalContext.Error.Error())
|
|
|
}
|
|
}
|
|
|
- if evalContext.ImagePublicUrl != "" {
|
|
|
|
|
- message += fmt.Sprintf("\n<a href=\"%s\">Show graph image</a>", evalContext.ImagePublicUrl)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
if message == "" {
|
|
if message == "" {
|
|
|
message = "Notification message missing (Set a notification message to replace this text.)"
|
|
message = "Notification message missing (Set a notification message to replace this text.)"
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- q := url.Values{}
|
|
|
|
|
- q.Add("user", this.UserKey)
|
|
|
|
|
- q.Add("token", this.ApiToken)
|
|
|
|
|
- q.Add("priority", strconv.Itoa(this.Priority))
|
|
|
|
|
- if this.Priority == 2 {
|
|
|
|
|
- q.Add("retry", strconv.Itoa(this.Retry))
|
|
|
|
|
- q.Add("expire", strconv.Itoa(this.Expire))
|
|
|
|
|
- }
|
|
|
|
|
- if this.Device != "" {
|
|
|
|
|
- q.Add("device", this.Device)
|
|
|
|
|
- }
|
|
|
|
|
- if this.Sound != "default" {
|
|
|
|
|
- q.Add("sound", this.Sound)
|
|
|
|
|
|
|
+ headers, uploadBody, err := this.genPushoverBody(evalContext, message, ruleUrl)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ this.log.Error("Failed to generate body for pushover", "error", err)
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
- q.Add("title", evalContext.GetNotificationTitle())
|
|
|
|
|
- q.Add("url", ruleUrl)
|
|
|
|
|
- q.Add("url_title", "Show dashboard with alert")
|
|
|
|
|
- q.Add("message", message)
|
|
|
|
|
- q.Add("html", "1")
|
|
|
|
|
|
|
|
|
|
cmd := &m.SendWebhookSync{
|
|
cmd := &m.SendWebhookSync{
|
|
|
Url: PUSHOVER_ENDPOINT,
|
|
Url: PUSHOVER_ENDPOINT,
|
|
|
HttpMethod: "POST",
|
|
HttpMethod: "POST",
|
|
|
- HttpHeader: map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
|
|
|
|
|
- Body: q.Encode(),
|
|
|
|
|
|
|
+ HttpHeader: headers,
|
|
|
|
|
+ Body: uploadBody.String(),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
|
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
|
@@ -181,3 +171,109 @@ func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
|
|
|
|
|
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+func (this *PushoverNotifier) genPushoverBody(evalContext *alerting.EvalContext, message string, ruleUrl string) (map[string]string, bytes.Buffer, error) {
|
|
|
|
|
+ var b bytes.Buffer
|
|
|
|
|
+ var err error
|
|
|
|
|
+ w := multipart.NewWriter(&b)
|
|
|
|
|
+
|
|
|
|
|
+ // Add image only if requested and available
|
|
|
|
|
+ if this.Upload && evalContext.ImageOnDiskPath != "" {
|
|
|
|
|
+ f, err := os.Open(evalContext.ImageOnDiskPath)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+ defer f.Close()
|
|
|
|
|
+
|
|
|
|
|
+ fw, err := w.CreateFormFile("attachment", evalContext.ImageOnDiskPath)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ _, err = io.Copy(fw, f)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add the user token
|
|
|
|
|
+ err = w.WriteField("user", this.UserKey)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add the api token
|
|
|
|
|
+ err = w.WriteField("token", this.ApiToken)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add priority
|
|
|
|
|
+ err = w.WriteField("priority", strconv.Itoa(this.Priority))
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if this.Priority == 2 {
|
|
|
|
|
+ err = w.WriteField("retry", strconv.Itoa(this.Retry))
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ err = w.WriteField("expire", strconv.Itoa(this.Expire))
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add device
|
|
|
|
|
+ if this.Device != "" {
|
|
|
|
|
+ err = w.WriteField("device", this.Device)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add sound
|
|
|
|
|
+ if this.Sound != "default" {
|
|
|
|
|
+ err = w.WriteField("sound", this.Sound)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add title
|
|
|
|
|
+ err = w.WriteField("title", evalContext.GetNotificationTitle())
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add URL
|
|
|
|
|
+ err = w.WriteField("url", ruleUrl)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+ // Add URL title
|
|
|
|
|
+ err = w.WriteField("url_title", "Show dashboard with alert")
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add message
|
|
|
|
|
+ err = w.WriteField("message", message)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Mark as html message
|
|
|
|
|
+ err = w.WriteField("html", "1")
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, b, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ w.Close()
|
|
|
|
|
+ headers := map[string]string{
|
|
|
|
|
+ "Content-Type": w.FormDataContentType(),
|
|
|
|
|
+ }
|
|
|
|
|
+ return headers, b, nil
|
|
|
|
|
+}
|