2021-03-18 20:25:11 +05:30
|
|
|
package channels
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2022-03-15 00:27:10 +01:00
|
|
|
"errors"
|
2021-05-19 19:58:31 +03:00
|
|
|
"net/url"
|
2022-05-23 23:08:28 +08:00
|
|
|
"os"
|
2021-04-22 19:31:55 +05:30
|
|
|
"path"
|
2021-03-18 20:25:11 +05:30
|
|
|
|
2021-04-15 16:01:41 +05:30
|
|
|
"github.com/prometheus/alertmanager/template"
|
|
|
|
|
"github.com/prometheus/alertmanager/types"
|
|
|
|
|
|
2021-03-18 20:25:11 +05:30
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
|
|
|
"github.com/grafana/grafana/pkg/models"
|
2022-05-26 13:29:56 +08:00
|
|
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
2022-01-26 16:42:40 +01:00
|
|
|
"github.com/grafana/grafana/pkg/services/notifications"
|
2021-03-18 20:25:11 +05:30
|
|
|
"github.com/grafana/grafana/pkg/util"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// EmailNotifier is responsible for sending
|
|
|
|
|
// alert notifications over email.
|
|
|
|
|
type EmailNotifier struct {
|
2021-10-22 10:11:06 +01:00
|
|
|
*Base
|
2021-03-18 20:25:11 +05:30
|
|
|
Addresses []string
|
|
|
|
|
SingleEmail bool
|
2021-05-17 16:05:09 +05:30
|
|
|
Message string
|
2022-05-30 11:55:34 -04:00
|
|
|
Subject string
|
2021-03-18 20:25:11 +05:30
|
|
|
log log.Logger
|
2022-01-26 16:42:40 +01:00
|
|
|
ns notifications.EmailSender
|
2022-05-23 23:08:28 +08:00
|
|
|
images ImageStore
|
2021-05-17 16:05:09 +05:30
|
|
|
tmpl *template.Template
|
2021-03-18 20:25:11 +05:30
|
|
|
}
|
|
|
|
|
|
2022-03-15 00:27:10 +01:00
|
|
|
type EmailConfig struct {
|
|
|
|
|
*NotificationChannelConfig
|
|
|
|
|
SingleEmail bool
|
|
|
|
|
Addresses []string
|
|
|
|
|
Message string
|
2022-05-30 11:55:34 -04:00
|
|
|
Subject string
|
2022-03-15 00:27:10 +01:00
|
|
|
}
|
2021-04-15 16:01:41 +05:30
|
|
|
|
2022-03-15 00:27:10 +01:00
|
|
|
func EmailFactory(fc FactoryConfig) (NotificationChannel, error) {
|
|
|
|
|
cfg, err := NewEmailConfig(fc.Config)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, receiverInitError{
|
|
|
|
|
Reason: err.Error(),
|
|
|
|
|
Cfg: *fc.Config,
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-05-23 23:08:28 +08:00
|
|
|
return NewEmailNotifier(cfg, fc.NotificationService, fc.ImageStore, fc.Template), nil
|
2022-03-15 00:27:10 +01:00
|
|
|
}
|
2021-03-18 20:25:11 +05:30
|
|
|
|
2022-03-15 00:27:10 +01:00
|
|
|
func NewEmailConfig(config *NotificationChannelConfig) (*EmailConfig, error) {
|
|
|
|
|
addressesString := config.Settings.Get("addresses").MustString()
|
2021-03-18 20:25:11 +05:30
|
|
|
if addressesString == "" {
|
2022-03-15 00:27:10 +01:00
|
|
|
return nil, errors.New("could not find addresses in settings")
|
2021-03-18 20:25:11 +05:30
|
|
|
}
|
|
|
|
|
// split addresses with a few different ways
|
|
|
|
|
addresses := util.SplitEmails(addressesString)
|
2022-03-15 00:27:10 +01:00
|
|
|
return &EmailConfig{
|
|
|
|
|
NotificationChannelConfig: config,
|
|
|
|
|
SingleEmail: config.Settings.Get("singleEmail").MustBool(false),
|
|
|
|
|
Message: config.Settings.Get("message").MustString(),
|
2022-05-30 11:55:34 -04:00
|
|
|
Subject: config.Settings.Get("subject").MustString(DefaultMessageTitleEmbed),
|
2022-03-15 00:27:10 +01:00
|
|
|
Addresses: addresses,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
2021-03-18 20:25:11 +05:30
|
|
|
|
2022-03-15 00:27:10 +01:00
|
|
|
// NewEmailNotifier is the constructor function
|
|
|
|
|
// for the EmailNotifier.
|
2022-05-23 23:08:28 +08:00
|
|
|
func NewEmailNotifier(config *EmailConfig, ns notifications.EmailSender, images ImageStore, t *template.Template) *EmailNotifier {
|
2021-03-18 20:25:11 +05:30
|
|
|
return &EmailNotifier{
|
2021-10-22 10:11:06 +01:00
|
|
|
Base: NewBase(&models.AlertNotification{
|
2022-03-15 00:27:10 +01:00
|
|
|
Uid: config.UID,
|
|
|
|
|
Name: config.Name,
|
|
|
|
|
Type: config.Type,
|
|
|
|
|
DisableResolveMessage: config.DisableResolveMessage,
|
|
|
|
|
Settings: config.Settings,
|
2021-05-18 13:34:47 +05:30
|
|
|
}),
|
2022-03-15 00:27:10 +01:00
|
|
|
Addresses: config.Addresses,
|
|
|
|
|
SingleEmail: config.SingleEmail,
|
|
|
|
|
Message: config.Message,
|
2022-05-30 11:55:34 -04:00
|
|
|
Subject: config.Subject,
|
2021-05-18 13:34:47 +05:30
|
|
|
log: log.New("alerting.notifier.email"),
|
2022-01-26 16:42:40 +01:00
|
|
|
ns: ns,
|
2022-05-23 23:08:28 +08:00
|
|
|
images: images,
|
2021-05-18 13:34:47 +05:30
|
|
|
tmpl: t,
|
2022-03-15 00:27:10 +01:00
|
|
|
}
|
2021-03-18 20:25:11 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Notify sends the alert notification.
|
2021-03-24 14:20:44 +00:00
|
|
|
func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
|
2021-05-26 20:19:39 +05:30
|
|
|
var tmplErr error
|
2021-06-03 19:39:32 +05:30
|
|
|
tmpl, data := TmplText(ctx, en.tmpl, as, en.log, &tmplErr)
|
2021-03-18 20:25:11 +05:30
|
|
|
|
2022-05-30 11:55:34 -04:00
|
|
|
subject := tmpl(en.Subject)
|
2021-03-26 12:53:14 +05:30
|
|
|
|
2021-06-03 19:39:32 +05:30
|
|
|
alertPageURL := en.tmpl.ExternalURL.String()
|
|
|
|
|
ruleURL := en.tmpl.ExternalURL.String()
|
2021-05-19 19:58:31 +03:00
|
|
|
u, err := url.Parse(en.tmpl.ExternalURL.String())
|
2021-06-03 19:39:32 +05:30
|
|
|
if err == nil {
|
|
|
|
|
basePath := u.Path
|
|
|
|
|
u.Path = path.Join(basePath, "/alerting/list")
|
|
|
|
|
ruleURL = u.String()
|
|
|
|
|
u.RawQuery = "alertState=firing&view=state"
|
|
|
|
|
alertPageURL = u.String()
|
|
|
|
|
} else {
|
|
|
|
|
en.log.Debug("failed to parse external URL", "url", en.tmpl.ExternalURL.String(), "err", err.Error())
|
2021-05-19 19:58:31 +03:00
|
|
|
}
|
|
|
|
|
|
2021-03-18 20:25:11 +05:30
|
|
|
cmd := &models.SendEmailCommandSync{
|
|
|
|
|
SendEmailCommand: models.SendEmailCommand{
|
2022-05-30 11:55:34 -04:00
|
|
|
Subject: subject,
|
2021-03-18 20:25:11 +05:30
|
|
|
Data: map[string]interface{}{
|
2022-05-30 11:55:34 -04:00
|
|
|
"Title": subject,
|
2021-05-17 16:05:09 +05:30
|
|
|
"Message": tmpl(en.Message),
|
2021-03-18 20:25:11 +05:30
|
|
|
"Status": data.Status,
|
|
|
|
|
"Alerts": data.Alerts,
|
|
|
|
|
"GroupLabels": data.GroupLabels,
|
|
|
|
|
"CommonLabels": data.CommonLabels,
|
|
|
|
|
"CommonAnnotations": data.CommonAnnotations,
|
|
|
|
|
"ExternalURL": data.ExternalURL,
|
2021-05-19 19:58:31 +03:00
|
|
|
"RuleUrl": ruleURL,
|
|
|
|
|
"AlertPageUrl": alertPageURL,
|
2021-03-18 20:25:11 +05:30
|
|
|
},
|
|
|
|
|
To: en.Addresses,
|
|
|
|
|
SingleEmail: en.SingleEmail,
|
2021-07-19 12:31:51 +02:00
|
|
|
Template: "ng_alert_notification",
|
2021-03-18 20:25:11 +05:30
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-23 23:08:28 +08:00
|
|
|
// TODO: modify the email sender code to support multiple file or image URL
|
|
|
|
|
// fields. We cannot use images from every alert yet.
|
2022-05-26 13:29:56 +08:00
|
|
|
_ = withStoredImage(ctx, en.log, en.images,
|
|
|
|
|
func(index int, image *ngmodels.Image) error {
|
|
|
|
|
if image == nil {
|
|
|
|
|
return nil
|
2022-05-23 23:08:28 +08:00
|
|
|
}
|
2022-05-26 13:29:56 +08:00
|
|
|
|
|
|
|
|
if len(image.URL) != 0 {
|
|
|
|
|
cmd.Data["ImageLink"] = image.URL
|
|
|
|
|
} else if len(image.Path) != 0 {
|
|
|
|
|
file, err := os.Stat(image.Path)
|
2022-05-23 23:08:28 +08:00
|
|
|
if err == nil {
|
2022-05-26 13:29:56 +08:00
|
|
|
cmd.EmbeddedFiles = []string{image.Path}
|
2022-05-23 23:08:28 +08:00
|
|
|
cmd.Data["EmbeddedImage"] = file.Name()
|
|
|
|
|
} else {
|
2022-06-07 18:54:23 +01:00
|
|
|
en.log.Warn("failed to access email notification image attachment data", "err", err)
|
2022-05-23 23:08:28 +08:00
|
|
|
}
|
|
|
|
|
}
|
2022-05-26 13:29:56 +08:00
|
|
|
return nil
|
|
|
|
|
}, 0, as...)
|
2022-05-23 23:08:28 +08:00
|
|
|
|
2021-05-17 16:05:09 +05:30
|
|
|
if tmplErr != nil {
|
2022-01-05 09:47:08 -06:00
|
|
|
en.log.Warn("failed to template email message", "err", tmplErr.Error())
|
2021-05-17 16:05:09 +05:30
|
|
|
}
|
|
|
|
|
|
2022-01-26 16:42:40 +01:00
|
|
|
if err := en.ns.SendEmailCommandHandlerSync(ctx, cmd); err != nil {
|
2021-03-24 14:20:44 +00:00
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (en *EmailNotifier) SendResolved() bool {
|
2021-04-22 21:46:26 +05:30
|
|
|
return !en.GetDisableResolveMessage()
|
2021-03-18 20:25:11 +05:30
|
|
|
}
|