add hipchat room notifier

This commit is contained in:
louis 2016-01-05 20:52:08 +01:00
parent 0c668ea4e5
commit 23db37dc98
5 changed files with 156 additions and 11 deletions

View File

@ -26,7 +26,7 @@ import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
var patAuthLine = regexp.MustCompile(`((?:api_token|api_key|service_key|api_url):\s+)(".+"|'.+'|[^\s]+)`) var patAuthLine = regexp.MustCompile(`((?:api_token|api_key|service_key|api_url|auth_token):\s+)(".+"|'.+'|[^\s]+)`)
// Secret is a string that must not be revealed on marshaling. // Secret is a string that must not be revealed on marshaling.
type Secret string type Secret string
@ -169,6 +169,23 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
sc.APIURL = c.Global.SlackAPIURL sc.APIURL = c.Global.SlackAPIURL
} }
} }
for _, hc := range rcv.HipchatConfigs {
if hc.APIURL == "" {
if c.Global.HipchatURL == "" {
return fmt.Errorf("no global Hipchat API URL set")
}
hc.APIURL = c.Global.HipchatURL
}
if !strings.HasSuffix(hc.APIURL, "/") {
hc.APIURL += "/"
}
if hc.AuthToken == "" {
if c.Global.HipchatAuthToken == "" {
return fmt.Errorf("no global Hipchat Auth Token set")
}
hc.AuthToken = c.Global.HipchatAuthToken
}
}
for _, pdc := range rcv.PagerdutyConfigs { for _, pdc := range rcv.PagerdutyConfigs {
if pdc.URL == "" { if pdc.URL == "" {
if c.Global.PagerdutyURL == "" { if c.Global.PagerdutyURL == "" {
@ -198,6 +215,7 @@ var DefaultGlobalConfig = GlobalConfig{
ResolveTimeout: model.Duration(5 * time.Minute), ResolveTimeout: model.Duration(5 * time.Minute),
PagerdutyURL: "https://events.pagerduty.com/generic/2010-04-15/create_event.json", PagerdutyURL: "https://events.pagerduty.com/generic/2010-04-15/create_event.json",
HipchatURL: "https://api.hipchat.com/",
OpsGenieAPIHost: "https://api.opsgenie.com/", OpsGenieAPIHost: "https://api.opsgenie.com/",
} }
@ -208,11 +226,13 @@ type GlobalConfig struct {
// if it has not been updated. // if it has not been updated.
ResolveTimeout model.Duration `yaml:"resolve_timeout"` ResolveTimeout model.Duration `yaml:"resolve_timeout"`
SMTPFrom string `yaml:"smtp_from"` SMTPFrom string `yaml:"smtp_from"`
SMTPSmarthost string `yaml:"smtp_smarthost"` SMTPSmarthost string `yaml:"smtp_smarthost"`
SlackAPIURL Secret `yaml:"slack_api_url"` SlackAPIURL Secret `yaml:"slack_api_url"`
PagerdutyURL string `yaml:"pagerduty_url"` PagerdutyURL string `yaml:"pagerduty_url"`
OpsGenieAPIHost string `yaml:"opsgenie_api_host"` HipchatURL string `yaml:"hipchat_url"`
HipchatAuthToken Secret `yaml:"hipchat_auth_token"`
OpsGenieAPIHost string `yaml:"opsgenie_api_host"`
} }
// UnmarshalYAML implements the yaml.Unmarshaler interface. // UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -336,6 +356,7 @@ type Receiver struct {
EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty"` EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty"`
PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty"` PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty"`
HipchatConfigs []*HipchatConfig `yaml:"hipchat_configs,omitempty"`
SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty"` SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty"`
WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty"` WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty"`
OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty"` OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty"`

View File

@ -67,6 +67,18 @@ var (
Fallback: `{{ template "slack.default.fallback" . }}`, Fallback: `{{ template "slack.default.fallback" . }}`,
} }
// DefaultHipchatConfig defines default values for Hipchat configurations.
DefaultHipchatConfig = HipchatConfig{
NotifierConfig: NotifierConfig{
VSendResolved: false,
},
Color: `{{ if eq .Status "firing" }}red{{ else }}green{{ end }}`,
From: `{{ template "hipchat.default.from" . }}`,
Notify: false,
Message: `{{ template "hipchat.default.message" . }}`,
MessageFormat: `text`,
}
// DefaultOpsGenieConfig defines default values for OpsGenie configurations. // DefaultOpsGenieConfig defines default values for OpsGenie configurations.
DefaultOpsGenieConfig = OpsGenieConfig{ DefaultOpsGenieConfig = OpsGenieConfig{
NotifierConfig: NotifierConfig{ NotifierConfig: NotifierConfig{
@ -188,6 +200,41 @@ func (c *SlackConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
return checkOverflow(c.XXX, "slack config") return checkOverflow(c.XXX, "slack config")
} }
// HipchatConfig configures notifications via Hipchat.
type HipchatConfig struct {
NotifierConfig `yaml:",inline"`
APIURL string `yaml:"api_url"`
AuthToken Secret `yaml:"auth_token"`
RoomID string `yaml:"room_id"`
From string `yaml:"from"`
Notify bool `yaml:"notify"`
Message string `yaml:"message"`
MessageFormat string `yaml:"message_format"`
Color string `yaml:"color"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (c *HipchatConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultHipchatConfig
type plain HipchatConfig
if err := unmarshal((*plain)(c)); err != nil {
return err
}
if c.RoomID == "" {
return fmt.Errorf("missing room id in Hipchat config")
}
if c.AuthToken == "" {
return fmt.Errorf("missing auth token in Hipchat config")
}
return checkOverflow(c.XXX, "hipchat config")
}
// WebhookConfig configures notifications via a generic webhook. // WebhookConfig configures notifications via a generic webhook.
type WebhookConfig struct { type WebhookConfig struct {
NotifierConfig `yaml:",inline"` NotifierConfig `yaml:",inline"`

View File

@ -98,6 +98,10 @@ func Build(confs []*config.Receiver, tmpl *template.Template) map[string]Fanout
n := NewSlack(c, tmpl) n := NewSlack(c, tmpl)
add(i, n, filter(n, c)) add(i, n, filter(n, c))
} }
for i, c := range nc.HipchatConfigs {
n := NewHipchat(c, tmpl)
add(i, n, filter(n, c))
}
res[nc.Name] = fo res[nc.Name] = fo
} }
@ -477,6 +481,75 @@ func (n *Slack) Notify(ctx context.Context, as ...*types.Alert) error {
return nil return nil
} }
// Hipchat implements a Notifier for Hipchat notifications.
type Hipchat struct {
conf *config.HipchatConfig
tmpl *template.Template
}
// NewHipchat returns a new Hipchat notification handler.
func NewHipchat(conf *config.HipchatConfig, tmpl *template.Template) *Hipchat {
return &Hipchat{
conf: conf,
tmpl: tmpl,
}
}
type hipchatReq struct {
From string `json:"from"`
Notify bool `json:"notify"`
Message string `json:"message"`
MessageFormat string `json:"message_format"`
Color string `json:"color"`
}
// Notify implements the Notifier interface.
func (n *Hipchat) Notify(ctx context.Context, as ...*types.Alert) error {
var err error
var msg string
var (
data = n.tmpl.Data(receiver(ctx), groupLabels(ctx), as...)
tmplText = tmplText(n.tmpl, data, &err)
tmplHTML = tmplHTML(n.tmpl, data, &err)
url = fmt.Sprintf("%sv2/room/%s/notification?auth_token=%s", n.conf.APIURL, n.conf.RoomID, n.conf.AuthToken)
)
if n.conf.MessageFormat == "html" {
msg = tmplHTML(n.conf.Message)
} else {
msg = tmplText(n.conf.Message)
}
req := &hipchatReq{
From: tmplText(n.conf.From),
Notify: n.conf.Notify,
Message: msg,
MessageFormat: n.conf.MessageFormat,
Color: tmplText(n.conf.Color),
}
if err != nil {
return err
}
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(req); err != nil {
return err
}
resp, err := ctxhttp.Post(ctx, http.DefaultClient, url, contentTypeJSON, &buf)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode/100 != 2 {
return fmt.Errorf("unexpected status code %v", resp.StatusCode)
}
return nil
}
// OpsGenie implements a Notifier for OpsGenie notifications. // OpsGenie implements a Notifier for OpsGenie notifications.
type OpsGenie struct { type OpsGenie struct {
conf *config.OpsGenieConfig conf *config.OpsGenieConfig

View File

@ -4,9 +4,9 @@
{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }} {{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }}
{{ define "__description" }}{{ end }} {{ define "__description" }}{{ end }}
{{ define "__text_alert_list" }}{{ range . }}Labels: {{ define "__text_alert_list" }}{{ range . }}Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}Annotations: {{ end }}Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end}} {{ end}}
{{ end }}{{ end }} {{ end }}{{ end }}
@ -20,6 +20,10 @@
{{ define "slack.default.text" }}{{ end }} {{ define "slack.default.text" }}{{ end }}
{{ define "hipchat.default.from" }}{{ template "__alertmanager" . }}{{ end }}
{{ define "hipchat.default.message" }}{{ template "__subject" . }}{{ end }}
{{ define "pagerduty.default.description" }}{{ template "__subject" . }}{{ end }} {{ define "pagerduty.default.description" }}{{ template "__subject" . }}{{ end }}
{{ define "pagerduty.default.client" }}{{ template "__alertmanager" . }}{{ end }} {{ define "pagerduty.default.client" }}{{ template "__alertmanager" . }}{{ end }}
{{ define "pagerduty.default.clientURL" }}{{ template "__alertmanagerURL" . }}{{ end }} {{ define "pagerduty.default.clientURL" }}{{ template "__alertmanagerURL" . }}{{ end }}
@ -52,7 +56,7 @@
<tr style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> <tr style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
<td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #E6522C; margin: 0; padding: 20px;" align="center" bgcolor="#E6522C" valign="top"> <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #E6522C; margin: 0; padding: 20px;" align="center" bgcolor="#E6522C" valign="top">
{{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for {{ range .GroupLabels.SortedPairs }} {{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for {{ range .GroupLabels.SortedPairs }}
{{ .Name }}={{ .Value }} {{ .Name }}={{ .Value }}
{{ end }} {{ end }}
</td> </td>
</tr> </tr>

File diff suppressed because one or more lines are too long