Skip to content

Commit e5ef62b

Browse files
committed
Return error when TmplText errors in sns notifier
Signed-off-by: Emmanuel Lodovice <[email protected]>
1 parent c6be0bc commit e5ef62b

File tree

2 files changed

+136
-8
lines changed

2 files changed

+136
-8
lines changed

notify/sns/sns.go

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/aws/aws-sdk-go/service/sns"
2929
"github.com/go-kit/log"
3030
"github.com/go-kit/log/level"
31+
"github.com/pkg/errors"
3132
commoncfg "github.com/prometheus/common/config"
3233

3334
"github.com/prometheus/alertmanager/config"
@@ -62,20 +63,20 @@ func New(c *config.SNSConfig, t *template.Template, l log.Logger, httpOpts ...co
6263

6364
func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, error) {
6465
var (
65-
err error
66-
data = notify.GetTemplateData(ctx, n.tmpl, alert, n.logger)
67-
tmpl = notify.TmplText(n.tmpl, data, &err)
66+
tmplErr error
67+
data = notify.GetTemplateData(ctx, n.tmpl, alert, n.logger)
68+
tmpl = notify.TmplText(n.tmpl, data, &tmplErr)
6869
)
6970

70-
client, err := n.createSNSClient(tmpl)
71+
client, err := n.createSNSClient(tmpl, &tmplErr)
7172
if err != nil {
7273
if e, ok := err.(awserr.RequestFailure); ok {
7374
return n.retrier.Check(e.StatusCode(), strings.NewReader(e.Message()))
7475
}
7576
return true, err
7677
}
7778

78-
publishInput, err := n.createPublishInput(ctx, tmpl)
79+
publishInput, err := n.createPublishInput(ctx, tmpl, &tmplErr)
7980
if err != nil {
8081
return true, err
8182
}
@@ -96,7 +97,7 @@ func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, err
9697
return false, nil
9798
}
9899

