package notifiers import ( "context" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/grafana/grafana/pkg/components/null" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/alerting" "github.com/grafana/grafana/pkg/services/encryption/ossencryption" "github.com/grafana/grafana/pkg/services/validations" "github.com/stretchr/testify/require" ) func presenceComparer(a, b string) bool { if a == "<>" { return b != "" } if b == "<>" { return a != "" } return a == b } func TestPagerdutyNotifier(t *testing.T) { t.Run("empty settings should return error", func(t *testing.T) { json := `{ }` settingsJSON, err := simplejson.NewJson([]byte(json)) require.Nil(t, err) model := &models.AlertNotification{ Name: "pageduty_testing", Type: "pagerduty", Settings: settingsJSON, } _, err = NewPagerdutyNotifier(model, ossencryption.ProvideService().GetDecryptedValue) require.Error(t, err) }) t.Run("severity should override default", func(t *testing.T) { json := `{ "integrationKey": "abcdefgh0123456789", "severity": "info", "tags": ["foo"]}` settingsJSON, err := simplejson.NewJson([]byte(json)) require.Nil(t, err) model := &models.AlertNotification{ Name: "pagerduty_testing", Type: "pagerduty", Settings: settingsJSON, } not, err := NewPagerdutyNotifier(model, ossencryption.ProvideService().GetDecryptedValue) pagerdutyNotifier := not.(*PagerdutyNotifier) require.Nil(t, err) require.Equal(t, "pagerduty_testing", pagerdutyNotifier.Name) require.Equal(t, "pagerduty", pagerdutyNotifier.Type) require.Equal(t, "abcdefgh0123456789", pagerdutyNotifier.Key) require.Equal(t, "info", pagerdutyNotifier.Severity) require.False(t, pagerdutyNotifier.AutoResolve) }) t.Run("auto resolve and severity should have expected defaults", func(t *testing.T) { json := `{ "integrationKey": "abcdefgh0123456789" }` settingsJSON, err := simplejson.NewJson([]byte(json)) require.Nil(t, err) model := &models.AlertNotification{ Name: "pagerduty_testing", Type: "pagerduty", Settings: settingsJSON, } not, err := NewPagerdutyNotifier(model, ossencryption.ProvideService().GetDecryptedValue) pagerdutyNotifier := not.(*PagerdutyNotifier) require.Nil(t, err) require.Equal(t, "pagerduty_testing", pagerdutyNotifier.Name) require.Equal(t, "pagerduty", pagerdutyNotifier.Type) require.Equal(t, "abcdefgh0123456789", pagerdutyNotifier.Key) require.Equal(t, "critical", pagerdutyNotifier.Severity) require.False(t, pagerdutyNotifier.AutoResolve) }) t.Run("settings should trigger incident", func(t *testing.T) { json := ` { "integrationKey": "abcdefgh0123456789", "autoResolve": false }` settingsJSON, err := simplejson.NewJson([]byte(json)) require.Nil(t, err) model := &models.AlertNotification{ Name: "pagerduty_testing", Type: "pagerduty", Settings: settingsJSON, } not, err := NewPagerdutyNotifier(model, ossencryption.ProvideService().GetDecryptedValue) pagerdutyNotifier := not.(*PagerdutyNotifier) require.Nil(t, err) require.Equal(t, "pagerduty_testing", pagerdutyNotifier.Name) require.Equal(t, "pagerduty", pagerdutyNotifier.Type) require.Equal(t, "abcdefgh0123456789", pagerdutyNotifier.Key) require.False(t, pagerdutyNotifier.AutoResolve) }) t.Run("should return properly formatted default v2 event payload", func(t *testing.T) { json := `{ "integrationKey": "abcdefgh0123456789", "autoResolve": false }` settingsJSON, err := simplejson.NewJson([]byte(json)) require.Nil(t, err) model := &models.AlertNotification{ Name: "pagerduty_testing", Type: "pagerduty", Settings: settingsJSON, } not, err := NewPagerdutyNotifier(model, ossencryption.ProvideService().GetDecryptedValue) require.Nil(t, err) pagerdutyNotifier := not.(*PagerdutyNotifier) evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{ ID: 0, Name: "someRule", Message: "someMessage", State: models.AlertStateAlerting, }, &validations.OSSPluginRequestValidator{}) evalContext.IsTestRun = true payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext) require.Nil(t, err) payload, err := simplejson.NewJson(payloadJSON) require.Nil(t, err) diff := cmp.Diff(map[string]interface{}{ "client": "Grafana", "client_url": "", "dedup_key": "alertId-0", "event_action": "trigger", "links": []interface{}{ map[string]interface{}{ "href": "", }, }, "payload": map[string]interface{}{ "component": "Grafana", "source": "<>", "custom_details": map[string]interface{}{ "state": "alerting", }, "severity": "critical", "summary": "someRule - someMessage", "timestamp": "<>", }, "routing_key": "abcdefgh0123456789", }, payload.Interface(), cmp.Comparer(presenceComparer)) require.Empty(t, diff) }) t.Run("should return properly formatted default v2 event payload with empty message", func(t *testing.T) { json := `{ "integrationKey": "abcdefgh0123456789", "autoResolve": false }` settingsJSON, err := simplejson.NewJson([]byte(json)) require.Nil(t, err) model := &models.AlertNotification{ Name: "pagerduty_testing", Type: "pagerduty", Settings: settingsJSON, } not, err := NewPagerdutyNotifier(model, ossencryption.ProvideService().GetDecryptedValue) require.Nil(t, err) pagerdutyNotifier := not.(*PagerdutyNotifier) evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{ ID: 0, Name: "someRule", State: models.AlertStateAlerting, }, &validations.OSSPluginRequestValidator{}) evalContext.IsTestRun = true payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext) require.Nil(t, err) payload, err := simplejson.NewJson(payloadJSON) require.Nil(t, err) diff := cmp.Diff(map[string]interface{}{ "client": "Grafana", "client_url": "", "dedup_key": "alertId-0", "event_action": "trigger", "links": []interface{}{ map[string]interface{}{ "href": "", }, }, "payload": map[string]interface{}{ "component": "Grafana", "source": "<>", "custom_details": map[string]interface{}{ "state": "alerting", }, "severity": "critical", "summary": "someRule", "timestamp": "<>", }, "routing_key": "abcdefgh0123456789", }, payload.Interface(), cmp.Comparer(presenceComparer)) require.Empty(t, diff) }) t.Run("should return properly formatted payload with message moved to details", func(t *testing.T) { json := `{ "integrationKey": "abcdefgh0123456789", "autoResolve": false, "messageInDetails": true }` settingsJSON, err := simplejson.NewJson([]byte(json)) require.Nil(t, err) model := &models.AlertNotification{ Name: "pagerduty_testing", Type: "pagerduty", Settings: settingsJSON, } not, err := NewPagerdutyNotifier(model, ossencryption.ProvideService().GetDecryptedValue) require.Nil(t, err) pagerdutyNotifier := not.(*PagerdutyNotifier) evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{ ID: 0, Name: "someRule", Message: "someMessage", State: models.AlertStateAlerting, }, &validations.OSSPluginRequestValidator{}) evalContext.IsTestRun = true evalContext.EvalMatches = []*alerting.EvalMatch{ { // nil is a terrible value to test with, but the cmp.Diff doesn't // like comparing actual floats. So this is roughly the equivalent // of <> Value: null.FloatFromPtr(nil), Metric: "someMetric", }, } payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext) require.NoError(t, err) payload, err := simplejson.NewJson(payloadJSON) require.NoError(t, err) diff := cmp.Diff(map[string]interface{}{ "client": "Grafana", "client_url": "", "dedup_key": "alertId-0", "event_action": "trigger", "links": []interface{}{ map[string]interface{}{ "href": "", }, }, "payload": map[string]interface{}{ "component": "Grafana", "source": "<>", "custom_details": map[string]interface{}{ "message": "someMessage", "queries": map[string]interface{}{ "someMetric": nil, }, "state": "alerting", }, "severity": "critical", "summary": "someRule", "timestamp": "<>", }, "routing_key": "abcdefgh0123456789", }, payload.Interface(), cmp.Comparer(presenceComparer)) require.Empty(t, diff) }) t.Run("should return properly formatted v2 event payload when using override tags", func(t *testing.T) { json := `{ "integrationKey": "abcdefgh0123456789", "autoResolve": false }` settingsJSON, err := simplejson.NewJson([]byte(json)) require.NoError(t, err) model := &models.AlertNotification{ Name: "pagerduty_testing", Type: "pagerduty", Settings: settingsJSON, } not, err := NewPagerdutyNotifier(model, ossencryption.ProvideService().GetDecryptedValue) require.NoError(t, err) pagerdutyNotifier := not.(*PagerdutyNotifier) evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{ ID: 0, Name: "someRule", Message: "someMessage", State: models.AlertStateAlerting, AlertRuleTags: []*models.Tag{ {Key: "keyOnly"}, {Key: "group", Value: "aGroup"}, {Key: "class", Value: "aClass"}, {Key: "component", Value: "aComponent"}, {Key: "severity", Value: "warning"}, {Key: "dedup_key", Value: "key-" + strings.Repeat("x", 260)}, }, }, &validations.OSSPluginRequestValidator{}) evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png" evalContext.IsTestRun = true payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext) require.NoError(t, err) payload, err := simplejson.NewJson(payloadJSON) require.NoError(t, err) diff := cmp.Diff(map[string]interface{}{ "client": "Grafana", "client_url": "", "dedup_key": "key-" + strings.Repeat("x", 250), "event_action": "trigger", "links": []interface{}{ map[string]interface{}{ "href": "", }, }, "payload": map[string]interface{}{ "source": "<>", "component": "aComponent", "custom_details": map[string]interface{}{ "group": "aGroup", "class": "aClass", "component": "aComponent", "severity": "warning", "dedup_key": "key-" + strings.Repeat("x", 250), "keyOnly": "", "state": "alerting", }, "severity": "warning", "summary": "someRule - someMessage", "timestamp": "<>", "class": "aClass", "group": "aGroup", }, "images": []interface{}{ map[string]interface{}{ "src": "http://somewhere.com/omg_dont_panic.png", }, }, "routing_key": "abcdefgh0123456789", }, payload.Interface(), cmp.Comparer(presenceComparer)) require.Empty(t, diff) }) t.Run("should support multiple levels of severity", func(t *testing.T) { json := `{ "integrationKey": "abcdefgh0123456789", "autoResolve": false }` settingsJSON, err := simplejson.NewJson([]byte(json)) require.NoError(t, err) model := &models.AlertNotification{ Name: "pagerduty_testing", Type: "pagerduty", Settings: settingsJSON, } not, err := NewPagerdutyNotifier(model, ossencryption.ProvideService().GetDecryptedValue) require.NoError(t, err) pagerdutyNotifier := not.(*PagerdutyNotifier) evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{ ID: 0, Name: "someRule", Message: "someMessage", State: models.AlertStateAlerting, AlertRuleTags: []*models.Tag{ {Key: "keyOnly"}, {Key: "group", Value: "aGroup"}, {Key: "class", Value: "aClass"}, {Key: "component", Value: "aComponent"}, {Key: "severity", Value: "info"}, }, }, &validations.OSSPluginRequestValidator{}) evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png" evalContext.IsTestRun = true payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext) require.NoError(t, err) payload, err := simplejson.NewJson(payloadJSON) require.NoError(t, err) diff := cmp.Diff(map[string]interface{}{ "client": "Grafana", "client_url": "", "dedup_key": "alertId-0", "event_action": "trigger", "links": []interface{}{ map[string]interface{}{ "href": "", }, }, "payload": map[string]interface{}{ "source": "<>", "component": "aComponent", "custom_details": map[string]interface{}{ "group": "aGroup", "class": "aClass", "component": "aComponent", "severity": "info", "keyOnly": "", "state": "alerting", }, "severity": "info", "summary": "someRule - someMessage", "timestamp": "<>", "class": "aClass", "group": "aGroup", }, "images": []interface{}{ map[string]interface{}{ "src": "http://somewhere.com/omg_dont_panic.png", }, }, "routing_key": "abcdefgh0123456789", }, payload.Interface(), cmp.Comparer(presenceComparer)) require.Empty(t, diff) }) t.Run("should ignore invalid severity for PD but keep the tag", func(t *testing.T) { json := `{ "integrationKey": "abcdefgh0123456789", "autoResolve": false, "severity": "critical" }` settingsJSON, err := simplejson.NewJson([]byte(json)) require.NoError(t, err) model := &models.AlertNotification{ Name: "pagerduty_testing", Type: "pagerduty", Settings: settingsJSON, } not, err := NewPagerdutyNotifier(model, ossencryption.ProvideService().GetDecryptedValue) require.NoError(t, err) pagerdutyNotifier := not.(*PagerdutyNotifier) evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{ ID: 0, Name: "someRule", Message: "someMessage", State: models.AlertStateAlerting, AlertRuleTags: []*models.Tag{ {Key: "keyOnly"}, {Key: "group", Value: "aGroup"}, {Key: "class", Value: "aClass"}, {Key: "component", Value: "aComponent"}, {Key: "severity", Value: "llama"}, }, }, &validations.OSSPluginRequestValidator{}) evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png" evalContext.IsTestRun = true payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext) require.NoError(t, err) payload, err := simplejson.NewJson(payloadJSON) require.NoError(t, err) diff := cmp.Diff(map[string]interface{}{ "client": "Grafana", "client_url": "", "dedup_key": "alertId-0", "event_action": "trigger", "links": []interface{}{ map[string]interface{}{ "href": "", }, }, "payload": map[string]interface{}{ "source": "<>", "component": "aComponent", "custom_details": map[string]interface{}{ "group": "aGroup", "class": "aClass", "component": "aComponent", "severity": "llama", "keyOnly": "", "state": "alerting", }, "severity": "critical", "summary": "someRule - someMessage", "timestamp": "<>", "class": "aClass", "group": "aGroup", }, "images": []interface{}{ map[string]interface{}{ "src": "http://somewhere.com/omg_dont_panic.png", }, }, "routing_key": "abcdefgh0123456789", }, payload.Interface(), cmp.Comparer(presenceComparer)) require.Empty(t, diff) }) }