Improve alerting tests

This commit is contained in:
TwinProduction 2021-07-29 19:54:40 -04:00
parent 2ce02b0d7f
commit 2074697efa
13 changed files with 104 additions and 74 deletions

View File

@ -332,6 +332,10 @@ services:
- name: twinnation
url: "https://twinnation.org/health"
interval: 30s
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
alerts:
- type: slack
enabled: true
@ -342,10 +346,6 @@ services:
failure-threshold: 5
description: "healthcheck failed 5 times in a row"
send-on-resolved: true
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
```
Here's an example of what the notifications look like:
@ -363,15 +363,15 @@ services:
- name: twinnation
url: "https://twinnation.org/health"
interval: 30s
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
alerts:
- type: discord
enabled: true
description: "healthcheck failed"
send-on-resolved: true
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
```
@ -390,6 +390,10 @@ services:
- name: twinnation
url: "https://twinnation.org/health"
interval: 30s
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
alerts:
- type: pagerduty
enabled: true
@ -397,10 +401,6 @@ services:
success-threshold: 5
send-on-resolved: true
description: "healthcheck failed"
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
```
@ -417,16 +417,16 @@ services:
- name: twinnation
interval: 30s
url: "https://twinnation.org/health"
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
alerts:
- type: twilio
enabled: true
failure-threshold: 5
send-on-resolved: true
description: "healthcheck failed"
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
```
@ -442,15 +442,15 @@ services:
- name: twinnation
url: "https://twinnation.org/health"
interval: 30s
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
alerts:
- type: mattermost
enabled: true
description: "healthcheck failed"
send-on-resolved: true
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
```
Here's an example of what the notifications look like:
@ -459,9 +459,7 @@ Here's an example of what the notifications look like:
#### Configuring Messagebird alerts
Example of sending **SMS** text message alert using Messagebird:
```yaml
alerting:
messagebird:
@ -472,16 +470,16 @@ services:
- name: twinnation
interval: 30s
url: "https://twinnation.org/health"
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
alerts:
- type: messagebird
enabled: true
failure-threshold: 3
send-on-resolved: true
description: "healthcheck failed"
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
```
#### Configuring Teams alerts
@ -494,15 +492,15 @@ services:
- name: twinnation
url: "https://twinnation.org/health"
interval: 30s
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
alerts:
- type: teams
enabled: true
description: "healthcheck failed"
send-on-resolved: true
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
```
Here's an example of what the notifications look like:
@ -520,13 +518,13 @@ services:
- name: twinnation
url: "https://twinnation.org/health"
interval: 30s
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
alerts:
- type: telegram
enabled: true
send-on-resolved: true
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
```
Here's an example of what the notifications look like:
@ -564,6 +562,10 @@ services:
- name: twinnation
url: "https://twinnation.org/health"
interval: 30s
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
alerts:
- type: custom
enabled: true
@ -571,10 +573,6 @@ services:
success-threshold: 3
send-on-resolved: true
description: "healthcheck failed"
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
```
Note that you can customize the resolved values for the `[ALERT_TRIGGERED_OR_RESOLVED]` placeholder like so:
@ -612,17 +610,17 @@ As a result, your service configuration looks a lot tidier:
services:
- name: example
url: "https://example.org"
alerts:
- type: slack
conditions:
- "[STATUS] == 200"
alerts:
- type: slack
- name: other-example
url: "https://example.com"
alerts:
- type: slack
conditions:
- "[STATUS] == 200"
alerts:
- type: slack
```
It also allows you to do things like this:
@ -630,6 +628,8 @@ It also allows you to do things like this:
services:
- name: twinnation
url: "https://twinnation.org/health"
conditions:
- "[STATUS] == 200"
alerts:
- type: slack
failure-threshold: 5
@ -637,8 +637,6 @@ services:
failure-threshold: 10
- type: slack
failure-threshold: 15
conditions:
- "[STATUS] == 200"
```

View File

@ -23,12 +23,12 @@ const (
// TypeSlack is the Type for the slack alerting provider
TypeSlack Type = "slack"
// TypeTeams is the Type for the teams alerting provider
TypeTeams Type = "teams"
// TypeTelegram is the Type for the telegram alerting provider
TypeTelegram Type = "telegram"
// TypeTwilio is the Type for the twilio alerting provider
TypeTwilio Type = "twilio"
// Teams is the Type for the teams alerting provider
TypeTeams Type = "teams"
)

View File