99-
func (n *Notifier) createSNSClient(tmpl func(string) string) (*sns.SNS, error) {
100+
func (n *Notifier) createSNSClient(tmpl func(string) string, tmplErr *error) (*sns.SNS, error) {
100101
var creds *credentials.Credentials
101102
// If there are provided sigV4 credentials we want to use those to create a session.
102103
if n.conf.Sigv4.AccessKey != "" && n.conf.Sigv4.SecretKey != "" {
@@ -112,6 +113,9 @@ func (n *Notifier) createSNSClient(tmpl func(string) string) (*sns.SNS, error) {
112113
if err != nil {
113114
return nil, err
114115
}
116+
if *tmplErr != nil {
117+
return nil, errors.Wrap(*tmplErr, "execute 'api_url' template")
118+
}
115119

116120
if n.conf.Sigv4.RoleARN != "" {
117121
var stsSess *session.Session
@@ -141,13 +145,19 @@ func (n *Notifier) createSNSClient(tmpl func(string) string) (*sns.SNS, error) {
141145
return client, nil
142146
}
143147

144-
func (n *Notifier) createPublishInput(ctx context.Context, tmpl func(string) string) (*sns.PublishInput, error) {
148+
func (n *Notifier) createPublishInput(ctx context.Context, tmpl func(string) string, tmplErr *error) (*sns.PublishInput, error) {
145149
publishInput := &sns.PublishInput{}
146150
messageAttributes := n.createMessageAttributes(tmpl)
151+
if *tmplErr != nil {
152+
return nil, errors.Wrap(*tmplErr, "execute 'attributes' template")
153+
}
147154
// Max message size for a message in a SNS publish request is 256KB, except for SMS messages where the limit is 1600 characters/runes.
148155
messageSizeLimit := 256 * 1024
149156
if n.conf.TopicARN != "" {
150157
topicARN := tmpl(n.conf.TopicARN)
158+
if *tmplErr != nil {
159+
return nil, errors.Wrap(*tmplErr, "execute 'topic_arn' template")
160+
}
151161
publishInput.SetTopicArn(topicARN)
152162
// If we are using a topic ARN, it could be a FIFO topic specified by the topic's suffix ".fifo".
153163
if strings.HasSuffix(topicARN, ".fifo") {
@@ -162,14 +172,24 @@ func (n *Notifier) createPublishInput(ctx context.Context, tmpl func(string) str
162172
}
163173
if n.conf.PhoneNumber != "" {
164174
publishInput.SetPhoneNumber(tmpl(n.conf.PhoneNumber))
175+
if *tmplErr != nil {
176+
return nil, errors.Wrap(*tmplErr, "execute 'phone_number' template")
177+
}
165178
// If we have an SMS message, we need to truncate to 1600 characters/runes.
166179
messageSizeLimit = 1600
167180
}
168181
if n.conf.TargetARN != "" {
169182
publishInput.SetTargetArn(tmpl(n.conf.TargetARN))
183+
if *tmplErr != nil {
184+
return nil, errors.Wrap(*tmplErr, "execute 'target_arn' template")
185+
}
170186
}
171187

172-
messageToSend, isTrunc, err := validateAndTruncateMessage(tmpl(n.conf.Message), messageSizeLimit)
188+
tmplMessage := tmpl(n.conf.Message)
189+
if *tmplErr != nil {
190+
return nil, errors.Wrap(*tmplErr, "execute 'message' template")
191+
}
192+
messageToSend, isTrunc, err := validateAndTruncateMessage(tmplMessage, messageSizeLimit)
173193
if err != nil {
174194
return nil, err
175195
}
@@ -183,6 +203,9 @@ func (n *Notifier) createPublishInput(ctx context.Context, tmpl func(string) str
183203

184204
if n.conf.Subject != "" {
185205
publishInput.SetSubject(tmpl(n.conf.Subject))
206+
if *tmplErr != nil {
207+
return nil, errors.Wrap(*tmplErr, "execute 'subject' template")
208+
}
186209
}
187210

188211
return publishInput, nil

notify/sns/sns_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,23 @@
1414
package sns
1515

1616
import (
17+
"context"
18+
"net/url"
19+
"strings"
1720
"testing"
1821

22+
"github.com/go-kit/log"
23+
commoncfg "github.com/prometheus/common/config"
24+
"github.com/prometheus/common/sigv4"
1925
"github.com/stretchr/testify/require"
26+
27+
"github.com/prometheus/alertmanager/config"
28+
"github.com/prometheus/alertmanager/template"
29+
"github.com/prometheus/alertmanager/types"
2030
)
2131

32+
var logger = log.NewNopLogger()
33+
2234
func TestValidateAndTruncateMessage(t *testing.T) {
2335
sBuff := make([]byte, 257*1024)
2436
for i := range sBuff {
@@ -43,3 +55,96 @@ func TestValidateAndTruncateMessage(t *testing.T) {
4355
_, _, err = validateAndTruncateMessage(invalidUtf8String, 100)
4456
require.Error(t, err)
4557
}
58+
59+
func TestNotifyWithInvalidTemplate(t *testing.T) {
60+
for _, tc := range []struct {
61+
title string
62+
errMsg string
63+
updateCfg func(*config.SNSConfig)
64+
}{
65+
{
66+
title: "with invalid Attribute template",
67+
errMsg: "execute 'attributes' template",
68+
updateCfg: func(cfg *config.SNSConfig) {
69+
cfg.Attributes = map[string]string{
70+
"attribName1": "{{ template \"unknown_template\" . }}",
71+
}
72+
},
73+
},
74+
{
75+
title: "with invalid TopicArn template",
76+
errMsg: "execute 'topic_arn' template",
77+
updateCfg: func(cfg *config.SNSConfig) {
78+
cfg.TopicARN = "{{ template \"unknown_template\" . }}"
79+
},
80+
},
81+
{
82+
title: "with invalid PhoneNumber template",
83+
errMsg: "execute 'phone_number' template",
84+
updateCfg: func(cfg *config.SNSConfig) {
85+
cfg.PhoneNumber = "{{ template \"unknown_template\" . }}"
86+
},
87+
},
88+
{
89+
title: "with invalid Message template",
90+
errMsg: "execute 'message' template",
91+
updateCfg: func(cfg *config.SNSConfig) {
92+
cfg.Message = "{{ template \"unknown_template\" . }}"
93+
},
94+
},
95+
{
96+
title: "with invalid Subject template",
97+
errMsg: "execute 'subject' template",
98+
updateCfg: func(cfg *config.SNSConfig) {
99+
cfg.Subject = "{{ template \"unknown_template\" . }}"
100+
},
101+
},
102+
{
103+
title: "with invalid APIUrl template",
104+
errMsg: "execute 'api_url' template",
105+
updateCfg: func(cfg *config.SNSConfig) {
106+
cfg.APIUrl = "{{ template \"unknown_template\" . }}"
107+
},
108+
},
109+
{
110+
title: "with invalid TargetARN template",
111+
errMsg: "execute 'target_arn' template",
112+
updateCfg: func(cfg *config.SNSConfig) {
113+
cfg.TargetARN = "{{ template \"unknown_template\" . }}"
114+
},
115+
},
116+
} {
117+
tc := tc
118+
t.Run(tc.title, func(t *testing.T) {
119+
snsCfg := &config.SNSConfig{
120+
HTTPConfig: &commoncfg.HTTPClientConfig{},
121+
TopicARN: "TestTopic",
122+
Sigv4: sigv4.SigV4Config{
123+
Region: "us-west-2",
124+
},
125+
}
126+
if tc.updateCfg != nil {
127+
tc.updateCfg(snsCfg)
128+
}
129+
notifier, err := New(
130+
snsCfg,
131+
createTmpl(t),
132+
logger,
133+
)
134+
require.NoError(t, err)
135+
var alerts []*types.Alert
136+
_, err = notifier.Notify(context.Background(), alerts...)
137+
require.Error(t, err)
138+
require.True(t, strings.Contains(err.Error(), "template \"unknown_template\" not defined"))
139+
require.True(t, strings.Contains(err.Error(), tc.errMsg))
140+
})
141+
}
142+
}
143+
144+
// CreateTmpl returns a ready-to-use template.
145+
func createTmpl(t *testing.T) *template.Template {
146+
tmpl, err := template.FromGlobs([]string{})
147+
require.NoError(t, err)
148+
tmpl.ExternalURL, _ = url.Parse("http://am")
149+
return tmpl
150+
}

0 commit comments

Comments
 (0)