2021-05-19 04:29:15 +02:00
|
|
|
package alert
|
2020-08-20 01:41:01 +02:00
|
|
|
|
2021-12-12 22:28:24 +01:00
|
|
|
import (
|
2024-05-16 03:29:45 +02:00
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/hex"
|
2021-12-12 22:28:24 +01:00
|
|
|
"errors"
|
2024-05-16 03:29:45 +02:00
|
|
|
"strconv"
|
2021-12-12 22:28:24 +01:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrAlertWithInvalidDescription is the error with which Gatus will panic if an alert has an invalid character
|
|
|
|
ErrAlertWithInvalidDescription = errors.New("alert description must not have \" or \\")
|
|
|
|
)
|
|
|
|
|
2024-05-10 04:56:16 +02:00
|
|
|
// Alert is a endpoint.Endpoint's alert configuration
|
2020-08-20 01:41:01 +02:00
|
|
|
type Alert struct {
|
2021-05-16 03:31:32 +02:00
|
|
|
// Type of alert (required)
|
2021-05-19 04:29:15 +02:00
|
|
|
Type Type `yaml:"type"`
|
2020-08-22 20:15:44 +02:00
|
|
|
|
2021-10-23 22:47:12 +02:00
|
|
|
// Enabled defines whether the alert is enabled
|
2021-05-16 03:31:32 +02:00
|
|
|
//
|
2022-12-06 05:15:19 +01:00
|
|
|
// Use Alert.IsEnabled() to retrieve the value of this field.
|
|
|
|
//
|
2021-05-16 03:31:32 +02:00
|
|
|
// This is a pointer, because it is populated by YAML and we need to know whether it was explicitly set to a value
|
|
|
|
// or not for provider.ParseWithDefaultAlert to work.
|
2021-11-18 06:11:32 +01:00
|
|
|
Enabled *bool `yaml:"enabled,omitempty"`
|
2020-08-22 20:15:44 +02:00
|
|
|
|
2020-09-17 02:22:33 +02:00
|
|
|
// FailureThreshold is the number of failures in a row needed before triggering the alert
|
|
|
|
FailureThreshold int `yaml:"failure-threshold"`
|
2020-08-22 20:15:44 +02:00
|
|
|
|
2024-05-16 03:29:45 +02:00
|
|
|
// SuccessThreshold defines how many successful executions must happen in a row before an ongoing incident is marked as resolved
|
|
|
|
SuccessThreshold int `yaml:"success-threshold"`
|
|
|
|
|
2020-08-22 20:15:44 +02:00
|
|
|
// Description of the alert. Will be included in the alert sent.
|
2021-05-16 03:31:32 +02:00
|
|
|
//
|
|
|
|
// This is a pointer, because it is populated by YAML and we need to know whether it was explicitly set to a value
|
|
|
|
// or not for provider.ParseWithDefaultAlert to work.
|
|
|
|
Description *string `yaml:"description"`
|
2020-09-05 00:23:56 +02:00
|
|
|
|
|
|
|
// SendOnResolved defines whether to send a second notification when the issue has been resolved
|
2021-05-16 03:31:32 +02:00
|
|
|
//
|
|
|
|
// This is a pointer, because it is populated by YAML and we need to know whether it was explicitly set to a value
|
|
|
|
// or not for provider.ParseWithDefaultAlert to work. Use Alert.IsSendingOnResolved() for a non-pointer
|
|
|
|
SendOnResolved *bool `yaml:"send-on-resolved"`
|
2020-09-17 01:26:19 +02:00
|
|
|
|
|
|
|
// ResolveKey is an optional field that is used by some providers (i.e. PagerDuty's dedup_key) to resolve
|
|
|
|
// ongoing/triggered incidents
|
2021-11-18 06:11:32 +01:00
|
|
|
ResolveKey string `yaml:"-"`
|
2020-09-17 01:26:19 +02:00
|
|
|
|
|
|
|
// Triggered is used to determine whether an alert has been triggered. When an alert is resolved, this value
|
|
|
|
// should be set back to false. It is used to prevent the same alert from going out twice.
|
2021-01-21 22:14:32 +01:00
|
|
|
//
|
|
|
|
// This value should only be modified if the provider.AlertProvider's Send function does not return an error for an
|
|
|
|
// alert that hasn't been triggered yet. This doubles as a lazy retry. The reason why this behavior isn't also
|
|
|
|
// applied for alerts that are already triggered and has become "healthy" again is to prevent a case where, for
|
|
|
|
// some reason, the alert provider always returns errors when trying to send the resolved notification
|
|
|
|
// (SendOnResolved).
|
2021-11-18 06:11:32 +01:00
|
|
|
Triggered bool `yaml:"-"`
|
2020-08-20 01:41:01 +02:00
|
|
|
}
|
|
|
|
|
2021-12-12 22:28:24 +01:00
|
|
|
// ValidateAndSetDefaults validates the alert's configuration and sets the default value of fields that have one
|
|
|
|
func (alert *Alert) ValidateAndSetDefaults() error {
|
|
|
|
if alert.FailureThreshold <= 0 {
|
|
|
|
alert.FailureThreshold = 3
|
|
|
|
}
|
|
|
|
if alert.SuccessThreshold <= 0 {
|
|
|
|
alert.SuccessThreshold = 2
|
|
|
|
}
|
|
|
|
if strings.ContainsAny(alert.GetDescription(), "\"\\") {
|
|
|
|
return ErrAlertWithInvalidDescription
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-05-16 03:31:32 +02:00
|
|
|
// GetDescription retrieves the description of the alert
|
2024-04-09 03:00:40 +02:00
|
|
|
func (alert *Alert) GetDescription() string {
|
2021-05-16 03:31:32 +02:00
|
|
|
if alert.Description == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return *alert.Description
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsEnabled returns whether an alert is enabled or not
|
2022-12-06 05:15:19 +01:00
|
|
|
// Returns true if not set
|
2024-04-09 03:00:40 +02:00
|
|
|
func (alert *Alert) IsEnabled() bool {
|
2021-05-16 03:31:32 +02:00
|
|
|
if alert.Enabled == nil {
|
2022-12-06 05:15:19 +01:00
|
|
|
return true
|
2021-05-16 03:31:32 +02:00
|
|
|
}
|
|
|
|
return *alert.Enabled
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsSendingOnResolved returns whether an alert is sending on resolve or not
|
2024-04-09 03:00:40 +02:00
|
|
|
func (alert *Alert) IsSendingOnResolved() bool {
|
2021-05-16 03:31:32 +02:00
|
|
|
if alert.SendOnResolved == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return *alert.SendOnResolved
|
|
|
|
}
|
2024-05-16 03:29:45 +02:00
|
|
|
|
|
|
|
// Checksum returns a checksum of the alert
|
|
|
|
// Used to determine which persisted triggered alert should be deleted on application start
|
|
|
|
func (alert *Alert) Checksum() string {
|
|
|
|
hash := sha256.New()
|
|
|
|
hash.Write([]byte(string(alert.Type) + "_" +
|
|
|
|
strconv.FormatBool(alert.IsEnabled()) + "_" +
|
|
|
|
strconv.FormatBool(alert.IsSendingOnResolved()) + "_" +
|
|
|
|
strconv.Itoa(alert.SuccessThreshold) + "_" +
|
|
|
|
strconv.Itoa(alert.FailureThreshold) + "_" +
|
|
|
|
alert.GetDescription()),
|
|
|
|
)
|
|
|
|
return hex.EncodeToString(hash.Sum(nil))
|
|
|
|
}
|