Improve PagerDuty templating
This commit is contained in:
parent
921c4b8b55
commit
9fbc76a52f
|
@ -340,7 +340,7 @@ func (c *Receiver) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
return checkOverflow(c.XXX, "receiver config")
|
||||
}
|
||||
|
||||
// Regexp encapsulates a regexp.Regexp and makes it YAML marshallable.
|
||||
// Regexp encapsulates a regexp.Regexp and makes it YAML marshalable.
|
||||
type Regexp struct {
|
||||
*regexp.Regexp
|
||||
}
|
||||
|
|
|
@ -21,10 +21,11 @@ import (
|
|||
var (
|
||||
// DefaultEmailConfig defines default values for Email configurations.
|
||||
DefaultEmailConfig = EmailConfig{
|
||||
HTML: `{{template "email.default.html" .}}`,
|
||||
HTML: `{{ template "email.default.html" . }}`,
|
||||
}
|
||||
// DefaultEmailSubject is a template used if no email subject has been provided.
|
||||
DefaultEmailSubject = `{{template "email.default.subject" .}}`
|
||||
|
||||
// DefaultEmailSubject defines the default Subject header of an Email.
|
||||
DefaultEmailSubject = `{{ template "email.default.subject" . }}`
|
||||
|
||||
// DefaultHipchatConfig defines default values for Hipchat configurations.
|
||||
DefaultHipchatConfig = HipchatConfig{
|
||||
|
@ -34,23 +35,31 @@ var (
|
|||
|
||||
// DefaultPagerdutyConfig defines default values for PagerDuty configurations.
|
||||
DefaultPagerdutyConfig = PagerdutyConfig{
|
||||
Description: `{{template "pagerduty.default.description" .}}`,
|
||||
// TODO: Add a details field with all the alerts.
|
||||
Description: `{{ template "pagerduty.default.description" .}}`,
|
||||
Client: `{{ template "pagerduty.default.client" . }}`,
|
||||
ClientURL: `{{ template "pagerduty.default.clientURL" . }}`,
|
||||
Details: map[string]string{
|
||||
"firing": `{{ template "pagerduty.default.instances" (.Alerts | firing) }}`,
|
||||
"resolved": `{{ template "pagerduty.default.instances" (.Alerts | resolved) }}`,
|
||||
"num_firing": `{{ .Alerts | firing | len }}`,
|
||||
"num_resolved": `{{ .Alerts | resolved | len }}`,
|
||||
},
|
||||
}
|
||||
|
||||
// DefaultSlackConfig defines default values for Slack configurations.
|
||||
DefaultSlackConfig = SlackConfig{
|
||||
Color: `{{ if eq .Status "firing" }}warning{{ else }}good{{ end }}`,
|
||||
Title: `{{template "slack.default.title" . }}`,
|
||||
TitleLink: `{{template "slack.default.title_link" . }}`,
|
||||
Pretext: `{{template "slack.default.pretext" . }}`,
|
||||
Text: `{{template "slack.default.text" . }}`,
|
||||
Fallback: `{{template "slack.default.fallback" . }}`,
|
||||
Title: `{{ template "slack.default.title" . }}`,
|
||||
TitleLink: `{{ template "slack.default.title_link" . }}`,
|
||||
Pretext: `{{ template "slack.default.pretext" . }}`,
|
||||
Text: `{{ template "slack.default.text" . }}`,
|
||||
Fallback: `{{ template "slack.default.fallback" . }}`,
|
||||
}
|
||||
|
||||
// DefaultOpsGenieConfig defines default values for OpsGenie configurations.
|
||||
DefaultOpsGenieConfig = OpsGenieConfig{
|
||||
Description: `{{template "opsgenie.default.description" .}}`,
|
||||
Description: `{{ template "opsgenie.default.description" . }}`,
|
||||
Source: `{{ template "opsgenie.default.source" }}`,
|
||||
// TODO: Add a details field with all the alerts.
|
||||
}
|
||||
)
|
||||
|
@ -186,6 +195,8 @@ func (c *HipchatConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
type PagerdutyConfig struct {
|
||||
ServiceKey string `yaml:"service_key"`
|
||||
URL string `yaml:"url"`
|
||||
Client string `yaml:"client"`
|
||||
ClientURL string `yaml:"client_url"`
|
||||
Description string `yaml:"description"`
|
||||
Details map[string]string `yaml:"details"`
|
||||
|
||||
|
@ -292,6 +303,7 @@ type OpsGenieConfig struct {
|
|||
APIKey string `yaml:"api_key"`
|
||||
APIHost string `yaml:"api_host"`
|
||||
Description string `yaml:"description"`
|
||||
Source string `yaml:"source"`
|
||||
Details map[string]string `yaml:"details"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
|
|
33
main.go
33
main.go
|
@ -18,7 +18,9 @@ import (
|
|||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
|
@ -137,6 +139,9 @@ func main() {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tmpl.ExternalURL, err = extURL(*externalURL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
disp.Stop()
|
||||
|
||||
|
@ -193,3 +198,31 @@ func printVersion() {
|
|||
}
|
||||
fmt.Fprintln(os.Stdout, strings.TrimSpace(buf.String()))
|
||||
}
|
||||
|
||||
func extURL(s string) (*url.URL, error) {
|
||||
if s == "" {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, port, err := net.SplitHostPort(*listenAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s = fmt.Sprintf("http://%s:%s/", hostname, port)
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ppref := strings.TrimRight(u.Path, "/")
|
||||
if ppref != "" && !strings.HasPrefix(ppref, "/") {
|
||||
ppref = "/" + ppref
|
||||
}
|
||||
u.Path = ppref
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
|
|
@ -190,7 +190,7 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) error {
|
|||
}
|
||||
|
||||
var (
|
||||
data = template.NewData(groupLabels(ctx), as...)
|
||||
data = n.tmpl.Data(receiver(ctx), groupLabels(ctx), as...)
|
||||
tmpl = tmplText(n.tmpl, data, &err)
|
||||
from = tmpl(n.conf.From)
|
||||
to = tmpl(n.conf.To)
|
||||
|
@ -274,7 +274,7 @@ type pagerDutyMessage struct {
|
|||
Description string `json:"description"`
|
||||
Client string `json:"client,omitempty"`
|
||||
ClientURL string `json:"client_url,omitempty"`
|
||||
Details map[string]string `json:"details"`
|
||||
Details map[string]string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
// Notify implements the Notifier interface.
|
||||
|
@ -289,7 +289,7 @@ func (n *PagerDuty) Notify(ctx context.Context, as ...*types.Alert) error {
|
|||
var err error
|
||||
var (
|
||||
alerts = types.Alerts(as...)
|
||||
data = template.NewData(groupLabels(ctx), as...)
|
||||
data = n.tmpl.Data(receiver(ctx), groupLabels(ctx), as...)
|
||||
tmpl = tmplText(n.tmpl, data, &err)
|
||||
eventType = pagerDutyEventTrigger
|
||||
)
|
||||
|
@ -310,11 +310,10 @@ func (n *PagerDuty) Notify(ctx context.Context, as ...*types.Alert) error {
|
|||
IncidentKey: key,
|
||||
Description: tmpl(n.conf.Description),
|
||||
Details: details,
|
||||
Client: "AlertManager",
|
||||
}
|
||||
if eventType == pagerDutyEventTrigger {
|
||||
msg.Client = "Prometheus Alertmanager"
|
||||
msg.ClientURL = ""
|
||||
msg.Client = tmpl(n.conf.Client)
|
||||
msg.ClientURL = tmpl(n.conf.ClientURL)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -375,7 +374,7 @@ func (n *Slack) Notify(ctx context.Context, as ...*types.Alert) error {
|
|||
var err error
|
||||
var (
|
||||
alerts = types.Alerts(as...)
|
||||
data = template.NewData(groupLabels(ctx), as...)
|
||||
data = n.tmpl.Data(receiver(ctx), groupLabels(ctx), as...)
|
||||
tmplText = tmplText(n.tmpl, data, &err)
|
||||
tmplHTML = tmplHTML(n.tmpl, data, &err)
|
||||
)
|
||||
|
@ -454,7 +453,7 @@ func (n *OpsGenie) Notify(ctx context.Context, as ...*types.Alert) error {
|
|||
if !ok {
|
||||
return fmt.Errorf("group key missing")
|
||||
}
|
||||
data := template.NewData(groupLabels(ctx), as...)
|
||||
data := n.tmpl.Data(receiver(ctx), groupLabels(ctx), as...)
|
||||
|
||||
log.With("incident", key).Debugln("notifying OpsGenie")
|
||||
|
||||
|
|
|
@ -74,6 +74,14 @@ func WithNow(ctx context.Context, t time.Time) context.Context {
|
|||
return context.WithValue(ctx, keyNow, t)
|
||||
}
|
||||
|
||||
func receiver(ctx context.Context) string {
|
||||
recv, ok := Receiver(ctx)
|
||||
if !ok {
|
||||
log.Error("missing receiver")
|
||||
}
|
||||
return recv
|
||||
}
|
||||
|
||||
// Receiver extracts a receiver from the context. Iff none exists, the
|
||||
// second argument is false.
|
||||
func Receiver(ctx context.Context) (string, bool) {
|
||||
|
|
|
@ -1,22 +1,36 @@
|
|||
{{ define "__alertmanager" }}AlertManager{{ end }}
|
||||
{{ define "__alertmanagerURL" }}{{ .ExternalURL }}{{ end }}
|
||||
{{ define "__subject" }}{{$dot := .}}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts | firing | len }}{{ end }}] {{ range $i, $p := .GroupLabels | sortedPairs }}{{ $p.Value }} {{ end }}{{if gt (len .CommonLabels) (len .GroupLabels) }}({{ range $i, $p := .CommonLabels | sortedPairs }}{{ if eq "" (index $dot.GroupLabels $p.Name) }}{{ $p.Value }} {{ end }}{{ end }}){{ end }}{{ end }}
|
||||
{{ define "__description" }}TODO{{ end }}
|
||||
{{ define "__alertmanagerURL" }}{{ .ExternalURL }}/#/alerts?receiver={{ .Receiver }}{{ end }}
|
||||
|
||||
|
||||
{{ define "__subject" }}{{$dot := .}}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts | firing | len }}{{ end }}] {{ range .GroupLabels | sortedPairs }}{{ .Value }} {{ end }}{{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ range .CommonLabels | sortedPairs }}{{ if eq "" (index $dot.GroupLabels .Name) }}{{ .Value }} {{ end }}{{ end }}){{ end }}{{ end }}
|
||||
{{ define "__description" }}{{ end }}
|
||||
|
||||
{{ define "__text_alert_list" }}{{ range . }}Labels:
|
||||
{{ range .Labels | sortedPairs }} - {{ .Name }} = {{ .Value }}
|
||||
{{ end }}Annotations:
|
||||
{{ range .Annotations | sortedPairs }} - {{ .Name }} = {{ .Value }}
|
||||
{{ end}}
|
||||
{{ end }}{{ end }}
|
||||
|
||||
|
||||
{{ define "slack.default.title" }}{{ template "__subject" . }}{{ end }}
|
||||
{{ define "slack.default.fallback" }}{{ template "__subject" . }}{{ end }}
|
||||
{{ define "slack.default.pretext" }}{{ end }}
|
||||
{{ define "slack.default.titlelink" }}{{ template "__alertmanagerURL" }}/something{{ end }}
|
||||
{{ define "slack.default.titlelink" }}{{ template "__alertmanagerURL" . }}{{ end }}
|
||||
{{ define "slack.default.text" }}{{ template "__subject" . }}{{ end }}
|
||||
|
||||
|
||||
{{ define "pagerduty.default.description" }}{{ template "__subject" . }}{{ end }}
|
||||
{{ define "pagerduty.default.client" }}{{ template "__alertmanager" . }}{{ end }}
|
||||
{{ define "pagerduty.default.clientURL" }}{{ template "__alertmanagerURL" . }}{{ end }}
|
||||
{{ define "pagerduty.default.instances" }}{{ template "__text_alert_list" . }}{{ end }}
|
||||
|
||||
{{ define "email.default.subject" }}{{ template "__subject" . }}{{ end }}
|
||||
|
||||
{{ define "opsgenie.default.description" }}{{ template "__subject" . }}{{ end }}
|
||||
{{ define "opsgenie.default.source" }}{{ template "__alertmanagerURL" . }}{{ end }}
|
||||
|
||||
|
||||
{{ define "email.default.subject" }}{{ template "__subject" . }}{{ end }}
|
||||
{{ define "email.default.html" }}
|
||||
{{/*
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ package template
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
|
@ -30,6 +31,8 @@ import (
|
|||
type Template struct {
|
||||
text *tmpltext.Template
|
||||
html *tmplhtml.Template
|
||||
|
||||
ExternalURL *url.URL
|
||||
}
|
||||
|
||||
// FromGlobs calls ParseGlob on all path globs provided and returns the
|
||||
|
@ -143,8 +146,9 @@ type Pair struct {
|
|||
// as this will confuse them and prevent simple things like
|
||||
// simple equality checks to fail. Map everything to float64/string.
|
||||
type Data struct {
|
||||
Status string
|
||||
Alerts []Alert
|
||||
Receiver string
|
||||
Status string
|
||||
Alerts []Alert
|
||||
|
||||
GroupLabels map[string]string
|
||||
CommonLabels map[string]string
|
||||
|
@ -160,16 +164,18 @@ type Alert struct {
|
|||
Annotations map[string]string
|
||||
}
|
||||
|
||||
func NewData(groupLabels model.LabelSet, as ...*types.Alert) *Data {
|
||||
// Data assembles data for template expansion.
|
||||
func (t *Template) Data(recv string, groupLabels model.LabelSet, as ...*types.Alert) *Data {
|
||||
alerts := types.Alerts(as...)
|
||||
|
||||
data := &Data{
|
||||
Receiver: strings.SplitN(recv, "/", 2)[0],
|
||||
Status: string(alerts.Status()),
|
||||
Alerts: make([]Alert, 0, len(alerts)),
|
||||
GroupLabels: map[string]string{},
|
||||
CommonLabels: map[string]string{},
|
||||
CommonAnnotations: map[string]string{},
|
||||
ExternalURL: "something",
|
||||
ExternalURL: t.ExternalURL.String(),
|
||||
}
|
||||
|
||||
for _, a := range alerts {
|
||||
|
|
Loading…
Reference in New Issue