Merge branch 'duration-intervals'
continuous-integration/drone/push Build is passing Details

This commit is contained in:
IamTheFij 2021-05-12 18:32:12 -07:00
commit 31a4b484bf
7 changed files with 88 additions and 20 deletions

View File

@ -54,7 +54,7 @@ The global configurations are:
|key|value| |key|value|
|---|---| |---|---|
|`check_interval`|Maximum frequency to run checks for each monitor| |`check_interval`|Maximum frequency to run checks for each monitor as duration, eg. 1m2s.|
|`monitors`|List of all monitors. Detailed description below| |`monitors`|List of all monitors. Detailed description below|
|`alerts`|List of all alerts. Detailed description below| |`alerts`|List of all alerts. Detailed description below|
@ -111,7 +111,7 @@ minitor -metrics -metrics-port 3000
## Contributing ## Contributing
Whether you're looking to submit a patch or just tell me I broke something, you can contribute through the Github mirror and I can merge PRs back to the source repository. Whether you're looking to submit a patch or tell me I broke something, you can contribute through the Github mirror and I can merge PRs back to the source repository.
Primary Repo: https://git.iamthefij.com/iamthefij/minitor.git Primary Repo: https://git.iamthefij.com/iamthefij/minitor.git
@ -143,6 +143,18 @@ alerts:
command: 'echo {{.MonitorName}}' command: 'echo {{.MonitorName}}'
``` ```
Interval durations have changed from being an integer number of seconds to a duration string supported by Go, for example:
minitor-py:
```yaml
check_interval: 90
```
minitor-go:
```yaml
check_interval: 1m30s
```
For the time being, legacy configs for the Python version of Minitor should be compatible if you apply the `-py-compat` flag when running Minitor. Eventually, this flag will go away when later breaking changes are introduced. For the time being, legacy configs for the Python version of Minitor should be compatible if you apply the `-py-compat` flag when running Minitor. Eventually, this flag will go away when later breaking changes are introduced.
## Future ## Future
@ -153,4 +165,3 @@ Future, potentially breaking changes
- [ ] Async checking - [ ] Async checking
- [ ] Revisit metrics and see if they all make sense - [ ] Revisit metrics and see if they all make sense
- [ ] Consider dropping `alert_up` and `alert_down` in favor of using Go templates that offer more control of messaging (Breaking) - [ ] Consider dropping `alert_up` and `alert_down` in favor of using Go templates that offer more control of messaging (Breaking)
- [ ] Use durations rather than seconds checked in event loop (Potentially breaking)

View File

@ -3,6 +3,7 @@ package main
import ( import (
"errors" "errors"
"io/ioutil" "io/ioutil"
"time"
"git.iamthefij.com/iamthefij/slog" "git.iamthefij.com/iamthefij/slog"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -12,7 +13,7 @@ var errInvalidConfig = errors.New("Invalid configuration")
// Config type is contains all provided user configuration // Config type is contains all provided user configuration
type Config struct { type Config struct {
CheckInterval int64 `yaml:"check_interval"` CheckInterval SecondsOrDuration `yaml:"check_interval"`
Monitors []*Monitor Monitors []*Monitor
Alerts map[string]*Alert Alerts map[string]*Alert
} }
@ -51,6 +52,34 @@ func (cos *CommandOrShell) UnmarshalYAML(unmarshal func(interface{}) error) erro
return nil return nil
} }
// SecondsOrDuration wraps a duration value for parsing a duration or seconds from YAML
// NOTE: This should be removed in favor of only parsing durations once compatibility is broken
type SecondsOrDuration struct {
value time.Duration
}
// Value returns a duration value
func (sod SecondsOrDuration) Value() time.Duration {
return sod.value
}
// UnmarshalYAML allows unmarshalling a duration value or seconds if an int was provided
func (sod *SecondsOrDuration) UnmarshalYAML(unmarshal func(interface{}) error) error {
var seconds int64
err := unmarshal(&seconds)
if err == nil {
sod.value = time.Second * time.Duration(seconds)
return nil
}
// Error indicates that we don't have an int
err = unmarshal(&sod.value)
return err
}
// IsValid checks config validity and returns true if valid // IsValid checks config validity and returns true if valid
func (config Config) IsValid() (isValid bool) { func (config Config) IsValid() (isValid bool) {
isValid = true isValid = true

View File

@ -3,6 +3,7 @@ package main
import ( import (
"log" "log"
"testing" "testing"
"time"
) )
func TestLoadConfig(t *testing.T) { func TestLoadConfig(t *testing.T) {
@ -35,11 +36,37 @@ func TestLoadConfig(t *testing.T) {
// Set PyCompat to default value // Set PyCompat to default value
PyCompat = false PyCompat = false
log.Println("-----")
} }
} }
func TestIntervalParsing(t *testing.T) {
log.Printf("Testing case TestIntervalParsing")
config, err := LoadConfig("./test/valid-config.yml")
if err != nil {
t.Errorf("Failed loading config: %v", err)
}
oneSecond := time.Second
tenSeconds := 10 * time.Second
oneMinute := time.Minute
// validate top level interval seconds represented as an int
if config.CheckInterval.Value() != oneSecond {
t.Errorf("Incorrectly parsed int seconds. expected=%v actual=%v", oneSecond, config.CheckInterval)
}
if config.Monitors[0].CheckInterval.Value() != tenSeconds {
t.Errorf("Incorrectly parsed seconds duration. expected=%v actual=%v", oneSecond, config.CheckInterval)
}
if config.Monitors[1].CheckInterval.Value() != oneMinute {
t.Errorf("Incorrectly parsed seconds duration. expected=%v actual=%v", oneSecond, config.CheckInterval)
}
log.Println("-----")
}
// TestMultiLineConfig is a more complicated test stepping through the parsing // TestMultiLineConfig is a more complicated test stepping through the parsing
// and execution of mutli-line strings presented in YAML // and execution of mutli-line strings presented in YAML
func TestMultiLineConfig(t *testing.T) { func TestMultiLineConfig(t *testing.T) {

View File

@ -120,7 +120,6 @@ func main() {
panic(err) panic(err)
} }
sleepTime := time.Duration(config.CheckInterval) * time.Second time.Sleep(config.CheckInterval.Value())
time.Sleep(sleepTime)
} }
} }

