Alertmanager: Add Amazon SNS Notification

This adds notifications to Amazon SNS.  It is using Amazon's go sdk. The notification config should look like
    amazon_sns_config {
      topic_arn: "arn:aws:sns:us-east-1:an-arn-id:topic"
      send_resolved: true
    }

The alert summary and status will be used as the SNS subject.
The alert description and status will be used as the SNS message.

SNS notifictions require Amazon AWS credentials to be setup as described by the Amazon go sdk.
This commit is contained in:
Kyle 2015-07-26 20:18:17 -05:00
parent 55074bdf19
commit 51357fd34c
6 changed files with 96 additions and 3 deletions

View File

@ -20,8 +20,11 @@ generic web hook,
[PagerDuty](http://www.pagerduty.com/),
[HipChat](http://www.hipchat.com/),
[Slack](http://www.slack.com/),
[Pushover](https://www.pushover.net/), or
[Flowdock](https://www.flowdock.com/))
[Pushover](https://www.pushover.net/),
[Flowdock](https://www.flowdock.com/), or
[Amazon SNS](http://aws.amazon.com/sns/))
Note: Amazon SNS notifications depend on your Amazon credentials being configured as described [here](https://github.com/aws/aws-sdk-go).
See [config/fixtures/sample.conf.input](config/fixtures/sample.conf.input) for
an example config. The full configuration schema including a documentation for

View File

@ -85,6 +85,11 @@ func (c Config) Validate() error {
return fmt.Errorf("Missing from_address Flowdock config: %s", proto.MarshalTextString(fc))
}
}
for _, snsConfig := range nc.AmazonSnsConfig {
if snsConfig.TopicArn == nil {
return fmt.Errorf("Missing Topic ARN in Amazon SNS config: %s", proto.MarshalTextString(snsConfig))
}
}
if _, ok := ncNames[nc.GetName()]; ok {
return fmt.Errorf("Notification config name not unique: %s", nc.GetName())

View File

@ -110,6 +110,14 @@ message WebhookConfig {
optional bool send_resolved = 2 [default = false];
}
// Configuration for notification via Amazon SNS.
message AmazonSnsConfig {
// SNS Topic ARN.
optional string topic_arn = 1;
// Notify when resolved.
optional bool send_resolved = 2 [default = false];
}
// Notification configuration definition.
message NotificationConfig {
// Name of this NotificationConfig. Referenced from AggregationRule.
@ -130,6 +138,8 @@ message NotificationConfig {
repeated WebhookConfig webhook_config = 8;
// Zero or more OpsGenieConfig notification configurations.
repeated OpsGenieConfig opsgenie_config = 9;
// Zero or more Amazon SNS notification configurations.
repeated AmazonSnsConfig amazon_sns_config = 10;
}
// A regex-based label filter used in aggregations.

View File

@ -24,6 +24,10 @@ notification_config {
from_address: "aliaswithgravatar@somehwere.com"
tag: "monitoring"
}
amazon_sns_config {
topic_arn: "arn:aws:sns:us-east-1:an-arn-id:topic"
send_resolved: true
}
}
aggregation_rule {

View File

@ -17,6 +17,7 @@ It has these top-level messages:
FlowdockConfig
OpsGenieConfig
WebhookConfig
AmazonSnsConfig
NotificationConfig
Filter
AggregationRule
@ -419,6 +420,35 @@ func (m *WebhookConfig) GetSendResolved() bool {
return Default_WebhookConfig_SendResolved
}
// Configuration for notification via Amazon SNS.
type AmazonSnsConfig struct {
// SNS Topic ARN.
TopicArn *string `protobuf:"bytes,1,opt,name=topic_arn" json:"topic_arn,omitempty"`
// Notify when resolved
SendResolved *bool `protobuf:"varint,2,opt,name=send_resolved,def=0" json:"send_resolved,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AmazonSnsConfig) Reset() { *m = AmazonSnsConfig{} }
func (m *AmazonSnsConfig) String() string { return proto.CompactTextString(m) }
func (*AmazonSnsConfig) ProtoMessage() {}
const Default_AmazonSnsConfig_SendResolved bool = false
func (m *AmazonSnsConfig) GetTopicArn() string {
if m != nil && m.TopicArn != nil {
return *m.TopicArn
}
return ""
}
func (m *AmazonSnsConfig) GetSendResolved() bool {
if m != nil && m.SendResolved != nil {
return *m.SendResolved
}
return Default_AmazonSnsConfig_SendResolved
}
// Notification configuration definition.
type NotificationConfig struct {
// Name of this NotificationConfig. Referenced from AggregationRule.
@ -439,7 +469,9 @@ type NotificationConfig struct {
WebhookConfig []*WebhookConfig `protobuf:"bytes,8,rep,name=webhook_config" json:"webhook_config,omitempty"`
// Zero or more OpsGenieConfig notification configurations.
OpsgenieConfig []*OpsGenieConfig `protobuf:"bytes,9,rep,name=opsgenie_config" json:"opsgenie_config,omitempty"`
XXX_unrecognized []byte `json:"-"`
// Zero or more Amazon SNS notification configurations.
AmazonSnsConfig []*AmazonSnsConfig `protobuf:"bytes,10,rep,name=amazon_sns_config" json:"amazon_sns_config,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *NotificationConfig) Reset() { *m = NotificationConfig{} }
@ -509,6 +541,13 @@ func (m *NotificationConfig) GetOpsgenieConfig() []*OpsGenieConfig {
return nil
}
func (m *NotificationConfig) GetAmazonSnsConfig() []*AmazonSnsConfig {
if m != nil {
return m.AmazonSnsConfig
}
return nil
}
// A regex-based label filter used in aggregations.
type Filter struct {
// The regex matching the label name.

View File

@ -35,6 +35,8 @@ import (
"github.com/prometheus/log"
"github.com/thorduri/pushover"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/sns"
pb "github.com/prometheus/alertmanager/config/generated"
)
@ -458,6 +460,28 @@ func (n *notifier) sendWebhookNotification(op notificationOp, config *pb.Webhook
return nil
}
func (n *notifier) sendAmazonSnsNotification(op notificationOp, config *pb.AmazonSnsConfig, a *Alert) error {
snsAPI := sns.New(nil)
status := ""
switch op {
case notificationOpTrigger:
status = "firing"
case notificationOpResolve:
status = "resolved"
}
params := &sns.PublishInput{
Message: aws.String(fmt.Sprintf("%s -- %s", a.Description, status)),
MessageStructure: aws.String("string"),
Subject: aws.String(fmt.Sprintf("%s -- %s", a.Summary, status)),
TopicARN: aws.String(config.GetTopicArn()),
}
_, err := snsAPI.Publish(params)
return err
}
func postJSON(jsonMessage []byte, url string) (*http.Response, error) {
timeout := time.Duration(5 * time.Second)
client := http.Client{
@ -775,6 +799,14 @@ func (n *notifier) handleNotification(a *Alert, op notificationOp, config *pb.No
log.Errorln("Error sending OpsGenie notification:", err)
}
}
for _, snsConfig := range config.AmazonSnsConfig {
if op == notificationOpResolve && !snsConfig.GetSendResolved() {
continue
}
if err := n.sendAmazonSnsNotification(op, snsConfig, a); err != nil {
log.Errorln("Error sending Amazon SNS notification:", err)
}
}
}
func (n *notifier) Dispatch() {