Reset
This commit is contained in:
284
alerting/provider/opsgenie/opsgenie.go
Normal file
284
alerting/provider/opsgenie/opsgenie.go
Normal file
@ -0,0 +1,284 @@
|
||||
package opsgenie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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 (
|
||||
restAPI = "https://api.opsgenie.com/v2/alerts"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAPIKeyNotSet = errors.New("api-key not set")
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// APIKey to use for
|
||||
APIKey string `yaml:"api-key"`
|
||||
|
||||
// Priority to be used in Opsgenie alert payload
|
||||
//
|
||||
// default: P1
|
||||
Priority string `yaml:"priority"`
|
||||
|
||||
// Source define source to be used in Opsgenie alert payload
|
||||
//
|
||||
// default: gatus
|
||||
Source string `yaml:"source"`
|
||||
|
||||
// EntityPrefix is a prefix to be used in entity argument in Opsgenie alert payload
|
||||
//
|
||||
// default: gatus-
|
||||
EntityPrefix string `yaml:"entity-prefix"`
|
||||
|
||||
//AliasPrefix is a prefix to be used in alias argument in Opsgenie alert payload
|
||||
//
|
||||
// default: gatus-healthcheck-
|
||||
AliasPrefix string `yaml:"alias-prefix"`
|
||||
|
||||
// Tags to be used in Opsgenie alert payload
|
||||
//
|
||||
// default: []
|
||||
Tags []string `yaml:"tags"`
|
||||
}
|
||||
|
||||
func (cfg *Config) Validate() error {
|
||||
if len(cfg.APIKey) == 0 {
|
||||
return ErrAPIKeyNotSet
|
||||
}
|
||||
if len(cfg.Source) == 0 {
|
||||
cfg.Source = "gatus"
|
||||
}
|
||||
if len(cfg.EntityPrefix) == 0 {
|
||||
cfg.EntityPrefix = "gatus-"
|
||||
}
|
||||
if len(cfg.AliasPrefix) == 0 {
|
||||
cfg.AliasPrefix = "gatus-healthcheck-"
|
||||
}
|
||||
if len(cfg.Priority) == 0 {
|
||||
cfg.Priority = "P1"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Config) Merge(override *Config) {
|
||||
if len(override.APIKey) > 0 {
|
||||
cfg.APIKey = override.APIKey
|
||||
}
|
||||
if len(override.Priority) > 0 {
|
||||
cfg.Priority = override.Priority
|
||||
}
|
||||
if len(override.Source) > 0 {
|
||||
cfg.Source = override.Source
|
||||
}
|
||||
if len(override.EntityPrefix) > 0 {
|
||||
cfg.EntityPrefix = override.EntityPrefix
|
||||
}
|
||||
if len(override.AliasPrefix) > 0 {
|
||||
cfg.AliasPrefix = override.AliasPrefix
|
||||
}
|
||||
if len(override.Tags) > 0 {
|
||||
cfg.Tags = override.Tags
|
||||
}
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
// Validate the provider's configuration
|
||||
func (provider *AlertProvider) Validate() error {
|
||||
return provider.DefaultConfig.Validate()
|
||||
}
|
||||
|
||||
// Send an alert using the provider
|
||||
//
|
||||
// Relevant: https://docs.opsgenie.com/docs/alert-api
|
||||
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
|
||||
}
|
||||
err = provider.sendAlertRequest(cfg, ep, alert, result, resolved)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resolved {
|
||||
err = provider.closeAlert(cfg, ep, alert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if alert.IsSendingOnResolved() {
|
||||
if resolved {
|
||||
// The alert has been resolved and there's no error, so we can clear the alert's ResolveKey
|
||||
alert.ResolveKey = ""
|
||||
} else {
|
||||
alert.ResolveKey = cfg.AliasPrefix + buildKey(ep)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *AlertProvider) sendAlertRequest(cfg *Config, ep *endpoint.Endpoint, alert *alert.Alert, result *endpoint.Result, resolved bool) error {
|
||||
payload := provider.buildCreateRequestBody(cfg, ep, alert, result, resolved)
|
||||
return provider.sendRequest(cfg, restAPI, http.MethodPost, payload)
|
||||
}
|
||||
|
||||
func (provider *AlertProvider) closeAlert(cfg *Config, ep *endpoint.Endpoint, alert *alert.Alert) error {
|
||||
payload := provider.buildCloseRequestBody(ep, alert)
|
||||
url := restAPI + "/" + cfg.AliasPrefix + buildKey(ep) + "/close?identifierType=alias"
|
||||
return provider.sendRequest(cfg, url, http.MethodPost, payload)
|
||||
}
|
||||
|
||||
func (provider *AlertProvider) sendRequest(cfg *Config, url, method string, payload interface{}) error {
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error build alert with payload %v: %w", payload, err)
|
||||
}
|
||||
request, err := http.NewRequest(method, url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.Header.Set("Authorization", "GenieKey "+cfg.APIKey)
|
||||
response, err := client.GetHTTPClient(nil).Do(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode > 399 {
|
||||
rBody, _ := io.ReadAll(response.Body)
|
||||
return fmt.Errorf("call to provider alert returned status code %d: %s", response.StatusCode, string(rBody))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *AlertProvider) buildCreateRequestBody(cfg *Config, ep *endpoint.Endpoint, alert *alert.Alert, result *endpoint.Result, resolved bool) alertCreateRequest {
|
||||
var message, description string
|
||||
if resolved {
|
||||
message = fmt.Sprintf("RESOLVED: %s - %s", ep.Name, alert.GetDescription())
|
||||
description = fmt.Sprintf("An alert for *%s* has been resolved after passing successfully %d time(s) in a row", ep.DisplayName(), alert.SuccessThreshold)
|
||||
} else {
|
||||
message = fmt.Sprintf("%s - %s", ep.Name, alert.GetDescription())
|
||||
description = fmt.Sprintf("An alert for *%s* has been triggered due to having failed %d time(s) in a row", ep.DisplayName(), alert.FailureThreshold)
|
||||
}
|
||||
if ep.Group != "" {
|
||||
message = fmt.Sprintf("[%s] %s", ep.Group, message)
|
||||
}
|
||||
var formattedConditionResults string
|
||||
for _, conditionResult := range result.ConditionResults {
|
||||
var prefix string
|
||||
if conditionResult.Success {
|
||||
prefix = "▣"
|
||||
} else {
|
||||
prefix = "▢"
|
||||
}
|
||||
formattedConditionResults += fmt.Sprintf("%s - `%s`\n", prefix, conditionResult.Condition)
|
||||
}
|
||||
description = description + "\n" + formattedConditionResults
|
||||
key := buildKey(ep)
|
||||
details := map[string]string{
|
||||
"endpoint:url": ep.URL,
|
||||
"endpoint:group": ep.Group,
|
||||
"result:hostname": result.Hostname,
|
||||
"result:ip": result.IP,
|
||||
"result:dns_code": result.DNSRCode,
|
||||
"result:errors": strings.Join(result.Errors, ","),
|
||||
}
|
||||
for k, v := range details {
|
||||
if v == "" {
|
||||
delete(details, k)
|
||||
}
|
||||
}
|
||||
if result.HTTPStatus > 0 {
|
||||
details["result:http_status"] = strconv.Itoa(result.HTTPStatus)
|
||||
}
|
||||
return alertCreateRequest{
|
||||
Message: message,
|
||||
Description: description,
|
||||
Source: cfg.Source,
|
||||
Priority: cfg.Priority,
|
||||
Alias: cfg.AliasPrefix + key,
|
||||
Entity: cfg.EntityPrefix + key,
|
||||
Tags: cfg.Tags,
|
||||
Details: details,
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *AlertProvider) buildCloseRequestBody(ep *endpoint.Endpoint, alert *alert.Alert) alertCloseRequest {
|
||||
return alertCloseRequest{
|
||||
Source: buildKey(ep),
|
||||
Note: fmt.Sprintf("RESOLVED: %s - %s", ep.Name, alert.GetDescription()),
|
||||
}
|
||||
}
|
||||
|
||||
// GetDefaultAlert returns the provider's default alert configuration
|
||||
func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
|
||||
return provider.DefaultAlert
|
||||
}
|
||||
|
||||
// GetConfig returns the configuration for the provider with the overrides applied
|
||||
func (provider *AlertProvider) GetConfig(group string, alert *alert.Alert) (*Config, error) {
|
||||
cfg := provider.DefaultConfig
|
||||
// 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
|
||||
}
|
||||
|
||||
func buildKey(ep *endpoint.Endpoint) string {
|
||||
name := toKebabCase(ep.Name)
|
||||
if ep.Group == "" {
|
||||
return name
|
||||
}
|
||||
return toKebabCase(ep.Group) + "-" + name
|
||||
}
|
||||
|
||||
func toKebabCase(val string) string {
|
||||
return strings.ToLower(strings.ReplaceAll(val, " ", "-"))
|
||||
}
|
||||
|
||||
type alertCreateRequest struct {
|
||||
Message string `json:"message"`
|
||||
Priority string `json:"priority"`
|
||||
Source string `json:"source"`
|
||||
Entity string `json:"entity"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Details map[string]string `json:"details"`
|
||||
}
|
||||
|
||||
type alertCloseRequest struct {
|
||||
Source string `json:"source"`
|
||||
Note string `json:"note"`
|
||||
}
|
361
alerting/provider/opsgenie/opsgenie_test.go
Normal file
361
alerting/provider/opsgenie/opsgenie_test.go
Normal file
@ -0,0 +1,361 @@
|
||||
package opsgenie
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"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) {
|
||||
invalidProvider := AlertProvider{DefaultConfig: Config{APIKey: ""}}
|
||||
if err := invalidProvider.Validate(); err == nil {
|
||||
t.Error("provider shouldn't have been valid")
|
||||
}
|
||||
validProvider := AlertProvider{DefaultConfig: Config{APIKey: "00000000-0000-0000-0000-000000000000"}}
|
||||
if err := validProvider.Validate(); err != nil {
|
||||
t.Error("provider should've been valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertProvider_Send(t *testing.T) {
|
||||
defer client.InjectHTTPClient(nil)
|
||||
description := "my bad alert description"
|
||||
scenarios := []struct {
|
||||
Name string
|
||||
Provider AlertProvider
|
||||
Alert alert.Alert
|
||||
Resolved bool
|
||||
MockRoundTripper test.MockRoundTripper
|
||||
ExpectedError bool
|
||||
}{
|
||||
{
|
||||
Name: "triggered",
|
||||
Provider: AlertProvider{DefaultConfig: Config{APIKey: "00000000-0000-0000-0000-000000000000"}},
|
||||
Alert: alert.Alert{Description: &description, SuccessThreshold: 1, FailureThreshold: 1},
|
||||
Resolved: false,
|
||||
ExpectedError: false,
|
||||
MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
|
||||
return &http.Response{StatusCode: http.StatusOK, Body: http.NoBody}
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "triggered-error",
|
||||
Provider: AlertProvider{DefaultConfig: Config{APIKey: "00000000-0000-0000-0000-000000000000"}},
|
||||
Alert: alert.Alert{Description: &description, SuccessThreshold: 5, FailureThreshold: 3},
|
||||
Resolved: false,
|
||||
ExpectedError: true,
|
||||
MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
|
||||
return &http.Response{StatusCode: http.StatusInternalServerError, Body: http.NoBody}
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "resolved",
|
||||
Provider: AlertProvider{DefaultConfig: Config{APIKey: "00000000-0000-0000-0000-000000000000"}},
|
||||
Alert: alert.Alert{Description: &description, SuccessThreshold: 5, FailureThreshold: 3},
|
||||
Resolved: true,
|
||||
ExpectedError: false,
|
||||
MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
|
||||
return &http.Response{StatusCode: http.StatusOK, Body: http.NoBody}
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "resolved-error",
|
||||
Provider: AlertProvider{DefaultConfig: Config{APIKey: "00000000-0000-0000-0000-000000000000"}},
|
||||
Alert: alert.Alert{Description: &description, SuccessThreshold: 5, FailureThreshold: 3},
|
||||
Resolved: true,
|
||||
ExpectedError: true,
|
||||
MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
|
||||
return &http.Response{StatusCode: http.StatusInternalServerError, Body: http.NoBody}
|
||||
}),
|
||||
},
|
||||
}
|
||||
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_buildCreateRequestBody(t *testing.T) {
|
||||
t.Parallel()
|
||||
description := "alert description"
|
||||
scenarios := []struct {
|
||||
Name string
|
||||
Provider *AlertProvider
|
||||
Alert *alert.Alert
|
||||
Endpoint *endpoint.Endpoint
|
||||
Result *endpoint.Result
|
||||
Resolved bool
|
||||
want alertCreateRequest
|
||||
}{
|
||||
{
|
||||
Name: "missing all params (unresolved)",
|
||||
Provider: &AlertProvider{DefaultConfig: Config{APIKey: "00000000-0000-0000-0000-000000000000"}},
|
||||
Alert: &alert.Alert{},
|
||||
Endpoint: &endpoint.Endpoint{},
|
||||
Result: &endpoint.Result{},
|
||||
Resolved: false,
|
||||
want: alertCreateRequest{
|
||||
Message: " - ",
|
||||
Priority: "P1",
|
||||
Source: "gatus",
|
||||
Entity: "gatus-",
|
||||
Alias: "gatus-healthcheck-",
|
||||
Description: "An alert for ** has been triggered due to having failed 0 time(s) in a row\n",
|
||||
Tags: nil,
|
||||
Details: map[string]string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "missing all params (resolved)",
|
||||
Provider: &AlertProvider{DefaultConfig: Config{APIKey: "00000000-0000-0000-0000-000000000000"}},
|
||||
Alert: &alert.Alert{},
|
||||
Endpoint: &endpoint.Endpoint{},
|
||||
Result: &endpoint.Result{},
|
||||
Resolved: true,
|
||||
want: alertCreateRequest{
|
||||
Message: "RESOLVED: - ",
|
||||
Priority: "P1",
|
||||
Source: "gatus",
|
||||
Entity: "gatus-",
|
||||
Alias: "gatus-healthcheck-",
|
||||
Description: "An alert for ** has been resolved after passing successfully 0 time(s) in a row\n",
|
||||
Tags: nil,
|
||||
Details: map[string]string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "with default options (unresolved)",
|
||||
Provider: &AlertProvider{DefaultConfig: Config{APIKey: "00000000-0000-0000-0000-000000000000"}},
|
||||
Alert: &alert.Alert{
|
||||
Description: &description,
|
||||
FailureThreshold: 3,
|
||||
},
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
Name: "my super app",
|
||||
},
|
||||
Result: &endpoint.Result{
|
||||
ConditionResults: []*endpoint.ConditionResult{
|
||||
{
|
||||
Condition: "[STATUS] == 200",
|
||||
Success: true,
|
||||
},
|
||||
{
|
||||
Condition: "[BODY] == OK",
|
||||
Success: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
Resolved: false,
|
||||
want: alertCreateRequest{
|
||||
Message: "my super app - " + description,
|
||||
Priority: "P1",
|
||||
Source: "gatus",
|
||||
Entity: "gatus-my-super-app",
|
||||
Alias: "gatus-healthcheck-my-super-app",
|
||||
Description: "An alert for *my super app* has been triggered due to having failed 3 time(s) in a row\n▣ - `[STATUS] == 200`\n▢ - `[BODY] == OK`\n",
|
||||
Tags: nil,
|
||||
Details: map[string]string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "with custom options (resolved)",
|
||||
Provider: &AlertProvider{
|
||||
DefaultConfig: Config{
|
||||
Priority: "P5",
|
||||
EntityPrefix: "oompa-",
|
||||
AliasPrefix: "loompa-",
|
||||
Source: "gatus-hc",
|
||||
Tags: []string{"do-ba-dee-doo"},
|
||||
},
|
||||
},
|
||||
Alert: &alert.Alert{
|
||||
Description: &description,
|
||||
SuccessThreshold: 4,
|
||||
},
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
Name: "my mega app",
|
||||
},
|
||||
Result: &endpoint.Result{
|
||||
ConditionResults: []*endpoint.ConditionResult{
|
||||
{
|
||||
Condition: "[STATUS] == 200",
|
||||
Success: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Resolved: true,
|
||||
want: alertCreateRequest{
|
||||
Message: "RESOLVED: my mega app - " + description,
|
||||
Priority: "P5",
|
||||
Source: "gatus-hc",
|
||||
Entity: "oompa-my-mega-app",
|
||||
Alias: "loompa-my-mega-app",
|
||||
Description: "An alert for *my mega app* has been resolved after passing successfully 4 time(s) in a row\n▣ - `[STATUS] == 200`\n",
|
||||
Tags: []string{"do-ba-dee-doo"},
|
||||
Details: map[string]string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "with default options and details (unresolved)",
|
||||
Provider: &AlertProvider{
|
||||
DefaultConfig: Config{Tags: []string{"foo"}, APIKey: "00000000-0000-0000-0000-000000000000"},
|
||||
},
|
||||
Alert: &alert.Alert{
|
||||
Description: &description,
|
||||
FailureThreshold: 6,
|
||||
},
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
Name: "my app",
|
||||
Group: "end game",
|
||||
URL: "https://my.go/app",
|
||||
},
|
||||
Result: &endpoint.Result{
|
||||
HTTPStatus: 400,
|
||||
Hostname: "my.go",
|
||||
Errors: []string{"error 01", "error 02"},
|
||||
Success: false,
|
||||
ConditionResults: []*endpoint.ConditionResult{
|
||||
{
|
||||
Condition: "[STATUS] == 200",
|
||||
Success: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
Resolved: false,
|
||||
want: alertCreateRequest{
|
||||
Message: "[end game] my app - " + description,
|
||||
Priority: "P1",
|
||||
Source: "gatus",
|
||||
Entity: "gatus-end-game-my-app",
|
||||
Alias: "gatus-healthcheck-end-game-my-app",
|
||||
Description: "An alert for *end game/my app* has been triggered due to having failed 6 time(s) in a row\n▢ - `[STATUS] == 200`\n",
|
||||
Tags: []string{"foo"},
|
||||
Details: map[string]string{
|
||||
"endpoint:url": "https://my.go/app",
|
||||
"endpoint:group": "end game",
|
||||
"result:hostname": "my.go",
|
||||
"result:errors": "error 01,error 02",
|
||||
"result:http_status": "400",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
actual := scenario
|
||||
t.Run(actual.Name, func(t *testing.T) {
|
||||
_ = scenario.Provider.Validate()
|
||||
if got := actual.Provider.buildCreateRequestBody(&scenario.Provider.DefaultConfig, actual.Endpoint, actual.Alert, actual.Result, actual.Resolved); !reflect.DeepEqual(got, actual.want) {
|
||||
t.Errorf("got:\n%v\nwant:\n%v", got, actual.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertProvider_buildCloseRequestBody(t *testing.T) {
|
||||
t.Parallel()
|
||||
description := "alert description"
|
||||
scenarios := []struct {
|
||||
Name string
|
||||
Provider *AlertProvider
|
||||
Alert *alert.Alert
|
||||
Endpoint *endpoint.Endpoint
|
||||
want alertCloseRequest
|
||||
}{
|
||||
{
|
||||
Name: "Missing all values",
|
||||
Provider: &AlertProvider{},
|
||||
Alert: &alert.Alert{},
|
||||
Endpoint: &endpoint.Endpoint{},
|
||||
want: alertCloseRequest{
|
||||
Source: "",
|
||||
Note: "RESOLVED: - ",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Basic values",
|
||||
Provider: &AlertProvider{},
|
||||
Alert: &alert.Alert{
|
||||
Description: &description,
|
||||
},
|
||||
Endpoint: &endpoint.Endpoint{
|
||||
Name: "endpoint name",
|
||||
},
|
||||
want: alertCloseRequest{
|
||||
Source: "endpoint-name",
|
||||
Note: "RESOLVED: endpoint name - alert description",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
actual := scenario
|
||||
t.Run(actual.Name, func(t *testing.T) {
|
||||
if got := actual.Provider.buildCloseRequestBody(actual.Endpoint, actual.Alert); !reflect.DeepEqual(got, actual.want) {
|
||||
t.Errorf("buildCloseRequestBody() = %v, want %v", got, actual.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertProvider_GetConfig(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
Name string
|
||||
Provider AlertProvider
|
||||
InputAlert alert.Alert
|
||||
ExpectedOutput Config
|
||||
}{
|
||||
{
|
||||
Name: "provider-no-override-should-default",
|
||||
Provider: AlertProvider{
|
||||
DefaultConfig: Config{APIKey: "00000000-0000-0000-0000-000000000000"},
|
||||
},
|
||||
InputAlert: alert.Alert{},
|
||||
ExpectedOutput: Config{APIKey: "00000000-0000-0000-0000-000000000000"},
|
||||
},
|
||||
{
|
||||
Name: "provider-with-alert-override--alert-override-should-take-precedence",
|
||||
Provider: AlertProvider{
|
||||
DefaultConfig: Config{APIKey: "00000000-0000-0000-0000-000000000000"},
|
||||
},
|
||||
InputAlert: alert.Alert{ProviderOverride: map[string]any{"api-key": "00000000-0000-0000-0000-000000000001"}},
|
||||
ExpectedOutput: Config{APIKey: "00000000-0000-0000-0000-000000000001"},
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
got, err := scenario.Provider.GetConfig("", &scenario.InputAlert)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if got.APIKey != scenario.ExpectedOutput.APIKey {
|
||||
t.Errorf("expected APIKey to be %s, got %s", scenario.ExpectedOutput.APIKey, got.APIKey)
|
||||
}
|
||||
// Test ValidateOverrides as well, since it really just calls GetConfig
|
||||
if err = scenario.Provider.ValidateOverrides("", &scenario.InputAlert); err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user