From 23fb69fca9011db32eab6cd232098f225d5de3d2 Mon Sep 17 00:00:00 2001 From: Bastien Ogier Date: Wed, 28 Jul 2021 14:15:56 +0200 Subject: [PATCH] Add teams alerting provider --- .github/assets/teams-alerts.png | Bin 0 -> 26659 bytes README.md | 31 ++++++++++- alerting/alert/type.go | 3 ++ alerting/config.go | 10 ++++ alerting/provider/provider.go | 2 + alerting/provider/teams/teams.go | 73 ++++++++++++++++++++++++++ alerting/provider/teams/teams_test.go | 66 +++++++++++++++++++++++ config.yaml | 66 +++++++---------------- config/config.go | 1 + config/config_test.go | 57 ++++++++++++++++++-- 10 files changed, 256 insertions(+), 53 deletions(-) create mode 100644 .github/assets/teams-alerts.png create mode 100644 alerting/provider/teams/teams.go create mode 100644 alerting/provider/teams/teams_test.go diff --git a/.github/assets/teams-alerts.png b/.github/assets/teams-alerts.png new file mode 100644 index 0000000000000000000000000000000000000000..44b6e898a57a7f9825db2f201aa328d080687f30 GIT binary patch literal 26659 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 0 +} + +// ToCustomAlertProvider converts the provider into a custom.AlertProvider +func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, alert *alert.Alert, result *core.Result, resolved bool) *custom.AlertProvider { + var message string + var color string + if resolved { + message = fmt.Sprintf("An alert for *%s* has been resolved after passing successfully %d time(s) in a row", service.Name, alert.SuccessThreshold) + color = "#36A64F" + } else { + message = fmt.Sprintf("An alert for *%s* has been triggered due to having failed %d time(s) in a row", service.Name, alert.FailureThreshold) + color = "#DD0000" + } + var results string + for _, conditionResult := range result.ConditionResults { + var prefix string + if conditionResult.Success { + prefix = "✅" + } else { + prefix = "❌" + } + results += fmt.Sprintf("%s - `%s`
", prefix, conditionResult.Condition) + } + return &custom.AlertProvider{ + URL: provider.WebhookURL, + Method: http.MethodPost, + Body: fmt.Sprintf(`{ + "@type": "MessageCard", + "@context": "http://schema.org/extensions", + "themeColor": "%s", + "title": "🚨 Gatus", + "text": "%s:\n> %s", + "sections": [ + { + "activityTitle": "URL", + "text": "%s" + }, + { + "activityTitle": "Condition results", + "text": "%s" + } + ] +}`, color, message, alert.GetDescription(), service.URL, results), + Headers: map[string]string{"Content-Type": "application/json"}, + } +} + +// 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 new file mode 100644 index 00000000..1bf8b4d3 --- /dev/null +++ b/alerting/provider/teams/teams_test.go @@ -0,0 +1,66 @@ +package teams + +import ( + "encoding/json" + "net/http" + "strings" + "testing" + + "github.com/TwinProduction/gatus/alerting/alert" + "github.com/TwinProduction/gatus/core" +) + +func TestAlertProvider_IsValid(t *testing.T) { + invalidProvider := AlertProvider{WebhookURL: ""} + if invalidProvider.IsValid() { + t.Error("provider shouldn't have been valid") + } + validProvider := AlertProvider{WebhookURL: "http://example.com"} + if !validProvider.IsValid() { + t.Error("provider should've been valid") + } +} + +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) + if customAlertProvider == nil { + t.Fatal("customAlertProvider shouldn't have been nil") + } + if !strings.Contains(customAlertProvider.Body, "resolved") { + t.Error("customAlertProvider.Body should've contained the substring resolved") + } + if customAlertProvider.URL != "http://example.org" { + t.Errorf("expected URL to be %s, got %s", "http://example.org", customAlertProvider.URL) + } + if customAlertProvider.Method != http.MethodPost { + t.Errorf("expected method to be %s, got %s", http.MethodPost, customAlertProvider.Method) + } + body := make(map[string]interface{}) + err := json.Unmarshal([]byte(customAlertProvider.Body), &body) + if err != nil { + t.Error("expected body to be valid JSON, got error:", err.Error()) + } +} + +func TestAlertProvider_ToCustomAlertProviderWithTriggeredAlert(t *testing.T) { + provider := AlertProvider{WebhookURL: "http://example.org"} + customAlertProvider := provider.ToCustomAlertProvider(&core.Service{}, &alert.Alert{}, &core.Result{ConditionResults: []*core.ConditionResult{{Condition: "UNSUCCESSFUL_CONDITION", Success: false}}}, false) + if customAlertProvider == nil { + t.Fatal("customAlertProvider shouldn't have been nil") + } + if !strings.Contains(customAlertProvider.Body, "triggered") { + t.Error("customAlertProvider.Body should've contained the substring triggered") + } + if customAlertProvider.URL != "http://example.org" { + t.Errorf("expected URL to be %s, got %s", "http://example.org", customAlertProvider.URL) + } + if customAlertProvider.Method != http.MethodPost { + t.Errorf("expected method to be %s, got %s", http.MethodPost, customAlertProvider.Method) + } + body := make(map[string]interface{}) + err := json.Unmarshal([]byte(customAlertProvider.Body), &body) + if err != nil { + t.Error("expected body to be valid JSON, got error:", err.Error()) + } +} diff --git a/config.yaml b/config.yaml index 4e7c68d5..9c508ae7 100644 --- a/config.yaml +++ b/config.yaml @@ -1,47 +1,19 @@ -services: - - name: front-end - group: core - url: "https://twinnation.org/health" - interval: 1m - conditions: - - "[STATUS] == 200" - - "[BODY].status == UP" - - "[RESPONSE_TIME] < 150" - - - name: back-end - group: core - url: "https://example.org/" - interval: 5m - conditions: - - "[STATUS] == 200" - - "[CERTIFICATE_EXPIRATION] > 48h" - - - name: monitoring - group: internal - url: "https://example.org/" - interval: 5m - conditions: - - "[STATUS] == 200" - - - name: nas - group: internal - url: "https://example.org/" - interval: 5m - conditions: - - "[STATUS] == 200" - - - name: example-dns-query - url: "8.8.8.8" # Address of the DNS server to use - interval: 5m - dns: - query-name: "example.com" - query-type: "A" - conditions: - - "[BODY] == 93.184.216.34" - - "[DNS_RCODE] == NOERROR" - - - name: icmp-ping - url: "icmp://example.org" - interval: 1m - conditions: - - "[CONNECTED] == true" +alerting: + teams: + webhook-url: "https://quartum.webhook.office.com/webhookb2/fa1bd615-fe85-4248-ab47-c775c6269f66@e4945be5-4b78-49eb-ac7e-de2875b58a6f/IncomingWebhook/b2333e8489f44f23ac020b5b3d8d314e/4e96d5d6-e1d3-42fc-a022-cc4608ac4d9f" + default-alert: + enabled: true + failure-threshold: 1 + success-threshold: 1 + send-on-resolved: true +services: + - name: front-end + group: core + url: "https://twinnation.org/health" + alerts: + - type: teams + interval: 1m + conditions: + - "[STATUS] == 201" + - "[BODY].status == UP" + - "[RESPONSE_TIME] < 150" diff --git a/config/config.go b/config/config.go index 1c546fda..620fb91b 100644 --- a/config/config.go +++ b/config/config.go @@ -272,6 +272,7 @@ func validateAlertingConfig(alertingConfig *alerting.Config, services []*core.Se alert.TypeSlack, alert.TypeTelegram, alert.TypeTwilio, + alert.TypeTeams, } var validProviders, invalidProviders []alert.Type for _, alertType := range alertTypes { diff --git a/config/config_test.go b/config/config_test.go index c9cb1d31..a6d674bb 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -16,6 +16,7 @@ import ( "github.com/TwinProduction/gatus/alerting/provider/slack" "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" "github.com/TwinProduction/gatus/k8stest" v1 "k8s.io/api/core/v1" @@ -351,6 +352,8 @@ alerting: token: "5678" from: "+1-234-567-8901" to: "+1-234-567-8901" + teams: + webhook-url: "http://example.com" services: - name: twinnation @@ -375,6 +378,8 @@ services: enabled: true failure-threshold: 12 success-threshold: 15 + - type: teams + enabled: true conditions: - "[STATUS] == 200" `)) @@ -401,8 +406,8 @@ services: if config.Services[0].Interval != 60*time.Second { t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second) } - if len(config.Services[0].Alerts) != 7 { - t.Fatal("There should've been 7 alerts configured") + if len(config.Services[0].Alerts) != 8 { + t.Fatal("There should've been 8 alerts configured") } if config.Services[0].Alerts[0].Type != alert.TypeSlack { @@ -489,6 +494,19 @@ services: if config.Services[0].Alerts[6].SuccessThreshold != 15 { t.Errorf("The default success threshold of the alert should've been %d, but it was %d", 15, config.Services[0].Alerts[6].SuccessThreshold) } + + if config.Services[0].Alerts[7].Type != alert.TypeTeams { + t.Errorf("The type of the alert should've been %s, but it was %s", alert.TypeTeams, config.Services[0].Alerts[7].Type) + } + if !config.Services[0].Alerts[7].IsEnabled() { + t.Error("The alert should've been enabled") + } + if config.Services[0].Alerts[7].FailureThreshold != 3 { + t.Errorf("The default failure threshold of the alert should've been %d, but it was %d", 3, config.Services[0].Alerts[7].FailureThreshold) + } + if config.Services[0].Alerts[7].SuccessThreshold != 2 { + t.Errorf("The default success threshold of the alert should've been %d, but it was %d", 2, config.Services[0].Alerts[7].SuccessThreshold) + } } func TestParseAndValidateConfigBytesWithAlertingAndDefaultAlert(t *testing.T) { @@ -538,6 +556,10 @@ alerting: enabled: true failure-threshold: 12 success-threshold: 15 + teams: + webhook-url: "http://example.com" + default-alert: + enabled: true services: - name: twinnation @@ -551,6 +573,7 @@ services: success-threshold: 2 # test service alert override - type: telegram - type: twilio + - type: teams conditions: - "[STATUS] == 200" `)) @@ -642,6 +665,14 @@ services: if config.Alerting.Twilio.GetDefaultAlert() == nil { t.Fatal("Twilio.GetDefaultAlert() shouldn't have returned nil") } + + if config.Alerting.Teams == nil || !config.Alerting.Teams.IsValid() { + t.Fatal("Teams alerting config should've been valid") + } + if config.Alerting.Teams.GetDefaultAlert() == nil { + t.Fatal("Teams.GetDefaultAlert() shouldn't have returned nil") + } + // Services if len(config.Services) != 1 { t.Error("There should've been 1 service") @@ -652,8 +683,8 @@ services: if config.Services[0].Interval != 60*time.Second { t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second) } - if len(config.Services[0].Alerts) != 7 { - t.Fatal("There should've been 7 alerts configured") + if len(config.Services[0].Alerts) != 8 { + t.Fatal("There should've been 8 alerts configured") } if config.Services[0].Alerts[0].Type != alert.TypeSlack { @@ -743,6 +774,20 @@ services: if config.Services[0].Alerts[6].SuccessThreshold != 15 { t.Errorf("The default success threshold of the alert should've been %d, but it was %d", 15, config.Services[0].Alerts[6].SuccessThreshold) } + + if config.Services[0].Alerts[7].Type != alert.TypeTeams { + t.Errorf("The type of the alert should've been %s, but it was %s", alert.TypeTeams, config.Services[0].Alerts[7].Type) + } + if !config.Services[0].Alerts[7].IsEnabled() { + t.Error("The alert should've been enabled") + } + if config.Services[0].Alerts[7].FailureThreshold != 3 { + t.Errorf("The default failure threshold of the alert should've been %d, but it was %d", 3, config.Services[0].Alerts[7].FailureThreshold) + } + if config.Services[0].Alerts[7].SuccessThreshold != 2 { + t.Errorf("The default success threshold of the alert should've been %d, but it was %d", 2, config.Services[0].Alerts[7].SuccessThreshold) + } + } func TestParseAndValidateConfigBytesWithAlertingAndDefaultAlertAndMultipleAlertsOfSameTypeWithOverriddenParameters(t *testing.T) { @@ -1249,6 +1294,7 @@ func TestGetAlertingProviderByAlertType(t *testing.T) { Slack: &slack.AlertProvider{}, Telegram: &telegram.AlertProvider{}, Twilio: &twilio.AlertProvider{}, + Teams: &teams.AlertProvider{}, } if alertingConfig.GetAlertingProviderByAlertType(alert.TypeCustom) != alertingConfig.Custom { t.Error("expected Custom configuration") @@ -1274,4 +1320,7 @@ func TestGetAlertingProviderByAlertType(t *testing.T) { if alertingConfig.GetAlertingProviderByAlertType(alert.TypeTwilio) != alertingConfig.Twilio { t.Error("expected Twilio configuration") } + if alertingConfig.GetAlertingProviderByAlertType(alert.TypeTeams) != alertingConfig.Teams { + t.Error("expected Teams configuration") + } }