Merge pull request #258 from stapelberg/pushover
Re-implement pushover notifications
This commit is contained in:
commit
1748a0e304
|
@ -389,6 +389,7 @@ type Receiver struct {
|
|||
SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty"`
|
||||
WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty"`
|
||||
OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty"`
|
||||
PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
|
|
|
@ -16,6 +16,7 @@ package config
|
|||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -89,6 +90,19 @@ var (
|
|||
Source: `{{ template "opsgenie.default.source" . }}`,
|
||||
// TODO: Add a details field with all the alerts.
|
||||
}
|
||||
|
||||
// DefaultPushoverConfig defines default values for Pushover configurations.
|
||||
DefaultPushoverConfig = PushoverConfig{
|
||||
NotifierConfig: NotifierConfig{
|
||||
VSendResolved: true,
|
||||
},
|
||||
Title: `{{ template "pushover.default.title" . }}`,
|
||||
Message: `{{ template "pushover.default.message" . }}`,
|
||||
URL: `{{ template "pushover.default.url" . }}`,
|
||||
Priority: `{{ if eq .Status "firing" }}2{{ else }}0{{ end }}`, // emergency (firing) or normal
|
||||
Retry: duration(1 * time.Minute),
|
||||
Expire: duration(1 * time.Hour),
|
||||
}
|
||||
)
|
||||
|
||||
// NotifierConfig contains base options common across all notifier configurations.
|
||||
|
@ -283,3 +297,49 @@ func (c *OpsGenieConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
|
|||
}
|
||||
return checkOverflow(c.XXX, "opsgenie config")
|
||||
}
|
||||
|
||||
type duration time.Duration
|
||||
|
||||
func (d *duration) UnmarshalText(text []byte) error {
|
||||
parsed, err := time.ParseDuration(string(text))
|
||||
if err == nil {
|
||||
*d = duration(parsed)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d duration) MarshalText() ([]byte, error) {
|
||||
return []byte(time.Duration(d).String()), nil
|
||||
}
|
||||
|
||||
type PushoverConfig struct {
|
||||
NotifierConfig `yaml:",inline"`
|
||||
|
||||
UserKey Secret `yaml:"user_key"`
|
||||
Token Secret `yaml:"token"`
|
||||
Title string `yaml:"title"`
|
||||
Message string `yaml:"message"`
|
||||
URL string `yaml:"url"`
|
||||
Priority string `yaml:"priority"`
|
||||
Retry duration `yaml:"retry"`
|
||||
Expire duration `yaml:"expire"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *PushoverConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*c = DefaultPushoverConfig
|
||||
type plain PushoverConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.UserKey == "" {
|
||||
return fmt.Errorf("missing user key in Pushover config")
|
||||
}
|
||||
if c.Token == "" {
|
||||
return fmt.Errorf("missing token in Pushover config")
|
||||
}
|
||||
return checkOverflow(c.XXX, "pushover config")
|
||||
}
|
||||
|
|
|
@ -19,11 +19,13 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -134,6 +136,10 @@ func Build(confs []*config.Receiver, tmpl *template.Template) map[string]Fanout
|
|||
n := NewHipchat(c, tmpl)
|
||||
add(i, n, filter(n, c))
|
||||
}
|
||||
for i, c := range nc.PushoverConfigs {
|
||||
n := NewPushover(c, tmpl)
|
||||
add(i, n, filter(n, c))
|
||||
}
|
||||
|
||||
res[nc.Name] = fo
|
||||
}
|
||||
|
@ -682,6 +688,76 @@ func (n *OpsGenie) Notify(ctx context.Context, as ...*types.Alert) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Pushover implements a Notifier for Pushover notifications.
|
||||
type Pushover struct {
|
||||
conf *config.PushoverConfig
|
||||
tmpl *template.Template
|
||||
}
|
||||
|
||||
// NewPushover returns a new Pushover notifier.
|
||||
func NewPushover(c *config.PushoverConfig, t *template.Template) *Pushover {
|
||||
return &Pushover{conf: c, tmpl: t}
|
||||
}
|
||||
|
||||
func (*Pushover) name() string { return "pushover" }
|
||||
|
||||
// Notify implements the Notifier interface.
|
||||
func (n *Pushover) Notify(ctx context.Context, as ...*types.Alert) error {
|
||||
key, ok := GroupKey(ctx)
|
||||
if !ok {
|
||||
return fmt.Errorf("group key missing")
|
||||
}
|
||||
data := n.tmpl.Data(receiver(ctx), groupLabels(ctx), as...)
|
||||
|
||||
log.With("incident", key).Debugln("notifying Pushover")
|
||||
|
||||
var err error
|
||||
tmpl := tmplText(n.tmpl, data, &err)
|
||||
|
||||
parameters := url.Values{}
|
||||
parameters.Add("token", tmpl(string(n.conf.Token)))
|
||||
parameters.Add("user", tmpl(string(n.conf.UserKey)))
|
||||
title := tmpl(n.conf.Title)
|
||||
message := tmpl(n.conf.Message)
|
||||
parameters.Add("title", title)
|
||||
if len(title)+len(message) > 512 {
|
||||
message = message[:512]
|
||||
log.With("incident", key).Debugf("Truncated message to %q due to Pushover message limit", message)
|
||||
}
|
||||
if message == "" {
|
||||
// Pushover rejects empty messages.
|
||||
message = "(no details)"
|
||||
}
|
||||
parameters.Add("message", message)
|
||||
parameters.Add("url", tmpl(n.conf.URL))
|
||||
parameters.Add("priority", tmpl(n.conf.Priority))
|
||||
parameters.Add("retry", fmt.Sprintf("%d", int64(time.Duration(n.conf.Retry).Seconds())))
|
||||
parameters.Add("expire", fmt.Sprintf("%d", int64(time.Duration(n.conf.Expire).Seconds())))
|
||||
|
||||
apiURL := "https://api.pushover.net/1/messages.json"
|
||||
u, err := url.Parse(apiURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.RawQuery = parameters.Encode()
|
||||
log.With("incident", key).Debugf("Pushover URL = %q", u.String())
|
||||
|
||||
resp, err := ctxhttp.Post(ctx, http.DefaultClient, u.String(), "text/plain", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode/100 != 2 {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("unexpected status code %v (body: %s)", resp.StatusCode, string(body))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func tmplText(tmpl *template.Template, data *template.Data, err *error) func(string) string {
|
||||
return func(name string) (s string) {
|
||||
if *err != nil {
|
||||
|
|
|
@ -162,3 +162,8 @@ SOFTWARE.
|
|||
</html>
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ define "pushover.default.title" }}{{ template "__subject" . }}{{ end }}
|
||||
{{ define "pushover.default.message" }}{{ .CommonAnnotations.SortedPairs.Values | join " " }}
|
||||
{{ template "__text_alert_list" .Alerts.Firing }}{{ end }}
|
||||
{{ define "pushover.default.url" }}{{ template "__alertmanagerURL" . }}{{ end }}
|
||||
|
|
Loading…
Reference in New Issue