feat(alert): Add group-specific to email list (#264)
* feat(alert): Add group-specific to email list Add group-specific to list for email alert https://github.com/TwiN/gatus/issues/96 Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> * docs: update Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> * Update README.md * Update README.md * Update README.md * chore: update Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> * Update README.md
This commit is contained in:
parent
bec2820969
commit
a81c81e42c
23
README.md
23
README.md
@ -377,7 +377,7 @@ endpoints:
|
|||||||
|
|
||||||
#### Configuring Email alerts
|
#### Configuring Email alerts
|
||||||
| Parameter | Description | Default |
|
| Parameter | Description | Default |
|
||||||
|:-------------------------------|:-------------------------------------------------------------------------------------------|:----------------------|
|
|:---------------------------------- |:------------------------------------------------------------------------------------------ |:------------- |
|
||||||
| `alerting.email` | Configuration for alerts of type `email` | `{}` |
|
| `alerting.email` | Configuration for alerts of type `email` | `{}` |
|
||||||
| `alerting.email.from` | Email used to send the alert | Required `""` |
|
| `alerting.email.from` | Email used to send the alert | Required `""` |
|
||||||
| `alerting.email.username` | Username of the SMTP server used to send the alert. If empty, uses `alerting.email.from`. | `""` |
|
| `alerting.email.username` | Username of the SMTP server used to send the alert. If empty, uses `alerting.email.from`. | `""` |
|
||||||
@ -386,6 +386,9 @@ endpoints:
|
|||||||
| `alerting.email.port` | Port the mail server is listening to (e.g. `587`) | Required `0` |
|
| `alerting.email.port` | Port the mail server is listening to (e.g. `587`) | Required `0` |
|
||||||
| `alerting.email.to` | Email(s) to send the alerts to | Required `""` |
|
| `alerting.email.to` | Email(s) to send the alerts to | Required `""` |
|
||||||
| `alerting.email.default-alert` | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A |
|
| `alerting.email.default-alert` | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A |
|
||||||
|
| `alerting.email.overrides` | List of overrides that may be prioritized over the default configuration | `[]` |
|
||||||
|
| `alerting.email.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` |
|
||||||
|
| `alerting.email.overrides[].to` | Email(s) to send the alerts to | `""` |
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
alerting:
|
alerting:
|
||||||
@ -396,6 +399,11 @@ alerting:
|
|||||||
host: "mail.example.com"
|
host: "mail.example.com"
|
||||||
port: 587
|
port: 587
|
||||||
to: "recipient1@example.com,recipient2@example.com"
|
to: "recipient1@example.com,recipient2@example.com"
|
||||||
|
# You can also add group-specific to keys, which will
|
||||||
|
# override the to key above for the specified groups
|
||||||
|
overrides:
|
||||||
|
- group: "core"
|
||||||
|
to: "recipient3@example.com,recipient4@example.com"
|
||||||
|
|
||||||
endpoints:
|
endpoints:
|
||||||
- name: website
|
- name: website
|
||||||
@ -410,6 +418,19 @@ endpoints:
|
|||||||
enabled: true
|
enabled: true
|
||||||
description: "healthcheck failed"
|
description: "healthcheck failed"
|
||||||
send-on-resolved: true
|
send-on-resolved: true
|
||||||
|
|
||||||
|
- name: back-end
|
||||||
|
group: core
|
||||||
|
url: "https://example.org/"
|
||||||
|
interval: 5m
|
||||||
|
conditions:
|
||||||
|
- "[STATUS] == 200"
|
||||||
|
- "[CERTIFICATE_EXPIRATION] > 48h"
|
||||||
|
alerts:
|
||||||
|
- type: email
|
||||||
|
enabled: true
|
||||||
|
description: "healthcheck failed"
|
||||||
|
send-on-resolved: true
|
||||||
```
|
```
|
||||||
|
|
||||||
**NOTE:** Some mail servers are painfully slow.
|
**NOTE:** Some mail servers are painfully slow.
|
||||||
|
@ -21,10 +21,29 @@ type AlertProvider struct {
|
|||||||
|
|
||||||
// DefaultAlert is the default alert configuration to use for endpoints with an alert of the appropriate type
|
// DefaultAlert is the default alert configuration to use for endpoints with an alert of the appropriate type
|
||||||
DefaultAlert *alert.Alert `yaml:"default-alert,omitempty"`
|
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"`
|
||||||
|
To string `yaml:"to"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValid returns whether the provider's configuration is valid
|
// IsValid returns whether the provider's configuration is valid
|
||||||
func (provider *AlertProvider) IsValid() bool {
|
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.To) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
registeredGroups[override.Group] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return len(provider.From) > 0 && len(provider.Password) > 0 && len(provider.Host) > 0 && len(provider.To) > 0 && provider.Port > 0 && provider.Port < math.MaxUint16
|
return len(provider.From) > 0 && len(provider.Password) > 0 && len(provider.Host) > 0 && len(provider.To) > 0 && provider.Port > 0 && provider.Port < math.MaxUint16
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +58,7 @@ func (provider *AlertProvider) Send(endpoint *core.Endpoint, alert *alert.Alert,
|
|||||||
subject, body := provider.buildMessageSubjectAndBody(endpoint, alert, result, resolved)
|
subject, body := provider.buildMessageSubjectAndBody(endpoint, alert, result, resolved)
|
||||||
m := gomail.NewMessage()
|
m := gomail.NewMessage()
|
||||||
m.SetHeader("From", provider.From)
|
m.SetHeader("From", provider.From)
|
||||||
m.SetHeader("To", strings.Split(provider.To, ",")...)
|
m.SetHeader("To", strings.Split(provider.getToForGroup(endpoint.Group), ",")...)
|
||||||
m.SetHeader("Subject", subject)
|
m.SetHeader("Subject", subject)
|
||||||
m.SetBody("text/plain", body)
|
m.SetBody("text/plain", body)
|
||||||
d := gomail.NewDialer(provider.Host, provider.Port, username, provider.Password)
|
d := gomail.NewDialer(provider.Host, provider.Port, username, provider.Password)
|
||||||
@ -72,6 +91,18 @@ func (provider *AlertProvider) buildMessageSubjectAndBody(endpoint *core.Endpoin
|
|||||||
return subject, message + description + "\n\nCondition results:\n" + results
|
return subject, message + description + "\n\nCondition results:\n" + results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getToForGroup returns the appropriate email integration to for a given group
|
||||||
|
func (provider *AlertProvider) getToForGroup(group string) string {
|
||||||
|
if provider.Overrides != nil {
|
||||||
|
for _, override := range provider.Overrides {
|
||||||
|
if group == override.Group {
|
||||||
|
return override.To
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return provider.To
|
||||||
|
}
|
||||||
|
|
||||||
// GetDefaultAlert returns the provider's default alert configuration
|
// GetDefaultAlert returns the provider's default alert configuration
|
||||||
func (provider AlertProvider) GetDefaultAlert() *alert.Alert {
|
func (provider AlertProvider) GetDefaultAlert() *alert.Alert {
|
||||||
return provider.DefaultAlert
|
return provider.DefaultAlert
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/TwiN/gatus/v3/core"
|
"github.com/TwiN/gatus/v3/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAlertProvider_IsValid(t *testing.T) {
|
func TestAlertDefaultProvider_IsValid(t *testing.T) {
|
||||||
invalidProvider := AlertProvider{}
|
invalidProvider := AlertProvider{}
|
||||||
if invalidProvider.IsValid() {
|
if invalidProvider.IsValid() {
|
||||||
t.Error("provider shouldn't have been valid")
|
t.Error("provider shouldn't have been valid")
|
||||||
@ -18,6 +18,47 @@ func TestAlertProvider_IsValid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAlertProvider_IsValidWithOverride(t *testing.T) {
|
||||||
|
providerWithInvalidOverrideGroup := AlertProvider{
|
||||||
|
Overrides: []Override{
|
||||||
|
{
|
||||||
|
To: "to@example.com",
|
||||||
|
Group: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if providerWithInvalidOverrideGroup.IsValid() {
|
||||||
|
t.Error("provider Group shouldn't have been valid")
|
||||||
|
}
|
||||||
|
providerWithInvalidOverrideTo := AlertProvider{
|
||||||
|
Overrides: []Override{
|
||||||
|
{
|
||||||
|
To: "",
|
||||||
|
Group: "group",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if providerWithInvalidOverrideTo.IsValid() {
|
||||||
|
t.Error("provider integration key shouldn't have been valid")
|
||||||
|
}
|
||||||
|
providerWithValidOverride := AlertProvider{
|
||||||
|
From: "from@example.com",
|
||||||
|
Password: "password",
|
||||||
|
Host: "smtp.gmail.com",
|
||||||
|
Port: 587,
|
||||||
|
To: "to@example.com",
|
||||||
|
Overrides: []Override{
|
||||||
|
{
|
||||||
|
To: "to@example.com",
|
||||||
|
Group: "group",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !providerWithValidOverride.IsValid() {
|
||||||
|
t.Error("provider should've been valid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAlertProvider_buildRequestBody(t *testing.T) {
|
func TestAlertProvider_buildRequestBody(t *testing.T) {
|
||||||
firstDescription := "description-1"
|
firstDescription := "description-1"
|
||||||
secondDescription := "description-2"
|
secondDescription := "description-2"
|
||||||
@ -77,3 +118,66 @@ func TestAlertProvider_GetDefaultAlert(t *testing.T) {
|
|||||||
t.Error("expected default alert to be nil")
|
t.Error("expected default alert to be nil")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAlertProvider_getToForGroup(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Name string
|
||||||
|
Provider AlertProvider
|
||||||
|
InputGroup string
|
||||||
|
ExpectedOutput string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "provider-no-override-specify-no-group-should-default",
|
||||||
|
Provider: AlertProvider{
|
||||||
|
To: "to@example.com",
|
||||||
|
Overrides: nil,
|
||||||
|
},
|
||||||
|
InputGroup: "",
|
||||||
|
ExpectedOutput: "to@example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "provider-no-override-specify-group-should-default",
|
||||||
|
Provider: AlertProvider{
|
||||||
|
To: "to@example.com",
|
||||||
|
Overrides: nil,
|
||||||
|
},
|
||||||
|
InputGroup: "group",
|
||||||
|
ExpectedOutput: "to@example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "provider-with-override-specify-no-group-should-default",
|
||||||
|
Provider: AlertProvider{
|
||||||
|
To: "to@example.com",
|
||||||
|
Overrides: []Override{
|
||||||
|
{
|
||||||
|
Group: "group",
|
||||||
|
To: "to01@example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InputGroup: "",
|
||||||
|
ExpectedOutput: "to@example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "provider-with-override-specify-group-should-override",
|
||||||
|
Provider: AlertProvider{
|
||||||
|
To: "to@example.com",
|
||||||
|
Overrides: []Override{
|
||||||
|
{
|
||||||
|
Group: "group",
|
||||||
|
To: "to01@example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InputGroup: "group",
|
||||||
|
ExpectedOutput: "to01@example.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.Name, func(t *testing.T) {
|
||||||
|
if got := tt.Provider.getToForGroup(tt.InputGroup); got != tt.ExpectedOutput {
|
||||||
|
t.Errorf("AlertProvider.getToForGroup() = %v, want %v", got, tt.ExpectedOutput)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user