@ -9,9 +9,9 @@ import (
"github.com/TwinProduction/gatus/alerting/provider/messagebird"
"github.com/TwinProduction/gatus/alerting/provider/pagerduty"
"github.com/TwinProduction/gatus/alerting/provider/slack"
"github.com/TwinProduction/gatus/alerting/provider/teams"
"github.com/TwinProduction/gatus/alerting/provider/telegram"
"github.com/TwinProduction/gatus/alerting/provider/twilio"
"github.com/TwinProduction/gatus/alerting/provider/teams"
)
// Config is the configuration for alerting providers
@ -34,14 +34,14 @@ type Config struct {
// Slack is the configuration for the slack alerting provider
Slack *slack.AlertProvider `yaml:"slack"`
// Teams is the configuration for the teams alerting provider
Teams *teams.AlertProvider `yaml:"teams"`
// Telegram is the configuration for the telegram alerting provider
Telegram *telegram.AlertProvider `yaml:"telegram"`
// Twilio is the configuration for the twilio alerting provider
Twilio *twilio.AlertProvider `yaml:"twilio"`
// Teams is the configuration for the teams alerting provider
Teams *teams.AlertProvider `yaml:"teams"`
}
// GetAlertingProviderByAlertType returns an provider.AlertProvider by its corresponding alert.Type
@ -83,6 +83,12 @@ func (config Config) GetAlertingProviderByAlertType(alertType alert.Type) provid
return nil
}
return config.Slack
case alert.TypeTeams:
if config.Teams == nil {
// Since we're returning an interface, we need to explicitly return nil, even if the provider itself is nil
return nil
}
return config.Teams
case alert.TypeTelegram:
if config.Telegram == nil {
// Since we're returning an interface, we need to explicitly return nil, even if the provider itself is nil
@ -95,12 +101,6 @@ func (config Config) GetAlertingProviderByAlertType(alertType alert.Type) provid
return nil
}
return config.Twilio
case alert.TypeTeams:
if config.Teams == nil {
// Since we're returning an interface, we need to explicitly return nil, even if the provider itself is nil
return nil
}
return config.Teams
}
return nil
}

View File

@ -42,6 +42,10 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
}
results += fmt.Sprintf("%s - `%s`\\n", prefix, conditionResult.Condition)
}
var description string
if alertDescription := alert.GetDescription(); len(alertDescription) > 0 {
description = ":\\n> " + alertDescription
}
return &custom.AlertProvider{
URL: provider.WebhookURL,
Method: http.MethodPost,
@ -50,7 +54,7 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
"embeds": [
{
"title": ":helmet_with_white_cross: Gatus",
"description": "%s:\n> %s",
"description": "%s%s",
"color": %d,
"fields": [
{
@ -61,7 +65,7 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
]
}
]
}`, message, alert.GetDescription(), colorCode, results),
}`, message, description, colorCode, results),
Headers: map[string]string{"Content-Type": "application/json"},
}
}

View File

@ -23,7 +23,8 @@ func TestAlertProvider_IsValid(t *testing.T) {
func TestAlertProvider_ToCustomAlertProviderWithResolvedAlert(t *testing.T) {
provider := AlertProvider{WebhookURL: "http://example.com"}
customAlertProvider := provider.ToCustomAlertProvider(&core.Service{}, &alert.Alert{}, &core.Result{ConditionResults: []*core.ConditionResult{{Condition: "SUCCESSFUL_CONDITION", Success: true}}}, true)
alertDescription := "test"
customAlertProvider := provider.ToCustomAlertProvider(&core.Service{Name: "svc"}, &alert.Alert{Description: &alertDescription}, &core.Result{ConditionResults: []*core.ConditionResult{{Condition: "SUCCESSFUL_CONDITION", Success: true}}}, true)
if customAlertProvider == nil {
t.Fatal("customAlertProvider shouldn't have been nil")
}
@ -41,6 +42,9 @@ func TestAlertProvider_ToCustomAlertProviderWithResolvedAlert(t *testing.T) {
if err != nil {
t.Error("expected body to be valid JSON, got error:", err.Error())
}
if expected := "An alert for **svc** has been resolved after passing successfully 0 time(s) in a row:\n> test"; expected != body["embeds"].([]interface{})[0].(map[string]interface{})["description"] {
t.Errorf("expected $.embeds[0].description to be %s, got %s", expected, body["embeds"].([]interface{})[0].(map[string]interface{})["description"])
}
}
func TestAlertProvider_ToCustomAlertProviderWithTriggeredAlert(t *testing.T) {

View File

@ -57,6 +57,10 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
}
results += fmt.Sprintf("%s - `%s`\\n", prefix, conditionResult.Condition)
}
var description string
if alertDescription := alert.GetDescription(); len(alertDescription) > 0 {
description = ":\\n> " + alertDescription
}
return &custom.AlertProvider{
URL: provider.WebhookURL,
Method: http.MethodPost,
@ -69,7 +73,7 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
{
"title": ":rescue_worker_helmet: Gatus",
"fallback": "Gatus - %s",
"text": "%s:\n> %s",
"text": "%s%s",
"short": false,
"color": "%s",
"fields": [
@ -86,7 +90,7 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
]
}
]
}`, message, message, alert.GetDescription(), color, service.URL, results),
}`, message, message, description, color, service.URL, results),
Headers: map[string]string{"Content-Type": "application/json"},
}
}

