diff --git a/go.mod b/go.mod index 0c138c0433e..4b8a65616a9 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,6 @@ require ( require ( cloud.google.com/go/storage v1.40.0 - github.com/Masterminds/sprig/v3 v3.2.1 github.com/alecthomas/chroma/v2 v2.12.0 github.com/alecthomas/kingpin/v2 v2.4.0 github.com/aws/aws-sdk-go v1.53.16 @@ -64,7 +63,7 @@ require ( github.com/google/go-github/v57 v57.0.0 github.com/google/uuid v1.6.0 github.com/grafana-tools/sdk v0.0.0-20220919052116-6562121319fc - github.com/grafana/alerting v0.0.0-20240607182251-835aff588914 + github.com/grafana/alerting v0.0.0-20240618145205-cdfc0e849e0b github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/vault/api v1.10.0 @@ -87,7 +86,6 @@ require ( golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f google.golang.org/api v0.183.0 google.golang.org/protobuf v1.34.1 - gopkg.in/mail.v2 v2.3.1 sigs.k8s.io/kustomize/kyaml v0.16.0 ) @@ -100,6 +98,7 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.1.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.1 // indirect github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect @@ -130,6 +129,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect + gopkg.in/mail.v2 v2.3.1 // indirect gopkg.in/telebot.v3 v3.2.1 // indirect k8s.io/apimachinery v0.29.3 // indirect k8s.io/client-go v0.29.3 // indirect diff --git a/go.sum b/go.sum index 03b4c42d396..7e91ad1c2fe 100644 --- a/go.sum +++ b/go.sum @@ -513,8 +513,8 @@ github.com/gosimple/slug v1.1.1 h1:fRu/digW+NMwBIP+RmviTK97Ho/bEj/C9swrCspN3D4= github.com/gosimple/slug v1.1.1/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0= github.com/grafana-tools/sdk v0.0.0-20220919052116-6562121319fc h1:PXZQA2WCxe85Tnn+WEvr8fDpfwibmEPgfgFEaC87G24= github.com/grafana-tools/sdk v0.0.0-20220919052116-6562121319fc/go.mod h1:AHHlOEv1+GGQ3ktHMlhuTUwo3zljV3QJbC0+8o2kn+4= -github.com/grafana/alerting v0.0.0-20240607182251-835aff588914 h1:WXLbSnnomltxdNcE20CI8RD8quZ/L0YpXP0WK+0S1BU= -github.com/grafana/alerting v0.0.0-20240607182251-835aff588914/go.mod h1:U7Ta3K4T7jVgqGSYuPsfuPKHFiL2GbCZSHa3nHjmCos= +github.com/grafana/alerting v0.0.0-20240618145205-cdfc0e849e0b h1:M86NakDlsYjYqEp/1kR54mhTjxYXTa59vl2pWhgi3f4= +github.com/grafana/alerting v0.0.0-20240618145205-cdfc0e849e0b/go.mod h1:my6It91EE/AanRkHj3hVjglp3mhuFNxSptm1etLSKHg= github.com/grafana/dskit v0.0.0-20240611171734-87c7e9e7a4fe h1:PpBnljz9oYSp05pCcQl27n69cL9fNeFH+wkG/ga6oGs= github.com/grafana/dskit v0.0.0-20240611171734-87c7e9e7a4fe/go.mod h1:HvSf3uf8Ps2vPpzHeAFyZTdUcbVr+Rxpq1xcx7J/muc= github.com/grafana/e2e v0.1.2-0.20240118170847-db90b84177fc h1:BW+LjKJDz0So5LI8UZfW5neWeKpSkWqhmGjQFzcFfLM= diff --git a/pkg/alertmanager/alertmanager.go b/pkg/alertmanager/alertmanager.go index 39822e0dcbe..6f59d15ba36 100644 --- a/pkg/alertmanager/alertmanager.go +++ b/pkg/alertmanager/alertmanager.go @@ -6,13 +6,11 @@ package alertmanager import ( - "bytes" "context" "crypto/md5" "encoding/binary" "encoding/json" "fmt" - htmlTemplate "html/template" "net/http" "net/url" "path" @@ -21,7 +19,6 @@ import ( "sync" "time" - "github.com/Masterminds/sprig/v3" "github.com/go-kit/log" "github.com/go-kit/log/level" "github.com/grafana/alerting/definition" @@ -29,6 +26,7 @@ import ( alertingNotify "github.com/grafana/alerting/notify" "github.com/grafana/alerting/notify/nfstatus" alertingReceivers "github.com/grafana/alerting/receivers" + alertingTemplates "github.com/grafana/alerting/templates" "github.com/grafana/dskit/flagext" "github.com/pkg/errors" "github.com/prometheus/alertmanager/api" @@ -317,19 +315,6 @@ func New(cfg *Config, reg *prometheus.Registry) (*Alertmanager, error) { am.dispatcherMetrics = dispatch.NewDispatcherMetrics(true, am.registry) - mailTemplates = htmlTemplate.New("name") - mailTemplates.Funcs(htmlTemplate.FuncMap{ - "Subject": subjectTemplateFunc, - "HiddenSubject": hiddenSubjectTemplateFunc, - "__dangerouslyInjectHTML": __dangerouslyInjectHTML, - }) - mailTemplates.Funcs(sprig.FuncMap()) - if _, err := mailTemplates.ParseFiles("./templates/ng_alert_notification.html"); err != nil { - return nil, err - } - - fmt.Println("Parsed:", mailTemplates.Templates()) - //TODO: From this point onward, the alertmanager _might_ receive requests - we need to make sure we've settled and are ready. return am, nil } @@ -389,6 +374,10 @@ func (am *Alertmanager) ApplyConfig(userID string, conf *definition.PostableApiA } tmpl.ExternalURL = am.cfg.ExternalURL + if err := tmpl.Parse(strings.NewReader(alertingTemplates.DefaultTemplateString)); err != nil { + return err + } + cfg := definition.GrafanaToUpstreamConfig(conf) am.api.Update(&cfg, func(_ model.LabelSet) {}) @@ -547,27 +536,22 @@ func buildIntegrationsMap(gCfg *config.GlobalConfig, externalURL string, nc []*d func buildGrafanaReceiverIntegrations(gCfg *config.GlobalConfig, externalURL string, rcv *definition.PostableApiReceiver, tmpl *template.Template, logger log.Logger) ([]*nfstatus.Integration, error) { loggerFactory := newLoggerFactory(logger) - smtpCfg := SmtpConfig{ - AuthPassword: string(gCfg.SMTPAuthPassword), - AuthUser: gCfg.SMTPAuthUsername, - CertFile: gCfg.HTTPConfig.TLSConfig.CertFile, - ContentTypes: []string{}, // (?) - EhloIdentity: gCfg.SMTPHello, - ExternalURL: externalURL, - FromAddress: gCfg.SMTPFrom, - FromName: "Grafana", - Host: gCfg.SMTPSmarthost.String(), - KeyFile: gCfg.HTTPConfig.TLSConfig.KeyFile, - SkipVerify: !gCfg.SMTPRequireTLS, - StartTLSPolicy: "", // (?) - StaticHeaders: map[string]string{}, // (?) + emailCfg := alertingReceivers.EmailSenderConfig{ + AuthPassword: string(gCfg.SMTPAuthPassword), + AuthUser: gCfg.SMTPAuthUsername, + CertFile: gCfg.HTTPConfig.TLSConfig.CertFile, + EhloIdentity: gCfg.SMTPHello, + ExternalURL: externalURL, + FromAddress: gCfg.SMTPFrom, + FromName: "Grafana", + Host: gCfg.SMTPSmarthost.String(), + KeyFile: gCfg.HTTPConfig.TLSConfig.KeyFile, + SkipVerify: !gCfg.SMTPRequireTLS, + StaticHeaders: map[string]string{}, // (?) } whFn := func(n alertingReceivers.Metadata) (alertingReceivers.WebhookSender, error) { - return NewSender(logger, smtpCfg), nil - } - emailFn := func(n alertingReceivers.Metadata) (alertingReceivers.EmailSender, error) { - return NewSender(logger, smtpCfg), nil + return NewSender(logger), nil } // The decrypt functions and the context are used to decrypt the configuration. @@ -583,7 +567,7 @@ func buildGrafanaReceiverIntegrations(gCfg *config.GlobalConfig, externalURL str &images.UnavailableProvider{}, // TODO: include images in notifications loggerFactory, whFn, - emailFn, + alertingReceivers.NewEmailSenderFactory(emailCfg), 1, // orgID is always 1. version.Version, ) @@ -860,43 +844,3 @@ func alertSize(alert model.Alert) int { size += len(alert.GeneratorURL) return size } - -// hiddenSubjectTemplateFunc sets the subject template (value) on the map represented by `.Subject.` (obj) so that it can be compiled and executed later. -// It returns a blank string, so there will be no resulting value left in place of the template. -func hiddenSubjectTemplateFunc(obj map[string]any, value string) string { - obj["value"] = value - return "" -} - -// subjectTemplateFunc does the same thing has hiddenSubjectTemplateFunc, but in addition it executes and returns the subject template using the data represented in `.TemplateData` (data) -// This results in the template being replaced by the subject string. -func subjectTemplateFunc(obj map[string]any, data map[string]any, value string) string { - obj["value"] = value - - titleTmpl, err := htmlTemplate.New("title").Parse(value) - if err != nil { - return "" - } - - var buf bytes.Buffer - err = titleTmpl.ExecuteTemplate(&buf, "title", data) - if err != nil { - return "" - } - - subj := buf.String() - // Since we have already executed the template, save it to subject data so we don't have to do it again later on - obj["executed_template"] = subj - return subj -} - -// __dangerouslyInjectHTML allows marking areas of am email template as HTML safe, this will _not_ sanitize the string and will allow HTML snippets to be rendered verbatim. -// Use with absolute care as this _could_ allow for XSS attacks when used in an insecure context. -// -// It's safe to ignore gosec warning G203 when calling this function in an HTML template because we assume anyone who has write access -// to the email templates folder is an administrator. -// -// nolint:gosec -func __dangerouslyInjectHTML(s string) htmlTemplate.HTML { - return htmlTemplate.HTML(s) -} diff --git a/pkg/alertmanager/sender.go b/pkg/alertmanager/sender.go index 1e95096bc14..e81a1331096 100644 --- a/pkg/alertmanager/sender.go +++ b/pkg/alertmanager/sender.go @@ -51,7 +51,7 @@ type Sender struct { smtp SmtpConfig } -func NewSender(log log.Logger, cfg SmtpConfig) *Sender { +func NewSender(log log.Logger) *Sender { netTransport := &http.Transport{ TLSClientConfig: &tls.Config{ Renegotiation: tls.RenegotiateFreelyAsClient, @@ -67,9 +67,8 @@ func NewSender(log log.Logger, cfg SmtpConfig) *Sender { Transport: netTransport, } return &Sender{ - c: c, - log: log, - smtp: cfg, + c: c, + log: log, } } diff --git a/pkg/alertmanager/sender_test.go b/pkg/alertmanager/sender_test.go index db4c7bfe9d4..7bdf720f6a5 100644 --- a/pkg/alertmanager/sender_test.go +++ b/pkg/alertmanager/sender_test.go @@ -26,7 +26,7 @@ func TestSendWebhook(t *testing.T) { got = r w.WriteHeader(http.StatusOK) })) - s := NewSender(alertingLogging.FakeLogger{}, SmtpConfig{}) + s := NewSender(alertingLogging.FakeLogger{}) // The method should be either POST or PUT. cmd := alertingReceivers.SendWebhookSettings{ diff --git a/pkg/alertmanager/sender_email.go b/vendor/github.com/grafana/alerting/receivers/email_sender.go similarity index 57% rename from pkg/alertmanager/sender_email.go rename to vendor/github.com/grafana/alerting/receivers/email_sender.go index 0beaba5d64c..bb33da95b54 100644 --- a/pkg/alertmanager/sender_email.go +++ b/vendor/github.com/grafana/alerting/receivers/email_sender.go @@ -1,9 +1,10 @@ -package alertmanager +package receivers import ( "bytes" "context" "crypto/tls" + _ "embed" "fmt" "html/template" "io" @@ -12,12 +13,56 @@ import ( "strconv" "strings" - alertingReceivers "github.com/grafana/alerting/receivers" - "github.com/grafana/mimir/pkg/util/version" + "github.com/Masterminds/sprig/v3" + "github.com/grafana/alerting/templates" gomail "gopkg.in/mail.v2" ) -var mailTemplates *template.Template +type defaultEmailSender struct { + cfg EmailSenderConfig + tmpl *template.Template +} + +type EmailSenderConfig struct { + AuthPassword string + AuthUser string + CertFile string + EhloIdentity string + ExternalURL string + FromName string + FromAddress string + Host string + KeyFile string + SkipVerify bool + StaticHeaders map[string]string + Version string +} + +//go:embed templates/ng_alert_notification.html +var defaultEmailTemplate string + +// NewEmailSenderFactory takes a configuration and returns a new EmailSender factory function. +func NewEmailSenderFactory(cfg EmailSenderConfig) func(n Metadata) (EmailSender, error) { + return func(n Metadata) (EmailSender, error) { + tmpl, err := template.New("ng_alert_notification"). + Funcs(template.FuncMap{ + "Subject": subjectTemplateFunc, + "HiddenSubject": hiddenSubjectTemplateFunc, + "__dangerouslyInjectHTML": __dangerouslyInjectHTML, + }). + Funcs(template.FuncMap(templates.DefaultFuncs)). + Funcs(sprig.FuncMap()). + Parse(defaultEmailTemplate) + if err != nil { + return nil, err + } + + return &defaultEmailSender{ + cfg: cfg, + tmpl: tmpl, + }, nil + } +} // AttachedFile is a definition of the attached files without path type AttachedFile struct { @@ -52,8 +97,7 @@ type Message struct { } // SendEmail implements alertingReceivers.EmailSender. -func (s *Sender) SendEmail(ctx context.Context, cmd *alertingReceivers.SendEmailSettings) error { - fmt.Println("SendEmail() called!") +func (s *defaultEmailSender) SendEmail(ctx context.Context, cmd *SendEmailSettings) error { var attached []*AttachedFile if cmd.AttachedFiles != nil { attached = make([]*AttachedFile, 0, len(cmd.AttachedFiles)) @@ -78,7 +122,7 @@ func (s *Sender) SendEmail(ctx context.Context, cmd *alertingReceivers.SendEmail }) } -func (s *Sender) SendEmailCommandHandlerSync(ctx context.Context, cmd *SendEmailCommand) error { +func (s *defaultEmailSender) SendEmailCommandHandlerSync(ctx context.Context, cmd *SendEmailCommand) error { message, err := s.buildEmailMessage(&SendEmailCommand{ Data: cmd.Data, Info: cmd.Info, @@ -99,16 +143,16 @@ func (s *Sender) SendEmailCommandHandlerSync(ctx context.Context, cmd *SendEmail return err } -func (s *Sender) buildEmailMessage(cmd *SendEmailCommand) (*Message, error) { +func (s *defaultEmailSender) buildEmailMessage(cmd *SendEmailCommand) (*Message, error) { data := cmd.Data if data == nil { data = make(map[string]any, 10) } - setDefaultTemplateData(s.smtp.ExternalURL, data) + s.setDefaultTemplateData(data) var buffer bytes.Buffer - if err := mailTemplates.ExecuteTemplate(&buffer, cmd.Template, data); err != nil { + if err := s.tmpl.ExecuteTemplate(&buffer, cmd.Template, data); err != nil { return nil, err } @@ -141,7 +185,7 @@ func (s *Sender) buildEmailMessage(cmd *SendEmailCommand) (*Message, error) { } } - addr := mail.Address{Name: s.smtp.FromName, Address: s.smtp.FromAddress} + addr := mail.Address{Name: s.cfg.FromName, Address: s.cfg.FromAddress} return &Message{ To: cmd.To, SingleEmail: cmd.SingleEmail, @@ -154,9 +198,9 @@ func (s *Sender) buildEmailMessage(cmd *SendEmailCommand) (*Message, error) { }, nil } -func setDefaultTemplateData(externalURL string, data map[string]any) { - data["AppUrl"] = externalURL - data["BuildVersion"] = version.Version +func (s *defaultEmailSender) setDefaultTemplateData(data map[string]any) { + data["AppUrl"] = s.cfg.ExternalURL + data["BuildVersion"] = s.cfg.Version data["Subject"] = map[string]any{} dataCopy := map[string]any{} for k, v := range data { @@ -165,7 +209,7 @@ func setDefaultTemplateData(externalURL string, data map[string]any) { data["TemplateData"] = dataCopy } -func (s *Sender) Send(ctx context.Context, messages ...*Message) (int, error) { +func (s *defaultEmailSender) Send(ctx context.Context, messages ...*Message) (int, error) { // TODO: add // ctx, span := tracer.Start(ctx, "notifications.SmtpClient.Send", // trace.WithAttributes(attribute.Int("messages", len(messages))), @@ -209,8 +253,8 @@ func (s *Sender) Send(ctx context.Context, messages ...*Message) (int, error) { return sentEmailsCount, err } -func (s *Sender) createDialer() (*gomail.Dialer, error) { - host, port, err := net.SplitHostPort(s.smtp.Host) +func (s *defaultEmailSender) createDialer() (*gomail.Dialer, error) { + host, port, err := net.SplitHostPort(s.cfg.Host) if err != nil { return nil, err } @@ -220,30 +264,30 @@ func (s *Sender) createDialer() (*gomail.Dialer, error) { } tlsconfig := &tls.Config{ - InsecureSkipVerify: s.smtp.SkipVerify, + InsecureSkipVerify: s.cfg.SkipVerify, ServerName: host, } - if s.smtp.CertFile != "" { - cert, err := tls.LoadX509KeyPair(s.smtp.CertFile, s.smtp.KeyFile) + if s.cfg.CertFile != "" { + cert, err := tls.LoadX509KeyPair(s.cfg.CertFile, s.cfg.KeyFile) if err != nil { return nil, fmt.Errorf("could not load cert or key file: %w", err) } tlsconfig.Certificates = []tls.Certificate{cert} } - d := gomail.NewDialer(host, iPort, s.smtp.AuthUser, s.smtp.AuthPassword) + d := gomail.NewDialer(host, iPort, s.cfg.AuthUser, s.cfg.AuthPassword) d.TLSConfig = tlsconfig - d.LocalName = s.smtp.EhloIdentity + d.LocalName = s.cfg.EhloIdentity return d, nil } // buildEmail converts the Message DTO to a gomail message. -func (s *Sender) buildEmail(ctx context.Context, msg *Message) *gomail.Message { +func (s *defaultEmailSender) buildEmail(ctx context.Context, msg *Message) *gomail.Message { m := gomail.NewMessage() // add all static headers to the email message - for h, val := range s.smtp.StaticHeaders { + for h, val := range s.cfg.StaticHeaders { m.SetHeader(h, val) } m.SetHeader("From", msg.From) @@ -280,3 +324,43 @@ func setFiles( })) } } + +// hiddenSubjectTemplateFunc sets the subject template (value) on the map represented by `.Subject.` (obj) so that it can be compiled and executed later. +// It returns a blank string, so there will be no resulting value left in place of the template. +func hiddenSubjectTemplateFunc(obj map[string]any, value string) string { + obj["value"] = value + return "" +} + +// subjectTemplateFunc does the same thing has hiddenSubjectTemplateFunc, but in addition it executes and returns the subject template using the data represented in `.TemplateData` (data) +// This results in the template being replaced by the subject string. +func subjectTemplateFunc(obj map[string]any, data map[string]any, value string) string { + obj["value"] = value + + titleTmpl, err := template.New("title").Parse(value) + if err != nil { + return "" + } + + var buf bytes.Buffer + err = titleTmpl.ExecuteTemplate(&buf, "title", data) + if err != nil { + return "" + } + + subj := buf.String() + // Since we have already executed the template, save it to subject data so we don't have to do it again later on + obj["executed_template"] = subj + return subj +} + +// __dangerouslyInjectHTML allows marking areas of am email template as HTML safe, this will _not_ sanitize the string and will allow HTML snippets to be rendered verbatim. +// Use with absolute care as this _could_ allow for XSS attacks when used in an insecure context. +// +// It's safe to ignore gosec warning G203 when calling this function in an HTML template because we assume anyone who has write access +// to the email templates folder is an administrator. +// +// nolint:gosec +func __dangerouslyInjectHTML(s string) template.HTML { + return template.HTML(s) +} diff --git a/vendor/github.com/grafana/alerting/receivers/templates/ng_alert_notification.html b/vendor/github.com/grafana/alerting/receivers/templates/ng_alert_notification.html new file mode 100644 index 00000000000..43fabdb6787 --- /dev/null +++ b/vendor/github.com/grafana/alerting/receivers/templates/ng_alert_notification.html @@ -0,0 +1,1279 @@ + + + + + + {{ Subject .Subject .TemplateData "{{ .Title }}" }} + + {{ __dangerouslyInjectHTML `` }} + + {{ __dangerouslyInjectHTML `` }} + + + + {{ __dangerouslyInjectHTML `` }} + {{ __dangerouslyInjectHTML `` }} + {{ __dangerouslyInjectHTML `` }} + + + {{ __dangerouslyInjectHTML `` }} + + + + + {{ $numberOfFiringInstance := (len .Alerts.Firing) }} + {{ $numberOfResolvedAlerts := (len .Alerts.Resolved) }} + + + +
+ + {{ if $numberOfFiringInstance }} + + {{ $numberOfFiringInstance }} firing alert {{ $numberOfFiringInstance| plural "instance" "instances" }} + + {{ end }} + + + {{ if and $numberOfFiringInstance $numberOfResolvedAlerts }} +  and  + {{ end }} + + + {{ if $numberOfResolvedAlerts }} + + {{ $numberOfResolvedAlerts }} resolved alert {{ $numberOfResolvedAlerts| plural "instance" "instances" }} + + {{ end }} + +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ +
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + {{ if eq (.GroupLabels.SortedPairs.Names | join ",") "alertname,grafana_folder" }} + + + + {{ else if gt (len .GroupLabels.SortedPairs) 0 }} + + + + {{ end }} + +
+
+

📁 {{ .GroupLabels.grafana_folder }} › {{ .GroupLabels.alertname }}

+
+
+
+

📁 Grouped by 

+ + {{ range .GroupLabels.SortedPairs }} + + {{ .Name }}={{ .Value }} + + {{ end }} + +
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ if .Message }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+
+ + {{ range $line := (splitList "\n" .Message) }} + + {{ $line }}
+ + {{ end }} + +
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ else }}{{ if .Alerts.Firing }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+
+

🔥 {{ .Alerts.Firing | len }} firing instances

+
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ range .Alerts.Firing }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ Firing +
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+
{{ .Labels.alertname }}
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ if gt (len .GeneratorURL) 0 }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ View alert +
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }} + {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ if .ImageURL }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ + + +
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }}{{ if .EmbeddedImage }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ +
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + {{ if .Annotations.summary }} + + + + + + + {{ end }}{{ if .Annotations.description }} + + + + + + + {{ end }} + +
+
Summary
+
+
{{- .Annotations.summary -}}
+
+
Description
+
+
+ {{ range $line := (splitList "\n" .Annotations.description) }} + {{ $line }}
+ {{ end }} +
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ if .Values }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+
Values
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+
+ {{ range $refID, $value := .Values }} + {{ $refID }}={{ $value }}  {{ end }} +
+
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + {{ if .Labels.SortedPairs }} + + + + + + + {{ end }}{{ if (without .Annotations.SortedPairs.Names "description" "summary") }} + + + + + + + {{ end }} + +
+
Labels
+
+ + {{ range .Labels.SortedPairs }} + + + + + {{ end }} +
+ {{ .Name }} + {{ .Value }}
+
+
Annotations
+
+ + {{ range .Annotations.SortedPairs }} + {{ if and (ne .Name "description") (ne .Name "summary") }} + + + + + {{ end }} + {{ end }} +
+ {{ .Name }} + {{ .Value }}
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} + {{ if .SilenceURL }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ Silence +
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }}{{ if .Annotations.runbook_url }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ View runbook +
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }}{{ if .DashboardURL }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ View dashboard +
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }}{{ if .PanelURL }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ View panel +
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }} + {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+
Observed {{ ago .StartsAt }} before this notification was delivered, at {{ .StartsAt }}
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }}{{ end }}{{ if .Alerts.Resolved }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+
+

✅ {{ .Alerts.Resolved | len }} resolved instances

+
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ range .Alerts.Resolved }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+

