minitor-go/alert.go

132 lines
3.5 KiB
Go
Raw Permalink Normal View History

2019-09-21 22:03:26 +00:00
package main
import (
"bytes"
2019-10-03 23:30:49 +00:00
"fmt"
"log"
2019-09-21 22:03:26 +00:00
"os/exec"
"strings"
2019-09-21 22:03:26 +00:00
"text/template"
"time"
)
2019-10-02 23:09:11 +00:00
// Alert is a config driven mechanism for sending a notice
2019-09-21 22:03:26 +00:00
type Alert struct {
Name string
Command CommandOrShell
2019-10-02 16:37:29 +00:00
commandTemplate []*template.Template
commandShellTemplate *template.Template
2019-09-21 22:03:26 +00:00
}
2019-10-02 23:09:11 +00:00
// AlertNotice captures the context for an alert to be sent
type AlertNotice struct {
MonitorName string
AlertCount int16
FailureCount int16
LastCheckOutput string
LastSuccess time.Time
IsUp bool
}
// IsValid returns a boolean indicating if the Alert has been correctly
// configured
2019-09-21 22:03:26 +00:00
func (alert Alert) IsValid() bool {
return !alert.Command.Empty()
2019-09-21 22:03:26 +00:00
}
2019-10-02 23:09:11 +00:00
// BuildTemplates compiles command templates for the Alert
2019-10-03 23:30:49 +00:00
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.Command != nil {
2019-10-02 16:37:29 +00:00
alert.commandTemplate = []*template.Template{}
for i, cmdPart := range alert.Command.Command {
2020-02-20 01:31:04 +00:00
if PyCompat {
cmdPart = legacy.Replace(cmdPart)
}
2019-10-02 16:37:29 +00:00
alert.commandTemplate = append(alert.commandTemplate, template.Must(
2020-11-16 23:56:31 +00:00
template.New(alert.Name+fmt.Sprint(i)).Parse(cmdPart),
2019-10-02 16:37:29 +00:00
))
}
} else if alert.commandShellTemplate == nil && alert.Command.ShellCommand != "" {
2020-02-20 01:31:04 +00:00
shellCmd := alert.Command.ShellCommand
if PyCompat {
shellCmd = legacy.Replace(shellCmd)
}
2019-09-21 22:03:26 +00:00
alert.commandShellTemplate = template.Must(
2020-02-20 01:31:04 +00:00
template.New(alert.Name).Parse(shellCmd),
2019-09-21 22:03:26 +00:00
)
} else {
2019-10-03 23:30:49 +00:00
return fmt.Errorf("No template provided for alert %s", alert.Name)
2019-09-21 22:03:26 +00:00
}
2019-10-03 23:30:49 +00:00
return nil
2019-09-21 22:03:26 +00:00
}
2019-10-02 23:09:11 +00:00
// Send will send an alert notice by executing the command template
2020-02-18 00:46:56 +00:00
func (alert Alert) Send(notice AlertNotice) (outputStr string, err error) {
log.Printf("INFO: Sending alert %s for %s", alert.Name, notice.MonitorName)
2019-09-21 22:03:26 +00:00
var cmd *exec.Cmd
if alert.commandTemplate != nil {
2019-10-02 16:37:29 +00:00
command := []string{}
for _, cmdTmp := range alert.commandTemplate {
var commandBuffer bytes.Buffer
2019-10-03 23:30:49 +00:00
err = cmdTmp.Execute(&commandBuffer, notice)
2019-10-02 16:37:29 +00:00
if err != nil {
2019-10-03 23:30:49 +00:00
return
2019-10-02 16:37:29 +00:00
}
command = append(command, commandBuffer.String())
}
cmd = exec.Command(command[0], command[1:]...)
2019-09-21 22:03:26 +00:00
} else if alert.commandShellTemplate != nil {
var commandBuffer bytes.Buffer
2019-10-03 23:30:49 +00:00
err = alert.commandShellTemplate.Execute(&commandBuffer, notice)
if err != nil {
2019-10-03 23:30:49 +00:00
return
}
2019-10-02 16:37:29 +00:00
shellCommand := commandBuffer.String()
2019-10-02 16:37:29 +00:00
cmd = ShellCommand(shellCommand)
2019-09-21 22:03:26 +00:00
} else {
2019-10-03 23:30:49 +00:00
err = fmt.Errorf("No templates compiled for alert %v", alert.Name)
return
2019-10-02 16:37:29 +00:00
}
2019-10-03 23:30:49 +00:00
// Exit if we're not ready to run the command
if cmd == nil || err != nil {
return
2019-09-21 22:03:26 +00:00
}
2019-10-03 23:30:49 +00:00
var output []byte
output, err = cmd.CombinedOutput()
2020-02-18 00:46:56 +00:00
outputStr = string(output)
if LogDebug {
2020-02-18 00:46:56 +00:00
log.Printf("DEBUG: Alert output for: %s\n---\n%s\n---", alert.Name, outputStr)
}
2019-10-03 23:30:49 +00:00
2020-02-18 00:46:56 +00:00
return outputStr, err
2019-09-21 22:03:26 +00:00
}
2020-02-18 00:47:43 +00:00
// NewLogAlert creates an alert that does basic logging using echo
func NewLogAlert() *Alert {
return &Alert{
Name: "log",
Command: CommandOrShell{
Command: []string{
"echo",
"{{.MonitorName}} {{if .IsUp}}has recovered{{else}}check has failed {{.FailureCount}} times{{end}}",
2020-02-18 00:47:43 +00:00
},
},
}
}