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