alerting: golint fixes for alert notifiers. (#17167)
This commit is contained in:
@@ -13,10 +13,11 @@ const (
|
||||
triggMetrString = "Triggered metrics:\n\n"
|
||||
)
|
||||
|
||||
// NotifierBase is the base implementation of a notifier.
|
||||
type NotifierBase struct {
|
||||
Name string
|
||||
Type string
|
||||
Uid string
|
||||
UID string
|
||||
IsDeault bool
|
||||
UploadImage bool
|
||||
SendReminder bool
|
||||
@@ -26,6 +27,7 @@ type NotifierBase struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// NewNotifierBase returns a new `NotifierBase`.
|
||||
func NewNotifierBase(model *models.AlertNotification) NotifierBase {
|
||||
uploadImage := true
|
||||
value, exist := model.Settings.CheckGet("uploadImage")
|
||||
@@ -34,7 +36,7 @@ func NewNotifierBase(model *models.AlertNotification) NotifierBase {
|
||||
}
|
||||
|
||||
return NotifierBase{
|
||||
Uid: model.Uid,
|
||||
UID: model.Uid,
|
||||
Name: model.Name,
|
||||
IsDeault: model.IsDefault,
|
||||
Type: model.Type,
|
||||
@@ -108,30 +110,40 @@ func (n *NotifierBase) ShouldNotify(ctx context.Context, context *alerting.EvalC
|
||||
return true
|
||||
}
|
||||
|
||||
// GetType returns the notifier type.
|
||||
func (n *NotifierBase) GetType() string {
|
||||
return n.Type
|
||||
}
|
||||
|
||||
// NeedsImage returns true if an image is expected in the notification.
|
||||
func (n *NotifierBase) NeedsImage() bool {
|
||||
return n.UploadImage
|
||||
}
|
||||
|
||||
// GetNotifierUid returns the notifier `uid`.
|
||||
func (n *NotifierBase) GetNotifierUid() string {
|
||||
return n.Uid
|
||||
return n.UID
|
||||
}
|
||||
|
||||
// GetIsDefault returns true if the notifiers should
|
||||
// be used for all alerts.
|
||||
func (n *NotifierBase) GetIsDefault() bool {
|
||||
return n.IsDeault
|
||||
}
|
||||
|
||||
// GetSendReminder returns true if reminders should be sent.
|
||||
func (n *NotifierBase) GetSendReminder() bool {
|
||||
return n.SendReminder
|
||||
}
|
||||
|
||||
// GetDisableResolveMessage returns true if ok alert notifications
|
||||
// should be skipped.
|
||||
func (n *NotifierBase) GetDisableResolveMessage() bool {
|
||||
return n.DisableResolveMessage
|
||||
}
|
||||
|
||||
// GetFrequency returns the freqency for how often
|
||||
// alerts should be evaluated.
|
||||
func (n *NotifierBase) GetFrequency() time.Duration {
|
||||
return n.Frequency
|
||||
}
|
||||
|
||||
@@ -178,24 +178,24 @@ func TestShouldSendAlertNotification(t *testing.T) {
|
||||
|
||||
func TestBaseNotifier(t *testing.T) {
|
||||
Convey("default constructor for notifiers", t, func() {
|
||||
bJson := simplejson.New()
|
||||
bJSON := simplejson.New()
|
||||
|
||||
model := &models.AlertNotification{
|
||||
Uid: "1",
|
||||
Name: "name",
|
||||
Type: "email",
|
||||
Settings: bJson,
|
||||
Settings: bJSON,
|
||||
}
|
||||
|
||||
Convey("can parse false value", func() {
|
||||
bJson.Set("uploadImage", false)
|
||||
bJSON.Set("uploadImage", false)
|
||||
|
||||
base := NewNotifierBase(model)
|
||||
So(base.UploadImage, ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("can parse true value", func() {
|
||||
bJson.Set("uploadImage", true)
|
||||
bJSON.Set("uploadImage", true)
|
||||
|
||||
base := NewNotifierBase(model)
|
||||
So(base.UploadImage, ShouldBeTrue)
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
)
|
||||
|
||||
const DefaultDingdingMsgType = "link"
|
||||
const DingdingOptionsTemplate = `
|
||||
const defaultDingdingMsgType = "link"
|
||||
const dingdingOptionsTemplate = `
|
||||
<h3 class="page-heading">DingDing settings</h3>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">Url</span>
|
||||
@@ -21,7 +21,7 @@ const DingdingOptionsTemplate = `
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">MessageType</span>
|
||||
<select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.msgType" ng-options="s for s in ['link','actionCard']" ng-init="ctrl.model.settings.msgType=ctrl.model.settings.msgType || '` + DefaultDingdingMsgType + `'"></select>
|
||||
<select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.msgType" ng-options="s for s in ['link','actionCard']" ng-init="ctrl.model.settings.msgType=ctrl.model.settings.msgType || '` + defaultDingdingMsgType + `'"></select>
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -30,57 +30,59 @@ func init() {
|
||||
Type: "dingding",
|
||||
Name: "DingDing",
|
||||
Description: "Sends HTTP POST request to DingDing",
|
||||
Factory: NewDingDingNotifier,
|
||||
OptionsTemplate: DingdingOptionsTemplate,
|
||||
Factory: newDingDingNotifier,
|
||||
OptionsTemplate: dingdingOptionsTemplate,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func NewDingDingNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
func newDingDingNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
|
||||
}
|
||||
|
||||
msgType := model.Settings.Get("msgType").MustString(DefaultDingdingMsgType)
|
||||
msgType := model.Settings.Get("msgType").MustString(defaultDingdingMsgType)
|
||||
|
||||
return &DingDingNotifier{
|
||||
NotifierBase: NewNotifierBase(model),
|
||||
MsgType: msgType,
|
||||
Url: url,
|
||||
URL: url,
|
||||
log: log.New("alerting.notifier.dingding"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DingDingNotifier is responsible for sending alert notifications to ding ding.
|
||||
type DingDingNotifier struct {
|
||||
NotifierBase
|
||||
MsgType string
|
||||
Url string
|
||||
URL string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Sending dingding")
|
||||
// Notify sends the alert notification to dingding.
|
||||
func (dd *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
dd.log.Info("Sending dingding")
|
||||
|
||||
messageUrl, err := evalContext.GetRuleUrl()
|
||||
messageURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed to get messageUrl", "error", err, "dingding", this.Name)
|
||||
messageUrl = ""
|
||||
dd.log.Error("Failed to get messageUrl", "error", err, "dingding", dd.Name)
|
||||
messageURL = ""
|
||||
}
|
||||
|
||||
q := url.Values{
|
||||
"pc_slide": {"false"},
|
||||
"url": {messageUrl},
|
||||
"url": {messageURL},
|
||||
}
|
||||
|
||||
// Use special link to auto open the message url outside of Dingding
|
||||
// Refer: https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=104972&docType=1#s9
|
||||
messageUrl = "dingtalk://dingtalkclient/page/link?" + q.Encode()
|
||||
messageURL = "dingtalk://dingtalkclient/page/link?" + q.Encode()
|
||||
|
||||
this.log.Info("messageUrl:" + messageUrl)
|
||||
dd.log.Info("messageUrl:" + messageURL)
|
||||
|
||||
message := evalContext.Rule.Message
|
||||
picUrl := evalContext.ImagePublicUrl
|
||||
picURL := evalContext.ImagePublicUrl
|
||||
title := evalContext.GetNotificationTitle()
|
||||
if message == "" {
|
||||
message = title
|
||||
@@ -91,10 +93,10 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
}
|
||||
|
||||
var bodyStr string
|
||||
if this.MsgType == "actionCard" {
|
||||
if dd.MsgType == "actionCard" {
|
||||
// Embed the pic into the markdown directly because actionCard doesn't have a picUrl field
|
||||
if picUrl != "" {
|
||||
message = "\\n\\n" + message
|
||||
if picURL != "" {
|
||||
message = "\\n\\n" + message
|
||||
}
|
||||
|
||||
bodyStr = `{
|
||||
@@ -103,7 +105,7 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
"text": "` + strings.Replace(message, `"`, "'", -1) + `",
|
||||
"title": "` + strings.Replace(title, `"`, "'", -1) + `",
|
||||
"singleTitle": "More",
|
||||
"singleURL": "` + messageUrl + `"
|
||||
"singleURL": "` + messageURL + `"
|
||||
}
|
||||
}`
|
||||
} else {
|
||||
@@ -112,8 +114,8 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
"link": {
|
||||
"text": "` + message + `",
|
||||
"title": "` + title + `",
|
||||
"picUrl": "` + picUrl + `",
|
||||
"messageUrl": "` + messageUrl + `"
|
||||
"picUrl": "` + picURL + `",
|
||||
"messageUrl": "` + messageURL + `"
|
||||
}
|
||||
}`
|
||||
}
|
||||
@@ -121,7 +123,7 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
bodyJSON, err := simplejson.NewJson([]byte(bodyStr))
|
||||
|
||||
if err != nil {
|
||||
this.log.Error("Failed to create Json data", "error", err, "dingding", this.Name)
|
||||
dd.log.Error("Failed to create Json data", "error", err, "dingding", dd.Name)
|
||||
}
|
||||
|
||||
body, err := bodyJSON.MarshalJSON()
|
||||
@@ -130,12 +132,12 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
}
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: this.Url,
|
||||
Url: dd.URL,
|
||||
Body: string(body),
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send DingDing", "error", err, "dingding", this.Name)
|
||||
dd.log.Error("Failed to send DingDing", "error", err, "dingding", dd.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestDingDingNotifier(t *testing.T) {
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
_, err := NewDingDingNotifier(model)
|
||||
_, err := newDingDingNotifier(model)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
})
|
||||
@@ -34,14 +34,13 @@ func TestDingDingNotifier(t *testing.T) {
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
not, err := NewDingDingNotifier(model)
|
||||
not, err := newDingDingNotifier(model)
|
||||
notifier := not.(*DingDingNotifier)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(notifier.Name, ShouldEqual, "dingding_testing")
|
||||
So(notifier.Type, ShouldEqual, "dingding")
|
||||
So(notifier.Url, ShouldEqual, "https://www.google.com")
|
||||
So(notifier.URL, ShouldEqual, "https://www.google.com")
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -30,12 +30,16 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// EmailNotifier is responsible for sending
|
||||
// alert notifications over email.
|
||||
type EmailNotifier struct {
|
||||
NotifierBase
|
||||
Addresses []string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// NewEmailNotifier is the constructor function
|
||||
// for the EmailNotifier.
|
||||
func NewEmailNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
addressesString := model.Settings.Get("addresses").MustString()
|
||||
|
||||
@@ -59,12 +63,13 @@ func NewEmailNotifier(model *models.AlertNotification) (alerting.Notifier, error
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *EmailNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Sending alert notification to", "addresses", this.Addresses)
|
||||
// Notify sends the alert notification.
|
||||
func (en *EmailNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
en.log.Info("Sending alert notification to", "addresses", en.Addresses)
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
en.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -83,13 +88,13 @@ func (this *EmailNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
"StateModel": evalContext.GetStateModel(),
|
||||
"Message": evalContext.Rule.Message,
|
||||
"Error": error,
|
||||
"RuleUrl": ruleUrl,
|
||||
"RuleUrl": ruleURL,
|
||||
"ImageLink": "",
|
||||
"EmbeddedImage": "",
|
||||
"AlertPageUrl": setting.AppUrl + "alerting",
|
||||
"EvalMatches": evalContext.EvalMatches,
|
||||
},
|
||||
To: this.Addresses,
|
||||
To: en.Addresses,
|
||||
Template: "alert_notification.html",
|
||||
EmbededFiles: []string{},
|
||||
},
|
||||
@@ -108,9 +113,9 @@ func (this *EmailNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
err = bus.DispatchCtx(evalContext.Ctx, cmd)
|
||||
|
||||
if err != nil {
|
||||
this.log.Error("Failed to send alert notification email", "error", err)
|
||||
en.log.Error("Failed to send alert notification email", "error", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func init() {
|
||||
Name: "Google Hangouts Chat",
|
||||
Description: "Sends notifications to Google Hangouts Chat via webhooks based on the official JSON message " +
|
||||
"format (https://developers.google.com/hangouts/chat/reference/message-formats/).",
|
||||
Factory: NewGoogleChatNotifier,
|
||||
Factory: newGoogleChatNotifier,
|
||||
OptionsTemplate: `
|
||||
<h3 class="page-heading">Google Hangouts Chat settings</h3>
|
||||
<div class="gf-form max-width-30">
|
||||
@@ -29,7 +29,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
func NewGoogleChatNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
func newGoogleChatNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
|
||||
@@ -37,14 +37,16 @@ func NewGoogleChatNotifier(model *models.AlertNotification) (alerting.Notifier,
|
||||
|
||||
return &GoogleChatNotifier{
|
||||
NotifierBase: NewNotifierBase(model),
|
||||
Url: url,
|
||||
URL: url,
|
||||
log: log.New("alerting.notifier.googlechat"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GoogleChatNotifier is responsible for sending
|
||||
// alert notifications to Google chat.
|
||||
type GoogleChatNotifier struct {
|
||||
NotifierBase
|
||||
Url string
|
||||
URL string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
@@ -90,7 +92,7 @@ type imageWidget struct {
|
||||
}
|
||||
|
||||
type image struct {
|
||||
ImageUrl string `json:"imageUrl"`
|
||||
ImageURL string `json:"imageUrl"`
|
||||
}
|
||||
|
||||
type button struct {
|
||||
@@ -107,19 +109,20 @@ type onClick struct {
|
||||
}
|
||||
|
||||
type openLink struct {
|
||||
Url string `json:"url"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
func (this *GoogleChatNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Executing Google Chat notification")
|
||||
// Notify send an alert notification to Google Chat.
|
||||
func (gcn *GoogleChatNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
gcn.log.Info("Executing Google Chat notification")
|
||||
|
||||
headers := map[string]string{
|
||||
"Content-Type": "application/json; charset=UTF-8",
|
||||
}
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("evalContext returned an invalid rule URL")
|
||||
gcn.log.Error("evalContext returned an invalid rule URL")
|
||||
}
|
||||
|
||||
// add a text paragraph widget for the message
|
||||
@@ -152,11 +155,11 @@ func (this *GoogleChatNotifier) Notify(evalContext *alerting.EvalContext) error
|
||||
if evalContext.ImagePublicUrl != "" {
|
||||
widgets = append(widgets, imageWidget{
|
||||
Image: image{
|
||||
ImageUrl: evalContext.ImagePublicUrl,
|
||||
ImageURL: evalContext.ImagePublicUrl,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
this.log.Info("Could not retrieve a public image URL.")
|
||||
gcn.log.Info("Could not retrieve a public image URL.")
|
||||
}
|
||||
|
||||
// add a button widget (link to Grafana)
|
||||
@@ -167,7 +170,7 @@ func (this *GoogleChatNotifier) Notify(evalContext *alerting.EvalContext) error
|
||||
Text: "OPEN IN GRAFANA",
|
||||
OnClick: onClick{
|
||||
OpenLink: openLink{
|
||||
Url: ruleUrl,
|
||||
URL: ruleURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -200,14 +203,14 @@ func (this *GoogleChatNotifier) Notify(evalContext *alerting.EvalContext) error
|
||||
body, _ := json.Marshal(res1D)
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: this.Url,
|
||||
Url: gcn.URL,
|
||||
HttpMethod: "POST",
|
||||
HttpHeader: headers,
|
||||
Body: string(body),
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send Google Hangouts Chat alert", "error", err, "webhook", this.Name)
|
||||
gcn.log.Error("Failed to send Google Hangouts Chat alert", "error", err, "webhook", gcn.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestGoogleChatNotifier(t *testing.T) {
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
_, err := NewGoogleChatNotifier(model)
|
||||
_, err := newGoogleChatNotifier(model)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
@@ -39,15 +39,14 @@ func TestGoogleChatNotifier(t *testing.T) {
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
not, err := NewGoogleChatNotifier(model)
|
||||
not, err := newGoogleChatNotifier(model)
|
||||
webhookNotifier := not.(*GoogleChatNotifier)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(webhookNotifier.Name, ShouldEqual, "ops")
|
||||
So(webhookNotifier.Type, ShouldEqual, "googlechat")
|
||||
So(webhookNotifier.Url, ShouldEqual, "http://google.com")
|
||||
So(webhookNotifier.URL, ShouldEqual, "http://google.com")
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ const (
|
||||
maxFieldCount int = 4
|
||||
)
|
||||
|
||||
// NewHipChatNotifier is the constructor functions
|
||||
// for the HipChatNotifier
|
||||
func NewHipChatNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if strings.HasSuffix(url, "/") {
|
||||
@@ -56,31 +58,34 @@ func NewHipChatNotifier(model *models.AlertNotification) (alerting.Notifier, err
|
||||
}
|
||||
|
||||
apikey := model.Settings.Get("apikey").MustString()
|
||||
roomId := model.Settings.Get("roomid").MustString()
|
||||
roomID := model.Settings.Get("roomid").MustString()
|
||||
|
||||
return &HipChatNotifier{
|
||||
NotifierBase: NewNotifierBase(model),
|
||||
Url: url,
|
||||
ApiKey: apikey,
|
||||
RoomId: roomId,
|
||||
URL: url,
|
||||
APIKey: apikey,
|
||||
RoomID: roomID,
|
||||
log: log.New("alerting.notifier.hipchat"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HipChatNotifier is responsible for sending
|
||||
// alert notifications to Hipchat.
|
||||
type HipChatNotifier struct {
|
||||
NotifierBase
|
||||
Url string
|
||||
ApiKey string
|
||||
RoomId string
|
||||
URL string
|
||||
APIKey string
|
||||
RoomID string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Executing hipchat notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||
// Notify sends an alert notification to HipChat
|
||||
func (hc *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
hc.log.Info("Executing hipchat notification", "ruleId", evalContext.Rule.Id, "notification", hc.Name)
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
hc.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -133,7 +138,7 @@ func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
// Add a card with link to the dashboard
|
||||
card := map[string]interface{}{
|
||||
"style": "application",
|
||||
"url": ruleUrl,
|
||||
"url": ruleURL,
|
||||
"id": "1",
|
||||
"title": evalContext.GetNotificationTitle(),
|
||||
"description": message,
|
||||
@@ -160,13 +165,13 @@ func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
"card": card,
|
||||
}
|
||||
|
||||
hipUrl := fmt.Sprintf("%s/v2/room/%s/notification?auth_token=%s", this.Url, this.RoomId, this.ApiKey)
|
||||
hipURL := fmt.Sprintf("%s/v2/room/%s/notification?auth_token=%s", hc.URL, hc.RoomID, hc.APIKey)
|
||||
data, _ := json.Marshal(&body)
|
||||
this.log.Info("Request payload", "json", string(data))
|
||||
cmd := &models.SendWebhookSync{Url: hipUrl, Body: string(data)}
|
||||
hc.log.Info("Request payload", "json", string(data))
|
||||
cmd := &models.SendWebhookSync{Url: hipURL, Body: string(data)}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send hipchat notification", "error", err, "webhook", this.Name)
|
||||
hc.log.Error("Failed to send hipchat notification", "error", err, "webhook", hc.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@ func TestHipChatNotifier(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(hipchatNotifier.Name, ShouldEqual, "ops")
|
||||
So(hipchatNotifier.Type, ShouldEqual, "hipchat")
|
||||
So(hipchatNotifier.Url, ShouldEqual, "http://google.com")
|
||||
So(hipchatNotifier.ApiKey, ShouldEqual, "")
|
||||
So(hipchatNotifier.RoomId, ShouldEqual, "")
|
||||
So(hipchatNotifier.URL, ShouldEqual, "http://google.com")
|
||||
So(hipchatNotifier.APIKey, ShouldEqual, "")
|
||||
So(hipchatNotifier.RoomID, ShouldEqual, "")
|
||||
})
|
||||
|
||||
Convey("from settings with Recipient and Mention", func() {
|
||||
@@ -71,11 +71,10 @@ func TestHipChatNotifier(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(hipchatNotifier.Name, ShouldEqual, "ops")
|
||||
So(hipchatNotifier.Type, ShouldEqual, "hipchat")
|
||||
So(hipchatNotifier.Url, ShouldEqual, "http://www.hipchat.com")
|
||||
So(hipchatNotifier.ApiKey, ShouldEqual, "1234")
|
||||
So(hipchatNotifier.RoomId, ShouldEqual, "1234")
|
||||
So(hipchatNotifier.URL, ShouldEqual, "http://www.hipchat.com")
|
||||
So(hipchatNotifier.APIKey, ShouldEqual, "1234")
|
||||
So(hipchatNotifier.RoomID, ShouldEqual, "1234")
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// NewKafkaNotifier is the constructor function for the Kafka notifier.
|
||||
func NewKafkaNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
endpoint := model.Settings.Get("kafkaRestProxy").MustString()
|
||||
if endpoint == "" {
|
||||
@@ -50,6 +51,8 @@ func NewKafkaNotifier(model *models.AlertNotification) (alerting.Notifier, error
|
||||
}, nil
|
||||
}
|
||||
|
||||
// KafkaNotifier is responsible for sending
|
||||
// alert notifications to Kafka.
|
||||
type KafkaNotifier struct {
|
||||
NotifierBase
|
||||
Endpoint string
|
||||
@@ -57,8 +60,8 @@ type KafkaNotifier struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
|
||||
// Notify sends the alert notification.
|
||||
func (kn *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
state := evalContext.Rule.State
|
||||
|
||||
customData := triggMetrString
|
||||
@@ -66,7 +69,7 @@ func (this *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
customData = customData + fmt.Sprintf("%s: %v\n", evt.Metric, evt.Value)
|
||||
}
|
||||
|
||||
this.log.Info("Notifying Kafka", "alert_state", state)
|
||||
kn.log.Info("Notifying Kafka", "alert_state", state)
|
||||
|
||||
recordJSON := simplejson.New()
|
||||
records := make([]interface{}, 1)
|
||||
@@ -77,12 +80,12 @@ func (this *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
bodyJSON.Set("details", customData)
|
||||
bodyJSON.Set("incident_key", "alertId-"+strconv.FormatInt(evalContext.Rule.Id, 10))
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
kn.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
bodyJSON.Set("client_url", ruleUrl)
|
||||
bodyJSON.Set("client_url", ruleURL)
|
||||
|
||||
if evalContext.ImagePublicUrl != "" {
|
||||
contexts := make([]interface{}, 1)
|
||||
@@ -99,10 +102,10 @@ func (this *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
recordJSON.Set("records", records)
|
||||
body, _ := recordJSON.MarshalJSON()
|
||||
|
||||
topicUrl := this.Endpoint + "/topics/" + this.Topic
|
||||
topicURL := kn.Endpoint + "/topics/" + kn.Topic
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: topicUrl,
|
||||
Url: topicURL,
|
||||
Body: string(body),
|
||||
HttpMethod: "POST",
|
||||
HttpHeader: map[string]string{
|
||||
@@ -112,7 +115,7 @@ func (this *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send notification to Kafka", "error", err, "body", string(body))
|
||||
kn.log.Error("Failed to send notification to Kafka", "error", err, "body", string(body))
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ func TestKafkaNotifier(t *testing.T) {
|
||||
So(kafkaNotifier.Endpoint, ShouldEqual, "http://localhost:8082")
|
||||
So(kafkaNotifier.Topic, ShouldEqual, "topic1")
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -29,9 +29,10 @@ func init() {
|
||||
}
|
||||
|
||||
const (
|
||||
lineNotifyUrl string = "https://notify-api.line.me/api/notify"
|
||||
lineNotifyURL string = "https://notify-api.line.me/api/notify"
|
||||
)
|
||||
|
||||
// NewLINENotifier is the constructor for the LINE notifier
|
||||
func NewLINENotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
token := model.Settings.Get("token").MustString()
|
||||
if token == "" {
|
||||
@@ -45,33 +46,36 @@ func NewLINENotifier(model *models.AlertNotification) (alerting.Notifier, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LineNotifier is responsible for sending
|
||||
// alert notifications to LINE.
|
||||
type LineNotifier struct {
|
||||
NotifierBase
|
||||
Token string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *LineNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Executing line notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||
// Notify send an alert notification to LINE
|
||||
func (ln *LineNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
ln.log.Info("Executing line notification", "ruleId", evalContext.Rule.Id, "notification", ln.Name)
|
||||
|
||||
var err error
|
||||
switch evalContext.Rule.State {
|
||||
case models.AlertStateAlerting:
|
||||
err = this.createAlert(evalContext)
|
||||
err = ln.createAlert(evalContext)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *LineNotifier) createAlert(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Creating Line notify", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
func (ln *LineNotifier) createAlert(evalContext *alerting.EvalContext) error {
|
||||
ln.log.Info("Creating Line notify", "ruleId", evalContext.Rule.Id, "notification", ln.Name)
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
ln.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
form := url.Values{}
|
||||
body := fmt.Sprintf("%s - %s\n%s", evalContext.Rule.Name, ruleUrl, evalContext.Rule.Message)
|
||||
body := fmt.Sprintf("%s - %s\n%s", evalContext.Rule.Name, ruleURL, evalContext.Rule.Message)
|
||||
form.Add("message", body)
|
||||
|
||||
if evalContext.ImagePublicUrl != "" {
|
||||
@@ -80,17 +84,17 @@ func (this *LineNotifier) createAlert(evalContext *alerting.EvalContext) error {
|
||||
}
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: lineNotifyUrl,
|
||||
Url: lineNotifyURL,
|
||||
HttpMethod: "POST",
|
||||
HttpHeader: map[string]string{
|
||||
"Authorization": fmt.Sprintf("Bearer %s", this.Token),
|
||||
"Authorization": fmt.Sprintf("Bearer %s", ln.Token),
|
||||
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
||||
},
|
||||
Body: form.Encode(),
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send notification to LINE", "error", err, "body", body)
|
||||
ln.log.Error("Failed to send notification to LINE", "error", err, "body", body)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,5 @@ func TestLineNotifier(t *testing.T) {
|
||||
So(lineNotifier.Type, ShouldEqual, "line")
|
||||
So(lineNotifier.Token, ShouldEqual, "abcdefgh0123456789")
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,54 +44,57 @@ var (
|
||||
opsgenieAlertURL = "https://api.opsgenie.com/v2/alerts"
|
||||
)
|
||||
|
||||
// NewOpsGenieNotifier is the constructor for OpsGenie.
|
||||
func NewOpsGenieNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
autoClose := model.Settings.Get("autoClose").MustBool(true)
|
||||
apiKey := model.Settings.Get("apiKey").MustString()
|
||||
apiUrl := model.Settings.Get("apiUrl").MustString()
|
||||
apiURL := model.Settings.Get("apiUrl").MustString()
|
||||
if apiKey == "" {
|
||||
return nil, alerting.ValidationError{Reason: "Could not find api key property in settings"}
|
||||
}
|
||||
if apiUrl == "" {
|
||||
apiUrl = opsgenieAlertURL
|
||||
if apiURL == "" {
|
||||
apiURL = opsgenieAlertURL
|
||||
}
|
||||
|
||||
return &OpsGenieNotifier{
|
||||
NotifierBase: NewNotifierBase(model),
|
||||
ApiKey: apiKey,
|
||||
ApiUrl: apiUrl,
|
||||
APIKey: apiKey,
|
||||
APIUrl: apiURL,
|
||||
AutoClose: autoClose,
|
||||
log: log.New("alerting.notifier.opsgenie"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// OpsGenieNotifier is responsible for sending
|
||||
// alert notifications to OpsGenie
|
||||
type OpsGenieNotifier struct {
|
||||
NotifierBase
|
||||
ApiKey string
|
||||
ApiUrl string
|
||||
APIKey string
|
||||
APIUrl string
|
||||
AutoClose bool
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *OpsGenieNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
|
||||
// Notify sends an alert notification to OpsGenie.
|
||||
func (on *OpsGenieNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
var err error
|
||||
switch evalContext.Rule.State {
|
||||
case models.AlertStateOK:
|
||||
if this.AutoClose {
|
||||
err = this.closeAlert(evalContext)
|
||||
if on.AutoClose {
|
||||
err = on.closeAlert(evalContext)
|
||||
}
|
||||
case models.AlertStateAlerting:
|
||||
err = this.createAlert(evalContext)
|
||||
err = on.createAlert(evalContext)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *OpsGenieNotifier) createAlert(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Creating OpsGenie alert", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||
func (on *OpsGenieNotifier) createAlert(evalContext *alerting.EvalContext) error {
|
||||
on.log.Info("Creating OpsGenie alert", "ruleId", evalContext.Rule.Id, "notification", on.Name)
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
on.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -104,10 +107,10 @@ func (this *OpsGenieNotifier) createAlert(evalContext *alerting.EvalContext) err
|
||||
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\n%s", evalContext.Rule.Name, ruleUrl, evalContext.Rule.Message, customData))
|
||||
bodyJSON.Set("description", fmt.Sprintf("%s - %s\n%s\n%s", evalContext.Rule.Name, ruleURL, evalContext.Rule.Message, customData))
|
||||
|
||||
details := simplejson.New()
|
||||
details.Set("url", ruleUrl)
|
||||
details.Set("url", ruleURL)
|
||||
if evalContext.ImagePublicUrl != "" {
|
||||
details.Set("image", evalContext.ImagePublicUrl)
|
||||
}
|
||||
@@ -116,41 +119,41 @@ func (this *OpsGenieNotifier) createAlert(evalContext *alerting.EvalContext) err
|
||||
body, _ := bodyJSON.MarshalJSON()
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: this.ApiUrl,
|
||||
Url: on.APIUrl,
|
||||
Body: string(body),
|
||||
HttpMethod: "POST",
|
||||
HttpHeader: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": fmt.Sprintf("GenieKey %s", this.ApiKey),
|
||||
"Authorization": fmt.Sprintf("GenieKey %s", on.APIKey),
|
||||
},
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send notification to OpsGenie", "error", err, "body", string(body))
|
||||
on.log.Error("Failed to send notification to OpsGenie", "error", err, "body", string(body))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *OpsGenieNotifier) closeAlert(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Closing OpsGenie alert", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||
func (on *OpsGenieNotifier) closeAlert(evalContext *alerting.EvalContext) error {
|
||||
on.log.Info("Closing OpsGenie alert", "ruleId", evalContext.Rule.Id, "notification", on.Name)
|
||||
|
||||
bodyJSON := simplejson.New()
|
||||
bodyJSON.Set("source", "Grafana")
|
||||
body, _ := bodyJSON.MarshalJSON()
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: fmt.Sprintf("%s/alertId-%d/close?identifierType=alias", this.ApiUrl, evalContext.Rule.Id),
|
||||
Url: fmt.Sprintf("%s/alertId-%d/close?identifierType=alias", on.APIUrl, evalContext.Rule.Id),
|
||||
Body: string(body),
|
||||
HttpMethod: "POST",
|
||||
HttpHeader: map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": fmt.Sprintf("GenieKey %s", this.ApiKey),
|
||||
"Authorization": fmt.Sprintf("GenieKey %s", on.APIKey),
|
||||
},
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send notification to OpsGenie", "error", err, "body", string(body))
|
||||
on.log.Error("Failed to send notification to OpsGenie", "error", err, "body", string(body))
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestOpsGenieNotifier(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(opsgenieNotifier.Name, ShouldEqual, "opsgenie_testing")
|
||||
So(opsgenieNotifier.Type, ShouldEqual, "opsgenie")
|
||||
So(opsgenieNotifier.ApiKey, ShouldEqual, "abcdefgh0123456789")
|
||||
So(opsgenieNotifier.APIKey, ShouldEqual, "abcdefgh0123456789")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,9 +40,10 @@ func init() {
|
||||
}
|
||||
|
||||
var (
|
||||
pagerdutyEventApiUrl = "https://events.pagerduty.com/v2/enqueue"
|
||||
pagerdutyEventAPIURL = "https://events.pagerduty.com/v2/enqueue"
|
||||
)
|
||||
|
||||
// NewPagerdutyNotifier is the constructor for the PagerDuty notifier
|
||||
func NewPagerdutyNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
autoResolve := model.Settings.Get("autoResolve").MustBool(false)
|
||||
key := model.Settings.Get("integrationKey").MustString()
|
||||
@@ -58,6 +59,8 @@ func NewPagerdutyNotifier(model *models.AlertNotification) (alerting.Notifier, e
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PagerdutyNotifier is responsible for sending
|
||||
// alert notifications to pagerduty
|
||||
type PagerdutyNotifier struct {
|
||||
NotifierBase
|
||||
Key string
|
||||
@@ -65,10 +68,11 @@ type PagerdutyNotifier struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
// Notify sends an alert notification to PagerDuty
|
||||
func (pn *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
|
||||
if evalContext.Rule.State == models.AlertStateOK && !this.AutoResolve {
|
||||
this.log.Info("Not sending a trigger to Pagerduty", "state", evalContext.Rule.State, "auto resolve", this.AutoResolve)
|
||||
if evalContext.Rule.State == models.AlertStateOK && !pn.AutoResolve {
|
||||
pn.log.Info("Not sending a trigger to Pagerduty", "state", evalContext.Rule.State, "auto resolve", pn.AutoResolve)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -81,7 +85,7 @@ func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
customData = customData + fmt.Sprintf("%s: %v\n", evt.Metric, evt.Value)
|
||||
}
|
||||
|
||||
this.log.Info("Notifying Pagerduty", "event_type", eventType)
|
||||
pn.log.Info("Notifying Pagerduty", "event_type", eventType)
|
||||
|
||||
payloadJSON := simplejson.New()
|
||||
payloadJSON.Set("summary", evalContext.Rule.Name+" - "+evalContext.Rule.Message)
|
||||
@@ -94,20 +98,20 @@ func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
payloadJSON.Set("custom_details", customData)
|
||||
|
||||
bodyJSON := simplejson.New()
|
||||
bodyJSON.Set("routing_key", this.Key)
|
||||
bodyJSON.Set("routing_key", pn.Key)
|
||||
bodyJSON.Set("event_action", eventType)
|
||||
bodyJSON.Set("dedup_key", "alertId-"+strconv.FormatInt(evalContext.Rule.Id, 10))
|
||||
bodyJSON.Set("payload", payloadJSON)
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
pn.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
links := make([]interface{}, 1)
|
||||
linkJSON := simplejson.New()
|
||||
linkJSON.Set("href", ruleUrl)
|
||||
bodyJSON.Set("client_url", ruleUrl)
|
||||
linkJSON.Set("href", ruleURL)
|
||||
bodyJSON.Set("client_url", ruleURL)
|
||||
bodyJSON.Set("client", "Grafana")
|
||||
links[0] = linkJSON
|
||||
bodyJSON.Set("links", links)
|
||||
@@ -123,7 +127,7 @@ func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
body, _ := bodyJSON.MarshalJSON()
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: pagerdutyEventApiUrl,
|
||||
Url: pagerdutyEventAPIURL,
|
||||
Body: string(body),
|
||||
HttpMethod: "POST",
|
||||
HttpHeader: map[string]string{
|
||||
@@ -132,7 +136,7 @@ func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send notification to Pagerduty", "error", err, "body", string(body))
|
||||
pn.log.Error("Failed to send notification to Pagerduty", "error", err, "body", string(body))
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
)
|
||||
|
||||
const PUSHOVER_ENDPOINT = "https://api.pushover.net/1/messages.json"
|
||||
const pushoverEndpoint = "https://api.pushover.net/1/messages.json"
|
||||
|
||||
func init() {
|
||||
sounds := `
|
||||
@@ -95,9 +95,10 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// NewPushoverNotifier is the constructor for the Pushover Notifier
|
||||
func NewPushoverNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
userKey := model.Settings.Get("userKey").MustString()
|
||||
apiToken := model.Settings.Get("apiToken").MustString()
|
||||
APIToken := model.Settings.Get("apiToken").MustString()
|
||||
device := model.Settings.Get("device").MustString()
|
||||
priority, _ := strconv.Atoi(model.Settings.Get("priority").MustString())
|
||||
retry, _ := strconv.Atoi(model.Settings.Get("retry").MustString())
|
||||
@@ -109,13 +110,13 @@ func NewPushoverNotifier(model *models.AlertNotification) (alerting.Notifier, er
|
||||
if userKey == "" {
|
||||
return nil, alerting.ValidationError{Reason: "User key not given"}
|
||||
}
|
||||
if apiToken == "" {
|
||||
if APIToken == "" {
|
||||
return nil, alerting.ValidationError{Reason: "API token not given"}
|
||||
}
|
||||
return &PushoverNotifier{
|
||||
NotifierBase: NewNotifierBase(model),
|
||||
UserKey: userKey,
|
||||
ApiToken: apiToken,
|
||||
APIToken: APIToken,
|
||||
Priority: priority,
|
||||
Retry: retry,
|
||||
Expire: expire,
|
||||
@@ -127,10 +128,12 @@ func NewPushoverNotifier(model *models.AlertNotification) (alerting.Notifier, er
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PushoverNotifier is responsible for sending
|
||||
// alert notifications to Pushover
|
||||
type PushoverNotifier struct {
|
||||
NotifierBase
|
||||
UserKey string
|
||||
ApiToken string
|
||||
APIToken string
|
||||
Priority int
|
||||
Retry int
|
||||
Expire int
|
||||
@@ -141,10 +144,11 @@ type PushoverNotifier struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
// Notify sends a alert notification to Pushover
|
||||
func (pn *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
pn.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -163,34 +167,34 @@ func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
message = "Notification message missing (Set a notification message to replace this text.)"
|
||||
}
|
||||
|
||||
headers, uploadBody, err := this.genPushoverBody(evalContext, message, ruleUrl)
|
||||
headers, uploadBody, err := pn.genPushoverBody(evalContext, message, ruleURL)
|
||||
if err != nil {
|
||||
this.log.Error("Failed to generate body for pushover", "error", err)
|
||||
pn.log.Error("Failed to generate body for pushover", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: PUSHOVER_ENDPOINT,
|
||||
Url: pushoverEndpoint,
|
||||
HttpMethod: "POST",
|
||||
HttpHeader: headers,
|
||||
Body: uploadBody.String(),
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send pushover notification", "error", err, "webhook", this.Name)
|
||||
pn.log.Error("Failed to send pushover notification", "error", err, "webhook", pn.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *PushoverNotifier) genPushoverBody(evalContext *alerting.EvalContext, message string, ruleUrl string) (map[string]string, bytes.Buffer, error) {
|
||||
func (pn *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 != "" {
|
||||
if pn.Upload && evalContext.ImageOnDiskPath != "" {
|
||||
f, err := os.Open(evalContext.ImageOnDiskPath)
|
||||
if err != nil {
|
||||
return nil, b, err
|
||||
@@ -209,47 +213,47 @@ func (this *PushoverNotifier) genPushoverBody(evalContext *alerting.EvalContext,
|
||||
}
|
||||
|
||||
// Add the user token
|
||||
err = w.WriteField("user", this.UserKey)
|
||||
err = w.WriteField("user", pn.UserKey)
|
||||
if err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
|
||||
// Add the api token
|
||||
err = w.WriteField("token", this.ApiToken)
|
||||
err = w.WriteField("token", pn.APIToken)
|
||||
if err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
|
||||
// Add priority
|
||||
err = w.WriteField("priority", strconv.Itoa(this.Priority))
|
||||
err = w.WriteField("priority", strconv.Itoa(pn.Priority))
|
||||
if err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
|
||||
if this.Priority == 2 {
|
||||
err = w.WriteField("retry", strconv.Itoa(this.Retry))
|
||||
if pn.Priority == 2 {
|
||||
err = w.WriteField("retry", strconv.Itoa(pn.Retry))
|
||||
if err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
|
||||
err = w.WriteField("expire", strconv.Itoa(this.Expire))
|
||||
err = w.WriteField("expire", strconv.Itoa(pn.Expire))
|
||||
if err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
}
|
||||
|
||||
// Add device
|
||||
if this.Device != "" {
|
||||
err = w.WriteField("device", this.Device)
|
||||
if pn.Device != "" {
|
||||
err = w.WriteField("device", pn.Device)
|
||||
if err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
}
|
||||
|
||||
// Add sound
|
||||
sound := this.AlertingSound
|
||||
sound := pn.AlertingSound
|
||||
if evalContext.Rule.State == models.AlertStateOK {
|
||||
sound = this.OkSound
|
||||
sound = pn.OkSound
|
||||
}
|
||||
if sound != "default" {
|
||||
err = w.WriteField("sound", sound)
|
||||
@@ -265,7 +269,7 @@ func (this *PushoverNotifier) genPushoverBody(evalContext *alerting.EvalContext,
|
||||
}
|
||||
|
||||
// Add URL
|
||||
err = w.WriteField("url", ruleUrl)
|
||||
err = w.WriteField("url", ruleURL)
|
||||
if err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func TestPushoverNotifier(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(pushoverNotifier.Name, ShouldEqual, "Pushover")
|
||||
So(pushoverNotifier.Type, ShouldEqual, "pushover")
|
||||
So(pushoverNotifier.ApiToken, ShouldEqual, "4SrUFQL4A5V5TQ1z5Pg9nxHXPXSTve")
|
||||
So(pushoverNotifier.APIToken, ShouldEqual, "4SrUFQL4A5V5TQ1z5Pg9nxHXPXSTve")
|
||||
So(pushoverNotifier.UserKey, ShouldEqual, "tzNZYf36y0ohWwXo4XoUrB61rz1A4o")
|
||||
So(pushoverNotifier.Priority, ShouldEqual, 1)
|
||||
So(pushoverNotifier.AlertingSound, ShouldEqual, "pushover")
|
||||
|
||||
@@ -44,6 +44,7 @@ func init() {
|
||||
|
||||
}
|
||||
|
||||
// NewSensuNotifier is the constructor for the Sensu Notifier.
|
||||
func NewSensuNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
@@ -52,7 +53,7 @@ func NewSensuNotifier(model *models.AlertNotification) (alerting.Notifier, error
|
||||
|
||||
return &SensuNotifier{
|
||||
NotifierBase: NewNotifierBase(model),
|
||||
Url: url,
|
||||
URL: url,
|
||||
User: model.Settings.Get("username").MustString(),
|
||||
Source: model.Settings.Get("source").MustString(),
|
||||
Password: model.Settings.Get("password").MustString(),
|
||||
@@ -61,9 +62,11 @@ func NewSensuNotifier(model *models.AlertNotification) (alerting.Notifier, error
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SensuNotifier is responsible for sending
|
||||
// alert notifications to Sensu.
|
||||
type SensuNotifier struct {
|
||||
NotifierBase
|
||||
Url string
|
||||
URL string
|
||||
Source string
|
||||
User string
|
||||
Password string
|
||||
@@ -71,8 +74,9 @@ type SensuNotifier struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Sending sensu result")
|
||||
// Notify send alert notification to Sensu
|
||||
func (sn *SensuNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
sn.log.Info("Sending sensu result")
|
||||
|
||||
bodyJSON := simplejson.New()
|
||||
bodyJSON.Set("ruleId", evalContext.Rule.Id)
|
||||
@@ -80,8 +84,8 @@ func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
bodyJSON.Set("name", strings.Replace(evalContext.Rule.Name, " ", "_", -1))
|
||||
// Sensu alerts require a source. We set it to the user-specified value (optional),
|
||||
// else we fallback and use the grafana ruleID.
|
||||
if this.Source != "" {
|
||||
bodyJSON.Set("source", this.Source)
|
||||
if sn.Source != "" {
|
||||
bodyJSON.Set("source", sn.Source)
|
||||
} else {
|
||||
bodyJSON.Set("source", "grafana_rule_"+strconv.FormatInt(evalContext.Rule.Id, 10))
|
||||
}
|
||||
@@ -98,13 +102,13 @@ func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
bodyJSON.Set("status", 0)
|
||||
}
|
||||
|
||||
if this.Handler != "" {
|
||||
bodyJSON.Set("handler", this.Handler)
|
||||
if sn.Handler != "" {
|
||||
bodyJSON.Set("handler", sn.Handler)
|
||||
}
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err == nil {
|
||||
bodyJSON.Set("ruleUrl", ruleUrl)
|
||||
bodyJSON.Set("ruleUrl", ruleURL)
|
||||
}
|
||||
|
||||
if evalContext.ImagePublicUrl != "" {
|
||||
@@ -118,15 +122,15 @@ func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
body, _ := bodyJSON.MarshalJSON()
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: this.Url,
|
||||
User: this.User,
|
||||
Password: this.Password,
|
||||
Url: sn.URL,
|
||||
User: sn.User,
|
||||
Password: sn.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)
|
||||
sn.log.Error("Failed to send sensu event", "error", err, "sensu", sn.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestSensuNotifier(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(sensuNotifier.Name, ShouldEqual, "sensu")
|
||||
So(sensuNotifier.Type, ShouldEqual, "sensu")
|
||||
So(sensuNotifier.Url, ShouldEqual, "http://sensu-api.example.com:4567/results")
|
||||
So(sensuNotifier.URL, ShouldEqual, "http://sensu-api.example.com:4567/results")
|
||||
So(sensuNotifier.Source, ShouldEqual, "grafana_instance_01")
|
||||
So(sensuNotifier.Handler, ShouldEqual, "myhandler")
|
||||
})
|
||||
|
||||
@@ -99,6 +99,7 @@ func init() {
|
||||
|
||||
}
|
||||
|
||||
// NewSlackNotifier is the constructor for the Slack notifier
|
||||
func NewSlackNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
@@ -108,18 +109,18 @@ func NewSlackNotifier(model *models.AlertNotification) (alerting.Notifier, error
|
||||
recipient := model.Settings.Get("recipient").MustString()
|
||||
username := model.Settings.Get("username").MustString()
|
||||
iconEmoji := model.Settings.Get("icon_emoji").MustString()
|
||||
iconUrl := model.Settings.Get("icon_url").MustString()
|
||||
iconURL := model.Settings.Get("icon_url").MustString()
|
||||
mention := model.Settings.Get("mention").MustString()
|
||||
token := model.Settings.Get("token").MustString()
|
||||
uploadImage := model.Settings.Get("uploadImage").MustBool(true)
|
||||
|
||||
return &SlackNotifier{
|
||||
NotifierBase: NewNotifierBase(model),
|
||||
Url: url,
|
||||
URL: url,
|
||||
Recipient: recipient,
|
||||
Username: username,
|
||||
IconEmoji: iconEmoji,
|
||||
IconUrl: iconUrl,
|
||||
IconURL: iconURL,
|
||||
Mention: mention,
|
||||
Token: token,
|
||||
Upload: uploadImage,
|
||||
@@ -127,25 +128,28 @@ func NewSlackNotifier(model *models.AlertNotification) (alerting.Notifier, error
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SlackNotifier is responsible for sending
|
||||
// alert notification to Slack.
|
||||
type SlackNotifier struct {
|
||||
NotifierBase
|
||||
Url string
|
||||
URL string
|
||||
Recipient string
|
||||
Username string
|
||||
IconEmoji string
|
||||
IconUrl string
|
||||
IconURL string
|
||||
Mention string
|
||||
Token string
|
||||
Upload bool
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Executing slack notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||
// Notify send alert notification to Slack.
|
||||
func (sn *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
sn.log.Info("Executing slack notification", "ruleId", evalContext.Rule.Id, "notification", sn.Name)
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
sn.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -170,14 +174,14 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
})
|
||||
}
|
||||
|
||||
message := this.Mention
|
||||
message := sn.Mention
|
||||
if evalContext.Rule.State != models.AlertStateOK { //don't add message when going back to alert state ok.
|
||||
message += " " + evalContext.Rule.Message
|
||||
}
|
||||
image_url := ""
|
||||
imageURL := ""
|
||||
// default to file.upload API method if a token is provided
|
||||
if this.Token == "" {
|
||||
image_url = evalContext.ImagePublicUrl
|
||||
if sn.Token == "" {
|
||||
imageURL = evalContext.ImagePublicUrl
|
||||
}
|
||||
|
||||
body := map[string]interface{}{
|
||||
@@ -186,10 +190,10 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
"fallback": evalContext.GetNotificationTitle(),
|
||||
"color": evalContext.GetStateModel().Color,
|
||||
"title": evalContext.GetNotificationTitle(),
|
||||
"title_link": ruleUrl,
|
||||
"title_link": ruleURL,
|
||||
"text": message,
|
||||
"fields": fields,
|
||||
"image_url": image_url,
|
||||
"image_url": imageURL,
|
||||
"footer": "Grafana v" + setting.BuildVersion,
|
||||
"footer_icon": "https://grafana.com/assets/img/fav32.png",
|
||||
"ts": time.Now().Unix(),
|
||||
@@ -199,26 +203,26 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
}
|
||||
|
||||
//recipient override
|
||||
if this.Recipient != "" {
|
||||
body["channel"] = this.Recipient
|
||||
if sn.Recipient != "" {
|
||||
body["channel"] = sn.Recipient
|
||||
}
|
||||
if this.Username != "" {
|
||||
body["username"] = this.Username
|
||||
if sn.Username != "" {
|
||||
body["username"] = sn.Username
|
||||
}
|
||||
if this.IconEmoji != "" {
|
||||
body["icon_emoji"] = this.IconEmoji
|
||||
if sn.IconEmoji != "" {
|
||||
body["icon_emoji"] = sn.IconEmoji
|
||||
}
|
||||
if this.IconUrl != "" {
|
||||
body["icon_url"] = this.IconUrl
|
||||
if sn.IconURL != "" {
|
||||
body["icon_url"] = sn.IconURL
|
||||
}
|
||||
data, _ := json.Marshal(&body)
|
||||
cmd := &models.SendWebhookSync{Url: this.Url, Body: string(data)}
|
||||
cmd := &models.SendWebhookSync{Url: sn.URL, Body: string(data)}
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send slack notification", "error", err, "webhook", this.Name)
|
||||
sn.log.Error("Failed to send slack notification", "error", err, "webhook", sn.Name)
|
||||
return err
|
||||
}
|
||||
if this.Token != "" && this.UploadImage {
|
||||
err = SlackFileUpload(evalContext, this.log, "https://slack.com/api/files.upload", this.Recipient, this.Token)
|
||||
if sn.Token != "" && sn.UploadImage {
|
||||
err = slackFileUpload(evalContext, sn.log, "https://slack.com/api/files.upload", sn.Recipient, sn.Token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -226,12 +230,12 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func SlackFileUpload(evalContext *alerting.EvalContext, log log.Logger, url string, recipient string, token string) error {
|
||||
func slackFileUpload(evalContext *alerting.EvalContext, log log.Logger, url string, recipient string, token string) error {
|
||||
if evalContext.ImageOnDiskPath == "" {
|
||||
evalContext.ImageOnDiskPath = filepath.Join(setting.HomePath, "public/img/mixed_styles.png")
|
||||
}
|
||||
log.Info("Uploading to slack via file.upload API")
|
||||
headers, uploadBody, err := GenerateSlackBody(evalContext.ImageOnDiskPath, token, recipient)
|
||||
headers, uploadBody, err := generateSlackBody(evalContext.ImageOnDiskPath, token, recipient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -246,7 +250,7 @@ func SlackFileUpload(evalContext *alerting.EvalContext, log log.Logger, url stri
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenerateSlackBody(file string, token string, recipient string) (map[string]string, bytes.Buffer, error) {
|
||||
func generateSlackBody(file string, token string, recipient string) (map[string]string, bytes.Buffer, error) {
|
||||
// Slack requires all POSTs to files.upload to present
|
||||
// an "application/x-www-form-urlencoded" encoded querystring
|
||||
// See https://api.slack.com/methods/files.upload
|
||||
|
||||
@@ -45,11 +45,11 @@ func TestSlackNotifier(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(slackNotifier.Name, ShouldEqual, "ops")
|
||||
So(slackNotifier.Type, ShouldEqual, "slack")
|
||||
So(slackNotifier.Url, ShouldEqual, "http://google.com")
|
||||
So(slackNotifier.URL, ShouldEqual, "http://google.com")
|
||||
So(slackNotifier.Recipient, ShouldEqual, "")
|
||||
So(slackNotifier.Username, ShouldEqual, "")
|
||||
So(slackNotifier.IconEmoji, ShouldEqual, "")
|
||||
So(slackNotifier.IconUrl, ShouldEqual, "")
|
||||
So(slackNotifier.IconURL, ShouldEqual, "")
|
||||
So(slackNotifier.Mention, ShouldEqual, "")
|
||||
So(slackNotifier.Token, ShouldEqual, "")
|
||||
})
|
||||
@@ -79,11 +79,11 @@ func TestSlackNotifier(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(slackNotifier.Name, ShouldEqual, "ops")
|
||||
So(slackNotifier.Type, ShouldEqual, "slack")
|
||||
So(slackNotifier.Url, ShouldEqual, "http://google.com")
|
||||
So(slackNotifier.URL, ShouldEqual, "http://google.com")
|
||||
So(slackNotifier.Recipient, ShouldEqual, "#ds-opentsdb")
|
||||
So(slackNotifier.Username, ShouldEqual, "Grafana Alerts")
|
||||
So(slackNotifier.IconEmoji, ShouldEqual, ":smile:")
|
||||
So(slackNotifier.IconUrl, ShouldEqual, "https://grafana.com/img/fav32.png")
|
||||
So(slackNotifier.IconURL, ShouldEqual, "https://grafana.com/img/fav32.png")
|
||||
So(slackNotifier.Mention, ShouldEqual, "@carl")
|
||||
So(slackNotifier.Token, ShouldEqual, "xoxb-XXXXXXXX-XXXXXXXX-XXXXXXXXXX")
|
||||
})
|
||||
|
||||
@@ -26,6 +26,7 @@ func init() {
|
||||
|
||||
}
|
||||
|
||||
// NewTeamsNotifier is the constructor for Teams notifier.
|
||||
func NewTeamsNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
@@ -34,23 +35,26 @@ func NewTeamsNotifier(model *models.AlertNotification) (alerting.Notifier, error
|
||||
|
||||
return &TeamsNotifier{
|
||||
NotifierBase: NewNotifierBase(model),
|
||||
Url: url,
|
||||
URL: url,
|
||||
log: log.New("alerting.notifier.teams"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TeamsNotifier is responsible for sending
|
||||
// alert notifications to Microsoft teams.
|
||||
type TeamsNotifier struct {
|
||||
NotifierBase
|
||||
Url string
|
||||
URL string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Executing teams notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||
// Notify send an alert notification to Microsoft teams.
|
||||
func (tn *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
tn.log.Info("Executing teams notification", "ruleId", evalContext.Rule.Id, "notification", tn.Name)
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
tn.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -108,7 +112,7 @@ func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
"name": "View Rule",
|
||||
"targets": []map[string]interface{}{
|
||||
{
|
||||
"os": "default", "uri": ruleUrl,
|
||||
"os": "default", "uri": ruleURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -126,10 +130,10 @@ func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
}
|
||||
|
||||
data, _ := json.Marshal(&body)
|
||||
cmd := &models.SendWebhookSync{Url: this.Url, Body: string(data)}
|
||||
cmd := &models.SendWebhookSync{Url: tn.URL, Body: string(data)}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send teams notification", "error", err, "webhook", this.Name)
|
||||
tn.log.Error("Failed to send teams notification", "error", err, "webhook", tn.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestTeamsNotifier(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(teamsNotifier.Name, ShouldEqual, "ops")
|
||||
So(teamsNotifier.Type, ShouldEqual, "teams")
|
||||
So(teamsNotifier.Url, ShouldEqual, "http://google.com")
|
||||
So(teamsNotifier.URL, ShouldEqual, "http://google.com")
|
||||
})
|
||||
|
||||
Convey("from settings with Recipient and Mention", func() {
|
||||
@@ -67,9 +67,8 @@ func TestTeamsNotifier(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(teamsNotifier.Name, ShouldEqual, "ops")
|
||||
So(teamsNotifier.Type, ShouldEqual, "teams")
|
||||
So(teamsNotifier.Url, ShouldEqual, "http://google.com")
|
||||
So(teamsNotifier.URL, ShouldEqual, "http://google.com")
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
telegramApiUrl = "https://api.telegram.org/bot%s/%s"
|
||||
telegramAPIURL = "https://api.telegram.org/bot%s/%s"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -52,6 +52,8 @@ func init() {
|
||||
|
||||
}
|
||||
|
||||
// TelegramNotifier is responsible for sending
|
||||
// alert notifications to Telegram.
|
||||
type TelegramNotifier struct {
|
||||
NotifierBase
|
||||
BotToken string
|
||||
@@ -60,50 +62,51 @@ type TelegramNotifier struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// NewTelegramNotifier is the constructor for the Telegram notifier
|
||||
func NewTelegramNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
if model.Settings == nil {
|
||||
return nil, alerting.ValidationError{Reason: "No Settings Supplied"}
|
||||
}
|
||||
|
||||
botToken := model.Settings.Get("bottoken").MustString()
|
||||
chatId := model.Settings.Get("chatid").MustString()
|
||||
chatID := model.Settings.Get("chatid").MustString()
|
||||
uploadImage := model.Settings.Get("uploadImage").MustBool()
|
||||
|
||||
if botToken == "" {
|
||||
return nil, alerting.ValidationError{Reason: "Could not find Bot Token in settings"}
|
||||
}
|
||||
|
||||
if chatId == "" {
|
||||
if chatID == "" {
|
||||
return nil, alerting.ValidationError{Reason: "Could not find Chat Id in settings"}
|
||||
}
|
||||
|
||||
return &TelegramNotifier{
|
||||
NotifierBase: NewNotifierBase(model),
|
||||
BotToken: botToken,
|
||||
ChatID: chatId,
|
||||
ChatID: chatID,
|
||||
UploadImage: uploadImage,
|
||||
log: log.New("alerting.notifier.telegram"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *TelegramNotifier) buildMessage(evalContext *alerting.EvalContext, sendImageInline bool) *models.SendWebhookSync {
|
||||
func (tn *TelegramNotifier) buildMessage(evalContext *alerting.EvalContext, sendImageInline bool) *models.SendWebhookSync {
|
||||
if sendImageInline {
|
||||
cmd, err := this.buildMessageInlineImage(evalContext)
|
||||
cmd, err := tn.buildMessageInlineImage(evalContext)
|
||||
if err == nil {
|
||||
return cmd
|
||||
}
|
||||
this.log.Error("Could not generate Telegram message with inline image.", "err", err)
|
||||
tn.log.Error("Could not generate Telegram message with inline image.", "err", err)
|
||||
}
|
||||
|
||||
return this.buildMessageLinkedImage(evalContext)
|
||||
return tn.buildMessageLinkedImage(evalContext)
|
||||
}
|
||||
|
||||
func (this *TelegramNotifier) buildMessageLinkedImage(evalContext *alerting.EvalContext) *models.SendWebhookSync {
|
||||
func (tn *TelegramNotifier) buildMessageLinkedImage(evalContext *alerting.EvalContext) *models.SendWebhookSync {
|
||||
message := fmt.Sprintf("<b>%s</b>\nState: %s\nMessage: %s\n", evalContext.GetNotificationTitle(), evalContext.Rule.Name, evalContext.Rule.Message)
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err == nil {
|
||||
message = message + fmt.Sprintf("URL: %s\n", ruleUrl)
|
||||
message = message + fmt.Sprintf("URL: %s\n", ruleURL)
|
||||
}
|
||||
|
||||
if evalContext.ImagePublicUrl != "" {
|
||||
@@ -115,14 +118,14 @@ func (this *TelegramNotifier) buildMessageLinkedImage(evalContext *alerting.Eval
|
||||
message = message + fmt.Sprintf("\n<i>Metrics:</i>%s", metrics)
|
||||
}
|
||||
|
||||
cmd := this.generateTelegramCmd(message, "text", "sendMessage", func(w *multipart.Writer) {
|
||||
cmd := tn.generateTelegramCmd(message, "text", "sendMessage", func(w *multipart.Writer) {
|
||||
fw, _ := w.CreateFormField("parse_mode")
|
||||
fw.Write([]byte("html"))
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (this *TelegramNotifier) buildMessageInlineImage(evalContext *alerting.EvalContext) (*models.SendWebhookSync, error) {
|
||||
func (tn *TelegramNotifier) buildMessageInlineImage(evalContext *alerting.EvalContext) (*models.SendWebhookSync, error) {
|
||||
var imageFile *os.File
|
||||
var err error
|
||||
|
||||
@@ -130,7 +133,7 @@ func (this *TelegramNotifier) buildMessageInlineImage(evalContext *alerting.Eval
|
||||
defer func() {
|
||||
err := imageFile.Close()
|
||||
if err != nil {
|
||||
this.log.Error("Could not close Telegram inline image.", "err", err)
|
||||
tn.log.Error("Could not close Telegram inline image.", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -138,27 +141,27 @@ func (this *TelegramNotifier) buildMessageInlineImage(evalContext *alerting.Eval
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metrics := generateMetricsMessage(evalContext)
|
||||
message := generateImageCaption(evalContext, ruleUrl, metrics)
|
||||
message := generateImageCaption(evalContext, ruleURL, metrics)
|
||||
|
||||
cmd := this.generateTelegramCmd(message, "caption", "sendPhoto", func(w *multipart.Writer) {
|
||||
cmd := tn.generateTelegramCmd(message, "caption", "sendPhoto", func(w *multipart.Writer) {
|
||||
fw, _ := w.CreateFormFile("photo", evalContext.ImageOnDiskPath)
|
||||
io.Copy(fw, imageFile)
|
||||
})
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (this *TelegramNotifier) generateTelegramCmd(message string, messageField string, apiAction string, extraConf func(writer *multipart.Writer)) *models.SendWebhookSync {
|
||||
func (tn *TelegramNotifier) generateTelegramCmd(message string, messageField string, apiAction string, extraConf func(writer *multipart.Writer)) *models.SendWebhookSync {
|
||||
var body bytes.Buffer
|
||||
w := multipart.NewWriter(&body)
|
||||
|
||||
fw, _ := w.CreateFormField("chat_id")
|
||||
fw.Write([]byte(this.ChatID))
|
||||
fw.Write([]byte(tn.ChatID))
|
||||
|
||||
fw, _ = w.CreateFormField(messageField)
|
||||
fw.Write([]byte(message))
|
||||
@@ -167,8 +170,8 @@ func (this *TelegramNotifier) generateTelegramCmd(message string, messageField s
|
||||
|
||||
w.Close()
|
||||
|
||||
this.log.Info("Sending telegram notification", "chat_id", this.ChatID, "bot_token", this.BotToken, "apiAction", apiAction)
|
||||
url := fmt.Sprintf(telegramApiUrl, this.BotToken, apiAction)
|
||||
tn.log.Info("Sending telegram notification", "chat_id", tn.ChatID, "bot_token", tn.BotToken, "apiAction", apiAction)
|
||||
url := fmt.Sprintf(telegramAPIURL, tn.BotToken, apiAction)
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: url,
|
||||
@@ -193,7 +196,7 @@ func generateMetricsMessage(evalContext *alerting.EvalContext) string {
|
||||
return metrics
|
||||
}
|
||||
|
||||
func generateImageCaption(evalContext *alerting.EvalContext, ruleUrl string, metrics string) string {
|
||||
func generateImageCaption(evalContext *alerting.EvalContext, ruleURL string, metrics string) string {
|
||||
message := evalContext.GetNotificationTitle()
|
||||
|
||||
if len(evalContext.Rule.Message) > 0 {
|
||||
@@ -205,8 +208,8 @@ func generateImageCaption(evalContext *alerting.EvalContext, ruleUrl string, met
|
||||
|
||||
}
|
||||
|
||||
if len(ruleUrl) > 0 {
|
||||
urlLine := fmt.Sprintf("\nURL: %s", ruleUrl)
|
||||
if len(ruleURL) > 0 {
|
||||
urlLine := fmt.Sprintf("\nURL: %s", ruleURL)
|
||||
message = appendIfPossible(message, urlLine, captionLengthLimit)
|
||||
}
|
||||
|
||||
@@ -226,16 +229,17 @@ func appendIfPossible(message string, extra string, sizeLimit int) string {
|
||||
return message
|
||||
}
|
||||
|
||||
func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
// Notify send an alert notification to Telegram.
|
||||
func (tn *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
var cmd *models.SendWebhookSync
|
||||
if evalContext.ImagePublicUrl == "" && this.UploadImage {
|
||||
cmd = this.buildMessage(evalContext, true)
|
||||
if evalContext.ImagePublicUrl == "" && tn.UploadImage {
|
||||
cmd = tn.buildMessage(evalContext, true)
|
||||
} else {
|
||||
cmd = this.buildMessage(evalContext, false)
|
||||
cmd = tn.buildMessage(evalContext, false)
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send webhook", "error", err, "webhook", this.Name)
|
||||
tn.log.Error("Failed to send webhook", "error", err, "webhook", tn.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ func init() {
|
||||
|
||||
}
|
||||
|
||||
// ThreemaNotifier is responsible for sending
|
||||
// alert notifications to Threema.
|
||||
type ThreemaNotifier struct {
|
||||
NotifierBase
|
||||
GatewayID string
|
||||
@@ -76,6 +78,7 @@ type ThreemaNotifier struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// NewThreemaNotifier is the constructor for the Threema notifer
|
||||
func NewThreemaNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
if model.Settings == nil {
|
||||
return nil, alerting.ValidationError{Reason: "No Settings Supplied"}
|
||||
@@ -114,6 +117,7 @@ func NewThreemaNotifier(model *models.AlertNotification) (alerting.Notifier, err
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Notify send an alert notification to Threema
|
||||
func (notifier *ThreemaNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
notifier.log.Info("Sending alert notification from", "threema_id", notifier.GatewayID)
|
||||
notifier.log.Info("Sending alert notification to", "threema_id", notifier.RecipientID)
|
||||
|
||||
@@ -113,7 +113,6 @@ func TestThreemaNotifier(t *testing.T) {
|
||||
So(not, ShouldBeNil)
|
||||
So(err.(alerting.ValidationError).Reason, ShouldEqual, "Invalid Threema Recipient ID: Must be 8 characters long")
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
// AlertStateCritical - Victorops uses "CRITICAL" string to indicate "Alerting" state
|
||||
const AlertStateCritical = "CRITICAL"
|
||||
|
||||
const AlertStateRecovery = "RECOVERY"
|
||||
const alertStateRecovery = "RECOVERY"
|
||||
|
||||
func init() {
|
||||
alerting.RegisterNotifier(&alerting.NotifierPlugin{
|
||||
@@ -69,17 +69,17 @@ type VictoropsNotifier struct {
|
||||
}
|
||||
|
||||
// Notify sends notification to Victorops via POST to URL endpoint
|
||||
func (this *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Executing victorops notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
|
||||
func (vn *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
vn.log.Info("Executing victorops notification", "ruleId", evalContext.Rule.Id, "notification", vn.Name)
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
vn.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if evalContext.Rule.State == models.AlertStateOK && !this.AutoResolve {
|
||||
this.log.Info("Not alerting VictorOps", "state", evalContext.Rule.State, "auto resolve", this.AutoResolve)
|
||||
if evalContext.Rule.State == models.AlertStateOK && !vn.AutoResolve {
|
||||
vn.log.Info("Not alerting VictorOps", "state", evalContext.Rule.State, "auto resolve", vn.AutoResolve)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ func (this *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
}
|
||||
|
||||
if evalContext.Rule.State == models.AlertStateOK {
|
||||
messageType = AlertStateRecovery
|
||||
messageType = alertStateRecovery
|
||||
}
|
||||
|
||||
fields := make(map[string]interface{}, 0)
|
||||
@@ -109,7 +109,7 @@ func (this *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
bodyJSON.Set("state_start_time", evalContext.StartTime.Unix())
|
||||
bodyJSON.Set("state_message", evalContext.Rule.Message)
|
||||
bodyJSON.Set("monitoring_tool", "Grafana v"+setting.BuildVersion)
|
||||
bodyJSON.Set("alert_url", ruleUrl)
|
||||
bodyJSON.Set("alert_url", ruleURL)
|
||||
bodyJSON.Set("metrics", fields)
|
||||
|
||||
if evalContext.Error != nil {
|
||||
@@ -121,10 +121,10 @@ func (this *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
}
|
||||
|
||||
data, _ := bodyJSON.MarshalJSON()
|
||||
cmd := &models.SendWebhookSync{Url: this.URL, Body: string(data)}
|
||||
cmd := &models.SendWebhookSync{Url: vn.URL, Body: string(data)}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send Victorops notification", "error", err, "webhook", this.Name)
|
||||
vn.log.Error("Failed to send Victorops notification", "error", err, "webhook", vn.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ func init() {
|
||||
|
||||
}
|
||||
|
||||
// NewWebHookNotifier is the constructor for
|
||||
// the WebHook notifier.
|
||||
func NewWebHookNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
@@ -48,25 +50,29 @@ func NewWebHookNotifier(model *models.AlertNotification) (alerting.Notifier, err
|
||||
|
||||
return &WebhookNotifier{
|
||||
NotifierBase: NewNotifierBase(model),
|
||||
Url: url,
|
||||
URL: url,
|
||||
User: model.Settings.Get("username").MustString(),
|
||||
Password: model.Settings.Get("password").MustString(),
|
||||
HttpMethod: model.Settings.Get("httpMethod").MustString("POST"),
|
||||
HTTPMethod: model.Settings.Get("httpMethod").MustString("POST"),
|
||||
log: log.New("alerting.notifier.webhook"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WebhookNotifier is responsible for sending
|
||||
// alert notifications as webhooks.
|
||||
type WebhookNotifier struct {
|
||||
NotifierBase
|
||||
Url string
|
||||
URL string
|
||||
User string
|
||||
Password string
|
||||
HttpMethod string
|
||||
HTTPMethod string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *WebhookNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Sending webhook")
|
||||
// Notify send alert notifications as
|
||||
// webhook as http requests.
|
||||
func (wn *WebhookNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
wn.log.Info("Sending webhook")
|
||||
|
||||
bodyJSON := simplejson.New()
|
||||
bodyJSON.Set("title", evalContext.GetNotificationTitle())
|
||||
@@ -75,9 +81,9 @@ func (this *WebhookNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
bodyJSON.Set("state", evalContext.Rule.State)
|
||||
bodyJSON.Set("evalMatches", evalContext.EvalMatches)
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
ruleURL, err := evalContext.GetRuleUrl()
|
||||
if err == nil {
|
||||
bodyJSON.Set("ruleUrl", ruleUrl)
|
||||
bodyJSON.Set("ruleUrl", ruleURL)
|
||||
}
|
||||
|
||||
if evalContext.ImagePublicUrl != "" {
|
||||
@@ -91,15 +97,15 @@ func (this *WebhookNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
body, _ := bodyJSON.MarshalJSON()
|
||||
|
||||
cmd := &models.SendWebhookSync{
|
||||
Url: this.Url,
|
||||
User: this.User,
|
||||
Password: this.Password,
|
||||
Url: wn.URL,
|
||||
User: wn.User,
|
||||
Password: wn.Password,
|
||||
Body: string(body),
|
||||
HttpMethod: this.HttpMethod,
|
||||
HttpMethod: wn.HTTPMethod,
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send webhook", "error", err, "webhook", this.Name)
|
||||
wn.log.Error("Failed to send webhook", "error", err, "webhook", wn.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestWebhookNotifier(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(webhookNotifier.Name, ShouldEqual, "ops")
|
||||
So(webhookNotifier.Type, ShouldEqual, "webhook")
|
||||
So(webhookNotifier.Url, ShouldEqual, "http://google.com")
|
||||
So(webhookNotifier.URL, ShouldEqual, "http://google.com")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user