From bb1ffa9a7a7586266cf6d2dab411300e1c87b532 Mon Sep 17 00:00:00 2001 From: Rani Date: Mon, 20 Jan 2025 13:58:11 +0200 Subject: [PATCH 01/24] feat(alerting): added incident.io provider alerting. --- alerting/alert/type.go | 3 + alerting/config.go | 4 + alerting/provider/incidentio/incident_io.go | 213 ++++++++++++++++++++ alerting/provider/provider.go | 3 + config/config.go | 1 + 5 files changed, 224 insertions(+) create mode 100644 alerting/provider/incidentio/incident_io.go diff --git a/alerting/alert/type.go b/alerting/alert/type.go index 029590d27d..a100fde952 100644 --- a/alerting/alert/type.go +++ b/alerting/alert/type.go @@ -73,4 +73,7 @@ const ( // TypeZulip is the Type for the Zulip alerting provider TypeZulip Type = "zulip" + + // TypeZulip is the Type for the incident.io alerting provider + TypeIncidentio Type = "incidentio" ) diff --git a/alerting/config.go b/alerting/config.go index 7a41e8b6b2..68c923e148 100644 --- a/alerting/config.go +++ b/alerting/config.go @@ -15,6 +15,7 @@ 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/incidentio" "github.com/TwiN/gatus/v5/alerting/provider/jetbrainsspace" "github.com/TwiN/gatus/v5/alerting/provider/matrix" "github.com/TwiN/gatus/v5/alerting/provider/mattermost" @@ -102,6 +103,9 @@ type Config struct { // Zulip is the configuration for the zulip alerting provider Zulip *zulip.AlertProvider `yaml:"zulip,omitempty"` + + // IncidentIo is the configuration for the incident.io alerting provider + IncidentIo *incidentio.AlertProvider `yaml:"incidentio,omitempty"` } // GetAlertingProviderByAlertType returns an provider.AlertProvider by its corresponding alert.Type diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go new file mode 100644 index 0000000000..08503dbfc0 --- /dev/null +++ b/alerting/provider/incidentio/incident_io.go @@ -0,0 +1,213 @@ +package incidentio + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strconv" + + "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.incident.io/v2/alert_events/http" + firingStatus = "firing" + resolvedStatus = "resolved" +) + +var ( + ErrAlertSourceConfigNotSet = errors.New("alert source config not set.") + ErrTitleNotSet = errors.New("title not set.") + ErrDuplicateGroupOverride = errors.New("duplicate group override") + ErrAuthTokenNotSet = errors.New("authentication token not set.") +) + +type Config struct { + AlertSourceConfigID string `yaml:"alert-source-config-id,omitempty"` + Title string `yaml:"title,omitempty"` + AuthToken string `yaml:"auth-token,omitempty"` + + //Status sent to incident.io, either "firing" or "resolved" + Status string + + //key of the alert,initalized on the first event. + DeduplicationKey string +} + +func (cfg *Config) Validate() error { + if len(cfg.AlertSourceConfigID) == 0 { + return ErrAlertSourceConfigNotSet + } + if len(cfg.Title) == 0 { + return ErrTitleNotSet + } + if len(cfg.AuthToken) == 0 { + return ErrAuthTokenNotSet + } + return nil +} + +func (cfg *Config) Merge(override *Config) { + if len(override.AlertSourceConfigID) > 0 { + cfg.AlertSourceConfigID = override.AlertSourceConfigID + } + if len(override.Title) > 0 { + cfg.Title = override.Title + } + if len(override.AuthToken) > 0 { + cfg.AuthToken = override.AuthToken + } +} + +// AlertProvider is the configuration necessary for sending an alert using incident.io +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)) + url := fmt.Sprintf("%s/%s", restAPIUrl, cfg.AlertSourceConfigID) + req, err := http.NewRequest(http.MethodPost, url, buffer) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+cfg.AuthToken) + 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)) + } + incidentioResponse := Resopnse{} + json.NewDecoder(response.Body).Decode(incidentioResponse) + cfg.DeduplicationKey = incidentioResponse.DeduplicationKey + return err +} + +type Body struct { + AlertSourceConfigID string `json:"alert_source_config_id"` + Status string `json:"status"` + Title string `json:"title"` + DeduplicationKey string `json:"deduplication_key"` + Description string `json:"description"` +} + +type Resopnse struct { + DeduplicationKey string `json:"deduplication_key"` +} + +func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoint, alert *alert.Alert, result *endpoint.Result, resolved bool) []byte { + var message, formattedConditionResults string + if resolved { + message = "An alert has been resolved after passing successfully " + strconv.Itoa(alert.SuccessThreshold) + " time(s) in a row" + cfg.Status = "resolved" + } else { + message = "An alert has been triggered due to having failed " + strconv.Itoa(alert.FailureThreshold) + " time(s) in a row" + cfg.Status = "firing" + } + for _, conditionResult := range result.ConditionResults { + var prefix string + if conditionResult.Success { + prefix = "🟢" + } else { + prefix = "🔴" + } + // No need for \n since incident.io trims it anyways. + formattedConditionResults += fmt.Sprintf(" %s %s ", prefix, conditionResult.Condition) + } + if len(alert.GetDescription()) > 0 { + message += " with the following description: " + alert.GetDescription() + } + + message += fmt.Sprintf(" and the following conditions: %s ", formattedConditionResults) + var body []byte + if len(cfg.DeduplicationKey) > 0 { + body, _ = json.Marshal(Body{ + AlertSourceConfigID: cfg.AlertSourceConfigID, + Title: "Gatus: " + ep.DisplayName(), + Status: cfg.Status, + DeduplicationKey: cfg.DeduplicationKey, + Description: message, + }) + } else { + body, _ = json.Marshal(Body{ + AlertSourceConfigID: cfg.AlertSourceConfigID, + Title: "Gatus: " + ep.DisplayName(), + Status: cfg.Status, + Description: message, + }) + } + return body + +} +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 +} + +// GetDefaultAlert returns the provider's default alert configuration +func (provider *AlertProvider) GetDefaultAlert() *alert.Alert { + return provider.DefaultAlert +} + +func (provider *AlertProvider) ValidateOverrides(group string, alert *alert.Alert) error { + _, err := provider.GetConfig(group, alert) + return err +} diff --git a/alerting/provider/provider.go b/alerting/provider/provider.go index cee8103d9a..a11d1635b3 100644 --- a/alerting/provider/provider.go +++ b/alerting/provider/provider.go @@ -10,6 +10,7 @@ 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/incidentio" "github.com/TwiN/gatus/v5/alerting/provider/jetbrainsspace" "github.com/TwiN/gatus/v5/alerting/provider/matrix" "github.com/TwiN/gatus/v5/alerting/provider/mattermost" @@ -93,6 +94,7 @@ var ( _ AlertProvider = (*telegram.AlertProvider)(nil) _ AlertProvider = (*twilio.AlertProvider)(nil) _ AlertProvider = (*zulip.AlertProvider)(nil) + _ AlertProvider = (*incidentio.AlertProvider)(nil) // Validate config interface implementation on compile _ Config[awsses.Config] = (*awsses.Config)(nil) @@ -117,4 +119,5 @@ var ( _ Config[telegram.Config] = (*telegram.Config)(nil) _ Config[twilio.Config] = (*twilio.Config)(nil) _ Config[zulip.Config] = (*zulip.Config)(nil) + _ Config[incidentio.Config] = (*incidentio.Config)(nil) ) diff --git a/config/config.go b/config/config.go index a666c63fea..29ebcafcc5 100644 --- a/config/config.go +++ b/config/config.go @@ -417,6 +417,7 @@ func validateAlertingConfig(alertingConfig *alerting.Config, endpoints []*endpoi alert.TypeTelegram, alert.TypeTwilio, alert.TypeZulip, + alert.TypeIncidentio, } var validProviders, invalidProviders []alert.Type for _, alertType := range alertTypes { From 7e814c5b42900e474558b09b88eb26b1cf3111d6 Mon Sep 17 00:00:00 2001 From: Rani Date: Mon, 20 Jan 2025 22:48:38 +0200 Subject: [PATCH 02/24] Tests: added incident.io provider unit tests. --- alerting/provider/incidentio/incident_io.go | 6 +- .../provider/incidentio/incident_io_test.go | 398 ++++++++++++++++++ 2 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 alerting/provider/incidentio/incident_io_test.go diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index 08503dbfc0..86ffd16a55 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -116,8 +116,8 @@ func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *alert.Alert, r body, _ := io.ReadAll(response.Body) return fmt.Errorf("call to provider alert returned status code %d: %s", response.StatusCode, string(body)) } - incidentioResponse := Resopnse{} - json.NewDecoder(response.Body).Decode(incidentioResponse) + incidentioResponse := Response{} + json.NewDecoder(response.Body).Decode(&incidentioResponse) cfg.DeduplicationKey = incidentioResponse.DeduplicationKey return err } @@ -130,7 +130,7 @@ type Body struct { Description string `json:"description"` } -type Resopnse struct { +type Response struct { DeduplicationKey string `json:"deduplication_key"` } diff --git a/alerting/provider/incidentio/incident_io_test.go b/alerting/provider/incidentio/incident_io_test.go new file mode 100644 index 0000000000..5ad7053a56 --- /dev/null +++ b/alerting/provider/incidentio/incident_io_test.go @@ -0,0 +1,398 @@ +package incidentio + +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{ + AlertSourceConfigID: "some-id", + Title: "some-title", + AuthToken: "some-token", + }, + }, + expected: true, + }, + { + name: "invalid-missing-auth-token", + provider: AlertProvider{ + DefaultConfig: Config{ + AlertSourceConfigID: "some-id", + Title: "some-title", + }, + }, + expected: false, + }, + { + name: "invalid-missing-title", + provider: AlertProvider{ + DefaultConfig: Config{ + AlertSourceConfigID: "some-id", + AuthToken: "some-token", + }, + }, + expected: false, + }, + { + name: "invalid-missing-alert-source-config-id", + provider: AlertProvider{ + DefaultConfig: Config{ + AuthToken: "some-token", + Title: "some-title", + }, + }, + expected: false, + }, + { + name: "valid-override", + provider: AlertProvider{ + DefaultConfig: Config{ + AuthToken: "some-token", + Title: "some-title", + }, + Overrides: []Override{{Group: "core", Config: Config{Title: "new-title"}}}, + }, + 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_Send(t *testing.T) { + defer client.InjectHTTPClient(nil) + firstDescription := "description-1" + secondDescription := "description-2" + scenarios := []struct { + Name string + Provider AlertProvider + Alert alert.Alert + Resolved bool + MockRoundTripper test.MockRoundTripper + ExpectedError bool + }{ + { + Name: "triggered", + Provider: AlertProvider{DefaultConfig: Config{ + AlertSourceConfigID: "some-id", + Title: "some-title", + AuthToken: "some-token", + }}, + Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, + Resolved: false, + MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response { + var b bytes.Buffer + + response := Response{DeduplicationKey: "some-key"} + json.NewEncoder(&b).Encode(response) + reader := io.NopCloser(&b) + return &http.Response{StatusCode: http.StatusAccepted, Body: reader} + }), + ExpectedError: false, + }, + { + Name: "triggered-error", + Provider: AlertProvider{DefaultConfig: Config{ + AlertSourceConfigID: "some-id", + Title: "some-title", + AuthToken: "some-token", + }}, + Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, + Resolved: false, + MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response { + return &http.Response{StatusCode: http.StatusInternalServerError, Body: http.NoBody} + }), + ExpectedError: true, + }, + { + Name: "resolved", + Provider: AlertProvider{DefaultConfig: Config{ + AlertSourceConfigID: "some-id", + Title: "some-title", + AuthToken: "some-token", + }}, + Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3}, + Resolved: true, + MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response { + var b bytes.Buffer + response := Response{DeduplicationKey: "some-key"} + json.NewEncoder(&b).Encode(response) + reader := io.NopCloser(&b) + return &http.Response{StatusCode: http.StatusAccepted, Body: reader} + }), + ExpectedError: false, + }, + { + Name: "resolved-error", + Provider: AlertProvider{DefaultConfig: Config{}}, + Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3}, + Resolved: true, + MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response { + return &http.Response{StatusCode: http.StatusInternalServerError, Body: http.NoBody} + }), + ExpectedError: true, + }, + } + 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" + scenarios := []struct { + Name string + Provider AlertProvider + Alert alert.Alert + Resolved bool + ExpectedBody string + }{ + { + Name: "triggered", + Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}}, + Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, + Resolved: false, + ExpectedBody: `{"alert_source_config_id":"some-id","status":"firing","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been triggered due to having failed 3 time(s) in a row with the following description: description-1 and the following conditions: 🔴 [CONNECTED] == true 🔴 [STATUS] == 200 "}`, + }, + { + Name: "resolved", + Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}}, + Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3}, + Resolved: true, + ExpectedBody: `{"alert_source_config_id":"some-id","status":"resolved","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been resolved after passing successfully 5 time(s) in a row with the following description: description-2 and the following conditions: 🟢 [CONNECTED] == true 🟢 [STATUS] == 200 "}`, + }, + { + Name: "group-override", + Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, Overrides: []Override{{Group: "g", Config: Config{AlertSourceConfigID: "different-id", AuthToken: "some-token"}}}}, + Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, + Resolved: false, + ExpectedBody: `{"alert_source_config_id":"different-id","status":"firing","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been triggered due to having failed 3 time(s) in a row with the following description: description-1 and the following conditions: 🔴 [CONNECTED] == true 🔴 [STATUS] == 200 "}`, + }, + } + + 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{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + Overrides: nil, + }, + InputGroup: "", + InputAlert: alert.Alert{}, + ExpectedOutput: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + }, + { + Name: "provider-no-override-specify-group-should-default", + Provider: AlertProvider{ + DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + Overrides: nil, + }, + InputGroup: "group", + InputAlert: alert.Alert{}, + ExpectedOutput: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + }, + { + Name: "provider-with-override-specify-no-group-should-default", + Provider: AlertProvider{ + DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + Overrides: []Override{ + { + Group: "group", + Config: Config{AlertSourceConfigID: "diff-id"}, + }, + }, + }, + InputGroup: "", + InputAlert: alert.Alert{}, + ExpectedOutput: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + }, + { + Name: "provider-with-override-specify-group-should-override", + Provider: AlertProvider{ + DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + Overrides: []Override{ + { + Group: "group", + Config: Config{AlertSourceConfigID: "diff-id", Title: "my-title", AuthToken: "some-token"}, + }, + }, + }, + InputGroup: "group", + InputAlert: alert.Alert{}, + ExpectedOutput: Config{AlertSourceConfigID: "diff-id", Title: "my-title", AuthToken: "some-token"}, + }, + { + Name: "provider-with-group-override-and-alert-override--alert-override-should-take-precedence", + Provider: AlertProvider{ + DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + Overrides: []Override{ + { + Group: "group", + Config: Config{AlertSourceConfigID: "diff-id", Title: "my-title", AuthToken: "some-token"}, + }, + }, + }, + InputGroup: "group", + InputAlert: alert.Alert{ProviderOverride: map[string]any{"alert-source-config-id": "another-id"}}, + ExpectedOutput: Config{AlertSourceConfigID: "another-id", Title: "my-title", AuthToken: "some-token"}, + }, + } + 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.AlertSourceConfigID != scenario.ExpectedOutput.AlertSourceConfigID { + t.Errorf("expected alert source config to be %s, got %s", scenario.ExpectedOutput.AlertSourceConfigID, got.AlertSourceConfigID) + } + if got.AuthToken != scenario.ExpectedOutput.AuthToken { + t.Errorf("expected alert auth token to be %s, got %s", scenario.ExpectedOutput.AuthToken, got.AuthToken) + } + + if got.Title != scenario.ExpectedOutput.Title { + t.Errorf("expected alert title to be %s, got %s", scenario.ExpectedOutput.Title, got.Title) + } + + if got.Status != scenario.ExpectedOutput.Status { + t.Errorf("expected alert status to be %s, got %s", scenario.ExpectedOutput.Status, got.Status) + } + + if got.DeduplicationKey != scenario.ExpectedOutput.DeduplicationKey { + t.Errorf("expected alert deduplication key to be %s, got %s", scenario.ExpectedOutput.DeduplicationKey, got.DeduplicationKey) + } + // 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) + } + }) + } +} + +func TestAlertProvider_ValidateWithOverride(t *testing.T) { + providerWithInvalidOverrideGroup := AlertProvider{ + Overrides: []Override{ + { + Config: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + Group: "", + }, + }, + } + if err := providerWithInvalidOverrideGroup.Validate(); err == nil { + t.Error("provider Group shouldn't have been valid") + } + providerWithInvalidOverrideTo := AlertProvider{ + Overrides: []Override{ + { + Config: Config{AlertSourceConfigID: "", Title: "my-title", AuthToken: "some-token"}, + Group: "group", + }, + }, + } + if err := providerWithInvalidOverrideTo.Validate(); err == nil { + t.Error("provider integration key shouldn't have been valid") + } + providerWithValidOverride := AlertProvider{ + DefaultConfig: Config{AlertSourceConfigID: "nice-id", Title: "my-title", AuthToken: "some-token"}, + Overrides: []Override{ + { + Config: Config{AlertSourceConfigID: "very-good-id", Title: "my-title", AuthToken: "some-token"}, + Group: "group", + }, + }, + } + if err := providerWithValidOverride.Validate(); err != nil { + t.Error("provider should've been valid") + } +} From fd1b09b525623c5a4398c240ecc7047111d1cb96 Mon Sep 17 00:00:00 2001 From: Rani Date: Tue, 21 Jan 2025 00:01:17 +0200 Subject: [PATCH 03/24] Documentation: added incidentio documentation. --- README.md | 36 ++++++++++ alerting/provider/incidentio/incident_io.go | 8 --- .../provider/incidentio/incident_io_test.go | 66 +++++++------------ 3 files changed, 59 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 312116f55d..c33611d4b1 100644 --- a/README.md +++ b/README.md @@ -592,6 +592,7 @@ endpoints: | `alerting.telegram` | Configuration for alerts of type `telegram`.
See [Configuring Telegram alerts](#configuring-telegram-alerts). | `{}` | | `alerting.twilio` | Settings for alerts of type `twilio`.
See [Configuring Twilio alerts](#configuring-twilio-alerts). | `{}` | | `alerting.zulip` | Configuration for alerts of type `zulip`.
See [Configuring Zulip alerts](#configuring-zulip-alerts). | `{}` | +| `alerting.incidentio` | Configuration for alerts of type `incidentio`.
See [Configuring Incident.io alerts](#configuring-incidentio-alerts). | `{}` | #### Configuring AWS SES alerts @@ -1256,6 +1257,41 @@ Here's an example of what the notifications look like: ![Slack notifications](.github/assets/slack-alerts.png) +#### Configuring Incident.io alerts +| Parameter | Description | Default | +|:-----------------------------------|:-------------------------------------------------------------------------------------------|:--------------| +| `alerting.incidentio` | Configuration for alerts of type `incidentio` | `{}` | +| `alerting.incidentio.alert-source-config-id` | Which alert source config produced this alert | Required `""` | +| `alerting.incidentio.auth-token` | Token that is used for authentication. | Required `""` | +| `alerting.incidentio.overrides` | List of overrides that may be prioritized over the default configuration | `[]` | +| `alerting.incidentio.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A | +| `alerting.incidentio.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` | +| `alerting.incidentio.overrides[].*` | See `alerting.incidentio.*` parameters | `{}` | + +```yaml +alerting: + incidentio: + alert-source-config-id: "*****************" + auth-token: "********************************************" + +endpoints: + - name: website + url: "https://twin.sh/health" + interval: 30s + conditions: + - "[STATUS] == 200" + - "[BODY].status == UP" + - "[RESPONSE_TIME] < 300" + alerts: + - type: incidentio + description: "healthcheck failed" + send-on-resolved: true +``` +in order to get the required alert source config id and authentication token, you must configure an HTTP alert source. + +> **_NOTE:_** the source config id is of the form { api.incident.io/v2/alert_events/http/{CONFIG-SOURCE-ID}} + +> **_NOTE:_** the auth token is of the form { "Authorization": "Bearer {AUTH-TOKEN}" } #### Configuring Teams alerts *(Deprecated)* diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index 86ffd16a55..915d3d3af7 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -23,14 +23,12 @@ const ( var ( ErrAlertSourceConfigNotSet = errors.New("alert source config not set.") - ErrTitleNotSet = errors.New("title not set.") ErrDuplicateGroupOverride = errors.New("duplicate group override") ErrAuthTokenNotSet = errors.New("authentication token not set.") ) type Config struct { AlertSourceConfigID string `yaml:"alert-source-config-id,omitempty"` - Title string `yaml:"title,omitempty"` AuthToken string `yaml:"auth-token,omitempty"` //Status sent to incident.io, either "firing" or "resolved" @@ -44,9 +42,6 @@ func (cfg *Config) Validate() error { if len(cfg.AlertSourceConfigID) == 0 { return ErrAlertSourceConfigNotSet } - if len(cfg.Title) == 0 { - return ErrTitleNotSet - } if len(cfg.AuthToken) == 0 { return ErrAuthTokenNotSet } @@ -57,9 +52,6 @@ func (cfg *Config) Merge(override *Config) { if len(override.AlertSourceConfigID) > 0 { cfg.AlertSourceConfigID = override.AlertSourceConfigID } - if len(override.Title) > 0 { - cfg.Title = override.Title - } if len(override.AuthToken) > 0 { cfg.AuthToken = override.AuthToken } diff --git a/alerting/provider/incidentio/incident_io_test.go b/alerting/provider/incidentio/incident_io_test.go index 5ad7053a56..8e0a3a949f 100644 --- a/alerting/provider/incidentio/incident_io_test.go +++ b/alerting/provider/incidentio/incident_io_test.go @@ -24,7 +24,6 @@ func TestAlertProvider_Validate(t *testing.T) { provider: AlertProvider{ DefaultConfig: Config{ AlertSourceConfigID: "some-id", - Title: "some-title", AuthToken: "some-token", }, }, @@ -35,17 +34,6 @@ func TestAlertProvider_Validate(t *testing.T) { provider: AlertProvider{ DefaultConfig: Config{ AlertSourceConfigID: "some-id", - Title: "some-title", - }, - }, - expected: false, - }, - { - name: "invalid-missing-title", - provider: AlertProvider{ - DefaultConfig: Config{ - AlertSourceConfigID: "some-id", - AuthToken: "some-token", }, }, expected: false, @@ -55,7 +43,6 @@ func TestAlertProvider_Validate(t *testing.T) { provider: AlertProvider{ DefaultConfig: Config{ AuthToken: "some-token", - Title: "some-title", }, }, expected: false, @@ -64,12 +51,12 @@ func TestAlertProvider_Validate(t *testing.T) { name: "valid-override", provider: AlertProvider{ DefaultConfig: Config{ - AuthToken: "some-token", - Title: "some-title", + AuthToken: "some-token", + AlertSourceConfigID: "some-id", }, - Overrides: []Override{{Group: "core", Config: Config{Title: "new-title"}}}, + Overrides: []Override{{Group: "core", Config: Config{AlertSourceConfigID: "another-id"}}}, }, - expected: false, + expected: true, }, } for _, scenario := range scenarios { @@ -101,7 +88,6 @@ func TestAlertProvider_Send(t *testing.T) { Name: "triggered", Provider: AlertProvider{DefaultConfig: Config{ AlertSourceConfigID: "some-id", - Title: "some-title", AuthToken: "some-token", }}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, @@ -120,7 +106,6 @@ func TestAlertProvider_Send(t *testing.T) { Name: "triggered-error", Provider: AlertProvider{DefaultConfig: Config{ AlertSourceConfigID: "some-id", - Title: "some-title", AuthToken: "some-token", }}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, @@ -134,7 +119,6 @@ func TestAlertProvider_Send(t *testing.T) { Name: "resolved", Provider: AlertProvider{DefaultConfig: Config{ AlertSourceConfigID: "some-id", - Title: "some-title", AuthToken: "some-token", }}, Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3}, @@ -195,21 +179,21 @@ func TestAlertProvider_BuildRequestBody(t *testing.T) { }{ { Name: "triggered", - Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}}, + Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: false, ExpectedBody: `{"alert_source_config_id":"some-id","status":"firing","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been triggered due to having failed 3 time(s) in a row with the following description: description-1 and the following conditions: 🔴 [CONNECTED] == true 🔴 [STATUS] == 200 "}`, }, { Name: "resolved", - Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}}, + Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}}, Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: true, ExpectedBody: `{"alert_source_config_id":"some-id","status":"resolved","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been resolved after passing successfully 5 time(s) in a row with the following description: description-2 and the following conditions: 🟢 [CONNECTED] == true 🟢 [STATUS] == 200 "}`, }, { Name: "group-override", - Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, Overrides: []Override{{Group: "g", Config: Config{AlertSourceConfigID: "different-id", AuthToken: "some-token"}}}}, + Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, Overrides: []Override{{Group: "g", Config: Config{AlertSourceConfigID: "different-id", AuthToken: "some-token"}}}}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: false, ExpectedBody: `{"alert_source_config_id":"different-id","status":"firing","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been triggered due to having failed 3 time(s) in a row with the following description: description-1 and the following conditions: 🔴 [CONNECTED] == true 🔴 [STATUS] == 200 "}`, @@ -265,27 +249,27 @@ func TestAlertProvider_GetConfig(t *testing.T) { { Name: "provider-no-override-specify-no-group-should-default", Provider: AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, Overrides: nil, }, InputGroup: "", InputAlert: alert.Alert{}, - ExpectedOutput: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + ExpectedOutput: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, }, { Name: "provider-no-override-specify-group-should-default", Provider: AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, Overrides: nil, }, InputGroup: "group", InputAlert: alert.Alert{}, - ExpectedOutput: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + ExpectedOutput: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, }, { Name: "provider-with-override-specify-no-group-should-default", Provider: AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, Overrides: []Override{ { Group: "group", @@ -295,37 +279,37 @@ func TestAlertProvider_GetConfig(t *testing.T) { }, InputGroup: "", InputAlert: alert.Alert{}, - ExpectedOutput: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + ExpectedOutput: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, }, { Name: "provider-with-override-specify-group-should-override", Provider: AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, Overrides: []Override{ { Group: "group", - Config: Config{AlertSourceConfigID: "diff-id", Title: "my-title", AuthToken: "some-token"}, + Config: Config{AlertSourceConfigID: "diff-id", AuthToken: "some-token"}, }, }, }, InputGroup: "group", InputAlert: alert.Alert{}, - ExpectedOutput: Config{AlertSourceConfigID: "diff-id", Title: "my-title", AuthToken: "some-token"}, + ExpectedOutput: Config{AlertSourceConfigID: "diff-id", AuthToken: "some-token"}, }, { Name: "provider-with-group-override-and-alert-override--alert-override-should-take-precedence", Provider: AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, Overrides: []Override{ { Group: "group", - Config: Config{AlertSourceConfigID: "diff-id", Title: "my-title", AuthToken: "some-token"}, + Config: Config{AlertSourceConfigID: "diff-id", AuthToken: "some-token"}, }, }, }, InputGroup: "group", InputAlert: alert.Alert{ProviderOverride: map[string]any{"alert-source-config-id": "another-id"}}, - ExpectedOutput: Config{AlertSourceConfigID: "another-id", Title: "my-title", AuthToken: "some-token"}, + ExpectedOutput: Config{AlertSourceConfigID: "another-id", AuthToken: "some-token"}, }, } for _, scenario := range scenarios { @@ -341,10 +325,6 @@ func TestAlertProvider_GetConfig(t *testing.T) { t.Errorf("expected alert auth token to be %s, got %s", scenario.ExpectedOutput.AuthToken, got.AuthToken) } - if got.Title != scenario.ExpectedOutput.Title { - t.Errorf("expected alert title to be %s, got %s", scenario.ExpectedOutput.Title, got.Title) - } - if got.Status != scenario.ExpectedOutput.Status { t.Errorf("expected alert status to be %s, got %s", scenario.ExpectedOutput.Status, got.Status) } @@ -364,7 +344,7 @@ func TestAlertProvider_ValidateWithOverride(t *testing.T) { providerWithInvalidOverrideGroup := AlertProvider{ Overrides: []Override{ { - Config: Config{AlertSourceConfigID: "some-id", Title: "my-title", AuthToken: "some-token"}, + Config: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, Group: "", }, }, @@ -375,7 +355,7 @@ func TestAlertProvider_ValidateWithOverride(t *testing.T) { providerWithInvalidOverrideTo := AlertProvider{ Overrides: []Override{ { - Config: Config{AlertSourceConfigID: "", Title: "my-title", AuthToken: "some-token"}, + Config: Config{AlertSourceConfigID: "", AuthToken: "some-token"}, Group: "group", }, }, @@ -384,10 +364,10 @@ func TestAlertProvider_ValidateWithOverride(t *testing.T) { t.Error("provider integration key shouldn't have been valid") } providerWithValidOverride := AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "nice-id", Title: "my-title", AuthToken: "some-token"}, + DefaultConfig: Config{AlertSourceConfigID: "nice-id", AuthToken: "some-token"}, Overrides: []Override{ { - Config: Config{AlertSourceConfigID: "very-good-id", Title: "my-title", AuthToken: "some-token"}, + Config: Config{AlertSourceConfigID: "very-good-id", AuthToken: "some-token"}, Group: "group", }, }, From caa98f0eff0c3339afc8e790e3ff1b1fb3e4007f Mon Sep 17 00:00:00 2001 From: Rani Date: Tue, 21 Jan 2025 09:15:22 +0200 Subject: [PATCH 04/24] Refactor: Changed documentation + types to an alphabetical order. --- README.md | 72 ++++++++++++++++++++++-------------------- alerting/alert/type.go | 6 ++-- alerting/config.go | 6 ++-- 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index c33611d4b1..f553538c1b 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ Have any feedback or questions? [Create a discussion](https://github.com/TwiN/ga - [Configuring GitLab alerts](#configuring-gitlab-alerts) - [Configuring Google Chat alerts](#configuring-google-chat-alerts) - [Configuring Gotify alerts](#configuring-gotify-alerts) + - [Configuring Incident.io alerts](#configuring-incidentio-alerts) - [Configuring JetBrains Space alerts](#configuring-jetbrains-space-alerts) - [Configuring Matrix alerts](#configuring-matrix-alerts) - [Configuring Mattermost alerts](#configuring-mattermost-alerts) @@ -578,6 +579,7 @@ endpoints: | `alerting.gitlab` | Configuration for alerts of type `gitlab`.
See [Configuring GitLab alerts](#configuring-gitlab-alerts). | `{}` | | `alerting.googlechat` | Configuration for alerts of type `googlechat`.
See [Configuring Google Chat alerts](#configuring-google-chat-alerts). | `{}` | | `alerting.gotify` | Configuration for alerts of type `gotify`.
See [Configuring Gotify alerts](#configuring-gotify-alerts). | `{}` | +| `alerting.incidentio` | Configuration for alerts of type `incidentio`.
See [Configuring Incident.io alerts](#configuring-incidentio-alerts). | `{}` | | `alerting.jetbrainsspace` | Configuration for alerts of type `jetbrainsspace`.
See [Configuring JetBrains Space alerts](#configuring-jetbrains-space-alerts). | `{}` | | `alerting.matrix` | Configuration for alerts of type `matrix`.
See [Configuring Matrix alerts](#configuring-matrix-alerts). | `{}` | | `alerting.mattermost` | Configuration for alerts of type `mattermost`.
See [Configuring Mattermost alerts](#configuring-mattermost-alerts). | `{}` | @@ -592,7 +594,6 @@ endpoints: | `alerting.telegram` | Configuration for alerts of type `telegram`.
See [Configuring Telegram alerts](#configuring-telegram-alerts). | `{}` | | `alerting.twilio` | Settings for alerts of type `twilio`.
See [Configuring Twilio alerts](#configuring-twilio-alerts). | `{}` | | `alerting.zulip` | Configuration for alerts of type `zulip`.
See [Configuring Zulip alerts](#configuring-zulip-alerts). | `{}` | -| `alerting.incidentio` | Configuration for alerts of type `incidentio`.
See [Configuring Incident.io alerts](#configuring-incidentio-alerts). | `{}` | #### Configuring AWS SES alerts @@ -904,6 +905,41 @@ Here's an example of what the notifications look like: ![Gotify notifications](.github/assets/gotify-alerts.png) +#### Configuring Incident.io alerts +| Parameter | Description | Default | +|:-----------------------------------|:-------------------------------------------------------------------------------------------|:--------------| +| `alerting.incidentio` | Configuration for alerts of type `incidentio` | `{}` | +| `alerting.incidentio.alert-source-config-id` | Which alert source config produced this alert | Required `""` | +| `alerting.incidentio.auth-token` | Token that is used for authentication. | Required `""` | +| `alerting.incidentio.overrides` | List of overrides that may be prioritized over the default configuration | `[]` | +| `alerting.incidentio.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A | +| `alerting.incidentio.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` | +| `alerting.incidentio.overrides[].*` | See `alerting.incidentio.*` parameters | `{}` | + +```yaml +alerting: + incident-io: + alert-source-config-id: "*****************" + auth-token: "********************************************" + +endpoints: + - name: website + url: "https://twin.sh/health" + interval: 30s + conditions: + - "[STATUS] == 200" + - "[BODY].status == UP" + - "[RESPONSE_TIME] < 300" + alerts: + - type: incident-io + description: "healthcheck failed" + send-on-resolved: true +``` +in order to get the required alert source config id and authentication token, you must configure an HTTP alert source. + +> **_NOTE:_** the source config id is of the form { api.incident.io/v2/alert_events/http/{CONFIG-SOURCE-ID}} + +> **_NOTE:_** the auth token is of the form { "Authorization": "Bearer {AUTH-TOKEN}" } #### Configuring JetBrains Space alerts | Parameter | Description | Default | @@ -1257,41 +1293,7 @@ Here's an example of what the notifications look like: ![Slack notifications](.github/assets/slack-alerts.png) -#### Configuring Incident.io alerts -| Parameter | Description | Default | -|:-----------------------------------|:-------------------------------------------------------------------------------------------|:--------------| -| `alerting.incidentio` | Configuration for alerts of type `incidentio` | `{}` | -| `alerting.incidentio.alert-source-config-id` | Which alert source config produced this alert | Required `""` | -| `alerting.incidentio.auth-token` | Token that is used for authentication. | Required `""` | -| `alerting.incidentio.overrides` | List of overrides that may be prioritized over the default configuration | `[]` | -| `alerting.incidentio.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A | -| `alerting.incidentio.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` | -| `alerting.incidentio.overrides[].*` | See `alerting.incidentio.*` parameters | `{}` | - -```yaml -alerting: - incidentio: - alert-source-config-id: "*****************" - auth-token: "********************************************" -endpoints: - - name: website - url: "https://twin.sh/health" - interval: 30s - conditions: - - "[STATUS] == 200" - - "[BODY].status == UP" - - "[RESPONSE_TIME] < 300" - alerts: - - type: incidentio - description: "healthcheck failed" - send-on-resolved: true -``` -in order to get the required alert source config id and authentication token, you must configure an HTTP alert source. - -> **_NOTE:_** the source config id is of the form { api.incident.io/v2/alert_events/http/{CONFIG-SOURCE-ID}} - -> **_NOTE:_** the auth token is of the form { "Authorization": "Bearer {AUTH-TOKEN}" } #### Configuring Teams alerts *(Deprecated)* diff --git a/alerting/alert/type.go b/alerting/alert/type.go index a100fde952..8cb097f440 100644 --- a/alerting/alert/type.go +++ b/alerting/alert/type.go @@ -32,6 +32,9 @@ const ( // TypeGotify is the Type for the gotify alerting provider TypeGotify Type = "gotify" + // TypeZulip is the Type for the incident.io alerting provider + TypeIncidentio Type = "incident-io" + // TypeJetBrainsSpace is the Type for the jetbrains alerting provider TypeJetBrainsSpace Type = "jetbrainsspace" @@ -73,7 +76,4 @@ const ( // TypeZulip is the Type for the Zulip alerting provider TypeZulip Type = "zulip" - - // TypeZulip is the Type for the incident.io alerting provider - TypeIncidentio Type = "incidentio" ) diff --git a/alerting/config.go b/alerting/config.go index 68c923e148..9c60fef503 100644 --- a/alerting/config.go +++ b/alerting/config.go @@ -62,6 +62,9 @@ type Config struct { // Gotify is the configuration for the gotify alerting provider Gotify *gotify.AlertProvider `yaml:"gotify,omitempty"` + // IncidentIo is the configuration for the incident.io alerting provider + IncidentIo *incidentio.AlertProvider `yaml:"incident-io,omitempty"` + // JetBrainsSpace is the configuration for the jetbrains space alerting provider JetBrainsSpace *jetbrainsspace.AlertProvider `yaml:"jetbrainsspace,omitempty"` @@ -103,9 +106,6 @@ type Config struct { // Zulip is the configuration for the zulip alerting provider Zulip *zulip.AlertProvider `yaml:"zulip,omitempty"` - - // IncidentIo is the configuration for the incident.io alerting provider - IncidentIo *incidentio.AlertProvider `yaml:"incidentio,omitempty"` } // GetAlertingProviderByAlertType returns an provider.AlertProvider by its corresponding alert.Type From 8ae19b489ddc0eb0d112e2da670b2e8475c8760d Mon Sep 17 00:00:00 2001 From: Rani <33231841+ImTheCurse@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:14:32 +0200 Subject: [PATCH 05/24] Refactor: change wrong comment. Co-authored-by: Maksim Zhylinski --- alerting/alert/type.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alerting/alert/type.go b/alerting/alert/type.go index 8cb097f440..9c2b8b4a18 100644 --- a/alerting/alert/type.go +++ b/alerting/alert/type.go @@ -32,7 +32,7 @@ const ( // TypeGotify is the Type for the gotify alerting provider TypeGotify Type = "gotify" - // TypeZulip is the Type for the incident.io alerting provider + // TypeIncidentio is the Type for the incident.io alerting provider TypeIncidentio Type = "incident-io" // TypeJetBrainsSpace is the Type for the jetbrains alerting provider From 60975c56b04b7d9f7a726f520a63e509c8d89c56 Mon Sep 17 00:00:00 2001 From: Rani <33231841+ImTheCurse@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:18:11 +0200 Subject: [PATCH 06/24] Update README.md Co-authored-by: Maksim Zhylinski --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6e9956ee6d..2686081849 100644 --- a/README.md +++ b/README.md @@ -920,8 +920,8 @@ Here's an example of what the notifications look like: ```yaml alerting: incident-io: - alert-source-config-id: "*****************" - auth-token: "********************************************" + alert-source-config-id: "*****************" + auth-token: "********************************************" endpoints: - name: website From 621fae58f150d4ddd4722391acad1b8472b61ccd Mon Sep 17 00:00:00 2001 From: Rani <33231841+ImTheCurse@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:11:18 +0200 Subject: [PATCH 07/24] Update alerting/provider/incidentio/incident_io.go Co-authored-by: Julien Limoges --- alerting/provider/incidentio/incident_io.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index 915d3d3af7..8dcd5d0b24 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -24,7 +24,7 @@ const ( var ( ErrAlertSourceConfigNotSet = errors.New("alert source config not set.") ErrDuplicateGroupOverride = errors.New("duplicate group override") - ErrAuthTokenNotSet = errors.New("authentication token not set.") + ErrAuthTokenNotSet = errors.New("auth-token not set.") ) type Config struct { From 51693ae81b3620b9309eefb5b36b67d5c9ca7fd8 Mon Sep 17 00:00:00 2001 From: Rani <33231841+ImTheCurse@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:13:28 +0200 Subject: [PATCH 08/24] Update alerting/provider/incidentio/incident_io.go Co-authored-by: Julien Limoges --- alerting/provider/incidentio/incident_io.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index 8dcd5d0b24..95b570833b 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -22,7 +22,7 @@ const ( ) var ( - ErrAlertSourceConfigNotSet = errors.New("alert source config not set.") + ErrAlertSourceConfigNotSet = errors.New("alert-source-config-id not set.") ErrDuplicateGroupOverride = errors.New("duplicate group override") ErrAuthTokenNotSet = errors.New("auth-token not set.") ) From 41b67237ae6edd2e05d0c216c142247c6bc6bfa1 Mon Sep 17 00:00:00 2001 From: Rani <33231841+ImTheCurse@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:24:17 +0200 Subject: [PATCH 09/24] Update alerting/provider/incidentio/incident_io.go Co-authored-by: Julien Limoges --- alerting/provider/incidentio/incident_io.go | 1 - 1 file changed, 1 deletion(-) diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index 95b570833b..5b3bbd1373 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -17,7 +17,6 @@ import ( const ( restAPIUrl = "https://api.incident.io/v2/alert_events/http" - firingStatus = "firing" resolvedStatus = "resolved" ) From d9b5129c817e00c55a1bbbdc7e1e2a949725421d Mon Sep 17 00:00:00 2001 From: Rani <33231841+ImTheCurse@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:25:05 +0200 Subject: [PATCH 10/24] Update alerting/provider/incidentio/incident_io.go Co-authored-by: Julien Limoges --- alerting/provider/incidentio/incident_io.go | 1 - 1 file changed, 1 deletion(-) diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index 5b3bbd1373..399fd7b23f 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -17,7 +17,6 @@ import ( const ( restAPIUrl = "https://api.incident.io/v2/alert_events/http" - resolvedStatus = "resolved" ) var ( From 888e891a9ffa31be3b8b9a6473acb807f688b59f Mon Sep 17 00:00:00 2001 From: rani Date: Sat, 25 Jan 2025 23:47:55 +0200 Subject: [PATCH 11/24] Refactor: changed alertSourceID to url. --- alerting/provider/incidentio/incident_io.go | 29 +++++++++++---------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index 399fd7b23f..568931ed3c 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "strconv" + "strings" "github.com/TwiN/gatus/v5/alerting/alert" "github.com/TwiN/gatus/v5/client" @@ -16,18 +17,18 @@ import ( ) const ( - restAPIUrl = "https://api.incident.io/v2/alert_events/http" + restAPIUrl = "https://api.incident.io/v2/alert_events/http/" ) var ( - ErrAlertSourceConfigNotSet = errors.New("alert-source-config-id not set.") - ErrDuplicateGroupOverride = errors.New("duplicate group override") - ErrAuthTokenNotSet = errors.New("auth-token not set.") + ErrURLNotSet = errors.New("url not set.") + ErrDuplicateGroupOverride = errors.New("duplicate group override") + ErrAuthTokenNotSet = errors.New("auth-token not set.") ) type Config struct { - AlertSourceConfigID string `yaml:"alert-source-config-id,omitempty"` - AuthToken string `yaml:"auth-token,omitempty"` + URL string `yaml:"url,omitempty"` + AuthToken string `yaml:"auth-token,omitempty"` //Status sent to incident.io, either "firing" or "resolved" Status string @@ -37,8 +38,8 @@ type Config struct { } func (cfg *Config) Validate() error { - if len(cfg.AlertSourceConfigID) == 0 { - return ErrAlertSourceConfigNotSet + if len(cfg.URL) == 0 { + return ErrURLNotSet } if len(cfg.AuthToken) == 0 { return ErrAuthTokenNotSet @@ -47,8 +48,8 @@ func (cfg *Config) Validate() error { } func (cfg *Config) Merge(override *Config) { - if len(override.AlertSourceConfigID) > 0 { - cfg.AlertSourceConfigID = override.AlertSourceConfigID + if len(override.URL) > 0 { + cfg.URL = override.URL } if len(override.AuthToken) > 0 { cfg.AuthToken = override.AuthToken @@ -90,8 +91,7 @@ func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *alert.Alert, r return err } buffer := bytes.NewBuffer(provider.buildRequestBody(cfg, ep, alert, result, resolved)) - url := fmt.Sprintf("%s/%s", restAPIUrl, cfg.AlertSourceConfigID) - req, err := http.NewRequest(http.MethodPost, url, buffer) + req, err := http.NewRequest(http.MethodPost, cfg.URL, buffer) if err != nil { return err } @@ -149,9 +149,10 @@ func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoi message += fmt.Sprintf(" and the following conditions: %s ", formattedConditionResults) var body []byte + alertSourceID := strings.Split(cfg.URL, restAPIUrl)[1] if len(cfg.DeduplicationKey) > 0 { body, _ = json.Marshal(Body{ - AlertSourceConfigID: cfg.AlertSourceConfigID, + AlertSourceConfigID: alertSourceID, Title: "Gatus: " + ep.DisplayName(), Status: cfg.Status, DeduplicationKey: cfg.DeduplicationKey, @@ -159,7 +160,7 @@ func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoi }) } else { body, _ = json.Marshal(Body{ - AlertSourceConfigID: cfg.AlertSourceConfigID, + AlertSourceConfigID: alertSourceID, Title: "Gatus: " + ep.DisplayName(), Status: cfg.Status, Description: message, From 8754beed272a8cbb31624c62225140e571782fde Mon Sep 17 00:00:00 2001 From: rani Date: Sat, 25 Jan 2025 23:55:30 +0200 Subject: [PATCH 12/24] Refactor: changed documentation. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2686081849..00233cabdb 100644 --- a/README.md +++ b/README.md @@ -910,7 +910,7 @@ Here's an example of what the notifications look like: | Parameter | Description | Default | |:-----------------------------------|:-------------------------------------------------------------------------------------------|:--------------| | `alerting.incidentio` | Configuration for alerts of type `incidentio` | `{}` | -| `alerting.incidentio.alert-source-config-id` | Which alert source config produced this alert | Required `""` | +| `alerting.incidentio.url` | url to trigger an alert event. | Required `""` | | `alerting.incidentio.auth-token` | Token that is used for authentication. | Required `""` | | `alerting.incidentio.overrides` | List of overrides that may be prioritized over the default configuration | `[]` | | `alerting.incidentio.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A | @@ -920,7 +920,7 @@ Here's an example of what the notifications look like: ```yaml alerting: incident-io: - alert-source-config-id: "*****************" + url: "*****************" auth-token: "********************************************" endpoints: From 9634886bfff0eeb9c28ef0c9c72a143ebea0cb13 Mon Sep 17 00:00:00 2001 From: rani Date: Sun, 26 Jan 2025 00:22:00 +0200 Subject: [PATCH 13/24] Refactor: refactored tests, removed status from config. --- alerting/provider/incidentio/incident_io.go | 13 ++-- .../provider/incidentio/incident_io_test.go | 76 +++++++++---------- 2 files changed, 42 insertions(+), 47 deletions(-) diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index 568931ed3c..5f0cea63a6 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -30,9 +30,6 @@ type Config struct { URL string `yaml:"url,omitempty"` AuthToken string `yaml:"auth-token,omitempty"` - //Status sent to incident.io, either "firing" or "resolved" - Status string - //key of the alert,initalized on the first event. DeduplicationKey string } @@ -125,13 +122,13 @@ type Response struct { } func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoint, alert *alert.Alert, result *endpoint.Result, resolved bool) []byte { - var message, formattedConditionResults string + var message, formattedConditionResults, status string if resolved { message = "An alert has been resolved after passing successfully " + strconv.Itoa(alert.SuccessThreshold) + " time(s) in a row" - cfg.Status = "resolved" + status = "resolved" } else { message = "An alert has been triggered due to having failed " + strconv.Itoa(alert.FailureThreshold) + " time(s) in a row" - cfg.Status = "firing" + status = "firing" } for _, conditionResult := range result.ConditionResults { var prefix string @@ -154,7 +151,7 @@ func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoi body, _ = json.Marshal(Body{ AlertSourceConfigID: alertSourceID, Title: "Gatus: " + ep.DisplayName(), - Status: cfg.Status, + Status: status, DeduplicationKey: cfg.DeduplicationKey, Description: message, }) @@ -162,7 +159,7 @@ func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoi body, _ = json.Marshal(Body{ AlertSourceConfigID: alertSourceID, Title: "Gatus: " + ep.DisplayName(), - Status: cfg.Status, + Status: status, Description: message, }) } diff --git a/alerting/provider/incidentio/incident_io_test.go b/alerting/provider/incidentio/incident_io_test.go index 8e0a3a949f..81f191fe5c 100644 --- a/alerting/provider/incidentio/incident_io_test.go +++ b/alerting/provider/incidentio/incident_io_test.go @@ -23,8 +23,8 @@ func TestAlertProvider_Validate(t *testing.T) { name: "valid", provider: AlertProvider{ DefaultConfig: Config{ - AlertSourceConfigID: "some-id", - AuthToken: "some-token", + URL: "some-id", + AuthToken: "some-token", }, }, expected: true, @@ -33,7 +33,7 @@ func TestAlertProvider_Validate(t *testing.T) { name: "invalid-missing-auth-token", provider: AlertProvider{ DefaultConfig: Config{ - AlertSourceConfigID: "some-id", + URL: "some-id", }, }, expected: false, @@ -51,10 +51,10 @@ func TestAlertProvider_Validate(t *testing.T) { name: "valid-override", provider: AlertProvider{ DefaultConfig: Config{ - AuthToken: "some-token", - AlertSourceConfigID: "some-id", + AuthToken: "some-token", + URL: "some-id", }, - Overrides: []Override{{Group: "core", Config: Config{AlertSourceConfigID: "another-id"}}}, + Overrides: []Override{{Group: "core", Config: Config{URL: "another-id"}}}, }, expected: true, }, @@ -76,6 +76,7 @@ func TestAlertProvider_Send(t *testing.T) { defer client.InjectHTTPClient(nil) firstDescription := "description-1" secondDescription := "description-2" + restAPIUrl := "https://api.incident.io/v2/alert_events/http/" scenarios := []struct { Name string Provider AlertProvider @@ -87,8 +88,8 @@ func TestAlertProvider_Send(t *testing.T) { { Name: "triggered", Provider: AlertProvider{DefaultConfig: Config{ - AlertSourceConfigID: "some-id", - AuthToken: "some-token", + URL: restAPIUrl + "some-id", + AuthToken: "some-token", }}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: false, @@ -105,8 +106,8 @@ func TestAlertProvider_Send(t *testing.T) { { Name: "triggered-error", Provider: AlertProvider{DefaultConfig: Config{ - AlertSourceConfigID: "some-id", - AuthToken: "some-token", + URL: restAPIUrl + "some-id", + AuthToken: "some-token", }}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: false, @@ -118,8 +119,8 @@ func TestAlertProvider_Send(t *testing.T) { { Name: "resolved", Provider: AlertProvider{DefaultConfig: Config{ - AlertSourceConfigID: "some-id", - AuthToken: "some-token", + URL: restAPIUrl + "some-id", + AuthToken: "some-token", }}, Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: true, @@ -170,6 +171,7 @@ func TestAlertProvider_Send(t *testing.T) { func TestAlertProvider_BuildRequestBody(t *testing.T) { firstDescription := "description-1" secondDescription := "description-2" + restAPIUrl := "https://api.incident.io/v2/alert_events/http/" scenarios := []struct { Name string Provider AlertProvider @@ -179,21 +181,21 @@ func TestAlertProvider_BuildRequestBody(t *testing.T) { }{ { Name: "triggered", - Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}}, + Provider: AlertProvider{DefaultConfig: Config{URL: restAPIUrl + "some-id", AuthToken: "some-token"}}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: false, ExpectedBody: `{"alert_source_config_id":"some-id","status":"firing","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been triggered due to having failed 3 time(s) in a row with the following description: description-1 and the following conditions: 🔴 [CONNECTED] == true 🔴 [STATUS] == 200 "}`, }, { Name: "resolved", - Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}}, + Provider: AlertProvider{DefaultConfig: Config{URL: restAPIUrl + "some-id", AuthToken: "some-token"}}, Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: true, ExpectedBody: `{"alert_source_config_id":"some-id","status":"resolved","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been resolved after passing successfully 5 time(s) in a row with the following description: description-2 and the following conditions: 🟢 [CONNECTED] == true 🟢 [STATUS] == 200 "}`, }, { Name: "group-override", - Provider: AlertProvider{DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, Overrides: []Override{{Group: "g", Config: Config{AlertSourceConfigID: "different-id", AuthToken: "some-token"}}}}, + Provider: AlertProvider{DefaultConfig: Config{URL: restAPIUrl + "some-id", AuthToken: "some-token"}, Overrides: []Override{{Group: "g", Config: Config{URL: restAPIUrl + "different-id", AuthToken: "some-token"}}}}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: false, ExpectedBody: `{"alert_source_config_id":"different-id","status":"firing","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been triggered due to having failed 3 time(s) in a row with the following description: description-1 and the following conditions: 🔴 [CONNECTED] == true 🔴 [STATUS] == 200 "}`, @@ -249,67 +251,67 @@ func TestAlertProvider_GetConfig(t *testing.T) { { Name: "provider-no-override-specify-no-group-should-default", Provider: AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, + DefaultConfig: Config{URL: "some-id", AuthToken: "some-token"}, Overrides: nil, }, InputGroup: "", InputAlert: alert.Alert{}, - ExpectedOutput: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, + ExpectedOutput: Config{URL: "some-id", AuthToken: "some-token"}, }, { Name: "provider-no-override-specify-group-should-default", Provider: AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, + DefaultConfig: Config{URL: "some-id", AuthToken: "some-token"}, Overrides: nil, }, InputGroup: "group", InputAlert: alert.Alert{}, - ExpectedOutput: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, + ExpectedOutput: Config{URL: "some-id", AuthToken: "some-token"}, }, { Name: "provider-with-override-specify-no-group-should-default", Provider: AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, + DefaultConfig: Config{URL: "some-id", AuthToken: "some-token"}, Overrides: []Override{ { Group: "group", - Config: Config{AlertSourceConfigID: "diff-id"}, + Config: Config{URL: "diff-id"}, }, }, }, InputGroup: "", InputAlert: alert.Alert{}, - ExpectedOutput: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, + ExpectedOutput: Config{URL: "some-id", AuthToken: "some-token"}, }, { Name: "provider-with-override-specify-group-should-override", Provider: AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, + DefaultConfig: Config{URL: "some-id", AuthToken: "some-token"}, Overrides: []Override{ { Group: "group", - Config: Config{AlertSourceConfigID: "diff-id", AuthToken: "some-token"}, + Config: Config{URL: "diff-id", AuthToken: "some-token"}, }, }, }, InputGroup: "group", InputAlert: alert.Alert{}, - ExpectedOutput: Config{AlertSourceConfigID: "diff-id", AuthToken: "some-token"}, + ExpectedOutput: Config{URL: "diff-id", AuthToken: "some-token"}, }, { Name: "provider-with-group-override-and-alert-override--alert-override-should-take-precedence", Provider: AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, + DefaultConfig: Config{URL: "some-id", AuthToken: "some-token"}, Overrides: []Override{ { Group: "group", - Config: Config{AlertSourceConfigID: "diff-id", AuthToken: "some-token"}, + Config: Config{URL: "diff-id", AuthToken: "some-token"}, }, }, }, InputGroup: "group", - InputAlert: alert.Alert{ProviderOverride: map[string]any{"alert-source-config-id": "another-id"}}, - ExpectedOutput: Config{AlertSourceConfigID: "another-id", AuthToken: "some-token"}, + InputAlert: alert.Alert{ProviderOverride: map[string]any{"url": "another-id"}}, + ExpectedOutput: Config{URL: "another-id", AuthToken: "some-token"}, }, } for _, scenario := range scenarios { @@ -318,17 +320,13 @@ func TestAlertProvider_GetConfig(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %s", err) } - if got.AlertSourceConfigID != scenario.ExpectedOutput.AlertSourceConfigID { - t.Errorf("expected alert source config to be %s, got %s", scenario.ExpectedOutput.AlertSourceConfigID, got.AlertSourceConfigID) + if got.URL != scenario.ExpectedOutput.URL { + t.Errorf("expected alert source config to be %s, got %s", scenario.ExpectedOutput.URL, got.URL) } if got.AuthToken != scenario.ExpectedOutput.AuthToken { t.Errorf("expected alert auth token to be %s, got %s", scenario.ExpectedOutput.AuthToken, got.AuthToken) } - if got.Status != scenario.ExpectedOutput.Status { - t.Errorf("expected alert status to be %s, got %s", scenario.ExpectedOutput.Status, got.Status) - } - if got.DeduplicationKey != scenario.ExpectedOutput.DeduplicationKey { t.Errorf("expected alert deduplication key to be %s, got %s", scenario.ExpectedOutput.DeduplicationKey, got.DeduplicationKey) } @@ -344,7 +342,7 @@ func TestAlertProvider_ValidateWithOverride(t *testing.T) { providerWithInvalidOverrideGroup := AlertProvider{ Overrides: []Override{ { - Config: Config{AlertSourceConfigID: "some-id", AuthToken: "some-token"}, + Config: Config{URL: "some-id", AuthToken: "some-token"}, Group: "", }, }, @@ -355,7 +353,7 @@ func TestAlertProvider_ValidateWithOverride(t *testing.T) { providerWithInvalidOverrideTo := AlertProvider{ Overrides: []Override{ { - Config: Config{AlertSourceConfigID: "", AuthToken: "some-token"}, + Config: Config{URL: "", AuthToken: "some-token"}, Group: "group", }, }, @@ -364,10 +362,10 @@ func TestAlertProvider_ValidateWithOverride(t *testing.T) { t.Error("provider integration key shouldn't have been valid") } providerWithValidOverride := AlertProvider{ - DefaultConfig: Config{AlertSourceConfigID: "nice-id", AuthToken: "some-token"}, + DefaultConfig: Config{URL: "nice-id", AuthToken: "some-token"}, Overrides: []Override{ { - Config: Config{AlertSourceConfigID: "very-good-id", AuthToken: "some-token"}, + Config: Config{URL: "very-good-id", AuthToken: "some-token"}, Group: "group", }, }, From 2c46f0adb64968b004e95e9a95219aaa91df6b46 Mon Sep 17 00:00:00 2001 From: rani Date: Sun, 26 Jan 2025 01:20:30 +0200 Subject: [PATCH 14/24] Readme: updated docs. --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 00233cabdb..7555b6d175 100644 --- a/README.md +++ b/README.md @@ -580,7 +580,7 @@ endpoints: | `alerting.gitlab` | Configuration for alerts of type `gitlab`.
See [Configuring GitLab alerts](#configuring-gitlab-alerts). | `{}` | | `alerting.googlechat` | Configuration for alerts of type `googlechat`.
See [Configuring Google Chat alerts](#configuring-google-chat-alerts). | `{}` | | `alerting.gotify` | Configuration for alerts of type `gotify`.
See [Configuring Gotify alerts](#configuring-gotify-alerts). | `{}` | -| `alerting.incidentio` | Configuration for alerts of type `incidentio`.
See [Configuring Incident.io alerts](#configuring-incidentio-alerts). | `{}` | +| `alerting.incident-io` | Configuration for alerts of type `incidentio`.
See [Configuring Incident.io alerts](#configuring-incidentio-alerts). | `{}` | | `alerting.jetbrainsspace` | Configuration for alerts of type `jetbrainsspace`.
See [Configuring JetBrains Space alerts](#configuring-jetbrains-space-alerts). | `{}` | | `alerting.matrix` | Configuration for alerts of type `matrix`.
See [Configuring Matrix alerts](#configuring-matrix-alerts). | `{}` | | `alerting.mattermost` | Configuration for alerts of type `mattermost`.
See [Configuring Mattermost alerts](#configuring-mattermost-alerts). | `{}` | @@ -909,13 +909,13 @@ Here's an example of what the notifications look like: #### Configuring Incident.io alerts | Parameter | Description | Default | |:-----------------------------------|:-------------------------------------------------------------------------------------------|:--------------| -| `alerting.incidentio` | Configuration for alerts of type `incidentio` | `{}` | -| `alerting.incidentio.url` | url to trigger an alert event. | Required `""` | -| `alerting.incidentio.auth-token` | Token that is used for authentication. | Required `""` | -| `alerting.incidentio.overrides` | List of overrides that may be prioritized over the default configuration | `[]` | -| `alerting.incidentio.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A | -| `alerting.incidentio.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` | -| `alerting.incidentio.overrides[].*` | See `alerting.incidentio.*` parameters | `{}` | +| `alerting.incident-io` | Configuration for alerts of type `incident-io` | `{}` | +| `alerting.incident-io.url` | url to trigger an alert event. | Required `""` | +| `alerting.incident-io.auth-token` | Token that is used for authentication. | Required `""` | +| `alerting.incident-io.overrides` | List of overrides that may be prioritized over the default configuration | `[]` | +| `alerting.incident-io.default-alert` | Default alert configuration.
See [Setting a default alert](#setting-a-default-alert) | N/A | +| `alerting.incident-io.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration | `""` | +| `alerting.incident-io.overrides[].*` | See `alerting.incident-io.*` parameters | `{}` | ```yaml alerting: From dabbec54170a488cb40cc0dccb000bb6a13532a9 Mon Sep 17 00:00:00 2001 From: rani Date: Sun, 26 Jan 2025 15:38:57 +0200 Subject: [PATCH 15/24] Refactor: removed duplication key in favor of ResolveKey. --- alerting/provider/incidentio/incident_io.go | 16 +++++++++------- alerting/provider/incidentio/incident_io_test.go | 3 --- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index 5f0cea63a6..f9cd55c3ab 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -13,6 +13,7 @@ import ( "github.com/TwiN/gatus/v5/alerting/alert" "github.com/TwiN/gatus/v5/client" "github.com/TwiN/gatus/v5/config/endpoint" + "github.com/TwiN/logr" "gopkg.in/yaml.v3" ) @@ -29,9 +30,6 @@ var ( type Config struct { URL string `yaml:"url,omitempty"` AuthToken string `yaml:"auth-token,omitempty"` - - //key of the alert,initalized on the first event. - DeduplicationKey string } func (cfg *Config) Validate() error { @@ -104,8 +102,12 @@ func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *alert.Alert, r return fmt.Errorf("call to provider alert returned status code %d: %s", response.StatusCode, string(body)) } incidentioResponse := Response{} - json.NewDecoder(response.Body).Decode(&incidentioResponse) - cfg.DeduplicationKey = incidentioResponse.DeduplicationKey + err = json.NewDecoder(response.Body).Decode(&incidentioResponse) + if err != nil { + // Silently fail. We don't want to create tons of alerts just because we failed to parse the body. + logr.Errorf("[incident-io.Send] Ran into error decoding pagerduty response: %s", err.Error()) + } + alert.ResolveKey = incidentioResponse.DeduplicationKey return err } @@ -147,12 +149,12 @@ func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoi message += fmt.Sprintf(" and the following conditions: %s ", formattedConditionResults) var body []byte alertSourceID := strings.Split(cfg.URL, restAPIUrl)[1] - if len(cfg.DeduplicationKey) > 0 { + if len(alert.ResolveKey) > 0 { body, _ = json.Marshal(Body{ AlertSourceConfigID: alertSourceID, Title: "Gatus: " + ep.DisplayName(), Status: status, - DeduplicationKey: cfg.DeduplicationKey, + DeduplicationKey: alert.ResolveKey, Description: message, }) } else { diff --git a/alerting/provider/incidentio/incident_io_test.go b/alerting/provider/incidentio/incident_io_test.go index 81f191fe5c..eb9f10dc42 100644 --- a/alerting/provider/incidentio/incident_io_test.go +++ b/alerting/provider/incidentio/incident_io_test.go @@ -327,9 +327,6 @@ func TestAlertProvider_GetConfig(t *testing.T) { t.Errorf("expected alert auth token to be %s, got %s", scenario.ExpectedOutput.AuthToken, got.AuthToken) } - if got.DeduplicationKey != scenario.ExpectedOutput.DeduplicationKey { - t.Errorf("expected alert deduplication key to be %s, got %s", scenario.ExpectedOutput.DeduplicationKey, got.DeduplicationKey) - } // 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) From ba047a06d56fd0b46379ed72cf8d143d3e474e1b Mon Sep 17 00:00:00 2001 From: rani Date: Sun, 26 Jan 2025 15:46:47 +0200 Subject: [PATCH 16/24] Refactor: change variable format. --- alerting/alert/type.go | 2 +- config/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/alerting/alert/type.go b/alerting/alert/type.go index 9c2b8b4a18..fa20fd44b1 100644 --- a/alerting/alert/type.go +++ b/alerting/alert/type.go @@ -33,7 +33,7 @@ const ( TypeGotify Type = "gotify" // TypeIncidentio is the Type for the incident.io alerting provider - TypeIncidentio Type = "incident-io" + TypeIncidentIO Type = "incident-io" // TypeJetBrainsSpace is the Type for the jetbrains alerting provider TypeJetBrainsSpace Type = "jetbrainsspace" diff --git a/config/config.go b/config/config.go index 29ebcafcc5..eddddfd7fa 100644 --- a/config/config.go +++ b/config/config.go @@ -417,7 +417,7 @@ func validateAlertingConfig(alertingConfig *alerting.Config, endpoints []*endpoi alert.TypeTelegram, alert.TypeTwilio, alert.TypeZulip, - alert.TypeIncidentio, + alert.TypeIncidentIO, } var validProviders, invalidProviders []alert.Type for _, alertType := range alertTypes { From cf28ca265d95505afd04d8ccc69375da3e0f2d71 Mon Sep 17 00:00:00 2001 From: rani Date: Mon, 27 Jan 2025 11:28:58 +0200 Subject: [PATCH 17/24] Feat + Test: added support for passing metadata and source url, added resolved-with-metadata-source-url test case. --- alerting/provider/incidentio/incident_io.go | 50 ++++++++++--------- .../provider/incidentio/incident_io_test.go | 13 +++-- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index f9cd55c3ab..e8333f7790 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -28,8 +28,10 @@ var ( ) type Config struct { - URL string `yaml:"url,omitempty"` - AuthToken string `yaml:"auth-token,omitempty"` + URL string `yaml:"url,omitempty"` + AuthToken string `yaml:"auth-token,omitempty"` + SourceURL string `yaml:"source-url,omitempty"` + Metadata map[string]interface{} `yaml:"metadata,omitempty"` } func (cfg *Config) Validate() error { @@ -49,6 +51,12 @@ func (cfg *Config) Merge(override *Config) { if len(override.AuthToken) > 0 { cfg.AuthToken = override.AuthToken } + if len(override.SourceURL) > 0 { + cfg.SourceURL = override.SourceURL + } + if len(override.Metadata) > 0 { + cfg.Metadata = override.Metadata + } } // AlertProvider is the configuration necessary for sending an alert using incident.io @@ -112,11 +120,13 @@ func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *alert.Alert, r } type Body struct { - AlertSourceConfigID string `json:"alert_source_config_id"` - Status string `json:"status"` - Title string `json:"title"` - DeduplicationKey string `json:"deduplication_key"` - Description string `json:"description"` + AlertSourceConfigID string `json:"alert_source_config_id"` + Status string `json:"status"` + Title string `json:"title"` + DeduplicationKey string `json:"deduplication_key,omitempty"` + Description string `json:"description,omitempty"` + SourceURL string `json:"source_url,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` } type Response struct { @@ -149,22 +159,16 @@ func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoi message += fmt.Sprintf(" and the following conditions: %s ", formattedConditionResults) var body []byte alertSourceID := strings.Split(cfg.URL, restAPIUrl)[1] - if len(alert.ResolveKey) > 0 { - body, _ = json.Marshal(Body{ - AlertSourceConfigID: alertSourceID, - Title: "Gatus: " + ep.DisplayName(), - Status: status, - DeduplicationKey: alert.ResolveKey, - Description: message, - }) - } else { - body, _ = json.Marshal(Body{ - AlertSourceConfigID: alertSourceID, - Title: "Gatus: " + ep.DisplayName(), - Status: status, - Description: message, - }) - } + body, _ = json.Marshal(Body{ + AlertSourceConfigID: alertSourceID, + Title: "Gatus: " + ep.DisplayName(), + Status: status, + DeduplicationKey: alert.ResolveKey, + Description: message, + SourceURL: cfg.SourceURL, + Metadata: cfg.Metadata, + }) + fmt.Printf("%v", string(body)) return body } diff --git a/alerting/provider/incidentio/incident_io_test.go b/alerting/provider/incidentio/incident_io_test.go index eb9f10dc42..a2c2af91dc 100644 --- a/alerting/provider/incidentio/incident_io_test.go +++ b/alerting/provider/incidentio/incident_io_test.go @@ -184,21 +184,28 @@ func TestAlertProvider_BuildRequestBody(t *testing.T) { Provider: AlertProvider{DefaultConfig: Config{URL: restAPIUrl + "some-id", AuthToken: "some-token"}}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: false, - ExpectedBody: `{"alert_source_config_id":"some-id","status":"firing","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been triggered due to having failed 3 time(s) in a row with the following description: description-1 and the following conditions: 🔴 [CONNECTED] == true 🔴 [STATUS] == 200 "}`, + ExpectedBody: `{"alert_source_config_id":"some-id","status":"firing","title":"Gatus: endpoint-name","description":"An alert has been triggered due to having failed 3 time(s) in a row with the following description: description-1 and the following conditions: 🔴 [CONNECTED] == true 🔴 [STATUS] == 200 "}`, }, { Name: "resolved", Provider: AlertProvider{DefaultConfig: Config{URL: restAPIUrl + "some-id", AuthToken: "some-token"}}, Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: true, - ExpectedBody: `{"alert_source_config_id":"some-id","status":"resolved","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been resolved after passing successfully 5 time(s) in a row with the following description: description-2 and the following conditions: 🟢 [CONNECTED] == true 🟢 [STATUS] == 200 "}`, + ExpectedBody: `{"alert_source_config_id":"some-id","status":"resolved","title":"Gatus: endpoint-name","description":"An alert has been resolved after passing successfully 5 time(s) in a row with the following description: description-2 and the following conditions: 🟢 [CONNECTED] == true 🟢 [STATUS] == 200 "}`, + }, + { + Name: "resolved-with-metadata-source-url", + Provider: AlertProvider{DefaultConfig: Config{URL: restAPIUrl + "some-id", AuthToken: "some-token", Metadata: map[string]interface{}{"service": "some-service", "team": "very-core"}, SourceURL: "some-source-url"}}, + Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3}, + Resolved: true, + ExpectedBody: `{"alert_source_config_id":"some-id","status":"resolved","title":"Gatus: endpoint-name","description":"An alert has been resolved after passing successfully 5 time(s) in a row with the following description: description-2 and the following conditions: 🟢 [CONNECTED] == true 🟢 [STATUS] == 200 ","source_url":"some-source-url","metadata":{"service":"some-service","team":"very-core"}}`, }, { Name: "group-override", Provider: AlertProvider{DefaultConfig: Config{URL: restAPIUrl + "some-id", AuthToken: "some-token"}, Overrides: []Override{{Group: "g", Config: Config{URL: restAPIUrl + "different-id", AuthToken: "some-token"}}}}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: false, - ExpectedBody: `{"alert_source_config_id":"different-id","status":"firing","title":"Gatus: endpoint-name","deduplication_key":"","description":"An alert has been triggered due to having failed 3 time(s) in a row with the following description: description-1 and the following conditions: 🔴 [CONNECTED] == true 🔴 [STATUS] == 200 "}`, + ExpectedBody: `{"alert_source_config_id":"different-id","status":"firing","title":"Gatus: endpoint-name","description":"An alert has been triggered due to having failed 3 time(s) in a row with the following description: description-1 and the following conditions: 🔴 [CONNECTED] == true 🔴 [STATUS] == 200 "}`, }, } From 851ffa385daf7edf8e221815d1143aed3cbdcb33 Mon Sep 17 00:00:00 2001 From: rani Date: Thu, 6 Feb 2025 07:59:44 +0200 Subject: [PATCH 18/24] Refactor: chaned variable naming --- alerting/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alerting/config.go b/alerting/config.go index 9c60fef503..714b6c46eb 100644 --- a/alerting/config.go +++ b/alerting/config.go @@ -63,7 +63,7 @@ type Config struct { Gotify *gotify.AlertProvider `yaml:"gotify,omitempty"` // IncidentIo is the configuration for the incident.io alerting provider - IncidentIo *incidentio.AlertProvider `yaml:"incident-io,omitempty"` + IncidentIO *incidentio.AlertProvider `yaml:"incident-io,omitempty"` // JetBrainsSpace is the configuration for the jetbrains space alerting provider JetBrainsSpace *jetbrainsspace.AlertProvider `yaml:"jetbrainsspace,omitempty"` From 70e8ad5bbbcc852888d9451c3de589d8d6b462b5 Mon Sep 17 00:00:00 2001 From: TwiN Date: Thu, 6 Feb 2025 18:58:58 -0500 Subject: [PATCH 19/24] Update alerting/config.go --- alerting/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alerting/config.go b/alerting/config.go index 714b6c46eb..8378a840a7 100644 --- a/alerting/config.go +++ b/alerting/config.go @@ -62,7 +62,7 @@ type Config struct { // Gotify is the configuration for the gotify alerting provider Gotify *gotify.AlertProvider `yaml:"gotify,omitempty"` - // IncidentIo is the configuration for the incident.io alerting provider + // IncidentIO is the configuration for the incident-io alerting provider IncidentIO *incidentio.AlertProvider `yaml:"incident-io,omitempty"` // JetBrainsSpace is the configuration for the jetbrains space alerting provider From dc03d61b0eddf317c825bc2ab795fee417231c1b Mon Sep 17 00:00:00 2001 From: TwiN Date: Thu, 6 Feb 2025 18:59:35 -0500 Subject: [PATCH 20/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7555b6d175..534da8d7fc 100644 --- a/README.md +++ b/README.md @@ -580,7 +580,7 @@ endpoints: | `alerting.gitlab` | Configuration for alerts of type `gitlab`.
See [Configuring GitLab alerts](#configuring-gitlab-alerts). | `{}` | | `alerting.googlechat` | Configuration for alerts of type `googlechat`.
See [Configuring Google Chat alerts](#configuring-google-chat-alerts). | `{}` | | `alerting.gotify` | Configuration for alerts of type `gotify`.
See [Configuring Gotify alerts](#configuring-gotify-alerts). | `{}` | -| `alerting.incident-io` | Configuration for alerts of type `incidentio`.
See [Configuring Incident.io alerts](#configuring-incidentio-alerts). | `{}` | +| `alerting.incident-io` | Configuration for alerts of type `incident-io`.
See [Configuring Incident.io alerts](#configuring-incidentio-alerts). | `{}` | | `alerting.jetbrainsspace` | Configuration for alerts of type `jetbrainsspace`.
See [Configuring JetBrains Space alerts](#configuring-jetbrains-space-alerts). | `{}` | | `alerting.matrix` | Configuration for alerts of type `matrix`.
See [Configuring Matrix alerts](#configuring-matrix-alerts). | `{}` | | `alerting.mattermost` | Configuration for alerts of type `mattermost`.
See [Configuring Mattermost alerts](#configuring-mattermost-alerts). | `{}` | From 668421876e4a5e7e93418e639e87a4c3b019a7a3 Mon Sep 17 00:00:00 2001 From: TwiN Date: Thu, 6 Feb 2025 19:02:06 -0500 Subject: [PATCH 21/24] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 534da8d7fc..9defac2df4 100644 --- a/README.md +++ b/README.md @@ -938,9 +938,10 @@ endpoints: ``` in order to get the required alert source config id and authentication token, you must configure an HTTP alert source. -> **_NOTE:_** the source config id is of the form { api.incident.io/v2/alert_events/http/{CONFIG-SOURCE-ID}} +> **_NOTE:_** the source config id is of the form `api.incident.io/v2/alert_events/http/$ID` and the token is expected to be passed as a bearer token like so: `Authorization: Bearer $TOKEN` -> **_NOTE:_** the auth token is of the form { "Authorization": "Bearer {AUTH-TOKEN}" } + +> **_NOTE:_** ``` #### Configuring JetBrains Space alerts | Parameter | Description | Default | From 0b3bda0b2841a21139226ab64d1e5cf07a64bb18 Mon Sep 17 00:00:00 2001 From: TwiN Date: Thu, 6 Feb 2025 19:02:34 -0500 Subject: [PATCH 22/24] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9defac2df4..4c64db133e 100644 --- a/README.md +++ b/README.md @@ -1296,7 +1296,6 @@ Here's an example of what the notifications look like: ![Slack notifications](.github/assets/slack-alerts.png) - #### Configuring Teams alerts *(Deprecated)* > [!CAUTION] From a144588ccc391563daedb25f35b7fb4323f7f9cb Mon Sep 17 00:00:00 2001 From: TwiN Date: Thu, 6 Feb 2025 19:05:39 -0500 Subject: [PATCH 23/24] Apply suggestions from code review --- alerting/alert/type.go | 2 +- alerting/provider/incidentio/incident_io.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/alerting/alert/type.go b/alerting/alert/type.go index fa20fd44b1..3875d4d8b5 100644 --- a/alerting/alert/type.go +++ b/alerting/alert/type.go @@ -32,7 +32,7 @@ const ( // TypeGotify is the Type for the gotify alerting provider TypeGotify Type = "gotify" - // TypeIncidentio is the Type for the incident.io alerting provider + // TypeIncidentIO is the Type for the incident-io alerting provider TypeIncidentIO Type = "incident-io" // TypeJetBrainsSpace is the Type for the jetbrains alerting provider diff --git a/alerting/provider/incidentio/incident_io.go b/alerting/provider/incidentio/incident_io.go index e8333f7790..b1af5b9814 100644 --- a/alerting/provider/incidentio/incident_io.go +++ b/alerting/provider/incidentio/incident_io.go @@ -22,9 +22,9 @@ const ( ) var ( - ErrURLNotSet = errors.New("url not set.") + ErrURLNotSet = errors.New("url not set") ErrDuplicateGroupOverride = errors.New("duplicate group override") - ErrAuthTokenNotSet = errors.New("auth-token not set.") + ErrAuthTokenNotSet = errors.New("auth-token not set") ) type Config struct { From 6c516f994becfa268619f878d736708c1b0ff64f Mon Sep 17 00:00:00 2001 From: rani Date: Fri, 7 Feb 2025 02:10:54 +0200 Subject: [PATCH 24/24] Refactor: sort var by abc --- alerting/provider/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alerting/provider/provider.go b/alerting/provider/provider.go index a11d1635b3..94e600f00c 100644 --- a/alerting/provider/provider.go +++ b/alerting/provider/provider.go @@ -105,6 +105,7 @@ var ( _ Config[github.Config] = (*github.Config)(nil) _ Config[gitlab.Config] = (*gitlab.Config)(nil) _ Config[googlechat.Config] = (*googlechat.Config)(nil) + _ Config[incidentio.Config] = (*incidentio.Config)(nil) _ Config[jetbrainsspace.Config] = (*jetbrainsspace.Config)(nil) _ Config[matrix.Config] = (*matrix.Config)(nil) _ Config[mattermost.Config] = (*mattermost.Config)(nil) @@ -119,5 +120,4 @@ var ( _ Config[telegram.Config] = (*telegram.Config)(nil) _ Config[twilio.Config] = (*twilio.Config)(nil) _ Config[zulip.Config] = (*zulip.Config)(nil) - _ Config[incidentio.Config] = (*incidentio.Config)(nil) )