Add support for PagerDuty API v2 (#1054)

This commit is contained in:
Steven Carvellas 2017-11-07 05:07:27 -05:00 committed by stuart nelson
parent 4ee40f97e6
commit 5db8055bab
7 changed files with 134 additions and 40 deletions

View File

@ -156,7 +156,7 @@ receivers:
email_configs:
- to: 'team-X+alerts-critical@example.org'
pagerduty_configs:
- service_key: <team-X-key>
- routing_key: <team-X-key>
- name: 'team-Y-mails'
email_configs:
@ -164,11 +164,11 @@ receivers:
- name: 'team-Y-pager'
pagerduty_configs:
- service_key: <team-Y-key>
- routing_key: <team-Y-key>
- name: 'team-DB-pager'
pagerduty_configs:
- service_key: <team-DB-key>
- routing_key: <team-DB-key>
```
## Amtool

View File

@ -298,7 +298,7 @@ var DefaultGlobalConfig = GlobalConfig{
ResolveTimeout: model.Duration(5 * time.Minute),
SMTPRequireTLS: true,
PagerdutyURL: "https://events.pagerduty.com/generic/2010-04-15/create_event.json",
PagerdutyURL: "https://events.pagerduty.com/v2/enqueue",
HipchatURL: "https://api.hipchat.com/",
OpsGenieAPIHost: "https://api.opsgenie.com/",
VictorOpsAPIURL: "https://alert.victorops.com/integrations/generic/20131114/alert/",

View File

@ -283,7 +283,7 @@ func TestEmptyFieldsAndRegex(t *testing.T) {
HipchatURL: "https://hipchat.foobar.org/",
SlackAPIURL: "mysecret",
SMTPRequireTLS: true,
PagerdutyURL: "https://events.pagerduty.com/generic/2010-04-15/create_event.json",
PagerdutyURL: "https://events.pagerduty.com/v2/enqueue",
OpsGenieAPIHost: "https://api.opsgenie.com/",
VictorOpsAPIURL: "https://alert.victorops.com/integrations/generic/20131114/alert/",
},

View File

@ -178,12 +178,16 @@ func (c *EmailConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
type PagerdutyConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`
ServiceKey Secret `yaml:"service_key,omitempty" json:"service_key,omitempty"`
ServiceKey Secret `yaml:"service_key,omitempty" json"service_key,omitempty"`
RoutingKey Secret `yaml:"routing_key,omitempty" json:"routing_key,omitempty"`
URL string `yaml:"url,omitempty" json:"url,omitempty"`
Client string `yaml:"client,omitempty" json:"client,omitempty"`
ClientURL string `yaml:"client_url,omitempty" json:"client_url,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"`
Severity string `yaml:"severity,omitempty" json:"severity,omitempty"`
Component string `yaml:"component,omitempty" json:"component,omitempty"`
Group string `yaml:"group,omitempty" json:"group,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline" json:"-"`
@ -196,8 +200,8 @@ func (c *PagerdutyConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
if err := unmarshal((*plain)(c)); err != nil {
return err
}
if c.ServiceKey == "" {
return fmt.Errorf("missing service key in PagerDuty config")
if c.RoutingKey == "" && c.ServiceKey == "" {
return fmt.Errorf("missing service or routing key in PagerDuty config")
}
return checkOverflow(c.XXX, "pagerduty config")
}

View File

@ -49,7 +49,7 @@ service_key: ''
var cfg PagerdutyConfig
err := yaml.Unmarshal([]byte(in), &cfg)
expected := "missing service key in PagerDuty config"
expected := "missing service or routing key in PagerDuty config"
if err == nil {
t.Fatalf("no error returned, expected:\n%v", expected)

View File

@ -108,7 +108,7 @@ receivers:
email_configs:
- to: 'team-X+alerts-critical@example.org'
pagerduty_configs:
- service_key: "mysecret"
- routing_key: "mysecret"
- name: 'team-Y-mails'
email_configs:
@ -116,11 +116,11 @@ receivers:
- name: 'team-Y-pager'
pagerduty_configs:
- service_key: "mysecret"
- routing_key: "mysecret"
- name: 'team-DB-pager'
pagerduty_configs:
- service_key: "mysecret"
- routing_key: "mysecret"
- name: 'team-X-hipchat'
hipchat_configs:
- auth_token: "mysecret"

View File

@ -438,18 +438,116 @@ const (
)
type pagerDutyMessage struct {
ServiceKey string `json:"service_key"`
IncidentKey string `json:"incident_key"`
EventType string `json:"event_type"`
Description string `json:"description"`
RoutingKey string `json:"routing_key,omitempty"`
ServiceKey string `json:"service_key,omitempty"`
DedupKey string `json:"dedup_key,omitempty"`
IncidentKey string `json:"incident_key,omitempty"`
EventType string `json:"event_type,omitempty"`
Description string `json:"description,omitempty"`
EventAction string `json:"event_action"`
Payload *pagerDutyPayload `json:"payload"`
Client string `json:"client,omitempty"`
ClientURL string `json:"client_url,omitempty"`
Details map[string]string `json:"details,omitempty"`
}
type pagerDutyPayload struct {
Summary string `json:"summary"`
Source string `json:"source"`
Severity string `json:"severity"`
Timestamp string `json:"timestamp,omitempty"`
Component string `json:"component,omitempty"`
Group string `json:"group,omitempty"`
CustomDetails map[string]string `json:"custom_details,omitempty"`
}
func (n *PagerDuty) notifyV1(ctx context.Context, eventType string, key string, tmpl func(string) string, details map[string]string, as ...*types.Alert) (bool, error) {
msg := &pagerDutyMessage{}
var err error
level.Info(n.logger).Log("msg", "PagerDuty v1 API will no longer be supported: https://v2.developer.pagerduty.com/v2/docs/api-v2-frequently-asked-questions")
msg.ServiceKey = string(n.conf.ServiceKey)
msg.EventType = eventType
msg.IncidentKey = hashKey(key)
msg.Description = tmpl(n.conf.Description)
msg.Details = details
n.conf.URL = "https://events.pagerduty.com/generic/2010-04-15/create_event.json"
if eventType == pagerDutyEventTrigger {
msg.Client = tmpl(n.conf.Client)
msg.ClientURL = tmpl(n.conf.ClientURL)
}
if err != nil {
return false, err
}
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(msg); err != nil {
return false, err
}
resp, err := ctxhttp.Post(ctx, http.DefaultClient, n.conf.URL, contentTypeJSON, &buf)
if err != nil {
return true, err
}
resp.Body.Close()
return n.retryV1(resp.StatusCode)
}
func (n *PagerDuty) notifyV2(ctx context.Context, eventType string, key string, tmpl func(string) string, details map[string]string, as ...*types.Alert) (bool, error) {
var err error
msg := &pagerDutyMessage{}
if n.conf.Severity == "" {
n.conf.Severity = "error"
}
msg.RoutingKey = string(n.conf.RoutingKey)
msg.EventAction = eventType
msg.DedupKey = hashKey(key)
if eventType == pagerDutyEventTrigger {
msgpayload := &pagerDutyPayload{
Summary: tmpl(n.conf.Description),
Source: n.conf.Client,
Severity: n.conf.Severity,
CustomDetails: details,
Component: n.conf.Component,
Group: n.conf.Group,
}
msg.Payload = msgpayload
}
if eventType == pagerDutyEventTrigger {
msg.Client = tmpl(n.conf.Client)
msg.ClientURL = tmpl(n.conf.ClientURL)
}
if err != nil {
return false, err
}
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(msg); err != nil {
return false, err
}
resp, err := ctxhttp.Post(ctx, http.DefaultClient, n.conf.URL, contentTypeJSON, &buf)
if err != nil {
return true, err
}
resp.Body.Close()
return n.retryV2(resp.StatusCode)
}
// Notify implements the Notifier interface.
//
// http://developer.pagerduty.com/documentation/integration/events/trigger
// https://v2.developer.pagerduty.com/docs/events-api-v2
func (n *PagerDuty) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
key, ok := GroupKey(ctx)
if !ok {
@ -474,36 +572,17 @@ func (n *PagerDuty) Notify(ctx context.Context, as ...*types.Alert) (bool, error
details[k] = tmpl(v)
}
msg := &pagerDutyMessage{
ServiceKey: tmpl(string(n.conf.ServiceKey)),
EventType: eventType,
IncidentKey: hashKey(key),
Description: tmpl(n.conf.Description),
Details: details,
}
if eventType == pagerDutyEventTrigger {
msg.Client = tmpl(n.conf.Client)
msg.ClientURL = tmpl(n.conf.ClientURL)
}
if err != nil {
return false, err
}
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(msg); err != nil {
return false, err
if n.conf.ServiceKey != "" {
return n.notifyV1(ctx, eventType, key, tmpl, details, as...)
}
resp, err := ctxhttp.Post(ctx, http.DefaultClient, n.conf.URL, contentTypeJSON, &buf)
if err != nil {
return true, err
}
resp.Body.Close()
return n.retry(resp.StatusCode)
return n.notifyV2(ctx, eventType, key, tmpl, details, as...)
}
func (n *PagerDuty) retry(statusCode int) (bool, error) {
func (n *PagerDuty) retryV1(statusCode int) (bool, error) {
// Retrying can solve the issue on 403 (rate limiting) and 5xx response codes.
// 2xx response codes indicate a successful request.
// https://v2.developer.pagerduty.com/docs/trigger-events
@ -514,6 +593,17 @@ func (n *PagerDuty) retry(statusCode int) (bool, error) {
return false, nil
}
func (n *PagerDuty) retryV2(statusCode int) (bool, error) {
// Retrying can solve the issue on 429 (rate limiting) and 5xx response codes.
// 2xx response codes indicate a successful request.
// https://v2.developer.pagerduty.com/docs/events-api-v2#api-response-codes--retry-logic
if statusCode/100 != 2 {
return (statusCode == 429 || statusCode/100 == 5), fmt.Errorf("unexpected status code %v", statusCode)
}
return false, nil
}
// Slack implements a Notifier for Slack notifications.
type Slack struct {
conf *config.SlackConfig