From f4f6ceccc4d4f19176f272180002d449d64caad1 Mon Sep 17 00:00:00 2001 From: Cameron Davison Date: Fri, 10 Apr 2015 12:12:24 -0500 Subject: [PATCH] Add simple support for HipChat notifications --- .gitignore | 3 ++ README.md | 3 +- config/config.go | 8 +++ config/config.proto | 16 ++++++ config/fixtures/sample.conf.input | 4 ++ config/generated/config.pb.go | 83 ++++++++++++++++++++++++++++--- manager/notifier.go | 45 +++++++++++++++++ 7 files changed, 155 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 3a32609b..8f11fae6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ +.deps/ alertmanager *-stamp +*.config +silences.json diff --git a/README.md b/README.md index 351dede1..306bba14 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ following aspects: * aggregating alerts by labelset * handling notification repeats * sending alert notifications via external services (currently email, -[PagerDuty](http://www.pagerduty.com/) or +[PagerDuty](http://www.pagerduty.com/), +[HipChat](http://www.hipchat.com/), or [Pushover](https://www.pushover.net/)) See [config/fixtures/sample.conf.input](config/fixtures/sample.conf.input) for diff --git a/config/config.go b/config/config.go index cd3e1cc0..3e7a5017 100644 --- a/config/config.go +++ b/config/config.go @@ -64,6 +64,14 @@ func (c Config) Validate() error { return fmt.Errorf("Missing user key in Pushover notification config: %s", proto.MarshalTextString(ec)) } } + for _, hcc := range nc.HipchatConfig { + if hcc.AuthToken == nil { + return fmt.Errorf("Missing token in HipChat config: %s", proto.MarshalTextString(hcc)) + } + if hcc.RoomId == nil { + return fmt.Errorf("Missing room in HipChat config: %s", proto.MarshalTextString(hcc)) + } + } if _, ok := ncNames[nc.GetName()]; ok { return fmt.Errorf("Notification config name not unique: %s", nc.GetName()) diff --git a/config/config.proto b/config/config.proto index 3661b32e..e53cf148 100644 --- a/config/config.proto +++ b/config/config.proto @@ -34,6 +34,20 @@ message PushoverConfig { optional string user_key = 2; } +// Configuration for notification via HipChat. +message HipChatConfig { + // https://www.hipchat.com/docs/apiv2/method/send_room_notification + + // HipChat auth token, (https://www.hipchat.com/docs/api/auth). + optional string auth_token = 1; + // HipChat room id, (https://www.hipchat.com/rooms/ids). + optional int32 room_id = 2; + // Color of message. + optional string color = 3 [default = "purple"]; + // Should this message notify or not. + optional bool notify = 4 [default = false]; +} + // Notification configuration definition. message NotificationConfig { // Name of this NotificationConfig. Referenced from AggregationRule. @@ -44,6 +58,8 @@ message NotificationConfig { repeated EmailConfig email_config = 3; // Zero or more pushover notification configurations. repeated PushoverConfig pushover_config = 4; + // Zero or more hipchat notification configurations. + repeated HipChatConfig hipchat_config = 5; } // A regex-based label filter used in aggregations. diff --git a/config/fixtures/sample.conf.input b/config/fixtures/sample.conf.input index d343b233..d9c29e9c 100644 --- a/config/fixtures/sample.conf.input +++ b/config/fixtures/sample.conf.input @@ -10,6 +10,10 @@ notification_config { token: "mypushovertoken" user_key: "mypushoverkey" } + hipchat_config { + auth_token: "hipchatauthtoken" + room_id: 123456 + } } aggregation_rule { diff --git a/config/generated/config.pb.go b/config/generated/config.pb.go index cd2ed8ef..e837fd4f 100644 --- a/config/generated/config.pb.go +++ b/config/generated/config.pb.go @@ -2,15 +2,30 @@ // source: config.proto // DO NOT EDIT! +/* +Package io_prometheus_alertmanager is a generated protocol buffer package. + +It is generated from these files: + config.proto + +It has these top-level messages: + PagerDutyConfig + EmailConfig + PushoverConfig + HipChatConfig + NotificationConfig + Filter + AggregationRule + InhibitRule + AlertManagerConfig +*/ package io_prometheus_alertmanager -import proto "code.google.com/p/goprotobuf/proto" -import json "encoding/json" +import proto "github.com/golang/protobuf/proto" import math "math" -// Reference proto, json, and math imports to suppress error if they are not otherwise used. +// Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal -var _ = &json.SyntaxError{} var _ = math.Inf // Configuration for notification via PagerDuty. @@ -77,6 +92,53 @@ func (m *PushoverConfig) GetUserKey() string { return "" } +type HipChatConfig struct { + // Hipchat auth token, https://www.hipchat.com/docs/api/auth + AuthToken *string `protobuf:"bytes,1,opt,name=auth_token" json:"auth_token,omitempty"` + // Hipchat room id, https://www.hipchat.com/rooms/ids + RoomId *int32 `protobuf:"varint,2,opt,name=room_id" json:"room_id,omitempty"` + // color of message + Color *string `protobuf:"bytes,3,opt,name=color,def=purple" json:"color,omitempty"` + // should this message notify or not + Notify *bool `protobuf:"varint,4,opt,name=notify,def=0" json:"notify,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *HipChatConfig) Reset() { *m = HipChatConfig{} } +func (m *HipChatConfig) String() string { return proto.CompactTextString(m) } +func (*HipChatConfig) ProtoMessage() {} + +const Default_HipChatConfig_Color string = "purple" +const Default_HipChatConfig_Notify bool = false + +func (m *HipChatConfig) GetAuthToken() string { + if m != nil && m.AuthToken != nil { + return *m.AuthToken + } + return "" +} + +func (m *HipChatConfig) GetRoomId() int32 { + if m != nil && m.RoomId != nil { + return *m.RoomId + } + return 0 +} + +func (m *HipChatConfig) GetColor() string { + if m != nil && m.Color != nil { + return *m.Color + } + return Default_HipChatConfig_Color +} + +func (m *HipChatConfig) GetNotify() bool { + if m != nil && m.Notify != nil { + return *m.Notify + } + return Default_HipChatConfig_Notify +} + // Notification configuration definition. type NotificationConfig struct { // Name of this NotificationConfig. Referenced from AggregationRule. @@ -86,8 +148,10 @@ type NotificationConfig struct { // Zero or more email notification configurations. EmailConfig []*EmailConfig `protobuf:"bytes,3,rep,name=email_config" json:"email_config,omitempty"` // Zero or more pushover notification configurations. - PushoverConfig []*PushoverConfig `protobuf:"bytes,4,rep,name=pushover_config" json:"pushover_config,omitempty"` - XXX_unrecognized []byte `json:"-"` + PushoverConfig []*PushoverConfig `protobuf:"bytes,4,rep,name=pushover_config" json:"pushover_config,omitempty"` + // Zero or more hipchat notification configuration. + HipchatConfig []*HipChatConfig `protobuf:"bytes,5,rep,name=hipchat_config" json:"hipchat_config,omitempty"` + XXX_unrecognized []byte `json:"-"` } func (m *NotificationConfig) Reset() { *m = NotificationConfig{} } @@ -122,6 +186,13 @@ func (m *NotificationConfig) GetPushoverConfig() []*PushoverConfig { return nil } +func (m *NotificationConfig) GetHipchatConfig() []*HipChatConfig { + if m != nil { + return m.HipchatConfig + } + return nil +} + // A regex-based label filter used in aggregations. type Filter struct { // The regex matching the label name. diff --git a/manager/notifier.go b/manager/notifier.go index fc287acb..022bd66b 100644 --- a/manager/notifier.go +++ b/manager/notifier.go @@ -19,6 +19,7 @@ import ( "encoding/json" "flag" "fmt" + "html" "io" "io/ioutil" "net" @@ -28,6 +29,7 @@ import ( "strings" "sync" "text/template" + "time" "github.com/golang/glog" "github.com/thorduri/pushover" @@ -56,6 +58,7 @@ var ( pagerdutyApiUrl = flag.String("pagerdutyApiUrl", "https://events.pagerduty.com/generic/2010-04-15/create_event.json", "PagerDuty API URL.") smtpSmartHost = flag.String("smtpSmartHost", "", "Address of the smarthost to send all email notifications to.") smtpSender = flag.String("smtpSender", "alertmanager@example.org", "Sender email address to use in email notifications.") + hipchatUrl = flag.String("hipchat.url", "https://api.hipchat.com/v2/", "HipChat API V2 URL.") ) // A Notifier is responsible for sending notifications for alerts according to @@ -164,6 +167,43 @@ func (n *notifier) sendPagerDutyNotification(serviceKey string, a *Alert) error return nil } +func (n *notifier) sendHipChatNotification(authToken string, roomId int32, color string, notify bool, a *Alert) error { + // https://www.hipchat.com/docs/apiv2/method/send_room_notification + incidentKey := a.Fingerprint() + buf, err := json.Marshal(map[string]interface{}{ + "color": color, + "message": fmt.Sprintf("%s: %s view", html.EscapeString(a.Labels["alertname"]), html.EscapeString(a.Summary), a.Payload["GeneratorURL"]), + "notify": notify, + "message_format": "html", + }) + if err != nil { + return err + } + + timeout := time.Duration(5 * time.Second) + client := http.Client{ + Timeout: timeout, + } + resp, err := client.Post( + *hipchatUrl+fmt.Sprintf("/room/%d/notification?auth_token=%s", roomId, authToken), + contentTypeJson, + bytes.NewBuffer(buf), + ) + if err != nil { + return err + } + defer resp.Body.Close() + + respBuf, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + glog.Infof("Sent HipChat notification: %v: HTTP %d: %s", incidentKey, resp.StatusCode, respBuf) + // BUG: Check response for result of operation. + return nil +} + func writeEmailBody(w io.Writer, from string, to string, a *Alert) error { err := bodyTmpl.Execute(w, struct { From string @@ -296,6 +336,11 @@ func (n *notifier) handleNotification(a *Alert, config *pb.NotificationConfig) { glog.Error("Error sending Pushover notification: ", err) } } + for _, hcConfig := range config.HipchatConfig { + if err := n.sendHipChatNotification(hcConfig.GetAuthToken(), hcConfig.GetRoomId(), hcConfig.GetColor(), hcConfig.GetNotify(), a); err != nil { + glog.Error("Error sending HipChat notification: ", err) + } + } } func (n *notifier) Dispatch() {