View File

@ -11,9 +11,9 @@ import (
// Monitor represents a particular periodic check of a command // Monitor represents a particular periodic check of a command
type Monitor struct { //nolint:maligned type Monitor struct { //nolint:maligned
// Config values // Config values
AlertAfter int16 `yaml:"alert_after"` AlertAfter int16 `yaml:"alert_after"`
AlertEvery int16 `yaml:"alert_every"` AlertEvery int16 `yaml:"alert_every"`
CheckInterval float64 `yaml:"check_interval"` CheckInterval SecondsOrDuration `yaml:"check_interval"`
Name string Name string
AlertDown []string `yaml:"alert_down"` AlertDown []string `yaml:"alert_down"`
AlertUp []string `yaml:"alert_up"` AlertUp []string `yaml:"alert_up"`
@ -43,9 +43,9 @@ func (monitor Monitor) ShouldCheck() bool {
return true return true
} }
sinceLastCheck := time.Since(monitor.lastCheck).Seconds() sinceLastCheck := time.Since(monitor.lastCheck)
return sinceLastCheck >= monitor.CheckInterval return sinceLastCheck >= monitor.CheckInterval.Value()
} }
// Check will run the command configured by the Monitor and return a status // Check will run the command configured by the Monitor and return a status

View File

@ -45,9 +45,9 @@ func TestMonitorShouldCheck(t *testing.T) {
name string name string
}{ }{
{Monitor{}, true, "Empty"}, {Monitor{}, true, "Empty"},
{Monitor{lastCheck: timeNow, CheckInterval: 15}, false, "Just checked"}, {Monitor{lastCheck: timeNow, CheckInterval: SecondsOrDuration{time.Second * 15}}, false, "Just checked"},
{Monitor{lastCheck: timeTenSecAgo, CheckInterval: 15}, false, "-10s"}, {Monitor{lastCheck: timeTenSecAgo, CheckInterval: SecondsOrDuration{time.Second * 15}}, false, "-10s"},
{Monitor{lastCheck: timeTwentySecAgo, CheckInterval: 15}, true, "-20s"}, {Monitor{lastCheck: timeTwentySecAgo, CheckInterval: SecondsOrDuration{time.Second * 15}}, true, "-20s"},
} }
for _, c := range cases { for _, c := range cases {

View File

@ -3,21 +3,23 @@ check_interval: 1
monitors: monitors:
- name: Command - name: Command
command: ['echo', '$PATH'] command: ["echo", "$PATH"]
alert_down: ['log_command', 'log_shell'] alert_down: ["log_command", "log_shell"]
alert_every: 0 alert_every: 0
check_interval: 10s
- name: Shell - name: Shell
command: > command: >
echo 'Some string with stuff'; echo 'Some string with stuff';
echo 'another line'; echo 'another line';
echo $PATH; echo $PATH;
exit 1 exit 1
alert_down: ['log_command', 'log_shell'] alert_down: ["log_command", "log_shell"]
alert_after: 5 alert_after: 5
alert_every: 0 alert_every: 0
check_interval: 1m
alerts: alerts:
log_command: log_command:
command: ['echo', 'regular', '"command!!!"', "{{.MonitorName}}"] command: ["echo", "regular", '"command!!!"', "{{.MonitorName}}"]
log_shell: log_shell:
command: echo "Failure on {{.MonitorName}} User is $USER" command: echo "Failure on {{.MonitorName}} User is $USER"