Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ otherwise no tag is added. {issue}42208[42208] {pull}42403[42403]

*Heartbeat*

- Added maintenance windows support for Heartbeat. {pull}41508[41508]


*Metricbeat*
Expand Down
31 changes: 31 additions & 0 deletions NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22813,6 +22813,37 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--------------------------------------------------------------------------------
Dependency : github.com/teambition/rrule-go
Version: v1.8.2
Licence type (autodetected): MIT
--------------------------------------------------------------------------------

Contents of probable licence file $GOMODCACHE/github.com/teambition/[email protected]/LICENSE:

MIT License

Copyright (c) 2017-2023 Teambition

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--------------------------------------------------------------------------------
Dependency : github.com/tklauser/go-sysconf
Version: v0.3.12
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ require (
github.com/pkg/xattr v0.4.9
github.com/prometheus/prometheus v0.300.1
github.com/shirou/gopsutil/v4 v4.25.4
github.com/teambition/rrule-go v1.8.2
github.com/tklauser/go-sysconf v0.3.12
github.com/xdg-go/scram v1.1.2
github.com/zyedidia/generic v1.2.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/teambition/rrule-go v1.8.2 h1:lIjpjvWTj9fFUZCmuoVDrKVOtdiyzbzc93qTmRVe/J8=
github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
Expand Down
60 changes: 60 additions & 0 deletions heartbeat/docs/monitors/monitor-common-options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,66 @@ Example:

```

[float]
[[monitor-maintenance-windows]]
=== `maintenance_windows`

Use the `maintenance_windows` option to define recurring time periods when a heartbeat monitor should be paused. This feature is implemented via `rrule`, allowing flexible scheduling of maintenance windows.

The `maintenance_windows` option supports the following top-level fields:

* `freq`: Specifies the frequency of the maintenance window. Supported values are `daily`, `weekly`, `monthly`, and `yearly`.
* `dtstart`: The start date and time for the first occurrence of the maintenance window in ISO 8601 format. This value cannot be older than two years to prevent excessive `rrule` iterations.
* `interval`: The interval at which the rule repeats. For example, an interval of `1` with `freq: weekly` means the maintenance window occurs every week.
* `byweekday`: (Optional) Specifies the days of the week when the maintenance window occurs. Accepts values like `MO`, `TU`, `WE`, `TH`, `FR`, `SA`, `SU`.
* `byhour`: (Optional) Specifies the hour(s) of the day when the maintenance window should trigger.
* `byminute`: (Optional) Specifies the minute(s) of the hour when the maintenance window should trigger.
* `bysecond`: (Optional) Specifies the second(s) of the minute when the maintenance window should trigger.
* `byeaster`: (Optional) Specifies the offset from Easter Sunday for the maintenance window.
* `bysetpos`: (Optional) Specifies the nth occurrence in the set of recurrence values.
* `bymonth`: (Optional) Specifies the month(s) when the maintenance window should trigger.
* `byweekno`: (Optional) Specifies the week(s) of the year when the maintenance window should trigger.
* `byyearday`: (Optional) Specifies the day(s) of the year when the maintenance window should trigger.
* `bymonthday`: (Optional) Specifies the day(s) of the month when the maintenance window should trigger.
* `wkst`: (Optional) Specifies the starting day of the week (e.g., `MO` for Monday).
* `duration`: The duration of each maintenance window in milliseconds.
* `count`: (Optional) The number of times the maintenance window should occur before stopping.

Example:

```yaml
- type: http
# ID used to uniquely identify this monitor
id: my-monitor
# List of URLs to query
urls: ["http://localhost:9200"]
# Define maintenance windows
maintenance_windows:
- freq: daily
dtstart: "2024-11-04T01:00:00.000Z"
interval: 1
byweekday: [MO, TU, WE, TH, FR]
byhour: [1, 2]
byminute: [0, 30]
bysecond: [0]
bymonth: [1, 6, 12]
byweekno: [10, 20, 30]
byyearday: [100, 200, 300]
bymonthday: [1, 15, 31]
wkst: MO
duration: 3600000 # 1 hour in milliseconds
count: 10
```

This configuration pauses the monitor every weekday at 01:00 and 02:00 UTC for one hour, repeating for ten occurrences.

**Limitations:**

- Only `daily`, `weekly`, `monthly`, and `yearly` frequencies are supported.
- `dtstart` must not be older than two years to limit the number of `rrule` iterations.



[float]
[[monitor-fields]]
==== `fields`
Expand Down
119 changes: 119 additions & 0 deletions heartbeat/monitors/maintwin/maintwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package maintwin

import (
"fmt"
"strings"
"time"

"github.com/teambition/rrule-go"
)

var weekdayLookup = map[string]rrule.Weekday{
"MO": rrule.MO, "TU": rrule.TU, "WE": rrule.WE, "TH": rrule.TH, "FR": rrule.FR, "SA": rrule.SA, "SU": rrule.SU,
}

type MaintWin struct {
Freq string `config:"freq" validate:"required"`
Dtstart string `config:"dtstart" validate:"required"`
Interval int `config:"interval"`
Duration time.Duration `config:"duration" validate:"required"`
Wkst rrule.Weekday `config:"wkst"`
Count int `config:"count"`
Bysetpos []int `config:"bysetpos"`
Bymonth []int `config:"bymonth"`
Bymonthday []int `config:"bymonthday"`
Byyearday []int `config:"byyearday"`
Byweekno []int `config:"byweekno"`
Byweekday []string `config:"byweekday"`
Byhour []int `config:"byhour"`
Byminute []int `config:"byminute"`
Bysecond []int `config:"bysecond"`
Byeaster []int `config:"byeaster"`
}

func (mw *MaintWin) Parse(validateDtStart bool) (r *rrule.RRule, err error) {

// validate the frequency, we don't support less than daily
freq, err := rrule.StrToFreq(strings.ToUpper(mw.Freq))
if err != nil || freq > rrule.DAILY {
return nil, fmt.Errorf("invalid frequency %s: only yearly, monthly, weekly, and daily are supported", mw.Freq)
}

dtstart, err := time.Parse(time.RFC3339, mw.Dtstart)
if err != nil {
return nil, err
}

// validate DTSTART and make sure it's not older than 2 years
if dtstart.Before(time.Now().AddDate(-2, 0, 0)) && validateDtStart {
return nil, fmt.Errorf(
"invalid dtstart: %s is more than 2 years in the past. "+
"To prevent excessive iterations, please use a more recent date",
dtstart.Format(time.RFC3339),
)
}

// Convert the string weekdays to rrule.Weekday
weekdays := []rrule.Weekday{}
for _, wd := range mw.Byweekday {
if weekday, exists := weekdayLookup[wd]; exists {
weekdays = append(weekdays, weekday)
}
}

dtstart = dtstart.UTC()

r, err = rrule.NewRRule(rrule.ROption{
Freq: freq,
Count: mw.Count,
Dtstart: dtstart,
Interval: mw.Interval,
Byweekday: weekdays,
Byhour: mw.Byhour,
Byminute: mw.Byminute,
Bysecond: mw.Bysecond,
Byeaster: mw.Byeaster,
Bysetpos: mw.Bysetpos,
Bymonth: mw.Bymonth,
Byweekno: mw.Byweekno,
Byyearday: mw.Byyearday,
Bymonthday: mw.Bymonthday,
Wkst: mw.Wkst,
})
if err != nil {
return nil, err
}

return r, nil
}

type ParsedMaintWin struct {
Rule *rrule.RRule
Duration time.Duration
}

func (pmw ParsedMaintWin) IsActive(tOrig time.Time) bool {
if pmw.Rule == nil {
return false
}
tOrig = tOrig.UTC()
window := pmw.Rule.Before(tOrig, true)
return !window.IsZero() && tOrig.Before(window.Add(pmw.Duration))
}
Loading