diff --git a/README.md b/README.md index c5b480270..0369d1527 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ For more details, see [Usage](#usage) Have any feedback or want to share your good/bad experience with Gatus? Feel free to email me at [feedback@gatus.io](mailto:feedback@gatus.io) ## Table of Contents +- [Table of Contents](#table-of-contents) - [Why Gatus?](#why-gatus) - [Features](#features) - [Usage](#usage) @@ -76,8 +77,8 @@ Have any feedback or want to share your good/bad experience with Gatus? Feel fre - [Endpoint groups](#endpoint-groups) - [Exposing Gatus on a custom port](#exposing-gatus-on-a-custom-port) - [Badges](#badges) - - [Uptime](#uptime) - - [Response time](#response-time) + - [Uptime](#uptime) + - [Response time](#response-time) - [API](#api) - [High level design overview](#high-level-design-overview) - [Sponsors](#sponsors) @@ -436,17 +437,26 @@ endpoints: **NOTE:** Some mail servers are painfully slow. #### Configuring Google Chat alerts -| Parameter | Description | Default | -|:------------------------------------|:--------------------------------------------------------------------------------------------|:--------------| -| `alerting.googlechat` | Configuration for alerts of type `googlechat` | `{}` | -| `alerting.googlechat.webhook-url` | Google Chat Webhook URL | Required `""` | -| `alerting.googlechat.client` | Client configuration.
See [Client configuration](#client-configuration). | `{}` | -| `alerting.googlechat.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert). | N/A | + +| Parameter | Description | Default | +|:--------------------------------------------- |:------------------------------------------------------------------------------------------- |:------------- | +| `alerting.googlechat` | Configuration for alerts of type `googlechat` | `{}` | +| `alerting.googlechat.webhook-url` | Google Chat Webhook URL | Required `""` | +| `alerting.googlechat.client` | Client configuration.
See [Client configuration](#client-configuration). | `{}` | +| `alerting.googlechat.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert). | N/A | +| `alerting.googlechat.overrides` | List of overrides that may be prioritized over the default configuration | `[]` | +| `alerting.googlechat.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` | +| `alerting.googlechat.overrides[].webhook-url` | Teams Webhook URL | `""` | ```yaml alerting: googlechat: webhook-url: "https://chat.googleapis.com/v1/spaces/*******/messages?key=**********&token=********" + # 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://chat.googleapis.com/v1/spaces/*******/messages?key=**********&token=********" endpoints: - name: website @@ -461,6 +471,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: googlechat + enabled: true + description: "healthcheck failed" + send-on-resolved: true ``` #### Configuring Mattermost alerts diff --git a/alerting/provider/googlechat/googlechat.go b/alerting/provider/googlechat/googlechat.go index d6162ae42..405f41f40 100644 --- a/alerting/provider/googlechat/googlechat.go +++ b/alerting/provider/googlechat/googlechat.go @@ -20,6 +20,15 @@ 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 @@ -27,13 +36,24 @@ func (provider *AlertProvider) IsValid() bool { if provider.ClientConfig == nil { provider.ClientConfig = client.GetDefaultConfig() } + + 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 } @@ -118,6 +138,18 @@ func (provider *AlertProvider) buildRequestBody(endpoint *core.Endpoint, alert * }`, endpoint.Name, endpoint.Group, message, description, results, endpoint.URL) } +// 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/googlechat/googlechat_test.go b/alerting/provider/googlechat/googlechat_test.go index 55126911c..543cee616 100644 --- a/alerting/provider/googlechat/googlechat_test.go +++ b/alerting/provider/googlechat/googlechat_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" @@ -158,3 +195,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) + } + }) + } +}