diff --git a/README.md b/README.md index 873007f1..c5b48027 100644 --- a/README.md +++ b/README.md @@ -652,16 +652,25 @@ Here's an example of what the notifications look like: #### Configuring Teams alerts -| Parameter | Description | Default | -|:-------------------------------|:-------------------------------------------------------------------------------------------|:--------------| -| `alerting.teams` | Configuration for alerts of type `teams` | `{}` | -| `alerting.teams.webhook-url` | Teams Webhook URL | Required `""` | -| `alerting.teams.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A | + +| Parameter | Description | Default | +|:---------------------------------------- |:------------------------------------------------------------------------------------------ |:------------- | +| `alerting.teams` | Configuration for alerts of type `teams` | `{}` | +| `alerting.teams.webhook-url` | Teams Webhook URL | Required `""` | +| `alerting.teams.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A | +| `alerting.teams.overrides` | List of overrides that may be prioritized over the default configuration | `[]` | +| `alerting.teams.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` | +| `alerting.teams.overrides[].webhook-url` | Teams Webhook URL | `""` | ```yaml alerting: teams: webhook-url: "https://********.webhook.office.com/webhookb2/************" + # You can also add group-specific to keys, which will + # override the to key above for the specified groups + overrides: + - group: "core" + webhook-url: "https://********.webhook.office.com/webhookb3/************" endpoints: - name: website @@ -676,6 +685,19 @@ endpoints: enabled: true description: "healthcheck failed" send-on-resolved: true + + - name: back-end + group: core + url: "https://example.org/" + interval: 5m + conditions: + - "[STATUS] == 200" + - "[CERTIFICATE_EXPIRATION] > 48h" + alerts: + - type: teams + enabled: true + description: "healthcheck failed" + send-on-resolved: true ``` Here's an example of what the notifications look like: diff --git a/alerting/provider/teams/teams.go b/alerting/provider/teams/teams.go index 217ad6ea..d1d7b457 100644 --- a/alerting/provider/teams/teams.go +++ b/alerting/provider/teams/teams.go @@ -17,17 +17,35 @@ type AlertProvider struct { // DefaultAlert is the default alert configuration to use for endpoints with an alert of the appropriate type DefaultAlert *alert.Alert `yaml:"default-alert,omitempty"` + + // Overrides is a list of Override that may be prioritized over the default configuration + Overrides []Override `yaml:"overrides,omitempty"` +} + +// Override is a case under which the default integration is overridden +type Override struct { + Group string `yaml:"group"` + WebhookURL string `yaml:"webhook-url"` } // IsValid returns whether the provider's configuration is valid func (provider *AlertProvider) IsValid() bool { + registeredGroups := make(map[string]bool) + if provider.Overrides != nil { + for _, override := range provider.Overrides { + if isAlreadyRegistered := registeredGroups[override.Group]; isAlreadyRegistered || override.Group == "" || len(override.WebhookURL) == 0 { + return false + } + registeredGroups[override.Group] = true + } + } return len(provider.WebhookURL) > 0 } // Send an alert using the provider func (provider *AlertProvider) Send(endpoint *core.Endpoint, alert *alert.Alert, result *core.Result, resolved bool) error { buffer := bytes.NewBuffer([]byte(provider.buildRequestBody(endpoint, alert, result, resolved))) - request, err := http.NewRequest(http.MethodPost, provider.WebhookURL, buffer) + request, err := http.NewRequest(http.MethodPost, provider.getWebhookURLForGroup(endpoint.Group), buffer) if err != nil { return err } @@ -86,6 +104,18 @@ func (provider *AlertProvider) buildRequestBody(endpoint *core.Endpoint, alert * }`, color, message, description, endpoint.URL, results) } +// getWebhookURLForGroup returns the appropriate Webhook URL integration to for a given group +func (provider *AlertProvider) getWebhookURLForGroup(group string) string { + if provider.Overrides != nil { + for _, override := range provider.Overrides { + if group == override.Group { + return override.WebhookURL + } + } + } + return provider.WebhookURL +} + // GetDefaultAlert returns the provider's default alert configuration func (provider AlertProvider) GetDefaultAlert() *alert.Alert { return provider.DefaultAlert diff --git a/alerting/provider/teams/teams_test.go b/alerting/provider/teams/teams_test.go index 3d91cd19..0a0477eb 100644 --- a/alerting/provider/teams/teams_test.go +++ b/alerting/provider/teams/teams_test.go @@ -11,7 +11,7 @@ import ( "github.com/TwiN/gatus/v3/test" ) -func TestAlertProvider_IsValid(t *testing.T) { +func TestAlertDefaultProvider_IsValid(t *testing.T) { invalidProvider := AlertProvider{WebhookURL: ""} if invalidProvider.IsValid() { t.Error("provider shouldn't have been valid") @@ -22,6 +22,43 @@ func TestAlertProvider_IsValid(t *testing.T) { } } +func TestAlertProvider_IsValidWithOverride(t *testing.T) { + providerWithInvalidOverrideGroup := AlertProvider{ + Overrides: []Override{ + { + WebhookURL: "http://example.com", + Group: "", + }, + }, + } + if providerWithInvalidOverrideGroup.IsValid() { + t.Error("provider Group shouldn't have been valid") + } + providerWithInvalidOverrideTo := AlertProvider{ + Overrides: []Override{ + { + WebhookURL: "", + Group: "group", + }, + }, + } + if providerWithInvalidOverrideTo.IsValid() { + t.Error("provider integration key shouldn't have been valid") + } + providerWithValidOverride := AlertProvider{ + WebhookURL: "http://example.com", + Overrides: []Override{ + { + WebhookURL: "http://example.com", + Group: "group", + }, + }, + } + if !providerWithValidOverride.IsValid() { + t.Error("provider should've been valid") + } +} + func TestAlertProvider_Send(t *testing.T) { defer client.InjectHTTPClient(nil) firstDescription := "description-1" @@ -156,3 +193,66 @@ func TestAlertProvider_GetDefaultAlert(t *testing.T) { t.Error("expected default alert to be nil") } } + +func TestAlertProvider_getWebhookURLForGroup(t *testing.T) { + tests := []struct { + Name string + Provider AlertProvider + InputGroup string + ExpectedOutput string + }{ + { + Name: "provider-no-override-specify-no-group-should-default", + Provider: AlertProvider{ + WebhookURL: "http://example.com", + Overrides: nil, + }, + InputGroup: "", + ExpectedOutput: "http://example.com", + }, + { + Name: "provider-no-override-specify-group-should-default", + Provider: AlertProvider{ + WebhookURL: "http://example.com", + Overrides: nil, + }, + InputGroup: "group", + ExpectedOutput: "http://example.com", + }, + { + Name: "provider-with-override-specify-no-group-should-default", + Provider: AlertProvider{ + WebhookURL: "http://example.com", + Overrides: []Override{ + { + Group: "group", + WebhookURL: "http://example01.com", + }, + }, + }, + InputGroup: "", + ExpectedOutput: "http://example.com", + }, + { + Name: "provider-with-override-specify-group-should-override", + Provider: AlertProvider{ + WebhookURL: "http://example.com", + Overrides: []Override{ + { + Group: "group", + WebhookURL: "http://example01.com", + }, + }, + }, + InputGroup: "group", + ExpectedOutput: "http://example01.com", + }, + } + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + if got := tt.Provider.getWebhookURLForGroup(tt.InputGroup); got != tt.ExpectedOutput { + t.Errorf("AlertProvider.getToForGroup() = %v, want %v", got, tt.ExpectedOutput) + } + }) + } +}