Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(alerting): Add group-specific WebHook URL for Google Chat #272

Merged
merged 3 commits into from
May 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -436,12 +436,16 @@ 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. <br />See [Client configuration](#client-configuration). | `{}` |
| `alerting.googlechat.default-alert` | Default alert configuration. <br />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. <br />See [Client configuration](#client-configuration). | `{}` |
| `alerting.googlechat.default-alert` | Default alert configuration. <br />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:
Expand Down
34 changes: 33 additions & 1 deletion alerting/provider/googlechat/googlechat.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,40 @@ 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 {
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
}
Expand Down Expand Up @@ -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
Expand Down
102 changes: 101 additions & 1 deletion alerting/provider/googlechat/googlechat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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"
Expand Down Expand Up @@ -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)
}
})
}
}