diff --git a/asset/assets_vfsdata.go b/asset/assets_vfsdata.go index 9eb94a83c0..3a33fa0308 100644 --- a/asset/assets_vfsdata.go +++ b/asset/assets_vfsdata.go @@ -163,9 +163,9 @@ var Assets = func() http.FileSystem { "/templates/default.tmpl": &vfsgen۰CompressedFileInfo{ name: "default.tmpl", modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC), - uncompressedSize: 4905, + uncompressedSize: 5233, - 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"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xec\x57\x41\x6f\xdb\x3c\x0c\xbd\xe7\x57\x10\xfe\x2e\xcd\x21\xee\x77\x2e\x50\x0c\xc5\xb0\xed\x52\x0c\x43\x8a\xec\x32\x0c\x86\x62\x33\xae\x5a\x59\x72\x25\x3a\x6d\xe0\xf8\xbf\x0f\xb2\xdd\xc4\x8e\xec\xd4\x0e\xb2\xd3\x72\xab\x59\xf2\x91\x7a\x7c\x22\x95\x3c\x87\x08\x57\x5c\x22\x78\x41\xc0\x04\x6a\x4a\x98\x64\x31\x6a\x0f\x8a\xe2\xae\xf1\x9d\xe7\x80\x32\x82\xa2\x98\xf4\x86\x2c\xe6\xf7\x36\x2a\xcf\xc1\xff\xf2\x46\xa8\x25\x13\x8b\xf9\x3d\x14\xc5\xf5\x7f\xd7\xa5\x9f\xf9\xa4\x31\x44\xbe\x46\x7d\x6b\x9d\xe6\xf5\x07\x6c\x21\xd3\xe2\x25\x43\xbd\xa9\xc2\xeb\x44\xed\x4c\x26\x5b\x3e\x61\x48\x36\xc3\x2f\x1b\xfd\x40\x8c\x32\x03\x5b\x20\xb5\x48\x53\xd4\x55\x28\x5f\x01\xbe\xec\xfe\xe9\xad\xb8\xe6\x32\xb6\x31\x37\x36\xa6\x3c\x90\xf1\xbf\x96\x56\xd8\x82\x40\xd9\xcc\xf8\x1b\xac\xd3\x37\xad\xb2\xf4\x9e\x2d\x51\x18\xff\x41\x69\xc2\xe8\x07\xe3\xda\xf8\x3f\x99\xc8\xd0\x26\x7c\x52\x5c\x82\x07\x16\x15\xaa\x94\x31\xc1\x95\xc5\xf2\x3f\xab\x24\x51\xb2\x0a\x9e\xd6\xb6\x06\xde\x14\x8a\xe2\x2a\xcf\xe1\x95\xd3\x63\xdb\xd9\x9f\x63\xa2\xd6\xd8\xce\xfe\x9d\x25\x68\x6a\x46\xbb\xb2\xef\x0a\x9f\xee\xfe\xea\x69\x53\x84\x26\xd4\x3c\x25\xae\xa4\x77\x84\x63\xc2\x37\xaa\x5a\x1a\x08\x6e\xa8\x76\xd5\x4c\xc6\x08\x3e\x14\x45\x55\xd7\xcd\x64\x6f\x74\x79\xb2\xac\xcc\x4a\x22\x6d\xf9\xf6\xeb\x16\x76\x07\xa8\x0b\xab\x92\xdf\x49\xa9\x88\xd9\x9a\x5a\x90\x0d\xf3\x69\xb8\x0f\x2a\xd3\x21\xde\x54\xcd\x44\x89\x9a\x91\xd2\x95\x12\x27\x1d\x44\xb5\x38\x30\x82\x85\xcf\x7e\x84\x2b\x96\x09\xf2\x89\x93\xc0\x9a\x05\xc2\x24\x15\x8c\xda\x5a\xf4\xfb\x28\x6f\xe3\x64\xc6\xde\x86\xa4\x0b\xaa\x7d\xe7\x06\xe2\xad\x98\x10\x4b\x16\x3e\x3b\x78\x9d\xe5\x5b\x50\xd8\xc2\x47\x8e\x82\xcb\xe7\xc1\x15\x84\x75\x05\x3c\xf2\x86\x05\xa4\x1a\xad\xba\x06\x7a\x37\x0a\x3a\xca\x58\x39\x72\x06\x96\xcc\x43\x25\x31\x51\x4f\xdc\x1b\xee\x9f\x69\x31\xb4\xe2\xe1\x87\x5b\x29\x45\xd5\x80\xed\x11\x61\x6a\x8f\x16\x65\xb4\xd9\x85\xb8\xf7\x77\x9c\x1c\x5d\xc4\x50\x70\x94\x74\xba\x20\xfb\x10\xf7\x4b\xe0\xb4\x9e\xb9\xb8\x5c\x1a\x62\x32\x44\xd3\x81\xeb\x0c\x2c\xbf\x9f\x55\x95\x9a\x18\x25\xc7\x1d\x70\x82\xc6\xb0\xf8\xb4\xfb\xed\x80\xb9\x1d\xaa\xe7\x7b\xcf\x38\xeb\x1c\xe8\x93\x83\x75\xd2\xda\x57\x53\xf8\x1f\x66\x45\x31\xa9\x8c\x50\x19\xcb\xc1\x79\x9c\x91\xf6\xd2\x2b\x93\xcc\x1a\x27\xea\xc8\x37\x47\xa3\xc4\x1a\xa3\x83\x8c\xef\xe6\xe1\x39\xdf\x23\x9c\xac\xb3\x21\x94\x9a\x72\x8e\x8f\x57\x53\xab\xeb\xaf\x18\x3e\x32\x1a\xdb\xf3\xc9\xa5\x7f\x47\xfa\xd7\x7c\x17\x2e\xb4\x70\xf0\x3a\xfb\xd3\xd3\xf5\x83\xfe\x90\x0a\xec\xb2\xec\x9d\xa4\xae\x7b\xca\x34\x6d\x46\xf8\x13\x8b\x87\x7a\xb3\x18\x25\x05\x87\x2b\xae\xad\xaf\x35\x0f\x49\x69\x95\x9a\xbd\x6c\x89\x11\x06\x6d\xa1\x5d\xb4\x34\x6e\x16\xb8\xac\xa2\x24\x4e\x9b\x20\xe2\x26\x15\x6c\x13\xf4\xbc\xa6\x3e\x1e\xdc\x2e\x72\xa2\x24\x27\x65\x09\x09\x48\x29\x31\x72\x25\xb6\x76\x57\x66\x1e\xd5\x1a\xf5\x19\xde\x8f\x0e\xd4\xdf\xd7\xd3\x79\xe4\x34\x5c\x4d\xe7\x13\x53\x23\xe7\x00\x26\xf7\x6f\xba\x31\x3b\xa5\xf9\x9a\x93\x8d\xcb\xbe\xff\x55\x3a\xfe\x37\x42\x03\xe7\xd2\xde\x31\xed\x6d\xb2\x48\x28\x30\xd6\x2c\xe9\xa2\xf2\x9f\x25\x25\xe2\x26\x54\x3a\x3a\xc3\x20\x3a\x44\xba\xb0\x6b\x9f\x09\x4b\x7c\xbb\x5c\xdd\x93\x78\xfc\x13\x00\x00\xff\xff\x40\x8b\x18\x6d\x71\x14\x00\x00"), }, "/templates/email.tmpl": &vfsgen۰CompressedFileInfo{ name: "email.tmpl", diff --git a/cmd/alertmanager/main.go b/cmd/alertmanager/main.go index 2879ada888..d915c8b4f0 100644 --- a/cmd/alertmanager/main.go +++ b/cmd/alertmanager/main.go @@ -58,6 +58,7 @@ import ( "github.com/prometheus/alertmanager/notify/sns" "github.com/prometheus/alertmanager/notify/telegram" "github.com/prometheus/alertmanager/notify/victorops" + "github.com/prometheus/alertmanager/notify/webex" "github.com/prometheus/alertmanager/notify/webhook" "github.com/prometheus/alertmanager/notify/wechat" "github.com/prometheus/alertmanager/provider/mem" @@ -177,6 +178,9 @@ func buildReceiverIntegrations(nc *config.Receiver, tmpl *template.Template, log for i, c := range nc.DiscordConfigs { add("discord", i, c, func(l log.Logger) (notify.Notifier, error) { return discord.New(c, tmpl, l) }) } + for i, c := range nc.WebexConfigs { + add("webex", i, c, func(l log.Logger) (notify.Notifier, error) { return webex.New(c, tmpl, l) }) + } if errs.Len() > 0 { return nil, &errs diff --git a/config/config.go b/config/config.go index e91db5c6fd..1b4bbb8a15 100644 --- a/config/config.go +++ b/config/config.go @@ -251,6 +251,9 @@ func resolveFilepaths(baseDir string, cfg *Config) { for _, cfg := range receiver.DiscordConfigs { cfg.HTTPConfig.SetDirectory(baseDir) } + for _, cfg := range receiver.WebexConfigs { + cfg.HTTPConfig.SetDirectory(baseDir) + } } } @@ -513,6 +516,14 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { return fmt.Errorf("no discord webhook URL provided") } } + for _, webex := range rcv.WebexConfigs { + if webex.HTTPConfig == nil { + webex.HTTPConfig = c.Global.HTTPConfig + } + if webex.WebhookURL == nil { + return fmt.Errorf("no webex webhook URL provided") + } + } names[rcv.Name] = struct{}{} } @@ -878,6 +889,7 @@ type Receiver struct { VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` SNSConfigs []*SNSConfig `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"` TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"` + WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for Receiver. diff --git a/config/notifiers.go b/config/notifiers.go index f344756102..a46503b445 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -33,6 +33,14 @@ var ( }, } + // DefaultWebexConfig defines default values for Webex configurations. + DefaultWebexConfig = WebexConfig{ + NotifierConfig: NotifierConfig{ + VSendResolved: true, + }, + Message: `{{ template "webex.default.message" . }}`, + } + // DefaultDiscordConfig defines default values for Discord configurations. DefaultDiscordConfig = DiscordConfig{ NotifierConfig: NotifierConfig{ @@ -166,6 +174,23 @@ func (nc *NotifierConfig) SendResolved() bool { return nc.VSendResolved } +// WebexConfig configures notifications via Webex. +type WebexConfig 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"` + + Message string `yaml:"message,omitempty" json:"message,omitempty"` + RoomID string `yaml:"room_id,omitempty" json:"room_id,omitempty"` +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (c *WebexConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + *c = DefaultWebexConfig + type plain WebexConfig + return unmarshal((*plain)(c)) +} + // DiscordConfig configures notifications via Discord. type DiscordConfig struct { NotifierConfig `yaml:",inline" json:",inline"` diff --git a/notify/webex/webex.go b/notify/webex/webex.go new file mode 100644 index 0000000000..97b03e5d8d --- /dev/null +++ b/notify/webex/webex.go @@ -0,0 +1,116 @@ +// Copyright 2022 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 webex + +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/alertmanager/config" + "github.com/prometheus/alertmanager/notify" + "github.com/prometheus/alertmanager/template" + "github.com/prometheus/alertmanager/types" +) + +const ( + // maxMessageSize represents the maximum message length that Webex supports. + maxMessageSize = 7439 +) + +type Notifier struct { + conf *config.WebexConfig + tmpl *template.Template + logger log.Logger + client *http.Client + retrier *notify.Retrier + webhookURL *config.SecretURL +} + +// New returns a new Webex notifier. +func New(c *config.WebexConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { + client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "webex", httpOpts...) + if err != nil { + return nil, err + } + + n := &Notifier{ + conf: c, + tmpl: t, + logger: l, + client: client, + retrier: ¬ify.Retrier{}, + webhookURL: c.WebhookURL, + } + + return n, nil +} + +type webhook struct { + Message string `json:"markdown"` + RoomID string `json:"roomId,omitempty"` +} + +// 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) + + data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger) + tmpl := notify.TmplText(n.tmpl, data, &err) + if err != nil { + return false, err + } + + message := tmpl(n.conf.Message) + if err != nil { + return false, err + } + + message, truncated := notify.Truncate(message, maxMessageSize) + if truncated { + level.Debug(n.logger).Log("msg", "message truncated due to exceeding maximum allowed length by webex", "truncated_message", message) + } + + w := webhook{ + Message: message, + RoomID: n.conf.RoomID, + } + + 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 +} diff --git a/notify/webex/webex_test.go b/notify/webex/webex_test.go new file mode 100644 index 0000000000..b05118ac42 --- /dev/null +++ b/notify/webex/webex_test.go @@ -0,0 +1,140 @@ +// Copyright 2022 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 webex + +import ( + "context" + "fmt" + "io" + "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" +) + +func TestWebexRetry(t *testing.T) { + testWebhookURL, err := url.Parse("https://api.ciscospark.com/v1/message") + require.NoError(t, err) + + notifier, err := New( + &config.WebexConfig{ + HTTPConfig: &commoncfg.HTTPClientConfig{}, + WebhookURL: &config.SecretURL{URL: testWebhookURL}, + }, + 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("error on status %d", statusCode)) + } +} + +func TestWebexTemplating(t *testing.T) { + tc := []struct { + name string + + cfg *config.WebexConfig + Message string + expJSON string + + retry bool + errMsg string + }{ + { + name: "with a valid message, it is formatted as expected", + cfg: &config.WebexConfig{ + Message: `{{ template "webex.default.message" . }}`, + }, + expJSON: `{"markdown":"\n\nAlerts Firing:\nLabels:\n - lbl1 = val1\n - lbl3 = val3\nAnnotations:\nSource: \nLabels:\n - lbl1 = val1\n - lbl2 = val2\nAnnotations:\nSource: \n\n\n\n"}`, + retry: false, + }, + { + name: "with templating errors, it fails.", + cfg: &config.WebexConfig{ + Message: "{{ ", + }, + errMsg: "template: :1: unclosed action", + }, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + var out []byte + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var err error + out, err = io.ReadAll(r.Body) + require.NoError(t, err) + })) + defer srv.Close() + u, _ := url.Parse(srv.URL) + + tt.cfg.WebhookURL = &config.SecretURL{URL: u} + tt.cfg.HTTPConfig = &commoncfg.HTTPClientConfig{} + notifierWebex, err := New(tt.cfg, test.CreateTmpl(t), log.NewNopLogger()) + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + ctx = notify.WithGroupKey(ctx, "1") + + ok, err := notifierWebex.Notify(ctx, []*types.Alert{ + { + Alert: model.Alert{ + Labels: model.LabelSet{ + "lbl1": "val1", + "lbl3": "val3", + }, + StartsAt: time.Now(), + EndsAt: time.Now().Add(time.Hour), + }, + }, + { + Alert: model.Alert{ + Labels: model.LabelSet{ + "lbl1": "val1", + "lbl2": "val2", + }, + StartsAt: time.Now(), + EndsAt: time.Now().Add(time.Hour), + }, + }, + }...) + + if tt.errMsg == "" { + require.NoError(t, err) + fmt.Println(string(out)) + require.JSONEq(t, tt.expJSON, string(out)) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tt.errMsg) + } + + require.Equal(t, tt.retry, ok) + }) + } +} diff --git a/template/default.tmpl b/template/default.tmpl index dd6697438f..82626b1a45 100644 --- a/template/default.tmpl +++ b/template/default.tmpl @@ -124,3 +124,14 @@ Alerts Resolved: {{ template "__text_alert_list" .Alerts.Resolved }} {{ end }} {{ end }} + +{{ define "webex.default.message" }}{{ .CommonAnnotations.SortedPairs.Values | join " " }} +{{ 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 }}