Skip to content

Commit

Permalink
Alerting: Change handling of settings to pagerduty contact point (gra…
Browse files Browse the repository at this point in the history
…fana#57524)

* Add custom title to pagerduty contact point

* Fix tests by saving decrypted key

* Use simplejson
  • Loading branch information
Alex Moreno authored Oct 27, 2022
1 parent f8d12af commit 10fdfa8
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 82 deletions.
115 changes: 49 additions & 66 deletions pkg/services/ngalert/notifier/channels/pagerduty.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,83 +31,68 @@ var (
// alert notifications to pagerduty
type PagerdutyNotifier struct {
*Base
Key string
Severity string
CustomDetails map[string]string
Class string
Component string
Group string
Summary string
tmpl *template.Template
log log.Logger
ns notifications.WebhookSender
images ImageStore
tmpl *template.Template
log log.Logger
ns notifications.WebhookSender
images ImageStore
settings pagerdutySettings
}

type PagerdutyConfig struct {
*NotificationChannelConfig
Key string
Severity string
Class string
Component string
Group string
Summary string
type pagerdutySettings struct {
Key string `json:"integrationKey,omitempty" yaml:"integrationKey,omitempty"`
Severity string `json:"severity,omitempty" yaml:"severity,omitempty"`
customDetails map[string]string
Class string `json:"class,omitempty" yaml:"class,omitempty"`
Component string `json:"component,omitempty" yaml:"component,omitempty"`
Group string `json:"group,omitempty" yaml:"group,omitempty"`
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
}

func PagerdutyFactory(fc FactoryConfig) (NotificationChannel, error) {
cfg, err := NewPagerdutyConfig(fc.Config, fc.DecryptFunc)
pdn, err := newPagerdutyNotifier(fc)
if err != nil {
return nil, receiverInitError{
Reason: err.Error(),
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())
// NewPagerdutyNotifier is the constructor for the PagerDuty notifier
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 &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
func NewPagerdutyNotifier(config *PagerdutyConfig, ns notifications.WebhookSender, images ImageStore, t *template.Template) *PagerdutyNotifier {
return &PagerdutyNotifier{
Base: NewBase(&models.AlertNotification{
Uid: config.UID,
Name: config.Name,
Type: config.Type,
DisableResolveMessage: config.DisableResolveMessage,
Settings: config.Settings,
Uid: fc.Config.UID,
Name: fc.Config.Name,
Type: fc.Config.Type,
DisableResolveMessage: fc.Config.DisableResolveMessage,
Settings: fc.Config.Settings,
}),
Key: config.Key,
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 }}`,
tmpl: fc.Template,
log: log.New("alerting.notifier." + fc.Config.Name),
ns: fc.NotificationService,
images: fc.ImageStore,
settings: pagerdutySettings{
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,
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,
}
}, nil
}

// Notify sends an alert notification to PagerDuty
Expand Down Expand Up @@ -158,8 +143,8 @@ func (pn *PagerdutyNotifier) buildPagerdutyMessage(ctx context.Context, alerts m
var tmplErr error
tmpl, data := TmplText(ctx, pn.tmpl, as, pn.log, &tmplErr)

details := make(map[string]string, len(pn.CustomDetails))
for k, v := range pn.CustomDetails {
details := make(map[string]string, len(pn.settings.customDetails))
for k, v := range pn.settings.customDetails {
detail, err := pn.tmpl.ExecuteTextString(v, data)
if err != nil {
return nil, "", fmt.Errorf("%q: failed to template %q: %w", k, v, err)
Expand All @@ -170,21 +155,20 @@ func (pn *PagerdutyNotifier) buildPagerdutyMessage(ctx context.Context, alerts m
msg := &pagerDutyMessage{
Client: "Grafana",
ClientURL: pn.tmpl.ExternalURL.String(),
RoutingKey: pn.Key,
RoutingKey: pn.settings.Key,
EventAction: eventType,
DedupKey: key.Hash(),
Links: []pagerDutyLink{{
HRef: pn.tmpl.ExternalURL.String(),
Text: "External URL",
}},
Description: tmpl(DefaultMessageTitleEmbed), // TODO: this can be configurable template.
Payload: pagerDutyPayload{
Component: tmpl(pn.Component),
Summary: tmpl(pn.Summary),
Severity: tmpl(pn.Severity),
Component: tmpl(pn.settings.Component),
Summary: tmpl(pn.settings.Summary),
Severity: tmpl(pn.settings.Severity),
CustomDetails: details,
Class: tmpl(pn.Class),
Group: tmpl(pn.Group),
Class: tmpl(pn.settings.Class),
Group: tmpl(pn.settings.Group),
},
}

Expand Down Expand Up @@ -223,7 +207,6 @@ type pagerDutyMessage struct {
RoutingKey string `json:"routing_key,omitempty"`
ServiceKey string `json:"service_key,omitempty"`
DedupKey string `json:"dedup_key,omitempty"`
Description string `json:"description,omitempty"`
EventAction string `json:"event_action"`
Payload pagerDutyPayload `json:"payload"`
Client string `json:"client,omitempty"`
Expand Down
59 changes: 47 additions & 12 deletions pkg/services/ngalert/notifier/channels/pagerduty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ func TestPagerdutyNotifier(t *testing.T) {
expMsg: &pagerDutyMessage{
RoutingKey: "abcdefgh0123456789",
DedupKey: "6e3538104c14b583da237e9693b76debbc17f0f8058ef20492e5853096cf8733",
Description: "[FIRING:1] (val1)",
EventAction: "trigger",
Payload: pagerDutyPayload{
Summary: "[FIRING:1] (val1)",
Expand All @@ -70,6 +69,40 @@ func TestPagerdutyNotifier(t *testing.T) {
Links: []pagerDutyLink{{HRef: "http://localhost", Text: "External URL"}},
},
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",
settings: `{
Expand All @@ -95,7 +128,6 @@ func TestPagerdutyNotifier(t *testing.T) {
expMsg: &pagerDutyMessage{
RoutingKey: "abcdefgh0123456789",
DedupKey: "6e3538104c14b583da237e9693b76debbc17f0f8058ef20492e5853096cf8733",
Description: "[FIRING:2] ",
EventAction: "trigger",
Payload: pagerDutyPayload{
Summary: "[FIRING:2] ",
Expand Down Expand Up @@ -128,18 +160,22 @@ func TestPagerdutyNotifier(t *testing.T) {
settingsJSON, err := simplejson.NewJson([]byte(c.settings))
require.NoError(t, err)
secureSettings := make(map[string][]byte)

m := &NotificationChannelConfig{
Name: "pageduty_testing",
Type: "pagerduty",
Settings: settingsJSON,
SecureSettings: secureSettings,
}

webhookSender := mockNotificationService()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
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 != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
Expand All @@ -149,7 +185,6 @@ func TestPagerdutyNotifier(t *testing.T) {

ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
pn := NewPagerdutyNotifier(cfg, webhookSender, &UnavailableImageStore{}, tmpl)
ok, err := pn.Notify(ctx, c.alerts...)
if c.expMsgError != nil {
require.False(t, ok)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,9 @@ func GetAvailableNotifiers() []*NotifierPlugin {
{ // New in 8.0.
Label: "Summary",
Description: "You can use templates for summary",
Element: ElementTypeTextArea,
Placeholder: channels.DefaultMessageEmbed,
Element: ElementTypeInput,
InputType: InputTypeText,
Placeholder: channels.DefaultMessageTitleEmbed,
PropertyName: "summary",
},
},
Expand Down
3 changes: 1 addition & 2 deletions pkg/tests/api/alerting/api_notification_channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1612,7 +1612,7 @@ const alertmanagerConfig = `
}
}
]
},
},
{
"name": "slack_recv2",
"grafana_managed_receiver_configs": [
Expand Down Expand Up @@ -2356,7 +2356,6 @@ var expNonEmailNotifications = map[string][]string{
`{
"routing_key": "pagerduty_recv/pagerduty_test",
"dedup_key": "234edb34441f942f713f3c2ccf58b1d719d921b4cbe34e57a1630f1dee847e3b",
"description": "[FIRING:1] PagerdutyAlert (default)",
"event_action": "trigger",
"payload": {
"summary": "Integration Test [FIRING:1] PagerdutyAlert (default)",
Expand Down

0 comments on commit 10fdfa8

Please sign in to comment.