mirror of
https://github.com/TwiN/gatus.git
synced 2024-12-14 11:58:04 +00:00
feat(alerting): add timezone for maintenance (#653)
* feat(alerting): add timezone for maintenance * Update config/maintenance/maintenance.go * docs: Add example of maintenance.timezone in readme.md * fix: Only set time to timezone location if the location is set * fix: Include the original error in the message --------- Co-authored-by: TwiN <twin@linux.com>
This commit is contained in:
parent
b2191391f6
commit
af00dfdb73
3 changed files with 86 additions and 6 deletions
|
@ -1459,15 +1459,15 @@ To do that, you'll have to use the maintenance configuration:
|
|||
| `maintenance.enabled` | Whether the maintenance period is enabled | `true` |
|
||||
| `maintenance.start` | Time at which the maintenance window starts in `hh:mm` format (e.g. `23:00`) | Required `""` |
|
||||
| `maintenance.duration` | Duration of the maintenance window (e.g. `1h`, `30m`) | Required `""` |
|
||||
| `maintenance.timezone` | Timezone of the maintenance window format (e.g. `Europe/Amsterdam`).<br />See [List of tz database time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for more info | `UTC` |
|
||||
| `maintenance.every` | Days on which the maintenance period applies (e.g. `[Monday, Thursday]`).<br />If left empty, the maintenance window applies every day | `[]` |
|
||||
|
||||
> 📝 The maintenance configuration uses UTC
|
||||
|
||||
Here's an example:
|
||||
```yaml
|
||||
maintenance:
|
||||
start: 23:00
|
||||
duration: 1h
|
||||
timezone: "Europe/Amsterdam"
|
||||
every: [Monday, Thursday]
|
||||
```
|
||||
Note that you can also specify each day on separate lines:
|
||||
|
@ -1475,6 +1475,7 @@ Note that you can also specify each day on separate lines:
|
|||
maintenance:
|
||||
start: 23:00
|
||||
duration: 1h
|
||||
timezone: "Europe/Amsterdam"
|
||||
every:
|
||||
- Monday
|
||||
- Thursday
|
||||
|
|
|
@ -12,6 +12,7 @@ var (
|
|||
errInvalidMaintenanceStartFormat = errors.New("invalid maintenance start format: must be hh:mm, between 00:00 and 23:59 inclusively (e.g. 23:00)")
|
||||
errInvalidMaintenanceDuration = errors.New("invalid maintenance duration: must be bigger than 0 (e.g. 30m)")
|
||||
errInvalidDayName = fmt.Errorf("invalid value specified for 'on'. supported values are %s", longDayNames)
|
||||
errInvalidTimezone = errors.New("invalid timezone specified or format not supported. Use IANA timezone format (e.g. America/Sao_Paulo)")
|
||||
|
||||
longDayNames = []string{
|
||||
"Sunday",
|
||||
|
@ -27,17 +28,19 @@ var (
|
|||
// Config allows for the configuration of a maintenance period.
|
||||
// During this maintenance period, no alerts will be sent.
|
||||
//
|
||||
// Uses UTC.
|
||||
// Uses UTC by default.
|
||||
type Config struct {
|
||||
Enabled *bool `yaml:"enabled"` // Whether the maintenance period is enabled. Enabled by default if nil.
|
||||
Start string `yaml:"start"` // Time at which the maintenance period starts (e.g. 23:00)
|
||||
Duration time.Duration `yaml:"duration"` // Duration of the maintenance period (e.g. 4h)
|
||||
Timezone string `yaml:"timezone"` // Timezone in string format which the maintenance period is configured (e.g. America/Sao_Paulo)
|
||||
|
||||
// Every is a list of days of the week during which maintenance period applies.
|
||||
// See longDayNames for list of valid values.
|
||||
// Every day if empty.
|
||||
Every []string `yaml:"every"`
|
||||
|
||||
TimezoneLocation *time.Location // Timezone in location format which the maintenance period is configured
|
||||
durationToStartFromMidnight time.Duration
|
||||
}
|
||||
|
||||
|
@ -85,6 +88,15 @@ func (c *Config) ValidateAndSetDefaults() error {
|
|||
if c.Duration <= 0 || c.Duration > 24*time.Hour {
|
||||
return errInvalidMaintenanceDuration
|
||||
}
|
||||
if c.Timezone != "" {
|
||||
c.TimezoneLocation, err = time.LoadLocation(c.Timezone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %w", errInvalidTimezone, err)
|
||||
}
|
||||
} else {
|
||||
c.Timezone = "UTC"
|
||||
c.TimezoneLocation = time.UTC
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -93,7 +105,10 @@ func (c Config) IsUnderMaintenance() bool {
|
|||
if !c.IsEnabled() {
|
||||
return false
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
now := time.Now()
|
||||
if c.TimezoneLocation != nil {
|
||||
now = now.In(c.TimezoneLocation)
|
||||
}
|
||||
var dayWhereMaintenancePeriodWouldStart time.Time
|
||||
if now.Hour() >= int(c.durationToStartFromMidnight.Hours()) {
|
||||
dayWhereMaintenancePeriodWouldStart = now.Truncate(24 * time.Hour)
|
||||
|
|
|
@ -90,6 +90,15 @@ func TestConfig_ValidateAndSetDefaults(t *testing.T) {
|
|||
},
|
||||
expectedError: errInvalidMaintenanceDuration,
|
||||
},
|
||||
{
|
||||
name: "invalid-timezone",
|
||||
cfg: &Config{
|
||||
Start: "23:00",
|
||||
Duration: time.Hour,
|
||||
Timezone: "invalid-timezone",
|
||||
},
|
||||
expectedError: errInvalidTimezone,
|
||||
},
|
||||
{
|
||||
name: "every-day-at-2300",
|
||||
cfg: &Config{
|
||||
|
@ -126,6 +135,33 @@ func TestConfig_ValidateAndSetDefaults(t *testing.T) {
|
|||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "timezone-amsterdam",
|
||||
cfg: &Config{
|
||||
Start: "23:00",
|
||||
Duration: time.Hour,
|
||||
Timezone: "Europe/Amsterdam",
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "timezone-cet",
|
||||
cfg: &Config{
|
||||
Start: "23:00",
|
||||
Duration: time.Hour,
|
||||
Timezone: "CET",
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "timezone-etc-plus-5",
|
||||
cfg: &Config{
|
||||
Start: "23:00",
|
||||
Duration: time.Hour,
|
||||
Timezone: "Etc/GMT+5",
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
|
@ -220,7 +256,25 @@ func TestConfig_IsUnderMaintenance(t *testing.T) {
|
|||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "under-maintenance-starting-4h-ago-for-3h",
|
||||
name: "under-maintenance-amsterdam-timezone-starting-now-for-2h",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", now.Hour()),
|
||||
Duration: 2 * time.Hour,
|
||||
Timezone: "Europe/Amsterdam",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "under-maintenance-utc-timezone-starting-now-for-2h",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", now.Hour()),
|
||||
Duration: 2 * time.Hour,
|
||||
Timezone: "UTC",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "not-under-maintenance-starting-4h-ago-for-3h",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", normalizeHour(now.Hour()-4)),
|
||||
Duration: 3 * time.Hour,
|
||||
|
@ -228,7 +282,7 @@ func TestConfig_IsUnderMaintenance(t *testing.T) {
|
|||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "under-maintenance-starting-5h-ago-for-1h",
|
||||
name: "not-under-maintenance-starting-5h-ago-for-1h",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", normalizeHour(now.Hour()-5)),
|
||||
Duration: time.Hour,
|
||||
|
@ -253,6 +307,16 @@ func TestConfig_IsUnderMaintenance(t *testing.T) {
|
|||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "not-under-maintenance-los-angeles-timezone-starting-now-for-2h-today",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", now.Hour()),
|
||||
Duration: 2 * time.Hour,
|
||||
Timezone: "America/Los_Angeles",
|
||||
Every: []string{now.Weekday().String()},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue