diff --git a/README.md b/README.md
index 34bc07ec..a2bfef4a 100644
--- a/README.md
+++ b/README.md
@@ -412,14 +412,14 @@ services:
#### Configuring PagerDuty alerts
-| Parameter | Description | Default |
-|:---------------------------------------- |:----------------------------------------------------------------------------- |:-------------- |
-| `alerting.pagerduty` | Configuration for alerts of type `pagerduty` | `{}` |
-| `alerting.pagerduty.integration-key` | PagerDuty Events API v2 integration key. | `""` |
-| `alerting.pagerduty.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A |
-| `alerting.pagerduty.integrations` | Pagerduty integrations per team configurations | `[]` |
-| `alerting.pagerduty.integrations[].integration-key` | Pagerduty integrationkey for a perticular team | `""` |
-| `alerting.pagerduty.integrations[].group` | the group that the integration key belongs to | `""` |
+| Parameter | Description | Default |
+|:------------------------------------------------------ |:----------------------------------------------------------------------------- |:-------------- |
+| `alerting.pagerduty` | Configuration for alerts of type `pagerduty` | `{}` |
+| `alerting.pagerduty.integration-key` | PagerDuty Events API v2 integration key | `""` |
+| `alerting.pagerduty.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A |
+| `alerting.pagerduty.overrides` | List of overrides that may be prioritized over the default configuration | `[]` |
+| `alerting.pagerduty.overrides[].group` | Service group for which the configuration will be overridden by this configuration | `""` |
+| `alerting.pagerduty.overrides[].integration-key` | PagerDuty Events API v2 integration key | `""` |
It is highly recommended to set `services[].alerts[].send-on-resolved` to `true` for alerts
of type `pagerduty`, because unlike other alerts, the operation resulting from setting said
@@ -427,18 +427,20 @@ parameter to `true` will not create another incident, but mark the incident as r
PagerDuty instead.
Behavior:
-- Team integration have priority over the general integration
-- If no team integration is provided it will defaults to the general pagerduty integration
-- If no team integration and no general integration were provided it defaults to the first team integration provided
+- By default, `alerting.pagerduty.integration-key` is used as the integration key
+- If there is a `services[].group` matching the value of `alerting.pagerduty.overrides[].group`, it will take precedence over `alerting.pagerduty.integration-key`
```yaml
alerting:
pagerduty:
integration-key: "********************************"
- intergrations:
- - integration-key: "********************************"
- group: "core"
+ # You can also add group-specific integration keys, which will
+ # override the integration key above for the specified groups
+ overrides:
+ - group: "core"
+ integration-key: "********************************"
+
services:
- name: website
@@ -455,6 +457,7 @@ services:
success-threshold: 5
send-on-resolved: true
description: "healthcheck failed"
+
- name: back-end
group: core
url: "https://example.org/"
diff --git a/alerting/provider/pagerduty/pagerduty.go b/alerting/provider/pagerduty/pagerduty.go
index 5318a879..afeba036 100644
--- a/alerting/provider/pagerduty/pagerduty.go
+++ b/alerting/provider/pagerduty/pagerduty.go
@@ -13,11 +13,6 @@ const (
restAPIURL = "https://events.pagerduty.com/v2/enqueue"
)
-type Integrations struct {
- IntegrationKey string `yaml:"integration-key"`
- Group string `yaml:"group"`
-}
-
// AlertProvider is the configuration necessary for sending an alert using PagerDuty
type AlertProvider struct {
IntegrationKey string `yaml:"integration-key"`
@@ -25,42 +20,34 @@ type AlertProvider struct {
// DefaultAlert is the default alert configuration to use for services with an alert of the appropriate type
DefaultAlert *alert.Alert `yaml:"default-alert"`
- Integrations []Integrations `yaml:"integrations"`
+ // Overrides is a list of Override that may be prioritized over the default configuration
+ Overrides []Override `yaml:"overrides"`
+}
+
+// Override is a case under which the default integration is overridden
+type Override struct {
+ Group string `yaml:"group"`
+ IntegrationKey string `yaml:"integration-key"`
}
// IsValid returns whether the provider's configuration is valid
func (provider *AlertProvider) IsValid() bool {
registeredGroups := make(map[string]bool)
- if provider.Integrations != nil {
- for _, integration := range provider.Integrations {
- if isAlreadyRegistered := registeredGroups[integration.Group]; isAlreadyRegistered || integration.Group == "" || len(integration.IntegrationKey) != 32 {
+ if provider.Overrides != nil {
+ for _, override := range provider.Overrides {
+ if isAlreadyRegistered := registeredGroups[override.Group]; isAlreadyRegistered || override.Group == "" || len(override.IntegrationKey) != 32 {
return false
}
- registeredGroups[integration.Group] = true
+ registeredGroups[override.Group] = true
}
}
- return len(provider.IntegrationKey) == 32 || provider.Integrations != nil
-}
-
-// GetPagerDutyIntegrationKey returns the appropriate pagerduty integration key
-func (provider *AlertProvider) GetPagerDutyIntegrationKey(group string) string {
- if provider.Integrations != nil {
- for _, integration := range provider.Integrations {
- if group == integration.Group {
- return integration.IntegrationKey
- }
- }
- }
- if provider.IntegrationKey != "" {
- return provider.IntegrationKey
- }
- return ""
+ // Either the default integration key has the right length, or there are overrides who are properly configured.
+ return len(provider.IntegrationKey) == 32 || len(provider.Overrides) != 0
}
// ToCustomAlertProvider converts the provider into a custom.AlertProvider
//
// relevant: https://developer.pagerduty.com/docs/events-api-v2/trigger-events/
-
func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, alert *alert.Alert, _ *core.Result, resolved bool) *custom.AlertProvider {
var message, eventAction, resolveKey string
if resolved {
@@ -84,13 +71,28 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
"source": "%s",
"severity": "critical"
}
-}`, provider.GetPagerDutyIntegrationKey(service.Group), resolveKey, eventAction, message, service.Name),
+}`, provider.getPagerDutyIntegrationKeyForGroup(service.Group), resolveKey, eventAction, message, service.Name),
Headers: map[string]string{
"Content-Type": "application/json",
},
}
}
+// getPagerDutyIntegrationKeyForGroup returns the appropriate pagerduty integration key for a given group
+func (provider *AlertProvider) getPagerDutyIntegrationKeyForGroup(group string) string {
+ if provider.Overrides != nil {
+ for _, override := range provider.Overrides {
+ if group == override.Group {
+ return override.IntegrationKey
+ }
+ }
+ }
+ if provider.IntegrationKey != "" {
+ return provider.IntegrationKey
+ }
+ return ""
+}
+
// GetDefaultAlert returns the provider's default alert configuration
func (provider AlertProvider) GetDefaultAlert() *alert.Alert {
return provider.DefaultAlert
diff --git a/alerting/provider/pagerduty/pagerduty_test.go b/alerting/provider/pagerduty/pagerduty_test.go
index c4b09c00..a4a86af5 100644
--- a/alerting/provider/pagerduty/pagerduty_test.go
+++ b/alerting/provider/pagerduty/pagerduty_test.go
@@ -10,7 +10,7 @@ import (
"github.com/TwinProduction/gatus/v3/core"
)
-func TestAlertDefaultProvider_IsValid(t *testing.T) {
+func TestAlertProvider_IsValid(t *testing.T) {
invalidProvider := AlertProvider{IntegrationKey: ""}
if invalidProvider.IsValid() {
t.Error("provider shouldn't have been valid")
@@ -20,41 +20,39 @@ func TestAlertDefaultProvider_IsValid(t *testing.T) {
t.Error("provider should've been valid")
}
}
-func TestAlertPerGroupProvider_IsValid(t *testing.T) {
- invalidGroup := Integrations{
- IntegrationKey: "00000000000000000000000000000000",
- Group: "",
+
+func TestAlertProvider_IsValidWithOverride(t *testing.T) {
+ providerWithInvalidOverrideGroup := AlertProvider{
+ Overrides: []Override{
+ {
+ IntegrationKey: "00000000000000000000000000000000",
+ Group: "",
+ },
+ },
}
- integrations := []Integrations{}
- integrations = append(integrations, invalidGroup)
- invalidProviderGroupNameError := AlertProvider{
- Integrations: integrations,
- }
- if invalidProviderGroupNameError.IsValid() {
+ if providerWithInvalidOverrideGroup.IsValid() {
t.Error("provider Group shouldn't have been valid")
}
- invalidIntegrationKey := Integrations{
- IntegrationKey: "",
- Group: "group",
+ providerWithInvalidOverrideIntegrationKey := AlertProvider{
+ Overrides: []Override{
+ {
+ IntegrationKey: "",
+ Group: "group",
+ },
+ },
}
- integrations = []Integrations{}
- integrations = append(integrations, invalidIntegrationKey)
- invalidProviderIntegrationKey := AlertProvider{
- Integrations: integrations,
- }
- if invalidProviderIntegrationKey.IsValid() {
+ if providerWithInvalidOverrideIntegrationKey.IsValid() {
t.Error("provider integration key shouldn't have been valid")
}
- validIntegration := Integrations{
- IntegrationKey: "00000000000000000000000000000000",
- Group: "group",
+ providerWithValidOverride := AlertProvider{
+ Overrides: []Override{
+ {
+ IntegrationKey: "00000000000000000000000000000000",
+ Group: "group",
+ },
+ },
}
- integrations = []Integrations{}
- integrations = append(integrations, validIntegration)
- validProvider := AlertProvider{
- Integrations: integrations,
- }
- if !validProvider.IsValid() {
+ if !providerWithValidOverride.IsValid() {
t.Error("provider should've been valid")
}
}
@@ -81,16 +79,15 @@ func TestAlertProvider_ToCustomAlertProviderWithResolvedAlert(t *testing.T) {
}
}
-func TestAlertPerGroupProvider_ToCustomAlertProviderWithResolvedAlert(t *testing.T) {
- validIntegration := Integrations{
- IntegrationKey: "00000000000000000000000000000000",
- Group: "group",
- }
- integrations := []Integrations{}
- integrations = append(integrations, validIntegration)
+func TestAlertProvider_ToCustomAlertProviderWithResolvedAlertAndOverride(t *testing.T) {
provider := AlertProvider{
IntegrationKey: "",
- Integrations: integrations,
+ Overrides: []Override{
+ {
+ IntegrationKey: "00000000000000000000000000000000",
+ Group: "group",
+ },
+ },
}
customAlertProvider := provider.ToCustomAlertProvider(&core.Service{}, &alert.Alert{}, &core.Result{}, true)
if customAlertProvider == nil {
@@ -134,16 +131,15 @@ func TestAlertProvider_ToCustomAlertProviderWithTriggeredAlert(t *testing.T) {
}
}
-func TestAlertPerGroupProvider_ToCustomAlertProviderWithTriggeredAlert(t *testing.T) {
- validIntegration := Integrations{
- IntegrationKey: "00000000000000000000000000000000",
- Group: "group",
- }
- integrations := []Integrations{}
- integrations = append(integrations, validIntegration)
+func TestAlertProvider_ToCustomAlertProviderWithTriggeredAlertAndOverride(t *testing.T) {
provider := AlertProvider{
IntegrationKey: "",
- Integrations: integrations,
+ Overrides: []Override{
+ {
+ IntegrationKey: "00000000000000000000000000000000",
+ Group: "group",
+ },
+ },
}
customAlertProvider := provider.ToCustomAlertProvider(&core.Service{}, &alert.Alert{}, &core.Result{}, false)
if customAlertProvider == nil {
@@ -164,3 +160,66 @@ func TestAlertPerGroupProvider_ToCustomAlertProviderWithTriggeredAlert(t *testin
t.Error("expected body to be valid JSON, got error:", err.Error())
}
}
+
+func TestAlertProvider_getPagerDutyIntegrationKey(t *testing.T) {
+ scenarios := []struct {
+ Name string
+ Provider AlertProvider
+ InputGroup string
+ ExpectedOutput string
+ }{
+ {
+ Name: "provider-no-override-specify-no-group-should-default",
+ Provider: AlertProvider{
+ IntegrationKey: "00000000000000000000000000000001",
+ Overrides: nil,
+ },
+ InputGroup: "",
+ ExpectedOutput: "00000000000000000000000000000001",
+ },
+ {
+ Name: "provider-no-override-specify-group-should-default",
+ Provider: AlertProvider{
+ IntegrationKey: "00000000000000000000000000000001",
+ Overrides: nil,
+ },
+ InputGroup: "group",
+ ExpectedOutput: "00000000000000000000000000000001",
+ },
+ {
+ Name: "provider-with-override-specify-no-group-should-default",
+ Provider: AlertProvider{
+ IntegrationKey: "00000000000000000000000000000001",
+ Overrides: []Override{
+ {
+ Group: "group",
+ IntegrationKey: "00000000000000000000000000000002",
+ },
+ },
+ },
+ InputGroup: "",
+ ExpectedOutput: "00000000000000000000000000000001",
+ },
+ {
+ Name: "provider-with-override-specify-group-should-override",
+ Provider: AlertProvider{
+ IntegrationKey: "00000000000000000000000000000001",
+ Overrides: []Override{
+ {
+ Group: "group",
+ IntegrationKey: "00000000000000000000000000000002",
+ },
+ },
+ },
+ InputGroup: "group",
+ ExpectedOutput: "00000000000000000000000000000002",
+ },
+ }
+ for _, scenario := range scenarios {
+ t.Run(scenario.Name, func(t *testing.T) {
+ if output := scenario.Provider.getPagerDutyIntegrationKeyForGroup(scenario.InputGroup); output != scenario.ExpectedOutput {
+ t.Errorf("expected %s, got %s", scenario.ExpectedOutput, output)
+ }
+ })
+ }
+}