mirror of
https://github.com/TwiN/gatus.git
synced 2025-06-26 04:31:26 +02:00
feat(alerting): Add ilert alert provider (#1082)
* feat(alerting): Add ilert alert provider * fix: removed additional endpoint from Readme * fix: removed newline Co-authored-by: TwiN <twin@linux.com> * fix: formatting * fix: restAPIUrl typo * fix: removed type field, adjusted tests --------- Co-authored-by: TwiN <twin@linux.com>
This commit is contained in:
parent
439ccaa372
commit
e8e0b0f71c
47
README.md
47
README.md
@ -60,6 +60,7 @@ Have any feedback or questions? [Create a discussion](https://github.com/TwiN/ga
|
||||
- [Configuring Google Chat alerts](#configuring-google-chat-alerts)
|
||||
- [Configuring Gotify alerts](#configuring-gotify-alerts)
|
||||
- [Configuring HomeAssistant alerts](#configuring-homeassistant-alerts)
|
||||
- [Configuring Ilert alerts](#configuring-ilert-alerts)
|
||||
- [Configuring Incident.io alerts](#configuring-incidentio-alerts)
|
||||
- [Configuring JetBrains Space alerts](#configuring-jetbrains-space-alerts)
|
||||
- [Configuring Matrix alerts](#configuring-matrix-alerts)
|
||||
@ -590,6 +591,7 @@ endpoints:
|
||||
| `alerting.gitlab` | Configuration for alerts of type `gitlab`. <br />See [Configuring GitLab alerts](#configuring-gitlab-alerts). | `{}` |
|
||||
| `alerting.googlechat` | Configuration for alerts of type `googlechat`. <br />See [Configuring Google Chat alerts](#configuring-google-chat-alerts). | `{}` |
|
||||
| `alerting.gotify` | Configuration for alerts of type `gotify`. <br />See [Configuring Gotify alerts](#configuring-gotify-alerts). | `{}` |
|
||||
| `alerting.ilert` | Configuration for alerts of type `ilert`. <br />See [Configuring ilert alerts](#configuring-ilert-alerts). | `{}` |
|
||||
| `alerting.incident-io` | Configuration for alerts of type `incident-io`. <br />See [Configuring Incident.io alerts](#configuring-incidentio-alerts). | `{}` |
|
||||
| `alerting.jetbrainsspace` | Configuration for alerts of type `jetbrainsspace`. <br />See [Configuring JetBrains Space alerts](#configuring-jetbrains-space-alerts). | `{}` |
|
||||
| `alerting.matrix` | Configuration for alerts of type `matrix`. <br />See [Configuring Matrix alerts](#configuring-matrix-alerts). | `{}` |
|
||||
@ -897,6 +899,51 @@ endpoints:
|
||||
| `alerting.gotify.title` | Title of the notification | `"Gatus: <endpoint>"` |
|
||||
| `alerting.gotify.default-alert` | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert). | N/A |
|
||||
|
||||
#### Configuring ilert alerts
|
||||
| Parameter | Description | Default |
|
||||
|:---------------------------------------|:-------------------------------------------------------------------------------------------|:--------|
|
||||
| `alerting.ilert` | Configuration for alerts of type `ilert` | `{}` |
|
||||
| `alerting.ilert.integration-key` | ilert Alert Source integration key | `""` |
|
||||
| `alerting.ilert.default-alert` | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A |
|
||||
| `alerting.ilert.overrides` | List of overrides that may be prioritized over the default configuration | `[]` |
|
||||
| `alerting.ilert.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` |
|
||||
| `alerting.ilert.overrides[].*` | See `alerting.ilert.*` parameters | `{}` |
|
||||
|
||||
It is highly recommended to set `endpoints[].alerts[].send-on-resolved` to `true` for alerts
|
||||
of type `ilert`, because unlike other alerts, the operation resulting from setting said
|
||||
parameter to `true` will not create another alert but mark the alert as resolved on
|
||||
ilert instead.
|
||||
|
||||
Behavior:
|
||||
- By default, `alerting.ilert.integration-key` is used as the integration key
|
||||
- If the endpoint being evaluated belongs to a group (`endpoints[].group`) matching the value of `alerting.ilert.overrides[].group`, the provider will use that override's integration key instead of `alerting.ilert.integration-key`'s
|
||||
|
||||
```yaml
|
||||
alerting:
|
||||
ilert:
|
||||
integration-key: "********************************"
|
||||
# You can also add group-specific integration keys, which will
|
||||
# override the integration key above for the specified groups
|
||||
overrides:
|
||||
- group: "core"
|
||||
integration-key: "********************************"
|
||||
|
||||
endpoints:
|
||||
- name: website
|
||||
url: "https://twin.sh/health"
|
||||
interval: 30s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- "[BODY].status == UP"
|
||||
- "[RESPONSE_TIME] < 300"
|
||||
alerts:
|
||||
- type: ilert
|
||||
failure-threshold: 3
|
||||
success-threshold: 5
|
||||
send-on-resolved: true
|
||||
description: "healthcheck failed"
|
||||
```
|
||||
|
||||
```yaml
|
||||
alerting:
|
||||
gotify:
|
||||
|
@ -32,9 +32,12 @@ const (
|
||||
// TypeGotify is the Type for the gotify alerting provider
|
||||
TypeGotify Type = "gotify"
|
||||
|
||||
// TypeHomeAssistant is the Type for the homeassistant alerting provider
|
||||
// TypeHomeAssistant is the Type for the homeassistant alerting provider
|
||||
TypeHomeAssistant Type = "homeassistant"
|
||||
|
||||
// TypeIlert is the Type for the ilert alerting provider
|
||||
TypeIlert Type = "ilert"
|
||||
|
||||
// TypeIncidentIO is the Type for the incident-io alerting provider
|
||||
TypeIncidentIO Type = "incident-io"
|
||||
|
||||
|
@ -15,7 +15,8 @@ import (
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/gitlab"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/googlechat"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/gotify"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/homeassistant"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/homeassistant"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/ilert"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/incidentio"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/jetbrainsspace"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/matrix"
|
||||
@ -66,6 +67,9 @@ type Config struct {
|
||||
// HomeAssistant is the configuration for the homeassistant alerting provider
|
||||
HomeAssistant *homeassistant.AlertProvider `yaml:"homeassistant,omitempty"`
|
||||
|
||||
// Ilert is the configuration for the ilert alerting provider
|
||||
Ilert *ilert.AlertProvider `yaml:"ilert,omitempty"`
|
||||
|
||||
// IncidentIO is the configuration for the incident-io alerting provider
|
||||
IncidentIO *incidentio.AlertProvider `yaml:"incident-io,omitempty"`
|
||||
|
||||
|
168
alerting/provider/ilert/ilert.go
Normal file
168
alerting/provider/ilert/ilert.go
Normal file
@ -0,0 +1,168 @@
|
||||
package ilert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/TwiN/gatus/v5/alerting/alert"
|
||||
"github.com/TwiN/gatus/v5/client"
|
||||
"github.com/TwiN/gatus/v5/config/endpoint"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
restAPIUrl = "https://api.ilert.com/api/v1/events/gatus/"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrIntegrationKeyNotSet = errors.New("integration key is not set")
|
||||
ErrDuplicateGroupOverride = errors.New("duplicate group override")
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
IntegrationKey string `yaml:"integration-key"`
|
||||
}
|
||||
|
||||
func (cfg *Config) Validate() error {
|
||||
if len(cfg.IntegrationKey) == 0 {
|
||||
return ErrIntegrationKeyNotSet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Config) Merge(override *Config) {
|
||||
if len(override.IntegrationKey) > 0 {
|
||||
cfg.IntegrationKey = override.IntegrationKey
|
||||
}
|
||||
}
|
||||
|
||||
// AlertProvider is the configuration necessary for sending an alert using ilert
|
||||
type AlertProvider struct {
|
||||
DefaultConfig Config `yaml:",inline"`
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
type Override struct {
|
||||
Group string `yaml:"group"`
|
||||
Config `yaml:",inline"`
|
||||
}
|
||||
|
||||
func (provider *AlertProvider) Validate() error {
|
||||
registeredGroups := make(map[string]bool)
|
||||
if provider.Overrides != nil {
|
||||
for _, override := range provider.Overrides {
|
||||
if isAlreadyRegistered := registeredGroups[override.Group]; isAlreadyRegistered || override.Group == "" {
|
||||
return ErrDuplicateGroupOverride
|
||||
}
|
||||
registeredGroups[override.Group] = true
|
||||
}
|
||||
}
|
||||
return provider.DefaultConfig.Validate()
|
||||
}
|
||||
|
||||
func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *alert.Alert, result *endpoint.Result, resolved bool) error {
|
||||
cfg, err := provider.GetConfig(ep.Group, alert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buffer := bytes.NewBuffer(provider.buildRequestBody(cfg, ep, alert, result, resolved))
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", restAPIUrl, cfg.IntegrationKey), buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
response, err := client.GetHTTPClient(nil).Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode > 399 {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return fmt.Errorf("call to provider alert returned status code %d: %s", response.StatusCode, string(body))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type Body struct {
|
||||
Alert alert.Alert `json:"alert"`
|
||||
Name string `json:"name"`
|
||||
Group string `json:"group"`
|
||||
Status string `json:"status"`
|
||||
Title string `json:"title"`
|
||||
Details string `json:"details,omitempty"`
|
||||
ConditionResults []*endpoint.ConditionResult `json:"condition_results"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoint, alert *alert.Alert, result *endpoint.Result, resolved bool) []byte {
|
||||
var details, status string
|
||||
if resolved {
|
||||
status = "resolved"
|
||||
} else {
|
||||
status = "firing"
|
||||
}
|
||||
|
||||
if len(alert.GetDescription()) > 0 {
|
||||
details = alert.GetDescription()
|
||||
} else {
|
||||
details = "No description"
|
||||
}
|
||||
|
||||
var body []byte
|
||||
body, _ = json.Marshal(Body{
|
||||
Alert: *alert,
|
||||
Name: ep.Name,
|
||||
Group: ep.Group,
|
||||
Title: ep.DisplayName(),
|
||||
Status: status,
|
||||
Details: details,
|
||||
ConditionResults: result.ConditionResults,
|
||||
URL: ep.URL,
|
||||
})
|
||||
return body
|
||||
}
|
||||
|
||||
func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
|
||||
return provider.DefaultAlert
|
||||
}
|
||||
|
||||
func (provider *AlertProvider) GetConfig(group string, alert *alert.Alert) (*Config, error) {
|
||||
cfg := provider.DefaultConfig
|
||||
// Handle group overrides
|
||||
if provider.Overrides != nil {
|
||||
for _, override := range provider.Overrides {
|
||||
if group == override.Group {
|
||||
cfg.Merge(&override.Config)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle alert overrides
|
||||
if len(alert.ProviderOverride) != 0 {
|
||||
overrideConfig := Config{}
|
||||
if err := yaml.Unmarshal(alert.ProviderOverrideAsBytes(), &overrideConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.Merge(&overrideConfig)
|
||||
}
|
||||
// Validate the configuration
|
||||
err := cfg.Validate()
|
||||
return &cfg, err
|
||||
}
|
||||
|
||||
// ValidateOverrides validates the alert's provider override and, if present, the group override
|
||||
func (provider *AlertProvider) ValidateOverrides(group string, alert *alert.Alert) error {
|
||||
_, err := provider.GetConfig(group, alert)
|
||||
return err
|
||||
}
|
322
alerting/provider/ilert/ilert_test.go
Normal file
322
alerting/provider/ilert/ilert_test.go
Normal file
@ -0,0 +1,322 @@
|
||||
package ilert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/TwiN/gatus/v5/alerting/alert"
|
||||
"github.com/TwiN/gatus/v5/client"
|
||||
"github.com/TwiN/gatus/v5/config/endpoint"
|
||||
"github.com/TwiN/gatus/v5/test"
|
||||
)
|
||||
|
||||
func TestAlertProvider_Validate(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
provider AlertProvider
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
provider: AlertProvider{
|
||||
DefaultConfig: Config{
|
||||
IntegrationKey: "some-random-key",
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "invalid-integration-key",
|
||||
provider: AlertProvider{
|
||||
DefaultConfig: Config{
|
||||
IntegrationKey: "",
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
err := scenario.provider.Validate()
|
||||
if scenario.expected && err != nil {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
if !scenario.expected && err == nil {
|
||||
t.Error("expected error, got none")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertProvider_ValidateWithOverride(t *testing.T) {
|
||||
providerWithInvalidOverrideGroup := AlertProvider{
|
||||
DefaultConfig: Config{IntegrationKey: "00000000000000000000000000000001"},
|
||||
Overrides: []Override{
|
||||
{
|
||||
Config: Config{IntegrationKey: "00000000000000000000000000000002"},
|
||||
Group: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := providerWithInvalidOverrideGroup.Validate(); err == nil {
|
||||
t.Error("provider Group shouldn't have been valid")
|
||||
}
|
||||
providerWithValidOverride := AlertProvider{
|
||||
DefaultConfig: Config{IntegrationKey: "00000000000000000000000000000001"},
|
||||
Overrides: []Override{
|
||||
{
|
||||
Config: Config{IntegrationKey: "00000000000000000000000000000002"},
|
||||
Group: "group",
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := providerWithValidOverride.Validate(); err != nil {
|
||||
t.Error("provider should've been valid, got error:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertProvider_Send(t *testing.T) {
|
||||
defer client.InjectHTTPClient(nil)
|
||||
firstDescription := "description-1"
|
||||
secondDescription := "description-2"
|
||||
sendOnResolved := true
|
||||
scenarios := []struct {
|
||||
Name string
|
||||
Provider AlertProvider
|
||||
Alert alert.Alert
|
||||
Resolved bool
|
||||
MockRoundTripper test.MockRoundTripper
|
||||
ExpectedError bool
|
||||
}{
|
||||
{
|
||||
Name: "triggered",
|
||||
Provider: AlertProvider{DefaultConfig: Config{
|
||||
IntegrationKey: "some-integration-key",
|
||||
}},
|
||||
Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 3, FailureThreshold: 3, ResolveKey: "123", Type: "ilert", SendOnResolved: &sendOnResolved},
|
||||
Resolved: false,
|
||||
MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
|
||||
var b bytes.Buffer
|
||||
|
||||
reader := io.NopCloser(&b)
|
||||
return &http.Response{StatusCode: http.StatusAccepted, Body: reader}
|
||||
}),
|
||||
ExpectedError: false,
|
||||
},
|
||||
{
|
||||
Name: "triggered-error",
|
||||
Provider: AlertProvider{DefaultConfig: Config{
|
||||
IntegrationKey: "some-integration-key",
|
||||
}},
|
||||
Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 3, FailureThreshold: 3, ResolveKey: "123", Type: "ilert", SendOnResolved: &sendOnResolved},
|
||||
Resolved: false,
|
||||
MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
|
||||
return &http.Response{StatusCode: http.StatusBadRequest, Body: http.NoBody}
|
||||
}),
|
||||
ExpectedError: true,
|
||||
},
|
||||
{
|
||||
Name: "resolved",
|
||||
Provider: AlertProvider{DefaultConfig: Config{
|
||||
IntegrationKey: "some-integration-key",
|
||||
}},
|
||||
Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 3, FailureThreshold: 3, ResolveKey: "123", Type: "ilert", SendOnResolved: &sendOnResolved},
|
||||
Resolved: true,
|
||||
MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
|
||||
var b bytes.Buffer
|
||||
reader := io.NopCloser(&b)
|
||||
return &http.Response{StatusCode: http.StatusAccepted, Body: reader}
|
||||
}),
|
||||
ExpectedError: false,
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
client.InjectHTTPClient(&http.Client{Transport: scenario.MockRoundTripper})
|
||||
err := scenario.Provider.Send(
|
||||
&endpoint.Endpoint{Name: "endpoint-name"},
|
||||
&scenario.Alert,
|
||||
&endpoint.Result{
|
||||
ConditionResults: []*endpoint.ConditionResult{
|
||||
{Condition: "[CONNECTED] == true", Success: scenario.Resolved},
|
||||
{Condition: "[STATUS] == 200", Success: scenario.Resolved},
|
||||
},
|
||||
},
|
||||
scenario.Resolved,
|
||||
)
|
||||
if scenario.ExpectedError && err == nil {
|
||||
t.Error("expected error, got none")
|
||||
}
|
||||
if !scenario.ExpectedError && err != nil {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertProvider_BuildRequestBody(t *testing.T) {
|
||||
firstDescription := "description-1"
|
||||
secondDescription := "description-2"
|
||||
sendOnResolved := true
|
||||
|
||||
scenarios := []struct {
|
||||
Name string
|
||||
Provider AlertProvider
|
||||
Alert alert.Alert
|
||||
Resolved bool
|
||||
ExpectedBody string
|
||||
}{
|
||||
{
|
||||
Name: "triggered",
|
||||
Provider: AlertProvider{DefaultConfig: Config{IntegrationKey: "some-integration-key"}},
|
||||
Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 3, FailureThreshold: 3, ResolveKey: "123", Type: "ilert", SendOnResolved: &sendOnResolved},
|
||||
Resolved: false,
|
||||
ExpectedBody: `{"alert":{"Type":"ilert","Enabled":null,"FailureThreshold":3,"SuccessThreshold":3,"Description":"description-1","SendOnResolved":true,"ProviderOverride":null,"ResolveKey":"123","Triggered":false},"name":"endpoint-name","group":"","status":"firing","title":"endpoint-name","details":"description-1","condition_results":[{"condition":"[CONNECTED] == true","success":false},{"condition":"[STATUS] == 200","success":false}],"url":""}`,
|
||||
},
|
||||
{
|
||||
Name: "resolved",
|
||||
Provider: AlertProvider{DefaultConfig: Config{IntegrationKey: "some-integration-key"}},
|
||||
Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 4, FailureThreshold: 3, ResolveKey: "123", Type: "ilert", SendOnResolved: &sendOnResolved},
|
||||
Resolved: true,
|
||||
ExpectedBody: `{"alert":{"Type":"ilert","Enabled":null,"FailureThreshold":3,"SuccessThreshold":4,"Description":"description-1","SendOnResolved":true,"ProviderOverride":null,"ResolveKey":"123","Triggered":false},"name":"endpoint-name","group":"","status":"resolved","title":"endpoint-name","details":"description-1","condition_results":[{"condition":"[CONNECTED] == true","success":true},{"condition":"[STATUS] == 200","success":true}],"url":""}`,
|
||||
},
|
||||
{
|
||||
Name: "group-override",
|
||||
Provider: AlertProvider{DefaultConfig: Config{IntegrationKey: "some-integration-key"}, Overrides: []Override{{Group: "g", Config: Config{IntegrationKey: "different-integration-key"}}}},
|
||||
Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3, ResolveKey: "123", Type: "ilert", SendOnResolved: &sendOnResolved},
|
||||
Resolved: false,
|
||||
ExpectedBody: `{"alert":{"Type":"ilert","Enabled":null,"FailureThreshold":3,"SuccessThreshold":5,"Description":"description-2","SendOnResolved":true,"ProviderOverride":null,"ResolveKey":"123","Triggered":false},"name":"endpoint-name","group":"","status":"firing","title":"endpoint-name","details":"description-2","condition_results":[{"condition":"[CONNECTED] == true","success":false},{"condition":"[STATUS] == 200","success":false}],"url":""}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
cfg, err := scenario.Provider.GetConfig("g", &scenario.Alert)
|
||||
if err != nil {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
body := scenario.Provider.buildRequestBody(
|
||||
cfg,
|
||||
&endpoint.Endpoint{Name: "endpoint-name"},
|
||||
&scenario.Alert,
|
||||
&endpoint.Result{
|
||||
ConditionResults: []*endpoint.ConditionResult{
|
||||
{Condition: "[CONNECTED] == true", Success: scenario.Resolved},
|
||||
{Condition: "[STATUS] == 200", Success: scenario.Resolved},
|
||||
},
|
||||
},
|
||||
scenario.Resolved,
|
||||
)
|
||||
if string(body) != scenario.ExpectedBody {
|
||||
t.Errorf("expected:\n%s\ngot:\n%s", scenario.ExpectedBody, body)
|
||||
}
|
||||
out := make(map[string]interface{})
|
||||
if err := json.Unmarshal(body, &out); err != nil {
|
||||
t.Error("expected body to be valid JSON, got error:", err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertProvider_GetDefaultAlert(t *testing.T) {
|
||||
if (&AlertProvider{DefaultAlert: &alert.Alert{}}).GetDefaultAlert() == nil {
|
||||
t.Error("expected default alert to be not nil")
|
||||
}
|
||||
if (&AlertProvider{DefaultAlert: nil}).GetDefaultAlert() != nil {
|
||||
t.Error("expected default alert to be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertProvider_GetConfig(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
Name string
|
||||
Provider AlertProvider
|
||||
InputGroup string
|
||||
InputAlert alert.Alert
|
||||
ExpectedOutput Config
|
||||
}{
|
||||
{
|
||||
Name: "provider-no-override-specify-no-group-should-default",
|
||||
Provider: AlertProvider{
|
||||
DefaultConfig: Config{IntegrationKey: "00000000000000000000000000000001"},
|
||||
Overrides: nil,
|
||||
},
|
||||
InputGroup: "",
|
||||
InputAlert: alert.Alert{},
|
||||
ExpectedOutput: Config{IntegrationKey: "00000000000000000000000000000001"},
|
||||
},
|
||||
{
|
||||
Name: "provider-no-override-specify-group-should-default",
|
||||
Provider: AlertProvider{
|
||||
DefaultConfig: Config{IntegrationKey: "00000000000000000000000000000001"},
|
||||
Overrides: nil,
|
||||
},
|
||||
InputGroup: "group",
|
||||
InputAlert: alert.Alert{},
|
||||
ExpectedOutput: Config{IntegrationKey: "00000000000000000000000000000001"},
|
||||
},
|
||||
{
|
||||
Name: "provider-with-override-specify-no-group-should-default",
|
||||
Provider: AlertProvider{
|
||||
DefaultConfig: Config{IntegrationKey: "00000000000000000000000000000001"},
|
||||
Overrides: []Override{
|
||||
{
|
||||
Group: "group",
|
||||
Config: Config{IntegrationKey: "00000000000000000000000000000002"},
|
||||
},
|
||||
},
|
||||
},
|
||||
InputGroup: "",
|
||||
InputAlert: alert.Alert{},
|
||||
ExpectedOutput: Config{IntegrationKey: "00000000000000000000000000000001"},
|
||||
},
|
||||
{
|
||||
Name: "provider-with-override-specify-group-should-override",
|
||||
Provider: AlertProvider{
|
||||
DefaultConfig: Config{IntegrationKey: "00000000000000000000000000000001"},
|
||||
Overrides: []Override{
|
||||
{
|
||||
Group: "group",
|
||||
Config: Config{IntegrationKey: "00000000000000000000000000000002"},
|
||||
},
|
||||
},
|
||||
},
|
||||
InputGroup: "group",
|
||||
InputAlert: alert.Alert{},
|
||||
ExpectedOutput: Config{IntegrationKey: "00000000000000000000000000000002"},
|
||||
},
|
||||
{
|
||||
Name: "provider-with-group-override-and-alert-override--alert-override-should-take-precedence",
|
||||
Provider: AlertProvider{
|
||||
DefaultConfig: Config{IntegrationKey: "00000000000000000000000000000001"},
|
||||
Overrides: []Override{
|
||||
{
|
||||
Group: "group",
|
||||
Config: Config{IntegrationKey: "00000000000000000000000000000002"},
|
||||
},
|
||||
},
|
||||
},
|
||||
InputGroup: "group",
|
||||
InputAlert: alert.Alert{ProviderOverride: map[string]any{"integration-key": "00000000000000000000000000000003"}},
|
||||
ExpectedOutput: Config{IntegrationKey: "00000000000000000000000000000003"},
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
got, err := scenario.Provider.GetConfig(scenario.InputGroup, &scenario.InputAlert)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if got.IntegrationKey != scenario.ExpectedOutput.IntegrationKey {
|
||||
t.Errorf("expected %s, got %s", scenario.ExpectedOutput.IntegrationKey, got.IntegrationKey)
|
||||
}
|
||||
// Test ValidateOverrides as well, since it really just calls GetConfig
|
||||
if err = scenario.Provider.ValidateOverrides(scenario.InputGroup, &scenario.InputAlert); err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -10,8 +10,9 @@ import (
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/github"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/gitlab"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/googlechat"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/gotify"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/gotify"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/homeassistant"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/ilert"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/incidentio"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/jetbrainsspace"
|
||||
"github.com/TwiN/gatus/v5/alerting/provider/matrix"
|
||||
@ -82,8 +83,9 @@ var (
|
||||
_ AlertProvider = (*github.AlertProvider)(nil)
|
||||
_ AlertProvider = (*gitlab.AlertProvider)(nil)
|
||||
_ AlertProvider = (*googlechat.AlertProvider)(nil)
|
||||
_ AlertProvider = (*gotify.AlertProvider)(nil)
|
||||
_ AlertProvider = (*gotify.AlertProvider)(nil)
|
||||
_ AlertProvider = (*homeassistant.AlertProvider)(nil)
|
||||
_ AlertProvider = (*ilert.AlertProvider)(nil)
|
||||
_ AlertProvider = (*incidentio.AlertProvider)(nil)
|
||||
_ AlertProvider = (*jetbrainsspace.AlertProvider)(nil)
|
||||
_ AlertProvider = (*matrix.AlertProvider)(nil)
|
||||
@ -109,8 +111,9 @@ var (
|
||||
_ Config[github.Config] = (*github.Config)(nil)
|
||||
_ Config[gitlab.Config] = (*gitlab.Config)(nil)
|
||||
_ Config[googlechat.Config] = (*googlechat.Config)(nil)
|
||||
_ Config[gotify.Config] = (*gotify.Config)(nil)
|
||||
_ Config[gotify.Config] = (*gotify.Config)(nil)
|
||||
_ Config[homeassistant.Config] = (*homeassistant.Config)(nil)
|
||||
_ Config[ilert.Config] = (*ilert.Config)(nil)
|
||||
_ Config[incidentio.Config] = (*incidentio.Config)(nil)
|
||||
_ Config[jetbrainsspace.Config] = (*jetbrainsspace.Config)(nil)
|
||||
_ Config[matrix.Config] = (*matrix.Config)(nil)
|
||||
|
@ -411,7 +411,8 @@ func validateAlertingConfig(alertingConfig *alerting.Config, endpoints []*endpoi
|
||||
alert.TypeGitea,
|
||||
alert.TypeGoogleChat,
|
||||
alert.TypeGotify,
|
||||
alert.TypeHomeAssistant,
|
||||
alert.TypeHomeAssistant,
|
||||
alert.TypeIlert,
|
||||
alert.TypeIncidentIO,
|
||||
alert.TypeJetBrainsSpace,
|
||||
alert.TypeMatrix,
|
||||
|
Loading…
x
Reference in New Issue
Block a user