Resolved

+
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+
{{ .Labels.alertname }}
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ if gt (len .GeneratorURL) 0 }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ View alert +
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }} + {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ if .ImageURL }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ + + +
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }}{{ if .EmbeddedImage }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ +
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + {{ if .Annotations.summary }} + + + + + + + {{ end }}{{ if .Annotations.description }} + + + + + + + {{ end }} + +
+
Summary
+
+
{{- .Annotations.summary -}}
+
+
Description
+
+
+ {{ range $line := (splitList "\n" .Annotations.description) }} + {{ $line }}
+ {{ end }} +
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ if .Values }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+
Values
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+
+ {{ range $refID, $value := .Values }} + {{ $refID }}={{ $value }}  {{ end }} +
+
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + {{ if .Labels.SortedPairs }} + + + + + + + {{ end }}{{ if (without .Annotations.SortedPairs.Names "description" "summary") }} + + + + + + + {{ end }} + +
+
Labels
+
+ + {{ range .Labels.SortedPairs }} + + + + + {{ end }} +
+ {{ .Name }} + {{ .Value }}
+
+
Annotations
+
+ + {{ range .Annotations.SortedPairs }} + {{ if and (ne .Name "description") (ne .Name "summary") }} + + + + + {{ end }} + {{ end }} +
+ {{ .Name }} + {{ .Value }}
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} + {{ if .SilenceURL }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ Silence +
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }}{{ if .Annotations.runbook_url }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ View runbook +
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }}{{ if .DashboardURL }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ View dashboard +
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }}{{ if .PanelURL }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ + + + + + +
+ View panel +
+
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }} + {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+
Observed {{ ago .StartsAt }} before this notification was delivered, at {{ .StartsAt }}
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} + {{ end }}{{ end }}{{ end }} + {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+ {{ __dangerouslyInjectHTML `` }} +
+ + + + + + +
+
© {{ now | date "2006" }} Grafana Labs. Sent by Grafana v{{ .BuildVersion }}.
+
+
+ {{ __dangerouslyInjectHTML `` }} +
+
+ {{ __dangerouslyInjectHTML `` }} +
+ + + diff --git a/vendor/modules.txt b/vendor/modules.txt index 3667ff3e0e8..7e303e1c51b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -577,7 +577,7 @@ github.com/gosimple/slug # github.com/grafana-tools/sdk v0.0.0-20220919052116-6562121319fc ## explicit; go 1.13 github.com/grafana-tools/sdk -# github.com/grafana/alerting v0.0.0-20240607182251-835aff588914 +# github.com/grafana/alerting v0.0.0-20240618145205-cdfc0e849e0b ## explicit; go 1.21 github.com/grafana/alerting/cluster github.com/grafana/alerting/definition