mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Change handling of settings to pagerduty contact point (#57524)
* Add custom title to pagerduty contact point * Fix tests by saving decrypted key * Use simplejson
This commit is contained in:
@@ -31,83 +31,68 @@ var (
|
|||||||
// alert notifications to pagerduty
|
// alert notifications to pagerduty
|
||||||
type PagerdutyNotifier struct {
|
type PagerdutyNotifier struct {
|
||||||
*Base
|
*Base
|
||||||
Key string
|
tmpl *template.Template
|
||||||
Severity string
|
log log.Logger
|
||||||
CustomDetails map[string]string
|
ns notifications.WebhookSender
|
||||||
Class string
|
images ImageStore
|
||||||
Component string
|
settings pagerdutySettings
|
||||||
Group string
|
|
||||||
Summary string
|
|
||||||
tmpl *template.Template
|
|
||||||
log log.Logger
|
|
||||||
ns notifications.WebhookSender
|
|
||||||
images ImageStore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PagerdutyConfig struct {
|
type pagerdutySettings struct {
|
||||||
*NotificationChannelConfig
|
Key string `json:"integrationKey,omitempty" yaml:"integrationKey,omitempty"`
|
||||||
Key string
|
Severity string `json:"severity,omitempty" yaml:"severity,omitempty"`
|
||||||
Severity string
|
customDetails map[string]string
|
||||||
Class string
|
Class string `json:"class,omitempty" yaml:"class,omitempty"`
|
||||||
Component string
|
Component string `json:"component,omitempty" yaml:"component,omitempty"`
|
||||||
Group string
|
Group string `json:"group,omitempty" yaml:"group,omitempty"`
|
||||||
Summary string
|
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func PagerdutyFactory(fc FactoryConfig) (NotificationChannel, error) {
|
func PagerdutyFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||||
cfg, err := NewPagerdutyConfig(fc.Config, fc.DecryptFunc)
|
pdn, err := newPagerdutyNotifier(fc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, receiverInitError{
|
return nil, receiverInitError{
|
||||||
Reason: err.Error(),
|
Reason: err.Error(),
|
||||||
Cfg: *fc.Config,
|
Cfg: *fc.Config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NewPagerdutyNotifier(cfg, fc.NotificationService, fc.ImageStore, fc.Template), nil
|
return pdn, nil
|
||||||
}
|
|
||||||
|
|
||||||
func NewPagerdutyConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*PagerdutyConfig, error) {
|
|
||||||
key := decryptFunc(context.Background(), config.SecureSettings, "integrationKey", config.Settings.Get("integrationKey").MustString())
|
|
||||||
if key == "" {
|
|
||||||
return nil, errors.New("could not find integration key property in settings")
|
|
||||||
}
|
|
||||||
return &PagerdutyConfig{
|
|
||||||
NotificationChannelConfig: config,
|
|
||||||
Key: key,
|
|
||||||
Severity: config.Settings.Get("severity").MustString("critical"),
|
|
||||||
Class: config.Settings.Get("class").MustString("default"),
|
|
||||||
Component: config.Settings.Get("component").MustString("Grafana"),
|
|
||||||
Group: config.Settings.Get("group").MustString("default"),
|
|
||||||
Summary: config.Settings.Get("summary").MustString(DefaultMessageTitleEmbed),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPagerdutyNotifier is the constructor for the PagerDuty notifier
|
// NewPagerdutyNotifier is the constructor for the PagerDuty notifier
|
||||||
func NewPagerdutyNotifier(config *PagerdutyConfig, ns notifications.WebhookSender, images ImageStore, t *template.Template) *PagerdutyNotifier {
|
func newPagerdutyNotifier(fc FactoryConfig) (*PagerdutyNotifier, error) {
|
||||||
|
key := fc.DecryptFunc(context.Background(), fc.Config.SecureSettings, "integrationKey", fc.Config.Settings.Get("integrationKey").MustString())
|
||||||
|
if key == "" {
|
||||||
|
return nil, errors.New("could not find integration key property in settings")
|
||||||
|
}
|
||||||
|
|
||||||
return &PagerdutyNotifier{
|
return &PagerdutyNotifier{
|
||||||
Base: NewBase(&models.AlertNotification{
|
Base: NewBase(&models.AlertNotification{
|
||||||
Uid: config.UID,
|
Uid: fc.Config.UID,
|
||||||
Name: config.Name,
|
Name: fc.Config.Name,
|
||||||
Type: config.Type,
|
Type: fc.Config.Type,
|
||||||
DisableResolveMessage: config.DisableResolveMessage,
|
DisableResolveMessage: fc.Config.DisableResolveMessage,
|
||||||
Settings: config.Settings,
|
Settings: fc.Config.Settings,
|
||||||
}),
|
}),
|
||||||
Key: config.Key,
|
tmpl: fc.Template,
|
||||||
CustomDetails: map[string]string{
|
log: log.New("alerting.notifier." + fc.Config.Name),
|
||||||
"firing": `{{ template "__text_alert_list" .Alerts.Firing }}`,
|
ns: fc.NotificationService,
|
||||||
"resolved": `{{ template "__text_alert_list" .Alerts.Resolved }}`,
|
images: fc.ImageStore,
|
||||||
"num_firing": `{{ .Alerts.Firing | len }}`,
|
settings: pagerdutySettings{
|
||||||
"num_resolved": `{{ .Alerts.Resolved | len }}`,
|
Key: key,
|
||||||
|
Severity: fc.Config.Settings.Get("severity").MustString("critical"),
|
||||||
|
customDetails: map[string]string{
|
||||||
|
"firing": `{{ template "__text_alert_list" .Alerts.Firing }}`,
|
||||||
|
"resolved": `{{ template "__text_alert_list" .Alerts.Resolved }}`,
|
||||||
|
"num_firing": `{{ .Alerts.Firing | len }}`,
|
||||||
|
"num_resolved": `{{ .Alerts.Resolved | len }}`,
|
||||||
|
},
|
||||||
|
Class: fc.Config.Settings.Get("class").MustString("default"),
|
||||||
|
Component: fc.Config.Settings.Get("component").MustString("Grafana"),
|
||||||
|
Group: fc.Config.Settings.Get("group").MustString("default"),
|
||||||
|
Summary: fc.Config.Settings.Get("summary").MustString(DefaultMessageTitleEmbed),
|
||||||
},
|
},
|
||||||
Severity: config.Severity,
|
}, nil
|
||||||
Class: config.Class,
|
|
||||||
Component: config.Component,
|
|
||||||
Group: config.Group,
|
|
||||||
Summary: config.Summary,
|
|
||||||
tmpl: t,
|
|
||||||
log: log.New("alerting.notifier." + config.Name),
|
|
||||||
ns: ns,
|
|
||||||
images: images,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify sends an alert notification to PagerDuty
|
// Notify sends an alert notification to PagerDuty
|
||||||
@@ -158,8 +143,8 @@ func (pn *PagerdutyNotifier) buildPagerdutyMessage(ctx context.Context, alerts m
|
|||||||
var tmplErr error
|
var tmplErr error
|
||||||
tmpl, data := TmplText(ctx, pn.tmpl, as, pn.log, &tmplErr)
|
tmpl, data := TmplText(ctx, pn.tmpl, as, pn.log, &tmplErr)
|
||||||
|
|
||||||
details := make(map[string]string, len(pn.CustomDetails))
|
details := make(map[string]string, len(pn.settings.customDetails))
|
||||||
for k, v := range pn.CustomDetails {
|
for k, v := range pn.settings.customDetails {
|
||||||
detail, err := pn.tmpl.ExecuteTextString(v, data)
|
detail, err := pn.tmpl.ExecuteTextString(v, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", fmt.Errorf("%q: failed to template %q: %w", k, v, err)
|
return nil, "", fmt.Errorf("%q: failed to template %q: %w", k, v, err)
|
||||||
@@ -170,21 +155,20 @@ func (pn *PagerdutyNotifier) buildPagerdutyMessage(ctx context.Context, alerts m
|
|||||||
msg := &pagerDutyMessage{
|
msg := &pagerDutyMessage{
|
||||||
Client: "Grafana",
|
Client: "Grafana",
|
||||||
ClientURL: pn.tmpl.ExternalURL.String(),
|
ClientURL: pn.tmpl.ExternalURL.String(),
|
||||||
RoutingKey: pn.Key,
|
RoutingKey: pn.settings.Key,
|
||||||
EventAction: eventType,
|
EventAction: eventType,
|
||||||
DedupKey: key.Hash(),
|
DedupKey: key.Hash(),
|
||||||
Links: []pagerDutyLink{{
|
Links: []pagerDutyLink{{
|
||||||
HRef: pn.tmpl.ExternalURL.String(),
|
HRef: pn.tmpl.ExternalURL.String(),
|
||||||
Text: "External URL",
|
Text: "External URL",
|
||||||
}},
|
}},
|
||||||
Description: tmpl(DefaultMessageTitleEmbed), // TODO: this can be configurable template.
|
|
||||||
Payload: pagerDutyPayload{
|
Payload: pagerDutyPayload{
|
||||||
Component: tmpl(pn.Component),
|
Component: tmpl(pn.settings.Component),
|
||||||
Summary: tmpl(pn.Summary),
|
Summary: tmpl(pn.settings.Summary),
|
||||||
Severity: tmpl(pn.Severity),
|
Severity: tmpl(pn.settings.Severity),
|
||||||
CustomDetails: details,
|
CustomDetails: details,
|
||||||
Class: tmpl(pn.Class),
|
Class: tmpl(pn.settings.Class),
|
||||||
Group: tmpl(pn.Group),
|
Group: tmpl(pn.settings.Group),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +207,6 @@ type pagerDutyMessage struct {
|
|||||||
RoutingKey string `json:"routing_key,omitempty"`
|
RoutingKey string `json:"routing_key,omitempty"`
|
||||||
ServiceKey string `json:"service_key,omitempty"`
|
ServiceKey string `json:"service_key,omitempty"`
|
||||||
DedupKey string `json:"dedup_key,omitempty"`
|
DedupKey string `json:"dedup_key,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
EventAction string `json:"event_action"`
|
EventAction string `json:"event_action"`
|
||||||
Payload pagerDutyPayload `json:"payload"`
|
Payload pagerDutyPayload `json:"payload"`
|
||||||
Client string `json:"client,omitempty"`
|
Client string `json:"client,omitempty"`
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
expMsg: &pagerDutyMessage{
|
expMsg: &pagerDutyMessage{
|
||||||
RoutingKey: "abcdefgh0123456789",
|
RoutingKey: "abcdefgh0123456789",
|
||||||
DedupKey: "6e3538104c14b583da237e9693b76debbc17f0f8058ef20492e5853096cf8733",
|
DedupKey: "6e3538104c14b583da237e9693b76debbc17f0f8058ef20492e5853096cf8733",
|
||||||
Description: "[FIRING:1] (val1)",
|
|
||||||
EventAction: "trigger",
|
EventAction: "trigger",
|
||||||
Payload: pagerDutyPayload{
|
Payload: pagerDutyPayload{
|
||||||
Summary: "[FIRING:1] (val1)",
|
Summary: "[FIRING:1] (val1)",
|
||||||
@@ -70,6 +69,40 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
Links: []pagerDutyLink{{HRef: "http://localhost", Text: "External URL"}},
|
Links: []pagerDutyLink{{HRef: "http://localhost", Text: "External URL"}},
|
||||||
},
|
},
|
||||||
expMsgError: nil,
|
expMsgError: nil,
|
||||||
|
}, {
|
||||||
|
name: "Default config with one alert and custom summary",
|
||||||
|
settings: `{"integrationKey": "abcdefgh0123456789", "summary": "Alerts firing: {{ len .Alerts.Firing }}"}`,
|
||||||
|
alerts: []*types.Alert{
|
||||||
|
{
|
||||||
|
Alert: model.Alert{
|
||||||
|
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
|
||||||
|
Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expMsg: &pagerDutyMessage{
|
||||||
|
RoutingKey: "abcdefgh0123456789",
|
||||||
|
DedupKey: "6e3538104c14b583da237e9693b76debbc17f0f8058ef20492e5853096cf8733",
|
||||||
|
EventAction: "trigger",
|
||||||
|
Payload: pagerDutyPayload{
|
||||||
|
Summary: "Alerts firing: 1",
|
||||||
|
Source: hostname,
|
||||||
|
Severity: "critical",
|
||||||
|
Class: "default",
|
||||||
|
Component: "Grafana",
|
||||||
|
Group: "default",
|
||||||
|
CustomDetails: map[string]string{
|
||||||
|
"firing": "\nValue: [no value]\nLabels:\n - alertname = alert1\n - lbl1 = val1\nAnnotations:\n - ann1 = annv1\nSilence: http://localhost/alerting/silence/new?alertmanager=grafana&matcher=alertname%3Dalert1&matcher=lbl1%3Dval1\nDashboard: http://localhost/d/abcd\nPanel: http://localhost/d/abcd?viewPanel=efgh\n",
|
||||||
|
"num_firing": "1",
|
||||||
|
"num_resolved": "0",
|
||||||
|
"resolved": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Client: "Grafana",
|
||||||
|
ClientURL: "http://localhost",
|
||||||
|
Links: []pagerDutyLink{{HRef: "http://localhost", Text: "External URL"}},
|
||||||
|
},
|
||||||
|
expMsgError: nil,
|
||||||
}, {
|
}, {
|
||||||
name: "Custom config with multiple alerts",
|
name: "Custom config with multiple alerts",
|
||||||
settings: `{
|
settings: `{
|
||||||
@@ -95,7 +128,6 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
expMsg: &pagerDutyMessage{
|
expMsg: &pagerDutyMessage{
|
||||||
RoutingKey: "abcdefgh0123456789",
|
RoutingKey: "abcdefgh0123456789",
|
||||||
DedupKey: "6e3538104c14b583da237e9693b76debbc17f0f8058ef20492e5853096cf8733",
|
DedupKey: "6e3538104c14b583da237e9693b76debbc17f0f8058ef20492e5853096cf8733",
|
||||||
Description: "[FIRING:2] ",
|
|
||||||
EventAction: "trigger",
|
EventAction: "trigger",
|
||||||
Payload: pagerDutyPayload{
|
Payload: pagerDutyPayload{
|
||||||
Summary: "[FIRING:2] ",
|
Summary: "[FIRING:2] ",
|
||||||
@@ -128,18 +160,22 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
settingsJSON, err := simplejson.NewJson([]byte(c.settings))
|
settingsJSON, err := simplejson.NewJson([]byte(c.settings))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
secureSettings := make(map[string][]byte)
|
secureSettings := make(map[string][]byte)
|
||||||
|
|
||||||
m := &NotificationChannelConfig{
|
|
||||||
Name: "pageduty_testing",
|
|
||||||
Type: "pagerduty",
|
|
||||||
Settings: settingsJSON,
|
|
||||||
SecureSettings: secureSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
webhookSender := mockNotificationService()
|
webhookSender := mockNotificationService()
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
decryptFn := secretsService.GetDecryptedValue
|
decryptFn := secretsService.GetDecryptedValue
|
||||||
cfg, err := NewPagerdutyConfig(m, decryptFn)
|
|
||||||
|
fc := FactoryConfig{
|
||||||
|
Config: &NotificationChannelConfig{
|
||||||
|
Name: "pageduty_testing",
|
||||||
|
Type: "pagerduty",
|
||||||
|
Settings: settingsJSON,
|
||||||
|
SecureSettings: secureSettings,
|
||||||
|
},
|
||||||
|
NotificationService: webhookSender,
|
||||||
|
DecryptFunc: decryptFn,
|
||||||
|
Template: tmpl,
|
||||||
|
}
|
||||||
|
pn, err := newPagerdutyNotifier(fc)
|
||||||
if c.expInitError != "" {
|
if c.expInitError != "" {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, c.expInitError, err.Error())
|
require.Equal(t, c.expInitError, err.Error())
|
||||||
@@ -149,7 +185,6 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
|
|
||||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||||
pn := NewPagerdutyNotifier(cfg, webhookSender, &UnavailableImageStore{}, tmpl)
|
|
||||||
ok, err := pn.Notify(ctx, c.alerts...)
|
ok, err := pn.Notify(ctx, c.alerts...)
|
||||||
if c.expMsgError != nil {
|
if c.expMsgError != nil {
|
||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
|
|||||||
@@ -286,8 +286,9 @@ func GetAvailableNotifiers() []*NotifierPlugin {
|
|||||||
{ // New in 8.0.
|
{ // New in 8.0.
|
||||||
Label: "Summary",
|
Label: "Summary",
|
||||||
Description: "You can use templates for summary",
|
Description: "You can use templates for summary",
|
||||||
Element: ElementTypeTextArea,
|
Element: ElementTypeInput,
|
||||||
Placeholder: channels.DefaultMessageEmbed,
|
InputType: InputTypeText,
|
||||||
|
Placeholder: channels.DefaultMessageTitleEmbed,
|
||||||
PropertyName: "summary",
|
PropertyName: "summary",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1612,7 +1612,7 @@ const alertmanagerConfig = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "slack_recv2",
|
"name": "slack_recv2",
|
||||||
"grafana_managed_receiver_configs": [
|
"grafana_managed_receiver_configs": [
|
||||||
@@ -2356,7 +2356,6 @@ var expNonEmailNotifications = map[string][]string{
|
|||||||
`{
|
`{
|
||||||
"routing_key": "pagerduty_recv/pagerduty_test",
|
"routing_key": "pagerduty_recv/pagerduty_test",
|
||||||
"dedup_key": "234edb34441f942f713f3c2ccf58b1d719d921b4cbe34e57a1630f1dee847e3b",
|
"dedup_key": "234edb34441f942f713f3c2ccf58b1d719d921b4cbe34e57a1630f1dee847e3b",
|
||||||
"description": "[FIRING:1] PagerdutyAlert (default)",
|
|
||||||
"event_action": "trigger",
|
"event_action": "trigger",
|
||||||
"payload": {
|
"payload": {
|
||||||
"summary": "Integration Test [FIRING:1] PagerdutyAlert (default)",
|
"summary": "Integration Test [FIRING:1] PagerdutyAlert (default)",
|
||||||
|
|||||||
Reference in New Issue
Block a user