Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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"`
Expand Down
62 changes: 0 additions & 62 deletions config/notifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"errors"
"fmt"
"net/textproto"
"regexp"
"slices"

commoncfg "github.com/prometheus/common/config"
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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"`
Expand Down
15 changes: 0 additions & 15 deletions config/notifiers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
85 changes: 85 additions & 0 deletions notify/wechat/config.go
Original file line number Diff line number Diff line change
@@ -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
}
31 changes: 31 additions & 0 deletions notify/wechat/config_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
5 changes: 2 additions & 3 deletions notify/wechat/wechat.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,14 @@ 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"
)

// Notifier implements a Notifier for wechat notifications.
type Notifier struct {
conf *config.WechatConfig
conf *WechatConfig
tmpl *template.Template
logger *slog.Logger
client *http.Client
Expand Down Expand Up @@ -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
Expand Down
13 changes: 6 additions & 7 deletions notify/wechat/wechat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (

amcommoncfg "github.com/prometheus/alertmanager/config/common"

"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/notify/test"
)

Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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)
Expand All @@ -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)
Expand Down
Loading