From fd10ee999b85e637aaebeb6aefa95a0e0e16b8d8 Mon Sep 17 00:00:00 2001 From: Christoph Maser Date: Thu, 17 Sep 2026 18:27:14 +0200 Subject: [PATCH] refactor(wechat): move configuration types into wechat package Signed-off-by: Christoph Maser --- config/config.go | 5 ++- config/notifiers.go | 62 -------------------------- config/notifiers_test.go | 15 ------- notify/wechat/config.go | 85 ++++++++++++++++++++++++++++++++++++ notify/wechat/config_test.go | 31 +++++++++++++ notify/wechat/wechat.go | 5 +-- notify/wechat/wechat_test.go | 13 +++--- 7 files changed, 127 insertions(+), 89 deletions(-) create mode 100644 notify/wechat/config.go create mode 100644 notify/wechat/config_test.go diff --git a/config/config.go b/config/config.go index 20a6bb7ac7..215a8d4773 100644 --- a/config/config.go +++ b/config/config.go @@ -46,6 +46,7 @@ import ( "github.com/prometheus/alertmanager/notify/sns" "github.com/prometheus/alertmanager/notify/telegram" "github.com/prometheus/alertmanager/notify/webhook" + "github.com/prometheus/alertmanager/notify/wechat" "github.com/prometheus/alertmanager/timeinterval" "github.com/prometheus/alertmanager/tracing" ) @@ -506,7 +507,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(any) error) error { } for _, wcc := range rcv.WechatConfigs { if wcc == nil { - wcc = &WechatConfig{} + wcc = &wechat.WechatConfig{} } wcc.HTTPConfig = cmp.Or(wcc.HTTPConfig, c.Global.HTTPConfig) wcc.APIURL = cmp.Or(wcc.APIURL, c.Global.WeChatAPIURL) @@ -995,7 +996,7 @@ type Receiver struct { SlackConfigs []*slack.SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` WebhookConfigs []*webhook.WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"` OpsGenieConfigs []*opsgenie.OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` - WechatConfigs []*WechatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"` + WechatConfigs []*wechat.WechatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"` PushoverConfigs []*pushover.PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"` VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` SNSConfigs []*sns.SNSConfig `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"` diff --git a/config/notifiers.go b/config/notifiers.go index 1d4f370e13..4031681973 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -17,7 +17,6 @@ import ( "errors" "fmt" "net/textproto" - "regexp" "slices" commoncfg "github.com/prometheus/common/config" @@ -47,18 +46,6 @@ var ( // DefaultEmailSubject defines the default Subject header of an Email. DefaultEmailSubject = `{{ template "email.default.subject" . }}` - // DefaultWechatConfig defines default values for wechat configurations. - DefaultWechatConfig = WechatConfig{ - NotifierConfig: amcommoncfg.NotifierConfig{ - VSendResolved: false, - }, - Message: `{{ template "wechat.default.message" . }}`, - ToUser: `{{ template "wechat.default.to_user" . }}`, - ToParty: `{{ template "wechat.default.to_party" . }}`, - ToTag: `{{ template "wechat.default.to_tag" . }}`, - AgentID: `{{ template "wechat.default.agent_id" . }}`, - } - // DefaultVictorOpsConfig defines default values for VictorOps configurations. DefaultVictorOpsConfig = VictorOpsConfig{ NotifierConfig: amcommoncfg.NotifierConfig{ @@ -181,55 +168,6 @@ func (c *EmailConfig) Validate() error { return nil } -// WechatConfig configures notifications via Wechat. -type WechatConfig struct { - amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` - - HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` - - APISecret commoncfg.Secret `yaml:"api_secret,omitempty" json:"api_secret,omitempty"` - APISecretFile string `yaml:"api_secret_file,omitempty" json:"api_secret_file,omitempty"` - CorpID string `yaml:"corp_id,omitempty" json:"corp_id,omitempty"` - Message string `yaml:"message,omitempty" json:"message,omitempty"` - APIURL *amcommoncfg.URL `yaml:"api_url,omitempty" json:"api_url,omitempty"` - ToUser string `yaml:"to_user,omitempty" json:"to_user,omitempty"` - ToParty string `yaml:"to_party,omitempty" json:"to_party,omitempty"` - ToTag string `yaml:"to_tag,omitempty" json:"to_tag,omitempty"` - AgentID string `yaml:"agent_id,omitempty" json:"agent_id,omitempty"` - MessageType string `yaml:"message_type,omitempty" json:"message_type,omitempty"` -} - -const wechatValidTypesRe = `^(text|markdown)$` - -var wechatTypeMatcher = regexp.MustCompile(wechatValidTypesRe) - -// UnmarshalYAML implements the yaml.Unmarshaler interface. -func (c *WechatConfig) UnmarshalYAML(unmarshal func(any) error) error { - *c = DefaultWechatConfig - type plain WechatConfig - if err := unmarshal((*plain)(c)); err != nil { - return err - } - - if c.MessageType == "" { - c.MessageType = "text" - } - - return c.Validate() -} - -func (c *WechatConfig) Validate() error { - if !wechatTypeMatcher.MatchString(c.MessageType) { - return fmt.Errorf("weChat message type %q does not match valid options %s", c.MessageType, wechatValidTypesRe) - } - - if c.APISecret != "" && len(c.APISecretFile) > 0 { - return errors.New("at most one of api_secret & api_secret_file must be configured") - } - - return nil -} - // VictorOpsConfig configures notifications via VictorOps. type VictorOpsConfig struct { amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` diff --git a/config/notifiers_test.go b/config/notifiers_test.go index 154719c30c..438506c2b8 100644 --- a/config/notifiers_test.go +++ b/config/notifiers_test.go @@ -193,21 +193,6 @@ custom_fields: } } -func TestWeChatTypeMatcher(t *testing.T) { - good := []string{"text", "markdown"} - for _, g := range good { - if !wechatTypeMatcher.MatchString(g) { - t.Fatalf("failed to match with %s", g) - } - } - bad := []string{"TEXT", "MarkDOwn"} - for _, b := range bad { - if wechatTypeMatcher.MatchString(b) { - t.Errorf("mistakenly match with %s", b) - } - } -} - func TestWebexConfiguration(t *testing.T) { tc := []struct { name string diff --git a/notify/wechat/config.go b/notify/wechat/config.go new file mode 100644 index 0000000000..a1a06ef825 --- /dev/null +++ b/notify/wechat/config.go @@ -0,0 +1,85 @@ +// Copyright The Prometheus Authors +// 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 wechat + +import ( + "errors" + "fmt" + "regexp" + + commoncfg "github.com/prometheus/common/config" + + amcommoncfg "github.com/prometheus/alertmanager/config/common" +) + +// DefaultWechatConfig defines default values for wechat configurations. +var DefaultWechatConfig = WechatConfig{ + NotifierConfig: amcommoncfg.NotifierConfig{ + VSendResolved: false, + }, + Message: `{{ template "wechat.default.message" . }}`, + ToUser: `{{ template "wechat.default.to_user" . }}`, + ToParty: `{{ template "wechat.default.to_party" . }}`, + ToTag: `{{ template "wechat.default.to_tag" . }}`, + AgentID: `{{ template "wechat.default.agent_id" . }}`, +} + +// WechatConfig configures notifications via Wechat. +type WechatConfig struct { + amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` + + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + + APISecret commoncfg.Secret `yaml:"api_secret,omitempty" json:"api_secret,omitempty"` + APISecretFile string `yaml:"api_secret_file,omitempty" json:"api_secret_file,omitempty"` + CorpID string `yaml:"corp_id,omitempty" json:"corp_id,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + APIURL *amcommoncfg.URL `yaml:"api_url,omitempty" json:"api_url,omitempty"` + ToUser string `yaml:"to_user,omitempty" json:"to_user,omitempty"` + ToParty string `yaml:"to_party,omitempty" json:"to_party,omitempty"` + ToTag string `yaml:"to_tag,omitempty" json:"to_tag,omitempty"` + AgentID string `yaml:"agent_id,omitempty" json:"agent_id,omitempty"` + MessageType string `yaml:"message_type,omitempty" json:"message_type,omitempty"` +} + +const wechatValidTypesRe = `^(text|markdown)$` + +var wechatTypeMatcher = regexp.MustCompile(wechatValidTypesRe) + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (c *WechatConfig) UnmarshalYAML(unmarshal func(any) error) error { + *c = DefaultWechatConfig + type plain WechatConfig + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + if c.MessageType == "" { + c.MessageType = "text" + } + + return c.Validate() +} + +func (c *WechatConfig) Validate() error { + if !wechatTypeMatcher.MatchString(c.MessageType) { + return fmt.Errorf("weChat message type %q does not match valid options %s", c.MessageType, wechatValidTypesRe) + } + + if c.APISecret != "" && len(c.APISecretFile) > 0 { + return errors.New("at most one of api_secret & api_secret_file must be configured") + } + + return nil +} diff --git a/notify/wechat/config_test.go b/notify/wechat/config_test.go new file mode 100644 index 0000000000..30b141b6a7 --- /dev/null +++ b/notify/wechat/config_test.go @@ -0,0 +1,31 @@ +// Copyright The Prometheus Authors +// 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 wechat + +import "testing" + +func TestWeChatTypeMatcher(t *testing.T) { + good := []string{"text", "markdown"} + for _, g := range good { + if !wechatTypeMatcher.MatchString(g) { + t.Fatalf("failed to match with %s", g) + } + } + bad := []string{"TEXT", "MarkDOwn"} + for _, b := range bad { + if wechatTypeMatcher.MatchString(b) { + t.Errorf("mistakenly match with %s", b) + } + } +} diff --git a/notify/wechat/wechat.go b/notify/wechat/wechat.go index f1101a52c4..4c1c835dcc 100644 --- a/notify/wechat/wechat.go +++ b/notify/wechat/wechat.go @@ -29,7 +29,6 @@ import ( 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" @@ -37,7 +36,7 @@ import ( // Notifier implements a Notifier for wechat notifications. type Notifier struct { - conf *config.WechatConfig + conf *WechatConfig tmpl *template.Template logger *slog.Logger client *http.Client @@ -72,7 +71,7 @@ type weChatResponse struct { } // New returns a new Wechat notifier. -func New(c *config.WechatConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *WechatConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := notify.NewClientWithTracing(*c.HTTPConfig, "wechat", httpOpts...) if err != nil { return nil, err diff --git a/notify/wechat/wechat_test.go b/notify/wechat/wechat_test.go index ec52caea5d..eb3d6b581e 100644 --- a/notify/wechat/wechat_test.go +++ b/notify/wechat/wechat_test.go @@ -25,7 +25,6 @@ import ( amcommoncfg "github.com/prometheus/alertmanager/config/common" - "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/notify/test" ) @@ -35,7 +34,7 @@ func TestWechatRedactedURLOnInitialAuthentication(t *testing.T) { secret := "secret_key" notifier, err := New( - &config.WechatConfig{ + &WechatConfig{ APIURL: &amcommoncfg.URL{URL: u}, HTTPConfig: &commoncfg.HTTPClientConfig{}, CorpID: "corpid", @@ -57,7 +56,7 @@ func TestWechatRedactedURLOnNotify(t *testing.T) { defer fn() notifier, err := New( - &config.WechatConfig{ + &WechatConfig{ APIURL: &amcommoncfg.URL{URL: u}, HTTPConfig: &commoncfg.HTTPClientConfig{}, CorpID: "corpid", @@ -79,7 +78,7 @@ func TestWechatMessageTypeSelector(t *testing.T) { defer fn() notifier, err := New( - &config.WechatConfig{ + &WechatConfig{ APIURL: &amcommoncfg.URL{URL: u}, HTTPConfig: &commoncfg.HTTPClientConfig{}, CorpID: "corpid", @@ -95,7 +94,7 @@ func TestWechatMessageTypeSelector(t *testing.T) { } func TestGetApiSecretFromSecret(t *testing.T) { - n := &Notifier{conf: &config.WechatConfig{APISecret: commoncfg.Secret("shhh")}} + n := &Notifier{conf: &WechatConfig{APISecret: commoncfg.Secret("shhh")}} s, err := n.getApiSecret() require.NoError(t, err) require.Equal(t, "shhh", s) @@ -109,14 +108,14 @@ func TestGetApiSecretFromFile(t *testing.T) { require.NoError(t, err) require.NoError(t, tmpFile.Close()) - n := &Notifier{conf: &config.WechatConfig{APISecretFile: tmpFile.Name()}} + n := &Notifier{conf: &WechatConfig{APISecretFile: tmpFile.Name()}} s, err := n.getApiSecret() require.NoError(t, err) require.Equal(t, "file-secret", s) } func TestGetApiSecretFromMissingFile(t *testing.T) { - n := &Notifier{conf: &config.WechatConfig{APISecretFile: "/non/existent/wechat-secret.txt"}} + n := &Notifier{conf: &WechatConfig{APISecretFile: "/non/existent/wechat-secret.txt"}} s, err := n.getApiSecret() var pathErr *os.PathError require.ErrorAs(t, err, &pathErr)