View File

@ -23,7 +23,8 @@ func TestAlertProvider_IsValid(t *testing.T) {
func TestAlertProvider_ToCustomAlertProviderWithResolvedAlert(t *testing.T) {
provider := AlertProvider{WebhookURL: "http://example.org"}
customAlertProvider := provider.ToCustomAlertProvider(&core.Service{}, &alert.Alert{}, &core.Result{ConditionResults: []*core.ConditionResult{{Condition: "SUCCESSFUL_CONDITION", Success: true}}}, true)
alertDescription := "test"
customAlertProvider := provider.ToCustomAlertProvider(&core.Service{Name: "svc"}, &alert.Alert{Description: &alertDescription}, &core.Result{ConditionResults: []*core.ConditionResult{{Condition: "SUCCESSFUL_CONDITION", Success: true}}}, true)
if customAlertProvider == nil {
t.Fatal("customAlertProvider shouldn't have been nil")
}
@ -41,6 +42,9 @@ func TestAlertProvider_ToCustomAlertProviderWithResolvedAlert(t *testing.T) {
if err != nil {
t.Error("expected body to be valid JSON, got error:", err.Error())
}
if expected := "An alert for *svc* has been resolved after passing successfully 0 time(s) in a row:\n> test"; expected != body["attachments"].([]interface{})[0].(map[string]interface{})["text"] {
t.Errorf("expected $.attachments[0].description to be %s, got %s", expected, body["attachments"].([]interface{})[0].(map[string]interface{})["text"])
}
}
func TestAlertProvider_ToCustomAlertProviderWithTriggeredAlert(t *testing.T) {

View File

@ -8,9 +8,9 @@ import (
"github.com/TwinProduction/gatus/alerting/provider/messagebird"
"github.com/TwinProduction/gatus/alerting/provider/pagerduty"
"github.com/TwinProduction/gatus/alerting/provider/slack"
"github.com/TwinProduction/gatus/alerting/provider/teams"
"github.com/TwinProduction/gatus/alerting/provider/telegram"
"github.com/TwinProduction/gatus/alerting/provider/twilio"
"github.com/TwinProduction/gatus/alerting/provider/teams"
"github.com/TwinProduction/gatus/core"
)
@ -56,7 +56,7 @@ var (
_ AlertProvider = (*messagebird.AlertProvider)(nil)
_ AlertProvider = (*pagerduty.AlertProvider)(nil)
_ AlertProvider = (*slack.AlertProvider)(nil)
_ AlertProvider = (*teams.AlertProvider)(nil)
_ AlertProvider = (*telegram.AlertProvider)(nil)
_ AlertProvider = (*twilio.AlertProvider)(nil)
_ AlertProvider = (*teams.AlertProvider)(nil)
)

View File

@ -41,6 +41,10 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
}
results += fmt.Sprintf("%s - `%s`\\n", prefix, conditionResult.Condition)
}
var description string
if alertDescription := alert.GetDescription(); len(alertDescription) > 0 {
description = ":\\n> " + alertDescription
}
return &custom.AlertProvider{
URL: provider.WebhookURL,
Method: http.MethodPost,
@ -49,7 +53,7 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
"attachments": [
{
"title": ":helmet_with_white_cross: Gatus",
"text": "%s:\n> %s",
"text": "%s%s",
"short": false,
"color": "%s",
"fields": [
@ -61,7 +65,7 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
]
}
]
}`, message, alert.GetDescription(), color, results),
}`, message, description, color, results),
Headers: map[string]string{"Content-Type": "application/json"},
}
}

View File

