Refactor validation for alert and monitor to return errors
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
e096217e43
commit
da5675c642
31
alert.go
31
alert.go
@ -37,13 +37,32 @@ type AlertNotice struct {
|
|||||||
LastCheckOutput string
|
LastCheckOutput string
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValid returns a boolean indicating if the Alert has been correctly
|
// Validate checks that the Alert is properly configured and returns errors if not
|
||||||
// configured
|
func (alert Alert) Validate() error {
|
||||||
func (alert Alert) IsValid() bool {
|
hasCommand := len(alert.Command) > 0
|
||||||
hasAtLeastOneCommand := alert.Command != nil || alert.ShellCommand != ""
|
hasShellCommand := alert.ShellCommand != ""
|
||||||
hasAtMostOneCommand := alert.Command == nil || alert.ShellCommand == ""
|
|
||||||
|
|
||||||
return hasAtLeastOneCommand && hasAtMostOneCommand
|
var err error
|
||||||
|
|
||||||
|
hasAtLeastOneCommand := hasCommand || hasShellCommand
|
||||||
|
if !hasAtLeastOneCommand {
|
||||||
|
err = errors.Join(err, fmt.Errorf(
|
||||||
|
"%w: alert %s has no command or shell_command configured",
|
||||||
|
ErrInvalidAlert,
|
||||||
|
alert.Name,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAtMostOneCommand := !(hasCommand && hasShellCommand)
|
||||||
|
if !hasAtMostOneCommand {
|
||||||
|
err = errors.Join(err, fmt.Errorf(
|
||||||
|
"%w: alert %s has both command and shell_command configured",
|
||||||
|
ErrInvalidAlert,
|
||||||
|
alert.Name,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildTemplates compiles command templates for the Alert
|
// BuildTemplates compiles command templates for the Alert
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
package main_test
|
package main_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
m "git.iamthefij.com/iamthefij/minitor-go"
|
m "git.iamthefij.com/iamthefij/minitor-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAlertIsValid(t *testing.T) {
|
func TestAlertValidate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
alert m.Alert
|
alert m.Alert
|
||||||
expected bool
|
expected error
|
||||||
name string
|
name string
|
||||||
}{
|
}{
|
||||||
{m.Alert{Command: []string{"echo", "test"}}, true, "Command only"},
|
{m.Alert{Command: []string{"echo", "test"}}, nil, "Command only"},
|
||||||
{m.Alert{ShellCommand: "echo test"}, true, "CommandShell only"},
|
{m.Alert{ShellCommand: "echo test"}, nil, "CommandShell only"},
|
||||||
{m.Alert{}, false, "No commands"},
|
{m.Alert{Command: []string{"echo", "test"}, ShellCommand: "echo test"}, m.ErrInvalidAlert, "Both commands"},
|
||||||
|
{m.Alert{}, m.ErrInvalidAlert, "No commands"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@ -23,8 +27,11 @@ func TestAlertIsValid(t *testing.T) {
|
|||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
actual := c.alert.IsValid()
|
actual := c.alert.Validate()
|
||||||
if actual != c.expected {
|
hasErr := (actual != nil)
|
||||||
|
expectErr := (c.expected != nil)
|
||||||
|
|
||||||
|
if hasErr != expectErr || !errors.Is(actual, c.expected) {
|
||||||
t.Errorf("expected=%t actual=%t", c.expected, actual)
|
t.Errorf("expected=%t actual=%t", c.expected, actual)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -73,9 +73,7 @@ func (config Config) IsValid() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, alert := range config.Alerts {
|
for _, alert := range config.Alerts {
|
||||||
if !alert.IsValid() {
|
err = errors.Join(err, alert.Validate())
|
||||||
err = errors.Join(err, fmt.Errorf("%w: %s", ErrInvalidAlert, alert.Name))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate monitors
|
// Validate monitors
|
||||||
@ -84,9 +82,7 @@ func (config Config) IsValid() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, monitor := range config.Monitors {
|
for _, monitor := range config.Monitors {
|
||||||
if !monitor.IsValid() {
|
err = errors.Join(err, monitor.Validate())
|
||||||
err = errors.Join(err, fmt.Errorf("%w: %s", ErrInvalidMonitor, monitor.Name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that all Monitor alerts actually exist
|
// Check that all Monitor alerts actually exist
|
||||||
for _, isUp := range []bool{true, false} {
|
for _, isUp := range []bool{true, false} {
|
||||||
|
49
monitor.go
49
monitor.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@ -65,21 +66,51 @@ func (monitor *Monitor) Init(defaultAlertAfter int, defaultAlertEvery *int, defa
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValid returns a boolean indicating if the Monitor has been correctly configured
|
// Validate checks that the Monitor is properly configured and returns errors if not
|
||||||
func (monitor Monitor) IsValid() bool {
|
func (monitor Monitor) Validate() error {
|
||||||
// TODO: Refactor and return an error containing more information on what was invalid
|
|
||||||
hasCommand := len(monitor.Command) > 0
|
hasCommand := len(monitor.Command) > 0
|
||||||
hasShellCommand := monitor.ShellCommand != ""
|
hasShellCommand := monitor.ShellCommand != ""
|
||||||
hasValidAlertAfter := monitor.AlertAfter > 0
|
hasValidAlertAfter := monitor.AlertAfter > 0
|
||||||
hasAlertDown := len(monitor.AlertDown) > 0
|
hasAlertDown := len(monitor.AlertDown) > 0
|
||||||
|
|
||||||
hasAtLeastOneCommand := hasCommand || hasShellCommand
|
var err error
|
||||||
hasAtMostOneCommand := !(hasCommand && hasShellCommand)
|
|
||||||
|
|
||||||
return hasAtLeastOneCommand &&
|
hasAtLeastOneCommand := hasCommand || hasShellCommand
|
||||||
hasAtMostOneCommand &&
|
if !hasAtLeastOneCommand {
|
||||||
hasValidAlertAfter &&
|
err = errors.Join(err, fmt.Errorf(
|
||||||
hasAlertDown
|
"%w: monitor %s has no command or shell_command configured",
|
||||||
|
ErrInvalidMonitor,
|
||||||
|
monitor.Name,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAtMostOneCommand := !(hasCommand && hasShellCommand)
|
||||||
|
if !hasAtMostOneCommand {
|
||||||
|
err = errors.Join(err, fmt.Errorf(
|
||||||
|
"%w: monitor %s has both command and shell_command configured",
|
||||||
|
ErrInvalidMonitor,
|
||||||
|
monitor.Name,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasValidAlertAfter {
|
||||||
|
err = errors.Join(err, fmt.Errorf(
|
||||||
|
"%w: monitor %s has invalid alert_after value %d. Must be greater than 0",
|
||||||
|
ErrInvalidMonitor,
|
||||||
|
monitor.Name,
|
||||||
|
monitor.AlertAfter,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAlertDown {
|
||||||
|
err = errors.Join(err, fmt.Errorf(
|
||||||
|
"%w: monitor %s has no alert_down configured. Configure one here or add a default_alert_down",
|
||||||
|
ErrInvalidMonitor,
|
||||||
|
monitor.Name,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (monitor Monitor) LastOutput() string {
|
func (monitor Monitor) LastOutput() string {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package main_test
|
package main_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -8,18 +9,19 @@ import (
|
|||||||
m "git.iamthefij.com/iamthefij/minitor-go"
|
m "git.iamthefij.com/iamthefij/minitor-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestMonitorIsValid tests the Monitor.IsValid()
|
func TestMonitorValidate(t *testing.T) {
|
||||||
func TestMonitorIsValid(t *testing.T) {
|
t.Parallel()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
monitor m.Monitor
|
monitor m.Monitor
|
||||||
expected bool
|
expected error
|
||||||
name string
|
name string
|
||||||
}{
|
}{
|
||||||
{m.Monitor{AlertAfter: 1, Command: []string{"echo", "test"}, AlertDown: []string{"log"}}, true, "Command only"},
|
{m.Monitor{AlertAfter: 1, Command: []string{"echo", "test"}, AlertDown: []string{"log"}}, nil, "Command only"},
|
||||||
{m.Monitor{AlertAfter: 1, ShellCommand: "echo test", AlertDown: []string{"log"}}, true, "CommandShell only"},
|
{m.Monitor{AlertAfter: 1, ShellCommand: "echo test", AlertDown: []string{"log"}}, nil, "CommandShell only"},
|
||||||
{m.Monitor{AlertAfter: 1, Command: []string{"echo", "test"}}, false, "No AlertDown"},
|
{m.Monitor{AlertAfter: 1, Command: []string{"echo", "test"}}, m.ErrInvalidMonitor, "No AlertDown"},
|
||||||
{m.Monitor{AlertAfter: 1, AlertDown: []string{"log"}}, false, "No commands"},
|
{m.Monitor{AlertAfter: 1, AlertDown: []string{"log"}}, m.ErrInvalidMonitor, "No commands"},
|
||||||
{m.Monitor{AlertAfter: -1, Command: []string{"echo", "test"}, AlertDown: []string{"log"}}, false, "Invalid alert threshold, -1"},
|
{m.Monitor{AlertAfter: -1, Command: []string{"echo", "test"}, AlertDown: []string{"log"}}, m.ErrInvalidMonitor, "Invalid alert threshold, -1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@ -28,8 +30,11 @@ func TestMonitorIsValid(t *testing.T) {
|
|||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
actual := c.monitor.IsValid()
|
actual := c.monitor.Validate()
|
||||||
if actual != c.expected {
|
hasErr := (actual != nil)
|
||||||
|
expectErr := (c.expected != nil)
|
||||||
|
|
||||||
|
if hasErr != expectErr || !errors.Is(actual, c.expected) {
|
||||||
t.Errorf("IsValid(%v), expected=%t actual=%t", c.name, c.expected, actual)
|
t.Errorf("IsValid(%v), expected=%t actual=%t", c.name, c.expected, actual)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user