From 15a4133502fafdcdd5ffdffbab0cca5983284098 Mon Sep 17 00:00:00 2001 From: TwiN Date: Thu, 25 Sep 2025 16:24:15 -0400 Subject: [PATCH] fix(alerting): Limit minimum-reminder-interval to >5m (#1290) --- README.md | 22 +++++++------- alerting/alert/alert.go | 5 ++++ alerting/alert/alert_test.go | 56 ++++++++++++++++++++++++++++++++++++ watchdog/alerting_test.go | 2 +- 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 150f7a51..b02bfb65 100644 --- a/README.md +++ b/README.md @@ -711,17 +711,17 @@ individual endpoints with configurable descriptions and thresholds. Alerts are configured at the endpoint level like so: -| Parameter | Description | Default | -|:-------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------|:--------------| -| `alerts` | List of all alerts for a given endpoint. | `[]` | -| `alerts[].type` | Type of alert.
See table below for all valid types. | Required `""` | -| `alerts[].enabled` | Whether to enable the alert. | `true` | -| `alerts[].failure-threshold` | Number of failures in a row needed before triggering the alert. | `3` | -| `alerts[].success-threshold` | Number of successes in a row before an ongoing incident is marked as resolved. | `2` | -| `alerts[].minimum-reminder-interval` | Minimum time interval between alert reminders. E.g. `"30m"`, `"1h45m30s"` or `"24h"`. If empty or `0`, reminders are disabled. | `0` | -| `alerts[].send-on-resolved` | Whether to send a notification once a triggered alert is marked as resolved. | `false` | -| `alerts[].description` | Description of the alert. Will be included in the alert sent. | `""` | -| `alerts[].provider-override` | Alerting provider configuration override for the given alert type | `{}` | +| Parameter | Description | Default | +|:-------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------| +| `alerts` | List of all alerts for a given endpoint. | `[]` | +| `alerts[].type` | Type of alert.
See table below for all valid types. | Required `""` | +| `alerts[].enabled` | Whether to enable the alert. | `true` | +| `alerts[].failure-threshold` | Number of failures in a row needed before triggering the alert. | `3` | +| `alerts[].success-threshold` | Number of successes in a row before an ongoing incident is marked as resolved. | `2` | +| `alerts[].minimum-reminder-interval` | Minimum time interval between alert reminders. E.g. `"30m"`, `"1h45m30s"` or `"24h"`. If empty or `0`, reminders are disabled. Cannot be lower than `5m`. | `0` | +| `alerts[].send-on-resolved` | Whether to send a notification once a triggered alert is marked as resolved. | `false` | +| `alerts[].description` | Description of the alert. Will be included in the alert sent. | `""` | +| `alerts[].provider-override` | Alerting provider configuration override for the given alert type | `{}` | Here's an example of what an alert configuration might look like at the endpoint level: ```yaml diff --git a/alerting/alert/alert.go b/alerting/alert/alert.go index ebd34c19..7a627df0 100644 --- a/alerting/alert/alert.go +++ b/alerting/alert/alert.go @@ -15,6 +15,8 @@ import ( 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 \\") + + ErrAlertWithInvalidMinimumReminderInterval = errors.New("minimum-reminder-interval must be either omitted or be at least 5m") ) // Alert is endpoint.Endpoint's alert configuration @@ -78,6 +80,9 @@ func (alert *Alert) ValidateAndSetDefaults() error { if alert.SuccessThreshold <= 0 { alert.SuccessThreshold = 2 } + if alert.MinimumReminderInterval != 0 && alert.MinimumReminderInterval < 5*time.Minute { + return ErrAlertWithInvalidMinimumReminderInterval + } if strings.ContainsAny(alert.GetDescription(), "\"\\") { return ErrAlertWithInvalidDescription } diff --git a/alerting/alert/alert_test.go b/alerting/alert/alert_test.go index ffe03eee..4d75b862 100644 --- a/alerting/alert/alert_test.go +++ b/alerting/alert/alert_test.go @@ -3,6 +3,7 @@ package alert import ( "errors" "testing" + "time" ) func TestAlert_ValidateAndSetDefaults(t *testing.T) { @@ -36,6 +37,61 @@ func TestAlert_ValidateAndSetDefaults(t *testing.T) { expectedFailureThreshold: 10, expectedSuccessThreshold: 5, }, + { + name: "valid-minimum-reminder-interval-0", + alert: Alert{ + MinimumReminderInterval: 0, + FailureThreshold: 10, + SuccessThreshold: 5, + }, + expectedError: nil, + expectedFailureThreshold: 10, + expectedSuccessThreshold: 5, + }, + { + name: "valid-minimum-reminder-interval-5m", + alert: Alert{ + MinimumReminderInterval: 5 * time.Minute, + FailureThreshold: 10, + SuccessThreshold: 5, + }, + expectedError: nil, + expectedFailureThreshold: 10, + expectedSuccessThreshold: 5, + }, + { + name: "valid-minimum-reminder-interval-10m", + alert: Alert{ + MinimumReminderInterval: 10 * time.Minute, + FailureThreshold: 10, + SuccessThreshold: 5, + }, + expectedError: nil, + expectedFailureThreshold: 10, + expectedSuccessThreshold: 5, + }, + { + name: "invalid-minimum-reminder-interval-1m", + alert: Alert{ + MinimumReminderInterval: 1 * time.Minute, + FailureThreshold: 10, + SuccessThreshold: 5, + }, + expectedError: ErrAlertWithInvalidMinimumReminderInterval, + expectedFailureThreshold: 10, + expectedSuccessThreshold: 5, + }, + { + name: "invalid-minimum-reminder-interval-1s", + alert: Alert{ + MinimumReminderInterval: 1 * time.Second, + FailureThreshold: 10, + SuccessThreshold: 5, + }, + expectedError: ErrAlertWithInvalidMinimumReminderInterval, + expectedFailureThreshold: 10, + expectedSuccessThreshold: 5, + }, } for _, scenario := range scenarios { t.Run(scenario.name, func(t *testing.T) { diff --git a/watchdog/alerting_test.go b/watchdog/alerting_test.go index d8314885..25fc7094 100644 --- a/watchdog/alerting_test.go +++ b/watchdog/alerting_test.go @@ -648,7 +648,7 @@ func TestHandleAlertingWithMinimumReminderInterval(t *testing.T) { SuccessThreshold: 3, SendOnResolved: &enabled, Triggered: false, - MinimumReminderInterval: 1 * time.Second, + MinimumReminderInterval: 5 * time.Minute, }, }, }