Compare commits
8 Commits
master
...
minitor-py
Author | SHA1 | Date | |
---|---|---|---|
a39c188a6c | |||
8f93997b80 | |||
65342fe0dd | |||
a7d1b8ab74 | |||
43ba2914de | |||
5ed691fdf3 | |||
0a0f6fe7c9 | |||
d4e2cb7b9f |
32
README.md
32
README.md
@ -8,29 +8,8 @@ Initial target is meant to be roughly compatible requiring only minor changes to
|
||||
|
||||
## Differences from Python version
|
||||
|
||||
There are a few key differences between the Python version and the v0.x Go version.
|
||||
|
||||
First, configuration keys cannot have multiple types in Go, so a different key must be used when specifying a Shell command as a string rather than a list of args. Instead of `command`, you must use `command_shell`. Eg:
|
||||
|
||||
minitor-py:
|
||||
```yaml
|
||||
monitors:
|
||||
- name: Exec command
|
||||
command: ['echo', 'test']
|
||||
- name: Shell command
|
||||
command: echo 'test'
|
||||
```
|
||||
|
||||
minitor-go:
|
||||
```yaml
|
||||
monitors:
|
||||
- name: Exec command
|
||||
command: ['echo', 'test']
|
||||
- name: Shell command
|
||||
command_shell: echo 'test'
|
||||
```
|
||||
|
||||
Second, templating for Alert messages has been updated. In the Python version, `str.format(...)` was used with certain keys passed in that could be used to format messages. In the Go version, we use a struct, `AlertNotice` defined in `alert.go` and the built in Go templating format. Eg.
|
||||
Templating for Alert messages has been updated. In the Python version, `str.format(...)` was used with certain keys passed in that could be used to format messages. In the Go version, we use a struct, `AlertNotice` defined in `alert.go` and the built in Go templating format. Eg.
|
||||
|
||||
minitor-py:
|
||||
```yaml
|
||||
@ -38,7 +17,7 @@ alerts:
|
||||
log_command:
|
||||
command: ['echo', '{monitor_name}']
|
||||
log_shell:
|
||||
command_shell: 'echo {monitor_name}'
|
||||
command: 'echo {monitor_name}'
|
||||
```
|
||||
|
||||
minitor-go:
|
||||
@ -47,7 +26,7 @@ alerts:
|
||||
log_command:
|
||||
command: ['echo', '{{.MonitorName}}']
|
||||
log_shell:
|
||||
command_shell: 'echo {{.MonitorName}}'
|
||||
command: 'echo {{.MonitorName}}'
|
||||
```
|
||||
|
||||
Finally, newlines in a shell command don't terminate a particular command. Semicolons must be used and continuations should not.
|
||||
@ -56,7 +35,7 @@ minitor-py:
|
||||
```yaml
|
||||
alerts:
|
||||
log_shell:
|
||||
command_shell: >
|
||||
command: >
|
||||
echo "line 1"
|
||||
echo "line 2"
|
||||
echo "continued" \
|
||||
@ -67,7 +46,7 @@ minitor-go:
|
||||
```yaml
|
||||
alerts:
|
||||
log_shell:
|
||||
command_shell: >
|
||||
command: >
|
||||
echo "line 1";
|
||||
echo "line 2";
|
||||
echo "continued"
|
||||
@ -87,6 +66,7 @@ Pairity:
|
||||
- [x] Implement Prometheus client to export metrics
|
||||
- [x] Test coverage
|
||||
- [x] Integration testing (manual or otherwise)
|
||||
- [x] Allow commands and shell commands in the same config key
|
||||
|
||||
Improvement (potentially breaking):
|
||||
|
||||
|
53
alert.go
53
alert.go
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
@ -12,8 +13,7 @@ import (
|
||||
// Alert is a config driven mechanism for sending a notice
|
||||
type Alert struct {
|
||||
Name string
|
||||
Command []string
|
||||
CommandShell string `yaml:"command_shell"`
|
||||
Command CommandOrShell
|
||||
commandTemplate []*template.Template
|
||||
commandShellTemplate *template.Template
|
||||
}
|
||||
@ -31,26 +31,40 @@ type AlertNotice struct {
|
||||
// IsValid returns a boolean indicating if the Alert has been correctly
|
||||
// configured
|
||||
func (alert Alert) IsValid() bool {
|
||||
atLeastOneCommand := (alert.CommandShell != "" || alert.Command != nil)
|
||||
atMostOneCommand := (alert.CommandShell == "" || alert.Command == nil)
|
||||
return atLeastOneCommand && atMostOneCommand
|
||||
return !alert.Command.Empty()
|
||||
}
|
||||
|
||||
// BuildTemplates compiles command templates for the Alert
|
||||
func (alert *Alert) BuildTemplates() error {
|
||||
// TODO: Remove legacy template support later after 1.0
|
||||
legacy := strings.NewReplacer(
|
||||
"{alert_count}", "{{.AlertCount}}",
|
||||
"{alert_message}", "{{.MonitorName}} check has failed {{.FailureCount}} times",
|
||||
"{failure_count}", "{{.FailureCount}}",
|
||||
"{last_output}", "{{.LastCheckOutput}}",
|
||||
"{last_success}", "{{.LastSuccess}}",
|
||||
"{monitor_name}", "{{.MonitorName}}",
|
||||
)
|
||||
if LogDebug {
|
||||
log.Printf("DEBUG: Building template for alert %s", alert.Name)
|
||||
}
|
||||
if alert.commandTemplate == nil && alert.Command != nil {
|
||||
if alert.commandTemplate == nil && alert.Command.Command != nil {
|
||||
alert.commandTemplate = []*template.Template{}
|
||||
for i, cmdPart := range alert.Command {
|
||||
for i, cmdPart := range alert.Command.Command {
|
||||
if PyCompat {
|
||||
cmdPart = legacy.Replace(cmdPart)
|
||||
}
|
||||
alert.commandTemplate = append(alert.commandTemplate, template.Must(
|
||||
template.New(alert.Name+string(i)).Parse(cmdPart),
|
||||
))
|
||||
}
|
||||
} else if alert.commandShellTemplate == nil && alert.CommandShell != "" {
|
||||
} else if alert.commandShellTemplate == nil && alert.Command.ShellCommand != "" {
|
||||
shellCmd := alert.Command.ShellCommand
|
||||
if PyCompat {
|
||||
shellCmd = legacy.Replace(shellCmd)
|
||||
}
|
||||
alert.commandShellTemplate = template.Must(
|
||||
template.New(alert.Name).Parse(alert.CommandShell),
|
||||
template.New(alert.Name).Parse(shellCmd),
|
||||
)
|
||||
} else {
|
||||
return fmt.Errorf("No template provided for alert %s", alert.Name)
|
||||
@ -60,7 +74,7 @@ func (alert *Alert) BuildTemplates() error {
|
||||
}
|
||||
|
||||
// Send will send an alert notice by executing the command template
|
||||
func (alert Alert) Send(notice AlertNotice) (output_str string, err error) {
|
||||
func (alert Alert) Send(notice AlertNotice) (outputStr string, err error) {
|
||||
log.Printf("INFO: Sending alert %s for %s", alert.Name, notice.MonitorName)
|
||||
var cmd *exec.Cmd
|
||||
if alert.commandTemplate != nil {
|
||||
@ -95,10 +109,23 @@ func (alert Alert) Send(notice AlertNotice) (output_str string, err error) {
|
||||
|
||||
var output []byte
|
||||
output, err = cmd.CombinedOutput()
|
||||
output_str = string(output)
|
||||
outputStr = string(output)
|
||||
if LogDebug {
|
||||
log.Printf("DEBUG: Alert output for: %s\n---\n%s\n---", alert.Name, output_str)
|
||||
log.Printf("DEBUG: Alert output for: %s\n---\n%s\n---", alert.Name, outputStr)
|
||||
}
|
||||
|
||||
return output_str, err
|
||||
return outputStr, err
|
||||
}
|
||||
|
||||
// NewLogAlert creates an alert that does basic logging using echo
|
||||
func NewLogAlert() *Alert {
|
||||
return &Alert{
|
||||
Name: "log",
|
||||
Command: CommandOrShell{
|
||||
Command: []string{
|
||||
"echo",
|
||||
"{{.MonitorName}} check has failed {{.FailureCount}} times",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,9 @@ func TestAlertIsValid(t *testing.T) {
|
||||
expected bool
|
||||
name string
|
||||
}{
|
||||
{Alert{Command: []string{"echo", "test"}}, true, "Command only"},
|
||||
{Alert{CommandShell: "echo test"}, true, "CommandShell only"},
|
||||
{Alert{Command: CommandOrShell{Command: []string{"echo", "test"}}}, true, "Command only"},
|
||||
{Alert{Command: CommandOrShell{ShellCommand: "echo test"}}, true, "CommandShell only"},
|
||||
{Alert{}, false, "No commands"},
|
||||
{
|
||||
Alert{Command: []string{"echo", "test"}, CommandShell: "echo test"},
|
||||
false,
|
||||
"Both commands",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@ -39,39 +34,54 @@ func TestAlertSend(t *testing.T) {
|
||||
expectedOutput string
|
||||
expectErr bool
|
||||
name string
|
||||
pyCompat bool
|
||||
}{
|
||||
{
|
||||
Alert{Command: []string{"echo", "{{.MonitorName}}"}},
|
||||
Alert{Command: CommandOrShell{Command: []string{"echo", "{{.MonitorName}}"}}},
|
||||
AlertNotice{MonitorName: "test"},
|
||||
"test\n",
|
||||
false,
|
||||
"Command with template",
|
||||
false,
|
||||
},
|
||||
{
|
||||
Alert{CommandShell: "echo {{.MonitorName}}"},
|
||||
Alert{Command: CommandOrShell{ShellCommand: "echo {{.MonitorName}}"}},
|
||||
AlertNotice{MonitorName: "test"},
|
||||
"test\n",
|
||||
false,
|
||||
"Command shell with template",
|
||||
false,
|
||||
},
|
||||
{
|
||||
Alert{Command: []string{"echo", "{{.Bad}}"}},
|
||||
Alert{Command: CommandOrShell{Command: []string{"echo", "{{.Bad}}"}}},
|
||||
AlertNotice{MonitorName: "test"},
|
||||
"",
|
||||
true,
|
||||
"Command with bad template",
|
||||
false,
|
||||
},
|
||||
{
|
||||
Alert{CommandShell: "echo {{.Bad}}"},
|
||||
Alert{Command: CommandOrShell{ShellCommand: "echo {{.Bad}}"}},
|
||||
AlertNotice{MonitorName: "test"},
|
||||
"",
|
||||
true,
|
||||
"Command shell with bad template",
|
||||
false,
|
||||
},
|
||||
{
|
||||
Alert{Command: CommandOrShell{ShellCommand: "echo {alert_message}"}},
|
||||
AlertNotice{MonitorName: "test", FailureCount: 1},
|
||||
"test check has failed 1 times\n",
|
||||
false,
|
||||
"Command shell with legacy template",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
// Set PyCompat to value of compat flag
|
||||
PyCompat = c.pyCompat
|
||||
c.alert.BuildTemplates()
|
||||
output, err := c.alert.Send(c.notice)
|
||||
hasErr := (err != nil)
|
||||
@ -83,6 +93,8 @@ func TestAlertSend(t *testing.T) {
|
||||
t.Errorf("Send(%v err), expected=%v actual=%v", c.name, "Err", err)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
// Set PyCompat back to default value
|
||||
PyCompat = false
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -103,8 +115,8 @@ func TestAlertBuildTemplate(t *testing.T) {
|
||||
expectErr bool
|
||||
name string
|
||||
}{
|
||||
{Alert{Command: []string{"echo", "test"}}, false, "Command only"},
|
||||
{Alert{CommandShell: "echo test"}, false, "CommandShell only"},
|
||||
{Alert{Command: CommandOrShell{Command: []string{"echo", "test"}}}, false, "Command only"},
|
||||
{Alert{Command: CommandOrShell{ShellCommand: "echo test"}}, false, "CommandShell only"},
|
||||
{Alert{}, true, "No commands"},
|
||||
}
|
||||
|
||||
|
67
config.go
67
config.go
@ -15,10 +15,54 @@ type Config struct {
|
||||
Alerts map[string]*Alert
|
||||
}
|
||||
|
||||
// CommandOrShell type wraps a string or list of strings
|
||||
// for executing a command directly or in a shell
|
||||
type CommandOrShell struct {
|
||||
ShellCommand string
|
||||
Command []string
|
||||
}
|
||||
|
||||
// Empty checks if the Command has a value
|
||||
func (cos CommandOrShell) Empty() bool {
|
||||
return (cos.ShellCommand == "" && cos.Command == nil)
|
||||
}
|
||||
|
||||
// UnmarshalYAML allows unmarshalling either a string or slice of strings
|
||||
// and parsing them as either a command or a shell command.
|
||||
func (cos *CommandOrShell) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var cmd []string
|
||||
err := unmarshal(&cmd)
|
||||
// Error indicates this is shell command
|
||||
if err != nil {
|
||||
var shellCmd string
|
||||
err := unmarshal(&shellCmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cos.ShellCommand = shellCmd
|
||||
} else {
|
||||
cos.Command = cmd
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsValid checks config validity and returns true if valid
|
||||
func (config Config) IsValid() (isValid bool) {
|
||||
isValid = true
|
||||
|
||||
// Validate alerts
|
||||
if config.Alerts == nil || len(config.Alerts) == 0 {
|
||||
// This should never happen because there is a default alert named 'log' for now
|
||||
log.Printf("ERROR: Invalid alert configuration: Must provide at least one alert")
|
||||
isValid = false
|
||||
}
|
||||
for _, alert := range config.Alerts {
|
||||
if !alert.IsValid() {
|
||||
log.Printf("ERROR: Invalid alert configuration: %s", alert.Name)
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
|
||||
// Validate monitors
|
||||
if config.Monitors == nil || len(config.Monitors) == 0 {
|
||||
log.Printf("ERROR: Invalid monitor configuration: Must provide at least one monitor")
|
||||
@ -43,18 +87,6 @@ func (config Config) IsValid() (isValid bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate alerts
|
||||
if config.Alerts == nil || len(config.Alerts) == 0 {
|
||||
log.Printf("ERROR: Invalid alert configuration: Must provide at least one alert")
|
||||
isValid = false
|
||||
}
|
||||
for _, alert := range config.Alerts {
|
||||
if !alert.IsValid() {
|
||||
log.Printf("ERROR: Invalid alert configuration: %s", alert.Name)
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -86,6 +118,17 @@ func LoadConfig(filePath string) (config Config, err error) {
|
||||
log.Printf("DEBUG: Config values:\n%v\n", config)
|
||||
}
|
||||
|
||||
// Add log alert if not present
|
||||
if PyCompat {
|
||||
// Intialize alerts list if not present
|
||||
if config.Alerts == nil {
|
||||
config.Alerts = map[string]*Alert{}
|
||||
}
|
||||
if _, ok := config.Alerts["log"]; !ok {
|
||||
config.Alerts["log"] = NewLogAlert()
|
||||
}
|
||||
}
|
||||
|
||||
if !config.IsValid() {
|
||||
err = errors.New("Invalid configuration")
|
||||
return
|
||||
|
@ -10,22 +10,29 @@ func TestLoadConfig(t *testing.T) {
|
||||
configPath string
|
||||
expectErr bool
|
||||
name string
|
||||
pyCompat bool
|
||||
}{
|
||||
{"./test/valid-config.yml", false, "Valid config file"},
|
||||
{"./test/does-not-exist", true, "Invalid config path"},
|
||||
{"./test/invalid-config-type.yml", true, "Invalid config type for key"},
|
||||
{"./test/invalid-config-missing-alerts.yml", true, "Invalid config missing alerts"},
|
||||
{"./test/invalid-config-unknown-alert.yml", true, "Invalid config unknown alert"},
|
||||
{"./test/valid-config.yml", false, "Valid config file", false},
|
||||
{"./test/valid-default-log-alert.yml", false, "Valid config file with default log alert PyCompat", true},
|
||||
{"./test/valid-default-log-alert.yml", true, "Invalid config file no log alert", false},
|
||||
{"./test/does-not-exist", true, "Invalid config path", false},
|
||||
{"./test/invalid-config-type.yml", true, "Invalid config type for key", false},
|
||||
{"./test/invalid-config-missing-alerts.yml", true, "Invalid config missing alerts", false},
|
||||
{"./test/invalid-config-unknown-alert.yml", true, "Invalid config unknown alert", false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
// Set PyCompat based on compatibility mode
|
||||
PyCompat = c.pyCompat
|
||||
_, err := LoadConfig(c.configPath)
|
||||
hasErr := (err != nil)
|
||||
if hasErr != c.expectErr {
|
||||
t.Errorf("LoadConfig(%v), expected_error=%v actual=%v", c.name, c.expectErr, err)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
// Set PyCompat to default value
|
||||
PyCompat = false
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -42,7 +49,7 @@ func TestMultiLineConfig(t *testing.T) {
|
||||
log.Println("-----")
|
||||
log.Println("TestMultiLineConfig(parse > string)")
|
||||
expected := "echo 'Some string with stuff'; echo \"<angle brackets>\"; exit 1\n"
|
||||
actual := config.Monitors[0].CommandShell
|
||||
actual := config.Monitors[0].Command.ShellCommand
|
||||
if expected != actual {
|
||||
t.Errorf("TestMultiLineConfig(>) failed")
|
||||
t.Logf("string expected=`%v`", expected)
|
||||
@ -70,7 +77,7 @@ func TestMultiLineConfig(t *testing.T) {
|
||||
log.Println("-----")
|
||||
log.Println("TestMultiLineConfig(parse | string)")
|
||||
expected = "echo 'Some string with stuff'\necho '<angle brackets>'\n"
|
||||
actual = config.Alerts["log_shell"].CommandShell
|
||||
actual = config.Alerts["log_shell"].Command.ShellCommand
|
||||
if expected != actual {
|
||||
t.Errorf("TestMultiLineConfig(|) failed")
|
||||
t.Logf("string expected=`%v`", expected)
|
||||
|
7
main.go
7
main.go
@ -18,6 +18,9 @@ var (
|
||||
// Metrics contains all active metrics
|
||||
Metrics = NewMetrics()
|
||||
|
||||
// PyCompat enables support for legacy Python templates
|
||||
PyCompat = false
|
||||
|
||||
// version of minitor being run
|
||||
version = "dev"
|
||||
)
|
||||
@ -83,7 +86,9 @@ func main() {
|
||||
// Get debug flag
|
||||
flag.BoolVar(&LogDebug, "debug", false, "Enables debug logs (default: false)")
|
||||
flag.BoolVar(&ExportMetrics, "metrics", false, "Enables prometheus metrics exporting (default: false)")
|
||||
flag.BoolVar(&PyCompat, "py-compat", false, "Enables support for legacy Python Minitor config. Will eventually be removed. (default: false)")
|
||||
var showVersion = flag.Bool("version", false, "Display the version of minitor and exit")
|
||||
var configPath = flag.String("config", "config.yml", "Alternate configuration path (default: config.yml)")
|
||||
flag.Parse()
|
||||
|
||||
// Print version if flag is provided
|
||||
@ -93,7 +98,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Load configuration
|
||||
config, err := LoadConfig("config.yml")
|
||||
config, err := LoadConfig(*configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading config: %v", err)
|
||||
}
|
||||
|
18
main_test.go
18
main_test.go
@ -18,7 +18,7 @@ func TestCheckMonitors(t *testing.T) {
|
||||
Monitors: []*Monitor{
|
||||
&Monitor{
|
||||
Name: "Success",
|
||||
Command: []string{"true"},
|
||||
Command: CommandOrShell{Command: []string{"true"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -30,12 +30,12 @@ func TestCheckMonitors(t *testing.T) {
|
||||
Monitors: []*Monitor{
|
||||
&Monitor{
|
||||
Name: "Failure",
|
||||
Command: []string{"false"},
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
AlertAfter: 1,
|
||||
},
|
||||
&Monitor{
|
||||
Name: "Failure",
|
||||
Command: []string{"false"},
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
AlertDown: []string{"unknown"},
|
||||
AlertAfter: 1,
|
||||
},
|
||||
@ -49,12 +49,12 @@ func TestCheckMonitors(t *testing.T) {
|
||||
Monitors: []*Monitor{
|
||||
&Monitor{
|
||||
Name: "Success",
|
||||
Command: []string{"ls"},
|
||||
Command: CommandOrShell{Command: []string{"ls"}},
|
||||
alertCount: 1,
|
||||
},
|
||||
&Monitor{
|
||||
Name: "Success",
|
||||
Command: []string{"true"},
|
||||
Command: CommandOrShell{Command: []string{"true"}},
|
||||
AlertUp: []string{"unknown"},
|
||||
alertCount: 1,
|
||||
},
|
||||
@ -68,14 +68,14 @@ func TestCheckMonitors(t *testing.T) {
|
||||
Monitors: []*Monitor{
|
||||
&Monitor{
|
||||
Name: "Failure",
|
||||
Command: []string{"false"},
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
AlertDown: []string{"good"},
|
||||
AlertAfter: 1,
|
||||
},
|
||||
},
|
||||
Alerts: map[string]*Alert{
|
||||
"good": &Alert{
|
||||
Command: []string{"true"},
|
||||
Command: CommandOrShell{Command: []string{"true"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -87,7 +87,7 @@ func TestCheckMonitors(t *testing.T) {
|
||||
Monitors: []*Monitor{
|
||||
&Monitor{
|
||||
Name: "Failure",
|
||||
Command: []string{"false"},
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
AlertDown: []string{"bad"},
|
||||
AlertAfter: 1,
|
||||
},
|
||||
@ -95,7 +95,7 @@ func TestCheckMonitors(t *testing.T) {
|
||||
Alerts: map[string]*Alert{
|
||||
"bad": &Alert{
|
||||
Name: "bad",
|
||||
Command: []string{"false"},
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
14
monitor.go
14
monitor.go
@ -11,8 +11,7 @@ import (
|
||||
type Monitor struct {
|
||||
// Config values
|
||||
Name string
|
||||
Command []string
|
||||
CommandShell string `yaml:"command_shell"`
|
||||
Command CommandOrShell
|
||||
AlertDown []string `yaml:"alert_down"`
|
||||
AlertUp []string `yaml:"alert_up"`
|
||||
CheckInterval float64 `yaml:"check_interval"`
|
||||
@ -29,10 +28,7 @@ type Monitor struct {
|
||||
// IsValid returns a boolean indicating if the Monitor has been correctly
|
||||
// configured
|
||||
func (monitor Monitor) IsValid() bool {
|
||||
atLeastOneCommand := (monitor.CommandShell != "" || monitor.Command != nil)
|
||||
atMostOneCommand := (monitor.CommandShell == "" || monitor.Command == nil)
|
||||
return (atLeastOneCommand &&
|
||||
atMostOneCommand &&
|
||||
return (!monitor.Command.Empty() &&
|
||||
monitor.getAlertAfter() > 0 &&
|
||||
monitor.AlertDown != nil)
|
||||
}
|
||||
@ -52,10 +48,10 @@ func (monitor Monitor) ShouldCheck() bool {
|
||||
// and a possible AlertNotice
|
||||
func (monitor *Monitor) Check() (bool, *AlertNotice) {
|
||||
var cmd *exec.Cmd
|
||||
if monitor.Command != nil {
|
||||
cmd = exec.Command(monitor.Command[0], monitor.Command[1:]...)
|
||||
if monitor.Command.Command != nil {
|
||||
cmd = exec.Command(monitor.Command.Command[0], monitor.Command.Command[1:]...)
|
||||
} else {
|
||||
cmd = ShellCommand(monitor.CommandShell)
|
||||
cmd = ShellCommand(monitor.Command.ShellCommand)
|
||||
}
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
@ -13,16 +13,11 @@ func TestMonitorIsValid(t *testing.T) {
|
||||
expected bool
|
||||
name string
|
||||
}{
|
||||
{Monitor{Command: []string{"echo", "test"}, AlertDown: []string{"log"}}, true, "Command only"},
|
||||
{Monitor{CommandShell: "echo test", AlertDown: []string{"log"}}, true, "CommandShell only"},
|
||||
{Monitor{Command: []string{"echo", "test"}}, false, "No AlertDown"},
|
||||
{Monitor{Command: CommandOrShell{Command: []string{"echo", "test"}}, AlertDown: []string{"log"}}, true, "Command only"},
|
||||
{Monitor{Command: CommandOrShell{ShellCommand: "echo test"}, AlertDown: []string{"log"}}, true, "CommandShell only"},
|
||||
{Monitor{Command: CommandOrShell{Command: []string{"echo", "test"}}}, false, "No AlertDown"},
|
||||
{Monitor{AlertDown: []string{"log"}}, false, "No commands"},
|
||||
{
|
||||
Monitor{Command: []string{"echo", "test"}, CommandShell: "echo test", AlertDown: []string{"log"}},
|
||||
false,
|
||||
"Both commands",
|
||||
},
|
||||
{Monitor{Command: []string{"echo", "test"}, AlertDown: []string{"log"}, AlertAfter: -1}, false, "Invalid alert threshold, -1"},
|
||||
{Monitor{Command: CommandOrShell{Command: []string{"echo", "test"}}, AlertDown: []string{"log"}, AlertAfter: -1}, false, "Invalid alert threshold, -1"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@ -254,22 +249,22 @@ func TestMonitorCheck(t *testing.T) {
|
||||
name string
|
||||
}{
|
||||
{
|
||||
Monitor{Command: []string{"echo", "success"}},
|
||||
Monitor{Command: CommandOrShell{Command: []string{"echo", "success"}}},
|
||||
expected{isSuccess: true, hasNotice: false, lastOutput: "success\n"},
|
||||
"Test successful command",
|
||||
},
|
||||
{
|
||||
Monitor{CommandShell: "echo success"},
|
||||
Monitor{Command: CommandOrShell{ShellCommand: "echo success"}},
|
||||
expected{isSuccess: true, hasNotice: false, lastOutput: "success\n"},
|
||||
"Test successful command shell",
|
||||
},
|
||||
{
|
||||
Monitor{Command: []string{"total", "failure"}},
|
||||
Monitor{Command: CommandOrShell{Command: []string{"total", "failure"}}},
|
||||
expected{isSuccess: false, hasNotice: true, lastOutput: ""},
|
||||
"Test failed command",
|
||||
},
|
||||
{
|
||||
Monitor{CommandShell: "false"},
|
||||
Monitor{Command: CommandOrShell{ShellCommand: "false"}},
|
||||
expected{isSuccess: false, hasNotice: true, lastOutput: ""},
|
||||
"Test failed command shell",
|
||||
},
|
||||
|
@ -25,7 +25,7 @@ alerts:
|
||||
email_up:
|
||||
command: [sendmail, "me@minitor.mon", "Recovered: {monitor_name}", "We're back!"]
|
||||
mailgun_down:
|
||||
command_shell: >
|
||||
command: >
|
||||
curl -s -X POST
|
||||
-F subject="Alert! {{.MonitorName}} failed"
|
||||
-F from="Minitor <minitor@minitor.mon>"
|
||||
@ -34,7 +34,7 @@ alerts:
|
||||
https://api.mailgun.net/v3/minitor.mon/messages
|
||||
-u "api:${MAILGUN_API_KEY}"
|
||||
sms_down:
|
||||
command_shell: >
|
||||
command: >
|
||||
curl -s -X POST -F "Body=Failure! {{.MonitorName}} has failed"
|
||||
-F "From=${AVAILABLE_NUMBER}" -F "To=${MY_PHONE}"
|
||||
"https://api.twilio.com/2010-04-01/Accounts/${ACCOUNT_SID}/Messages"
|
||||
|
@ -7,7 +7,7 @@ monitors:
|
||||
alert_down: ['log_command', 'log_shell']
|
||||
alert_every: 0
|
||||
- name: Shell
|
||||
command_shell: >
|
||||
command: >
|
||||
echo 'Some string with stuff';
|
||||
echo 'another line';
|
||||
echo $PATH;
|
||||
@ -20,4 +20,4 @@ alerts:
|
||||
log_command:
|
||||
command: ['echo', 'regular', '"command!!!"', "{{.MonitorName}}"]
|
||||
log_shell:
|
||||
command_shell: echo "Failure on {{.MonitorName}} User is $USER"
|
||||
command: echo "Failure on {{.MonitorName}} User is $USER"
|
||||
|
8
test/valid-default-log-alert.yml
Normal file
8
test/valid-default-log-alert.yml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
check_interval: 1
|
||||
|
||||
monitors:
|
||||
- name: Command
|
||||
command: ['echo', '$PATH']
|
||||
alert_down: ['log']
|
||||
alert_every: 0
|
@ -3,7 +3,7 @@ check_interval: 1
|
||||
|
||||
monitors:
|
||||
- name: Shell
|
||||
command_shell: >
|
||||
command: >
|
||||
echo 'Some string with stuff';
|
||||
echo "<angle brackets>";
|
||||
exit 1
|
||||
@ -13,6 +13,6 @@ monitors:
|
||||
|
||||
alerts:
|
||||
log_shell:
|
||||
command_shell: |
|
||||
command: |
|
||||
echo 'Some string with stuff'
|
||||
echo '<angle brackets>'
|
||||
|
Loading…
Reference in New Issue
Block a user