Merge pull request #2948 from metalmatze/discord

notify/discord: Create Discord integration
This commit is contained in:
Matthias Loibl 2022-10-25 18:24:19 +02:00 committed by GitHub
commit 28c3df0173
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 321 additions and 3 deletions

View File

@ -163,9 +163,9 @@ var Assets = func() http.FileSystem {
"/templates/default.tmpl": &vfsgen۰CompressedFileInfo{ "/templates/default.tmpl": &vfsgen۰CompressedFileInfo{
name: "default.tmpl", name: "default.tmpl",
modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC), modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
uncompressedSize: 4554, uncompressedSize: 4905,
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xec\x57\xc1\x6e\xe3\x38\x0c\xbd\xe7\x2b\x08\xef\xa5\x39\xc4\xdd\x73\x81\x62\x51\x2c\x76\xe7\x52\x0c\x06\x29\x32\x97\xc1\xc0\x50\x6d\xc6\x55\x2b\x4b\xae\x44\xa7\x0d\x1c\xff\xfb\x40\xb6\x9b\x58\x91\x9d\xda\x41\xe7\x34\xbd\xd5\x2c\xf9\x48\x3d\x3e\x91\x4a\x59\x42\x82\x6b\x2e\x11\x82\x28\x62\x02\x35\x65\x4c\xb2\x14\x75\x00\x55\x75\xd3\xf9\x2e\x4b\x40\x99\x40\x55\xcd\x06\x43\x56\xcb\x5b\x1b\x55\x96\x10\xfe\xf7\x4a\xa8\x25\x13\xab\xe5\x2d\x54\xd5\xe5\x5f\x97\xb5\x9f\xf9\x47\x63\x8c\x7c\x83\xfa\xda\x3a\x2d\xdb\x0f\xd8\x41\xa1\xc5\x73\x81\x7a\xdb\x84\xb7\x89\xdc\x4c\xa6\xb8\x7f\xc4\x98\x6c\x86\x1f\x36\xfa\x8e\x18\x15\x06\x76\x40\x6a\x95\xe7\xa8\x9b\x50\xbe\x06\x7c\xde\xff\x33\x58\x73\xcd\x65\x6a\x63\xae\x6c\x4c\x7d\x20\x13\xfe\x5f\x5b\x61\x07\x02\x65\x37\xe3\x4f\xb0\x4e\x5f\xb4\x2a\xf2\x5b\x76\x8f\xc2\x84\x77\x4a\x13\x26\xdf\x18\xd7\x26\xfc\xce\x44\x81\x36\xe1\xa3\xe2\x12\x02\xb0\xa8\xd0\xa4\x4c\x09\x2e\x2c\x56\xf8\xaf\xca\x32\x25\x9b\xe0\x79\x6b\xeb\xe0\xcd\xa1\xaa\x2e\xca\x12\x5e\x38\x3d\xb8\xce\xe1\x12\x33\xb5\x41\x37\xfb\x57\x96\xa1\x69\x19\xed\xcb\xbe\x2f\x7c\xbe\xff\x6b\xa0\x4d\x09\x9a\x58\xf3\x9c\xb8\x92\xc1\x09\x8e\x09\x5f\xa9\x69\x69\x24\xb8\xa1\xd6\x55\x33\x99\x22\x84\x50\x55\x4d\x5d\x57\xb3\x83\xd1\xe7\xc9\xb2\xb2\xa8\x89\xb4\xe5\xdb\xaf\x6b\xd8\x1f\xa0\x2d\xac\x49\x7e\x23\xa5\x22\x66\x6b\x72\x20\x3b\xe6\xf3\x70\xef\x54\xa1\x63\xbc\x6a\x9a\x89\x12\x35\x23\xa5\x1b\x25\xce\x7a\x88\x72\x38\x30\x82\xc5\x4f\x61\x82\x6b\x56\x08\x0a\x89\x93\xc0\x96\x05\xc2\x2c\x17\x8c\x5c\x2d\x86\x43\x94\xbb\x38\x85\xb1\xb7\x21\xeb\x83\x72\xef\xdc\x48\xbc\x35\x13\xe2\x9e\xc5\x4f\x1e\x5e\x6f\xf9\x16\x14\x76\xf0\x9e\xa3\xe0\xf2\x69\x74\x05\x71\x5b\x01\x4f\x82\x71\x01\xb9\x46\xab\xae\x91\xde\x9d\x82\x4e\x32\x56\x8f\x9c\x91\x25\xf3\x58\x49\xcc\xd4\x23\x0f\xc6\xfb\x17\x5a\x8c\xad\x78\xfc\xe1\xd6\x4a\x51\x33\x60\x07\x44\x98\xdb\xa3\x25\x05\x6d\xf7\x21\xfe\xfd\x9d\x26\x47\x1f\x31\x16\x1c\x25\x9d\x2f\xc8\x21\xc4\xc3\x12\x38\xaf\x67\x3e\x2e\x97\x86\x98\x8c\xd1\xf4\xe0\x7a\x03\x2b\x1c\x66\x55\xe5\x26\x45\xc9\x71\x0f\x9c\xa1\x31\x2c\x3d\xef\x7e\x7b\x60\x7e\x87\xda\xf9\x3e\x30\xce\x7a\x07\xfa\xec\x68\x9d\x38\xfb\x6a\x0e\x7f\xc3\xa2\xaa\x66\x8d\x11\x1a\x63\x3d\x38\x4f\x33\xe2\x2e\xbd\x3a\xc9\xa2\x73\xa2\x9e\x7c\x4b\x34\x4a\x6c\x30\x39\xca\xf8\x66\x1e\x9f\xf3\x2d\xc2\xcb\xba\x18\x43\xa9\xa9\xe7\xf8\x74\x35\x39\x5d\x7f\xc1\xf8\x81\xd1\xd4\x9e\xcf\x3e\xfb\x77\xa2\x7f\xdd\x77\xe1\x4a\x0b\x0f\xaf\xb7\x3f\x03\x5d\x3f\xea\x0f\xa9\xc8\x2e\xcb\xc1\x49\xea\xbb\xe7\x4c\xd3\x76\x82\x3f\xb1\x74\xac\x37\x4b\x51\x52\x74\xbc\xe2\x5c\x7d\x6d\x78\x4c\x4a\xab\xdc\x1c\x64\x4b\x8c\x30\x72\x85\xf6\xa9\xa5\x69\xb3\xc0\x67\x15\x25\x71\xda\x46\x09\x37\xb9\x60\xdb\x68\xe0\x35\xf5\xfe\xe0\xf6\x91\x33\x25\x39\x29\x4b\x48\x44\x4a\x89\x89\x2b\xd1\xd9\x5d\x85\x79\x50\x1b\xd4\x1f\xf0\x7e\xf4\xa0\x7e\xbf\x9e\x3e\x46\x4e\xe3\xd5\xf4\x71\x62\xea\xe4\x1c\xc1\xe4\xe1\x4d\x37\x65\xa7\x74\x5f\x73\xb2\x73\xd9\x0f\xbf\x4a\xa7\xff\x46\xe8\xe0\x7c\xb6\x77\x4a\x7b\xbb\x2c\x12\x0a\x4c\x35\xcb\xfa\xa8\xfc\x93\x48\xf9\x15\x00\x00\xff\xff\xf5\xfe\x1f\x22\xca\x11\x00\x00"), compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xec\x57\x41\x6f\xdb\x3a\x0c\xbe\xe7\x57\x10\x79\x97\xe6\x10\xf7\x9d\x0b\x14\x0f\xc5\xc3\xb6\x4b\x31\x0c\x29\xb2\xcb\x30\x18\xaa\xcd\xb8\x6a\x65\xc9\x95\xe8\xb4\x81\xe3\xff\x3e\xc8\x76\x13\x2b\xb2\x53\x3b\xc8\x4e\xed\xad\x66\xc9\x8f\xd4\xc7\x4f\xa4\x52\x14\x10\xe3\x8a\x4b\x84\x69\x18\x32\x81\x9a\x52\x26\x59\x82\x7a\x0a\x65\x79\xd3\xfa\x2e\x0a\x40\x19\x43\x59\x4e\x7a\x43\x96\x8b\x5b\x1b\x55\x14\x10\x7c\x79\x25\xd4\x92\x89\xe5\xe2\x16\xca\xf2\xf2\x9f\xcb\xca\xcf\xfc\xa7\x31\x42\xbe\x46\x7d\x6d\x9d\x16\xcd\x07\x6c\x21\xd7\xe2\x39\x47\xbd\xa9\xc3\x9b\x44\x6e\x26\x93\xdf\x3f\x62\x44\x36\xc3\x2f\x1b\x7d\x47\x8c\x72\x03\x5b\x20\xb5\xcc\x32\xd4\x75\x28\x5f\x01\x3e\xef\xfe\x39\x5d\x71\xcd\x65\x62\x63\xae\x6c\x4c\x75\x20\x13\x7c\xad\xac\xb0\x05\x81\xb2\x9d\xf1\x37\x58\xa7\x6f\x5a\xe5\xd9\x2d\xbb\x47\x61\x82\x3b\xa5\x09\xe3\x1f\x8c\x6b\x13\xfc\x64\x22\x47\x9b\xf0\x51\x71\x09\x53\xb0\xa8\x50\xa7\x4c\x08\x2e\x2c\x56\xf0\xbf\x4a\x53\x25\xeb\xe0\x59\x63\x6b\xe1\xcd\xa0\x2c\x2f\x8a\x02\x5e\x38\x3d\xb8\xce\xc1\x02\x53\xb5\x46\x37\xfb\x77\x96\xa2\x69\x18\xed\xca\xbe\x2b\x7c\xb6\xfb\xab\xa7\x4d\x31\x9a\x48\xf3\x8c\xb8\x92\xd3\x23\x1c\x13\xbe\x52\xdd\xd2\x50\x70\x43\x8d\xab\x66\x32\x41\x08\xa0\x2c\xeb\xba\xae\x26\x7b\xa3\xcf\x93\x65\x65\x5e\x11\x69\xcb\xb7\x5f\xd7\xb0\x3b\x40\x53\x58\x9d\xfc\x46\x4a\x45\xcc\xd6\xe4\x40\xb6\xcc\xa7\xe1\xde\xa9\x5c\x47\x78\x55\x37\x13\x25\x6a\x46\x4a\xd7\x4a\x9c\x74\x10\xe5\x70\x60\x04\x8b\x9e\x82\x18\x57\x2c\x17\x14\x10\x27\x81\x0d\x0b\x84\x69\x26\x18\xb9\x5a\x0c\xfa\x28\x77\x71\x72\x63\x6f\x43\xda\x05\xe5\xde\xb9\x81\x78\x2b\x26\xc4\x3d\x8b\x9e\x3c\xbc\xce\xf2\x2d\x28\x6c\xe1\x3d\x47\xc1\xe5\xd3\xe0\x0a\xa2\xa6\x02\x1e\x4f\x87\x05\x64\x1a\xad\xba\x06\x7a\xb7\x0a\x3a\xca\x58\x35\x72\x06\x96\xcc\x23\x25\x31\x55\x8f\x7c\x3a\xdc\x3f\xd7\x62\x68\xc5\xc3\x0f\xb7\x52\x8a\xea\x01\xdb\x23\xc2\xcc\x1e\x2d\xce\x69\xb3\x0b\xf1\xef\xef\x38\x39\xfa\x88\x91\xe0\x28\xe9\x74\x41\xf6\x21\xee\x97\xc0\x69\x3d\xf3\x71\xb9\x34\xc4\x64\x84\xa6\x03\xd7\x1b\x58\x41\x3f\xab\x2a\x33\x09\x4a\x8e\x3b\xe0\x14\x8d\x61\xc9\x69\xf7\xdb\x03\xf3\x3b\xd4\xcc\xf7\x9e\x71\xd6\x39\xd0\x27\x07\xeb\xc4\xd9\x57\x33\xf8\x17\xe6\x65\x39\xa9\x8d\x50\x1b\xab\xc1\x79\x9c\x11\x77\xe9\x55\x49\xe6\xad\x13\x75\xe4\x5b\xa0\x51\x62\x8d\xf1\x41\xc6\x37\xf3\xf0\x9c\x6f\x11\x5e\xd6\xf9\x10\x4a\x4d\x35\xc7\xc7\xab\xc9\xe9\xfa\x0b\x46\x0f\x8c\xc6\xf6\x7c\xf2\xd9\xbf\x23\xfd\x6b\xbf\x0b\x97\x5a\x78\x78\x9d\xfd\xe9\xe9\xfa\x41\x7f\x48\x85\x76\x59\xf6\x4e\x52\xdf\x3d\x63\x9a\x36\x23\xfc\x89\x25\x43\xbd\x59\x82\x92\xc2\xc3\x15\xe7\xea\x6b\xcd\x23\x52\x5a\x65\x66\x2f\x5b\x62\x84\xa1\x2b\xb4\x4f\x2d\x8d\x9b\x05\x3e\xab\x28\x89\xd3\x26\x8c\xb9\xc9\x04\xdb\x84\x3d\xaf\xa9\xf7\x07\xb7\x8f\x9c\x2a\xc9\x49\x59\x42\x42\x52\x4a\x8c\x5c\x89\xce\xee\xca\xcd\x83\x5a\xa3\x3e\xc3\xfb\xd1\x83\xfa\xfb\x7a\x3a\x8f\x9c\x86\xab\xe9\x7c\x62\x6a\xe5\x1c\xc0\xe4\xfe\x4d\x37\x66\xa7\xb4\x5f\x73\xb2\x75\xd9\xf7\xbf\x4a\xc7\xff\x46\x68\xe1\x7c\xb6\x77\x4c\x7b\xdb\x2c\x12\x0a\x4c\x34\x4b\xbb\xa8\xfc\xb0\xa4\xc4\xdc\x44\x4a\xc7\x67\x18\x44\x87\x48\x1f\x95\xdd\x3f\x01\x00\x00\xff\xff\xcf\xc2\xdd\x36\x29\x13\x00\x00"),
}, },
"/templates/email.tmpl": &vfsgen۰CompressedFileInfo{ "/templates/email.tmpl": &vfsgen۰CompressedFileInfo{
name: "email.tmpl", name: "email.tmpl",

View File

@ -49,6 +49,7 @@ import (
"github.com/prometheus/alertmanager/inhibit" "github.com/prometheus/alertmanager/inhibit"
"github.com/prometheus/alertmanager/nflog" "github.com/prometheus/alertmanager/nflog"
"github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/notify/discord"
"github.com/prometheus/alertmanager/notify/email" "github.com/prometheus/alertmanager/notify/email"
"github.com/prometheus/alertmanager/notify/opsgenie" "github.com/prometheus/alertmanager/notify/opsgenie"
"github.com/prometheus/alertmanager/notify/pagerduty" "github.com/prometheus/alertmanager/notify/pagerduty"
@ -173,6 +174,10 @@ func buildReceiverIntegrations(nc *config.Receiver, tmpl *template.Template, log
for i, c := range nc.TelegramConfigs { for i, c := range nc.TelegramConfigs {
add("telegram", i, c, func(l log.Logger) (notify.Notifier, error) { return telegram.New(c, tmpl, l) }) add("telegram", i, c, func(l log.Logger) (notify.Notifier, error) { return telegram.New(c, tmpl, l) })
} }
for i, c := range nc.DiscordConfigs {
add("discord", i, c, func(l log.Logger) (notify.Notifier, error) { return discord.New(c, tmpl, l) })
}
if errs.Len() > 0 { if errs.Len() > 0 {
return nil, &errs return nil, &errs
} }

View File

@ -248,6 +248,9 @@ func resolveFilepaths(baseDir string, cfg *Config) {
for _, cfg := range receiver.TelegramConfigs { for _, cfg := range receiver.TelegramConfigs {
cfg.HTTPConfig.SetDirectory(baseDir) cfg.HTTPConfig.SetDirectory(baseDir)
} }
for _, cfg := range receiver.DiscordConfigs {
cfg.HTTPConfig.SetDirectory(baseDir)
}
} }
} }
@ -502,6 +505,14 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
telegram.APIUrl = c.Global.TelegramAPIUrl telegram.APIUrl = c.Global.TelegramAPIUrl
} }
} }
for _, discord := range rcv.DiscordConfigs {
if discord.HTTPConfig == nil {
discord.HTTPConfig = c.Global.HTTPConfig
}
if discord.WebhookURL == nil {
return fmt.Errorf("no discord webhook URL provided")
}
}
names[rcv.Name] = struct{}{} names[rcv.Name] = struct{}{}
} }
@ -856,6 +867,7 @@ type Receiver struct {
// A unique identifier for this receiver. // A unique identifier for this receiver.
Name string `yaml:"name" json:"name"` Name string `yaml:"name" json:"name"`
DiscordConfigs []*DiscordConfig `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"`
EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"` EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"`
PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"`
SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"`

View File

@ -33,6 +33,15 @@ var (
}, },
} }
// DefaultDiscordConfig defines default values for Discord configurations.
DefaultDiscordConfig = DiscordConfig{
NotifierConfig: NotifierConfig{
VSendResolved: true,
},
Title: `{{ template "discord.default.title" . }}`,
Message: `{{ template "discord.default.message" . }}`,
}
// DefaultEmailConfig defines default values for Email configurations. // DefaultEmailConfig defines default values for Email configurations.
DefaultEmailConfig = EmailConfig{ DefaultEmailConfig = EmailConfig{
NotifierConfig: NotifierConfig{ NotifierConfig: NotifierConfig{
@ -157,6 +166,24 @@ func (nc *NotifierConfig) SendResolved() bool {
return nc.VSendResolved return nc.VSendResolved
} }
// DiscordConfig configures notifications via Discord.
type DiscordConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
WebhookURL *SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"`
Title string `yaml:"title,omitempty" json:"title,omitempty"`
Message string `yaml:"message,omitempty" json:"message,omitempty"`
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (c *DiscordConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultDiscordConfig
type plain DiscordConfig
return unmarshal((*plain)(c))
}
// EmailConfig configures notifications via mail. // EmailConfig configures notifications via mail.
type EmailConfig struct { type EmailConfig struct {
NotifierConfig `yaml:",inline" json:",inline"` NotifierConfig `yaml:",inline" json:",inline"`

133
notify/discord/discord.go Normal file
View File

@ -0,0 +1,133 @@
// Copyright 2021 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package discord
import (
"bytes"
"context"
"encoding/json"
"net/http"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
commoncfg "github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
)
const (
colorRed = 0x992D22
colorGreen = 0x2ECC71
colorGrey = 0x95A5A6
)
// Notifier implements a Notifier for Discord notifications.
type Notifier struct {
conf *config.DiscordConfig
tmpl *template.Template
logger log.Logger
client *http.Client
retrier *notify.Retrier
webhookURL *config.SecretURL
}
// New returns a new Discord notifier.
func New(c *config.DiscordConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) {
client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "discord", httpOpts...)
if err != nil {
return nil, err
}
n := &Notifier{
conf: c,
tmpl: t,
logger: l,
client: client,
retrier: &notify.Retrier{},
webhookURL: c.WebhookURL,
}
return n, nil
}
type webhook struct {
Content string `json:"content"`
Embeds []webhookEmbed `json:"embeds"`
}
type webhookEmbed struct {
Title string `json:"title"`
Description string `json:"description"`
Color int `json:"color"`
}
// Notify implements the Notifier interface.
func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
key, err := notify.ExtractGroupKey(ctx)
if err != nil {
return false, err
}
level.Debug(n.logger).Log("incident", key)
alerts := types.Alerts(as...)
data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger)
tmpl := notify.TmplText(n.tmpl, data, &err)
if err != nil {
return false, err
}
title := tmpl(n.conf.Title)
if err != nil {
return false, err
}
description := tmpl(n.conf.Message)
if err != nil {
return false, err
}
color := colorGrey
if alerts.Status() == model.AlertFiring {
color = colorRed
}
if alerts.Status() == model.AlertResolved {
color = colorGreen
}
w := webhook{
Embeds: []webhookEmbed{{
Title: title,
Description: description,
Color: color,
}},
}
var payload bytes.Buffer
if err = json.NewEncoder(&payload).Encode(w); err != nil {
return false, err
}
resp, err := notify.PostJSON(ctx, n.client, n.webhookURL.String(), &payload)
if err != nil {
return true, notify.RedactURL(err)
}
shouldRetry, err := n.retrier.Check(resp.StatusCode, resp.Body)
if err != nil {
return shouldRetry, err
}
return false, nil
}

View File

@ -0,0 +1,129 @@
// Copyright 2021 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package discord
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"
"github.com/go-kit/log"
commoncfg "github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/notify/test"
"github.com/prometheus/alertmanager/types"
)
// This is a test URL that has been modified to not be valid.
var testWebhookURL, _ = url.Parse("https://discord.com/api/webhooks/971139602272503183/78ZWZ4V3xwZUBKRFF-G9m1nRtDtNTChl_WzW6Q4kxShjSB02oLSiPTPa8TS2tTGO9EYf")
func TestDiscordRetry(t *testing.T) {
notifier, err := New(
&config.DiscordConfig{
WebhookURL: &config.SecretURL{URL: testWebhookURL},
HTTPConfig: &commoncfg.HTTPClientConfig{},
},
test.CreateTmpl(t),
log.NewNopLogger(),
)
require.NoError(t, err)
for statusCode, expected := range test.RetryTests(test.DefaultRetryCodes()) {
actual, _ := notifier.retrier.Check(statusCode, nil)
require.Equal(t, expected, actual, fmt.Sprintf("retry - error on status %d", statusCode))
}
}
func TestDiscordTemplating(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
dec := json.NewDecoder(r.Body)
out := make(map[string]interface{})
err := dec.Decode(&out)
if err != nil {
panic(err)
}
}))
defer srv.Close()
u, _ := url.Parse(srv.URL)
for _, tc := range []struct {
title string
cfg *config.DiscordConfig
retry bool
errMsg string
}{
{
title: "full-blown message",
cfg: &config.DiscordConfig{
Title: `{{ template "discord.default.title" . }}`,
Message: `{{ template "discord.default.message" . }}`,
},
retry: false,
},
{
title: "title with templating errors",
cfg: &config.DiscordConfig{
Title: "{{ ",
},
errMsg: "template: :1: unclosed action",
},
{
title: "message with templating errors",
cfg: &config.DiscordConfig{
Title: `{{ template "discord.default.title" . }}`,
Message: "{{ ",
},
errMsg: "template: :1: unclosed action",
},
} {
t.Run(tc.title, func(t *testing.T) {
tc.cfg.WebhookURL = &config.SecretURL{URL: u}
tc.cfg.HTTPConfig = &commoncfg.HTTPClientConfig{}
pd, err := New(tc.cfg, test.CreateTmpl(t), log.NewNopLogger())
require.NoError(t, err)
ctx := context.Background()
ctx = notify.WithGroupKey(ctx, "1")
ok, err := pd.Notify(ctx, []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{
"lbl1": "val1",
},
StartsAt: time.Now(),
EndsAt: time.Now().Add(time.Hour),
},
},
}...)
if tc.errMsg == "" {
require.NoError(t, err)
} else {
require.Error(t, err)
require.Contains(t, err.Error(), tc.errMsg)
}
require.Equal(t, tc.retry, ok)
})
}
}

View File

@ -111,4 +111,16 @@ Alerts Firing:
Alerts Resolved: Alerts Resolved:
{{ template "__text_alert_list" .Alerts.Resolved }} {{ template "__text_alert_list" .Alerts.Resolved }}
{{ end }} {{ end }}
{{ end }} {{ end }}
{{ define "discord.default.title" }}{{ template "__subject" . }}{{ end }}
{{ define "discord.default.message" }}
{{ if gt (len .Alerts.Firing) 0 }}
Alerts Firing:
{{ template "__text_alert_list" .Alerts.Firing }}
{{ end }}
{{ if gt (len .Alerts.Resolved) 0 }}
Alerts Resolved:
{{ template "__text_alert_list" .Alerts.Resolved }}
{{ end }}
{{ end }}