@ -23,7 +23,8 @@ func TestAlertProvider_IsValid(t *testing.T) {
func TestAlertProvider_ToCustomAlertProviderWithResolvedAlert(t *testing.T) {
provider := AlertProvider{WebhookURL: "http://example.com"}
customAlertProvider := provider.ToCustomAlertProvider(&core.Service{}, &alert.Alert{}, &core.Result{ConditionResults: []*core.ConditionResult{{Condition: "SUCCESSFUL_CONDITION", Success: true}}}, true)
alertDescription := "test"
customAlertProvider := provider.ToCustomAlertProvider(&core.Service{Name: "svc"}, &alert.Alert{Description: &alertDescription}, &core.Result{ConditionResults: []*core.ConditionResult{{Condition: "SUCCESSFUL_CONDITION", Success: true}}}, true)
if customAlertProvider == nil {
t.Fatal("customAlertProvider shouldn't have been nil")
}
@ -41,6 +42,9 @@ func TestAlertProvider_ToCustomAlertProviderWithResolvedAlert(t *testing.T) {
if err != nil {
t.Error("expected body to be valid JSON, got error:", err.Error())
}
if expected := "An alert for *svc* has been resolved after passing successfully 0 time(s) in a row:\n> test"; expected != body["attachments"].([]interface{})[0].(map[string]interface{})["text"] {
t.Errorf("expected $.attachments[0].description to be %s, got %s", expected, body["attachments"].([]interface{})[0].(map[string]interface{})["text"])
}
}
func TestAlertProvider_ToCustomAlertProviderWithTriggeredAlert(t *testing.T) {

View File

@ -37,21 +37,25 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
for _, conditionResult := range result.ConditionResults {
var prefix string
if conditionResult.Success {
prefix = "&#x2705;"
prefix = "&#x2705;"
} else {
prefix = "&#x274C;"
}
results += fmt.Sprintf("%s - `%s`<br/>", prefix, conditionResult.Condition)
}
var description string
if alertDescription := alert.GetDescription(); len(alertDescription) > 0 {
description = ":\\n> " + alertDescription
}
return &custom.AlertProvider{
URL: provider.WebhookURL,
Method: http.MethodPost,
URL: provider.WebhookURL,
Method: http.MethodPost,
Body: fmt.Sprintf(`{
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
"themeColor": "%s",
"title": "&#x1F6A8; Gatus",
"text": "%s:\n> %s",
"text": "%s%s",
"sections": [
{
"activityTitle": "URL",
@ -62,7 +66,7 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
"text": "%s"
}
]
}`, color, message, alert.GetDescription(), service.URL, results),
}`, color, message, description, service.URL, results),
Headers: map[string]string{"Content-Type": "application/json"},
}
}

View File

@ -23,7 +23,8 @@ func TestAlertProvider_IsValid(t *testing.T) {
func TestAlertProvider_ToCustomAlertProviderWithResolvedAlert(t *testing.T) {
provider := AlertProvider{WebhookURL: "http://example.org"}
customAlertProvider := provider.ToCustomAlertProvider(&core.Service{}, &alert.Alert{}, &core.Result{ConditionResults: []*core.ConditionResult{{Condition: "SUCCESSFUL_CONDITION", Success: true}}}, true)
alertDescription := "test"
customAlertProvider := provider.ToCustomAlertProvider(&core.Service{Name: "svc"}, &alert.Alert{Description: &alertDescription}, &core.Result{ConditionResults: []*core.ConditionResult{{Condition: "SUCCESSFUL_CONDITION", Success: true}}}, true)
if customAlertProvider == nil {
t.Fatal("customAlertProvider shouldn't have been nil")
}
@ -41,6 +42,9 @@ func TestAlertProvider_ToCustomAlertProviderWithResolvedAlert(t *testing.T) {
if err != nil {
t.Error("expected body to be valid JSON, got error:", err.Error())
}
if expected := "An alert for *svc* has been resolved after passing successfully 0 time(s) in a row:\n> test"; expected != body["text"] {
t.Errorf("expected $.text to be %s, got %s", expected, body["text"])
}
}
func TestAlertProvider_ToCustomAlertProviderWithTriggeredAlert(t *testing.T) {

View File

@ -270,9 +270,9 @@ func validateAlertingConfig(alertingConfig *alerting.Config, services []*core.Se
alert.TypeMessagebird,
alert.TypePagerDuty,
alert.TypeSlack,
alert.TypeTeams,
alert.TypeTelegram,
alert.TypeTwilio,
alert.TypeTeams,
}
var validProviders, invalidProviders []alert.Type
for _, alertType := range alertTypes {