1package channels 2 3import ( 4 "context" 5 "net/url" 6 "path" 7 8 "github.com/prometheus/alertmanager/template" 9 "github.com/prometheus/alertmanager/types" 10 11 "github.com/grafana/grafana/pkg/bus" 12 "github.com/grafana/grafana/pkg/infra/log" 13 "github.com/grafana/grafana/pkg/models" 14 "github.com/grafana/grafana/pkg/util" 15) 16 17// EmailNotifier is responsible for sending 18// alert notifications over email. 19type EmailNotifier struct { 20 *Base 21 Addresses []string 22 SingleEmail bool 23 Message string 24 log log.Logger 25 tmpl *template.Template 26} 27 28// NewEmailNotifier is the constructor function 29// for the EmailNotifier. 30func NewEmailNotifier(model *NotificationChannelConfig, t *template.Template) (*EmailNotifier, error) { 31 if model.Settings == nil { 32 return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"} 33 } 34 35 addressesString := model.Settings.Get("addresses").MustString() 36 singleEmail := model.Settings.Get("singleEmail").MustBool(false) 37 38 if addressesString == "" { 39 return nil, receiverInitError{Reason: "could not find addresses in settings", Cfg: *model} 40 } 41 42 // split addresses with a few different ways 43 addresses := util.SplitEmails(addressesString) 44 45 return &EmailNotifier{ 46 Base: NewBase(&models.AlertNotification{ 47 Uid: model.UID, 48 Name: model.Name, 49 Type: model.Type, 50 DisableResolveMessage: model.DisableResolveMessage, 51 Settings: model.Settings, 52 }), 53 Addresses: addresses, 54 SingleEmail: singleEmail, 55 Message: model.Settings.Get("message").MustString(), 56 log: log.New("alerting.notifier.email"), 57 tmpl: t, 58 }, nil 59} 60 61// Notify sends the alert notification. 62func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { 63 var tmplErr error 64 tmpl, data := TmplText(ctx, en.tmpl, as, en.log, &tmplErr) 65 66 title := tmpl(DefaultMessageTitleEmbed) 67 68 alertPageURL := en.tmpl.ExternalURL.String() 69 ruleURL := en.tmpl.ExternalURL.String() 70 u, err := url.Parse(en.tmpl.ExternalURL.String()) 71 if err == nil { 72 basePath := u.Path 73 u.Path = path.Join(basePath, "/alerting/list") 74 ruleURL = u.String() 75 u.RawQuery = "alertState=firing&view=state" 76 alertPageURL = u.String() 77 } else { 78 en.log.Debug("failed to parse external URL", "url", en.tmpl.ExternalURL.String(), "err", err.Error()) 79 } 80 81 cmd := &models.SendEmailCommandSync{ 82 SendEmailCommand: models.SendEmailCommand{ 83 Subject: title, 84 Data: map[string]interface{}{ 85 "Title": title, 86 "Message": tmpl(en.Message), 87 "Status": data.Status, 88 "Alerts": data.Alerts, 89 "GroupLabels": data.GroupLabels, 90 "CommonLabels": data.CommonLabels, 91 "CommonAnnotations": data.CommonAnnotations, 92 "ExternalURL": data.ExternalURL, 93 "RuleUrl": ruleURL, 94 "AlertPageUrl": alertPageURL, 95 }, 96 To: en.Addresses, 97 SingleEmail: en.SingleEmail, 98 Template: "ng_alert_notification", 99 }, 100 } 101 102 if tmplErr != nil { 103 en.log.Warn("failed to template email message", "err", tmplErr.Error()) 104 } 105 106 if err := bus.DispatchCtx(ctx, cmd); err != nil { 107 return false, err 108 } 109 110 return true, nil 111} 112 113func (en *EmailNotifier) SendResolved() bool { 114 return !en.GetDisableResolveMessage() 115} 116