From ac0dc96127e88206a6ea811bc4bdd437945a7974 Mon Sep 17 00:00:00 2001 From: gotjosh Date: Wed, 9 Nov 2022 14:16:00 +0000 Subject: [PATCH] A few changes to better align with what I know think it's the best setup in Webex Teams - Move away from Webhook to APIRUL - Make the Room ID a require field - Set Authorization Credentails via Headers Signed-off-by: gotjosh --- config/config.go | 10 ++++++++-- config/config_test.go | 1 + config/notifiers.go | 17 +++++++++++++---- config/notifiers_test.go | 34 ++++++++++++++++++++++++++++++++++ notify/webex/webex.go | 33 ++++++++++++++++++++------------- notify/webex/webex_test.go | 9 ++++++--- 6 files changed, 82 insertions(+), 22 deletions(-) diff --git a/config/config.go b/config/config.go index 1b4bbb8a..21698681 100644 --- a/config/config.go +++ b/config/config.go @@ -520,8 +520,12 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { if webex.HTTPConfig == nil { webex.HTTPConfig = c.Global.HTTPConfig } - if webex.WebhookURL == nil { - return fmt.Errorf("no webex webhook URL provided") + if webex.APIURL == nil { + if c.Global.WebexAPIURL == nil { + return fmt.Errorf("no global Webex URL set") + } + + webex.APIURL = c.Global.WebexAPIURL } } @@ -624,6 +628,7 @@ func DefaultGlobalConfig() GlobalConfig { WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"), VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"), TelegramAPIUrl: mustParseURL("https://api.telegram.org"), + WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"), } } @@ -747,6 +752,7 @@ type GlobalConfig struct { VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"` VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"` TelegramAPIUrl *URL `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"` + WebexAPIURL *URL `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for GlobalConfig. diff --git a/config/config_test.go b/config/config_test.go index c7a6b394..5855eac7 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -867,6 +867,7 @@ func TestEmptyFieldsAndRegex(t *testing.T) { WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"), VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"), TelegramAPIUrl: mustParseURL("https://api.telegram.org"), + WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"), }, Templates: []string{ diff --git a/config/notifiers.go b/config/notifiers.go index a46503b4..bc1ea5bf 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -178,17 +178,26 @@ func (nc *NotifierConfig) SendResolved() bool { 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"` + APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"` - Message string `yaml:"message,omitempty" json:"message,omitempty"` - RoomID string `yaml:"room_id,omitempty" json:"room_id,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + RoomID string `yaml:"room_id" json:"room_id"` + BotToken Secret `yaml:"bot_token" yaml:"bot_token"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *WebexConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultWebexConfig type plain WebexConfig - return unmarshal((*plain)(c)) + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + if c.RoomID == "" { + return fmt.Errorf("missing room_id on webex_config") + } + + return nil } // DiscordConfig configures notifications via Discord. diff --git a/config/notifiers_test.go b/config/notifiers_test.go index 602cd182..fb10de4f 100644 --- a/config/notifiers_test.go +++ b/config/notifiers_test.go @@ -14,6 +14,7 @@ package config import ( + "errors" "strings" "testing" @@ -866,6 +867,39 @@ func TestWeChatTypeMatcher(t *testing.T) { } } +func TestWebexConfiguration(t *testing.T) { + tc := []struct { + name string + + in string + expected error + }{ + { + name: "with no room_id - it fails", + in: ` +bot_token: xyz123 +`, + expected: errors.New("missing room_id on webex_config"), + }, + { + name: "with room_id and bot_token present - it succeeds", + in: ` +room_id: 2 +bot_token: xyz123 +`, + }, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + var cfg WebexConfig + err := yaml.UnmarshalStrict([]byte(tt.in), &cfg) + + require.Equal(t, tt.expected, err) + }) + } +} + func newBoolPointer(b bool) *bool { return &b } diff --git a/notify/webex/webex.go b/notify/webex/webex.go index 97b03e5d..968fac4b 100644 --- a/notify/webex/webex.go +++ b/notify/webex/webex.go @@ -35,28 +35,35 @@ const ( ) type Notifier struct { - conf *config.WebexConfig - tmpl *template.Template - logger log.Logger - client *http.Client - retrier *notify.Retrier - webhookURL *config.SecretURL + conf *config.WebexConfig + tmpl *template.Template + logger log.Logger + client *http.Client + retrier *notify.Retrier + APIURL *config.URL } // New returns a new Webex notifier. func New(c *config.WebexConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { + if c.HTTPConfig.Authorization == nil && c.HTTPConfig.BearerToken == "" { + c.HTTPConfig.Authorization = &commoncfg.Authorization{ + Type: "Bearer", + Credentials: commoncfg.Secret(c.BotToken), + } + } + 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, + conf: c, + tmpl: t, + logger: l, + client: client, + retrier: ¬ify.Retrier{}, + APIURL: c.APIURL, } return n, nil @@ -102,7 +109,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) return false, err } - resp, err := notify.PostJSON(ctx, n.client, n.webhookURL.String(), &payload) + resp, err := notify.PostJSON(ctx, n.client, n.APIURL.String(), &payload) if err != nil { return true, notify.RedactURL(err) } diff --git a/notify/webex/webex_test.go b/notify/webex/webex_test.go index b05118ac..99a56005 100644 --- a/notify/webex/webex_test.go +++ b/notify/webex/webex_test.go @@ -41,7 +41,7 @@ func TestWebexRetry(t *testing.T) { notifier, err := New( &config.WebexConfig{ HTTPConfig: &commoncfg.HTTPClientConfig{}, - WebhookURL: &config.SecretURL{URL: testWebhookURL}, + APIURL: &config.URL{URL: testWebhookURL}, }, test.CreateTmpl(t), log.NewNopLogger(), @@ -85,15 +85,18 @@ func TestWebexTemplating(t *testing.T) { for _, tt := range tc { t.Run(tt.name, func(t *testing.T) { var out []byte + var header http.Header srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var err error out, err = io.ReadAll(r.Body) + header = r.Header.Clone() require.NoError(t, err) })) defer srv.Close() u, _ := url.Parse(srv.URL) - tt.cfg.WebhookURL = &config.SecretURL{URL: u} + tt.cfg.APIURL = &config.URL{URL: u} + tt.cfg.BotToken = "xxxyyyzz" tt.cfg.HTTPConfig = &commoncfg.HTTPClientConfig{} notifierWebex, err := New(tt.cfg, test.CreateTmpl(t), log.NewNopLogger()) require.NoError(t, err) @@ -127,7 +130,7 @@ func TestWebexTemplating(t *testing.T) { if tt.errMsg == "" { require.NoError(t, err) - fmt.Println(string(out)) + require.Equal(t, "sss", header.Get("Authorization")) require.JSONEq(t, tt.expJSON, string(out)) } else { require.Error(t, err)