Remove isFifo config option; use template strings; use retier; other code review comments
Signed-off-by: Tyler Reid <tyler.reid@grafana.com>
This commit is contained in:
parent
009f8b17e9
commit
72d63a5d72
|
@ -135,7 +135,6 @@ var (
|
|||
},
|
||||
APIVersion: "sns.default.api_version",
|
||||
Message: `{{ template "sns.default.message" . }}`,
|
||||
IsFIFOTopic: false,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -590,11 +589,11 @@ func (c *PushoverConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
|
|||
return nil
|
||||
}
|
||||
|
||||
// TODO: Move to common?
|
||||
|
||||
// SigV4Config is the configuration for signing remote write requests with
|
||||
// AWS's SigV4 verification process. Empty values will be retrieved using the
|
||||
// AWS default credentials chain.
|
||||
// TODO: Move to common.
|
||||
type SigV4Config struct {
|
||||
Region string `yaml:"region,omitempty"`
|
||||
AccessKey string `yaml:"access_key,omitempty"`
|
||||
|
@ -614,7 +613,6 @@ type SNSConfig struct {
|
|||
TopicARN string `yaml:"topic_arn,omitempty" json:"topic_arn,omitempty"`
|
||||
PhoneNumber string `yaml:"phone_number,omitempty" json:"phone_number,omitempty"`
|
||||
TargetARN string `yaml:"target_arn,omitempty" json:"target_arn,omitempty"`
|
||||
IsFIFOTopic bool `yaml:"is_fifo_topic,omitempty" json:"is_fifo_topic,omitempty"`
|
||||
Subject string `yaml:"subject,omitempty" json:"subject,omitempty"`
|
||||
Message string `yaml:"message,omitempty" json:"message,omitempty"`
|
||||
Attributes map[string]string `yaml:"attributes,omitempty" json:"attributes,omitempty"`
|
||||
|
|
|
@ -17,9 +17,11 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
|
@ -41,17 +43,38 @@ type Notifier struct {
|
|||
retrier *notify.Retrier
|
||||
}
|
||||
|
||||
// New returns a new SNS notification handler.
|
||||
func New(c *config.SNSConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) {
|
||||
client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "sns", append(httpOpts, commoncfg.WithHTTP2Disabled())...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Notifier{
|
||||
conf: c,
|
||||
tmpl: t,
|
||||
logger: l,
|
||||
client: client,
|
||||
retrier: ¬ify.Retrier{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, error) {
|
||||
credentials := credentials.NewStaticCredentials(n.conf.Sigv4.AccessKey, string(n.conf.Sigv4.SecretKey), "")
|
||||
if n.conf.Sigv4.AccessKey == "" {
|
||||
credentials = nil
|
||||
var(
|
||||
err error
|
||||
data = notify.GetTemplateData(ctx, n.tmpl, alert, n.logger)
|
||||
tmpl = notify.TmplText(n.tmpl, data, &err)
|
||||
creds *credentials.Credentials = nil
|
||||
)
|
||||
if n.conf.Sigv4.AccessKey != "" && n.conf.Sigv4.SecretKey != "" {
|
||||
creds = credentials.NewStaticCredentials(n.conf.Sigv4.AccessKey, string(n.conf.Sigv4.SecretKey), "")
|
||||
}
|
||||
|
||||
sess, err := session.NewSessionWithOptions(session.Options{
|
||||
Config: aws.Config{
|
||||
Region: aws.String(n.conf.Sigv4.Region),
|
||||
Credentials: credentials,
|
||||
Endpoint: aws.String(n.conf.APIUrl),
|
||||
Credentials: creds,
|
||||
Endpoint: aws.String(tmpl(n.conf.APIUrl)),
|
||||
},
|
||||
Profile: n.conf.Sigv4.Profile,
|
||||
})
|
||||
|
@ -64,37 +87,44 @@ func (n Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, erro
|
|||
sess.Config.Credentials = stscreds.NewCredentials(sess, n.conf.Sigv4.RoleARN)
|
||||
}
|
||||
|
||||
data := notify.GetTemplateData(ctx, n.tmpl, alert, n.logger)
|
||||
tmpl := notify.TmplText(n.tmpl, data, &err)
|
||||
message := tmpl(n.conf.Message)
|
||||
|
||||
client := sns.New(sess, &aws.Config{Credentials: credentials})
|
||||
client := sns.New(sess, &aws.Config{Credentials: creds})
|
||||
publishInput := &sns.PublishInput{}
|
||||
|
||||
if n.conf.TopicARN != "" {
|
||||
publishInput.SetTopicArn(n.conf.TopicARN)
|
||||
messageToSend, isTrunc, err := validateAndTruncateMessage(message)
|
||||
publishInput.SetTopicArn(tmpl(n.conf.TopicARN))
|
||||
messageToSend, isTrunc, err := validateAndTruncateMessage(tmpl(n.conf.Message))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if isTrunc {
|
||||
n.conf.Attributes["truncated"] = "true"
|
||||
}
|
||||
|
||||
// Deduplication key and Message Group ID are only added if it's a FIFO SNS Topic.
|
||||
if isFIFOTopic(n.conf.TopicARN) {
|
||||
key, err := notify.ExtractGroupKey(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
publishInput.SetMessageDeduplicationId(key.Hash())
|
||||
publishInput.SetMessageGroupId(key.Hash())
|
||||
}
|
||||
|
||||
publishInput.SetMessage(messageToSend)
|
||||
}
|
||||
if n.conf.PhoneNumber != "" {
|
||||
publishInput.SetPhoneNumber(n.conf.PhoneNumber)
|
||||
publishInput.SetPhoneNumber(tmpl(n.conf.PhoneNumber))
|
||||
// If SMS message is over 1600 chars, SNS will reject the message.
|
||||
_, isTruncated := notify.Truncate(message, 1600)
|
||||
_, isTruncated := notify.Truncate(tmpl(n.conf.Message), 1600)
|
||||
if isTruncated {
|
||||
return false, fmt.Errorf("SMS message exeeds length of 1600 charactors")
|
||||
} else {
|
||||
publishInput.SetMessage(message)
|
||||
publishInput.SetMessage(tmpl(n.conf.Message))
|
||||
}
|
||||
}
|
||||
if n.conf.TargetARN != "" {
|
||||
publishInput.SetTargetArn(n.conf.TargetARN)
|
||||
messageToSend, isTrunc, err := validateAndTruncateMessage(message)
|
||||
publishInput.SetTargetArn(tmpl(n.conf.TargetARN))
|
||||
messageToSend, isTrunc, err := validateAndTruncateMessage(tmpl(n.conf.Message))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -107,28 +137,18 @@ func (n Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, erro
|
|||
if len(n.conf.Attributes) > 0 {
|
||||
attributes := map[string]*sns.MessageAttributeValue{}
|
||||
for k, v := range n.conf.Attributes {
|
||||
attributes[k] = &sns.MessageAttributeValue{DataType: aws.String("String"), StringValue: aws.String(v)}
|
||||
attributes[tmpl(k)] = &sns.MessageAttributeValue{DataType: aws.String("String"), StringValue: aws.String(tmpl(v))}
|
||||
}
|
||||
publishInput.SetMessageAttributes(attributes)
|
||||
}
|
||||
|
||||
if n.conf.Subject != "" {
|
||||
publishInput.SetSubject(n.conf.Subject)
|
||||
}
|
||||
|
||||
// Deduplication key is only added if it's a FIFO SNS Topic.
|
||||
if n.conf.IsFIFOTopic {
|
||||
key, err := notify.ExtractGroupKey(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
publishInput.SetMessageDeduplicationId(key.Hash())
|
||||
publishInput.SetSubject(tmpl(n.conf.Subject))
|
||||
}
|
||||
|
||||
publishOutput, err := client.Publish(publishInput)
|
||||
if err != nil {
|
||||
// AWS Response is bad, probably a config issue.
|
||||
return false, err
|
||||
return n.retrier.Check(err.(awserr.RequestFailure).StatusCode(), strings.NewReader(err.(awserr.RequestFailure).Message()))
|
||||
}
|
||||
|
||||
err = n.logger.Log(publishOutput.String())
|
||||
|
@ -136,10 +156,16 @@ func (n Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, erro
|
|||
return false, err
|
||||
}
|
||||
|
||||
// Response is good and does not need to be retried.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func isFIFOTopic(topicARN string) bool {
|
||||
if topicARN[len(topicARN)-5:] == ".fifo" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validateAndTruncateMessage(message string) (string, bool, error) {
|
||||
if utf8.ValidString(message) {
|
||||
// if the message is larger than 256KB we have to truncate.
|
||||
|
@ -152,20 +178,3 @@ func validateAndTruncateMessage(message string) (string, bool, error) {
|
|||
}
|
||||
return "", false, fmt.Errorf("non utf8 encoded message string")
|
||||
}
|
||||
|
||||
// New returns a new SNS notification handler.
|
||||
func New(c *config.SNSConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) {
|
||||
|
||||
client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "sns", append(httpOpts, commoncfg.WithHTTP2Disabled())...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Notifier{
|
||||
conf: c,
|
||||
tmpl: t,
|
||||
logger: l,
|
||||
client: client,
|
||||
retrier: ¬ify.Retrier{},
|
||||
}, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue