WIP
This commit is contained in:
parent
0aa0fef19d
commit
342b12432e
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@
|
|||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
|
config.yml
|
||||||
|
59
alert.go
Normal file
59
alert.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Alert struct {
|
||||||
|
Name string
|
||||||
|
Command []string
|
||||||
|
CommandShell string `yaml:"command_shell"`
|
||||||
|
commandTemplate []template.Template
|
||||||
|
commandShellTemplate template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
func (alert Alert) IsValid() bool {
|
||||||
|
atLeastOneCommand := (alert.CommandShell != "" || alert.Command != nil)
|
||||||
|
atMostOneCommand := (alert.CommandShell == "" || alert.Command == nil)
|
||||||
|
return atLeastOneCommand && atMostOneCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (alert *Alert) BuildTemplates() {
|
||||||
|
if alert.commandTemplate == nil && alert.Command != nil {
|
||||||
|
// build template
|
||||||
|
fmt.Println("Building template for command...")
|
||||||
|
} else if alert.commandShellTemplate == nil && alert.CommandShell != "" {
|
||||||
|
alert.commandShellTemplate = template.Must(
|
||||||
|
template.New(alert.Name).Parse(alert.CommandShell),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
panic("No template?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (alert Alert) Send(notice AlertNotice) {
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
|
||||||
|
if alert.commandTemplate != nil {
|
||||||
|
// build template
|
||||||
|
fmt.Println("Send command thing...")
|
||||||
|
} else if alert.commandShellTemplate != nil {
|
||||||
|
var commandBuffer bytes.Buffer
|
||||||
|
err := alert.commandShellTemplate.Execute(&commandBuffer, notice)
|
||||||
|
// TODO handle error
|
||||||
|
cmd = exec.Command(commandBuffer.String())
|
||||||
|
} else {
|
||||||
|
panic("No template?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlertNotice struct {
|
||||||
|
MonitorName string
|
||||||
|
AlertCount int64
|
||||||
|
FailureCount int64
|
||||||
|
LastCheckOutput string
|
||||||
|
LastSuccess time.Time
|
||||||
|
}
|
32
config.go
Normal file
32
config.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
CheckInterval int64 `yaml:"check_interval"`
|
||||||
|
Monitors []Monitor
|
||||||
|
Alerts map[string]Alert
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConfig(filePath string) (config Config) {
|
||||||
|
data, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
env_expanded := os.ExpandEnv(string(data))
|
||||||
|
|
||||||
|
err = yaml.Unmarshal([]byte(env_expanded), &config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error: %v", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("config:\n%v\n", config)
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
20
main.go
Normal file
20
main.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config := LoadConfig("config.yml")
|
||||||
|
|
||||||
|
for {
|
||||||
|
for _, monitor := range config.Monitors {
|
||||||
|
if monitor.ShouldCheck() {
|
||||||
|
monitor.Check()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sleepTime := time.Duration(config.CheckInterval) * time.Second
|
||||||
|
time.Sleep(sleepTime)
|
||||||
|
}
|
||||||
|
}
|
77
monitor.go
Normal file
77
monitor.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Monitor struct {
|
||||||
|
// Config values
|
||||||
|
Name string
|
||||||
|
Command []string
|
||||||
|
CommandShell string `yaml:"command_shell`
|
||||||
|
AlertDown []string `yaml:"alert_down"`
|
||||||
|
AlertUp []string `yaml:"alert_up"`
|
||||||
|
CheckInterval float64 `yaml:"check_interval"`
|
||||||
|
AlertAfter int16 `yaml:"alert_after"`
|
||||||
|
AlertEvey int16 `yaml:"alert_every"`
|
||||||
|
// Other values
|
||||||
|
LastCheck time.Time
|
||||||
|
LastOutput string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (monitor Monitor) IsValid() bool {
|
||||||
|
atLeastOneCommand := (monitor.CommandShell != "" || monitor.Command != nil)
|
||||||
|
atMostOneCommand := (monitor.CommandShell == "" || monitor.Command == nil)
|
||||||
|
return atLeastOneCommand && atMostOneCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (monitor Monitor) ShouldCheck() bool {
|
||||||
|
if monitor.LastCheck.IsZero() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sinceLastCheck := time.Now().Sub(monitor.LastCheck).Seconds()
|
||||||
|
return sinceLastCheck >= monitor.CheckInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
func (monitor *Monitor) Check() bool {
|
||||||
|
// TODO: This should probably return a list of alerts since the `raise`
|
||||||
|
// pattern doesn't carry over from Python
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
|
||||||
|
if monitor.Command != nil {
|
||||||
|
cmd = exec.Command(monitor.Command[0], monitor.Command[1:]...)
|
||||||
|
} else {
|
||||||
|
// TODO: Handle a command shell as well. This is untested
|
||||||
|
cmd = exec.Command(monitor.CommandShell)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
log.Printf("Check %s\n---\n%s\n---", monitor.Name, string(output))
|
||||||
|
|
||||||
|
is_success := (err == nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor.LastCheck = time.Now()
|
||||||
|
monitor.LastOutput = string(output)
|
||||||
|
|
||||||
|
if is_success {
|
||||||
|
monitor.success()
|
||||||
|
} else {
|
||||||
|
monitor.failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_success
|
||||||
|
}
|
||||||
|
|
||||||
|
func (monitor Monitor) success() {
|
||||||
|
log.Printf("Great success!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (monitor *Monitor) failure() {
|
||||||
|
log.Printf("Devastating failure. :(")
|
||||||
|
}
|
29
sample-config.yml
Normal file
29
sample-config.yml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
check_interval: 30
|
||||||
|
|
||||||
|
monitors:
|
||||||
|
- name: My Website
|
||||||
|
command: [ 'curl', '-s', '-o', '/dev/null', 'https://minitor.mon' ]
|
||||||
|
alert_down: [ log, mailgun_down, sms_down ]
|
||||||
|
alert_up: [ log, email_up ]
|
||||||
|
check_interval: 30 # Must be at minimum the global `check_interval`
|
||||||
|
alert_after: 3
|
||||||
|
alert_every: -1 # Defaults to -1 for exponential backoff. 0 to disable repeating
|
||||||
|
|
||||||
|
alerts:
|
||||||
|
email_up:
|
||||||
|
command: [ sendmail, "me@minitor.mon", "Recovered: {monitor_name}", "We're back!" ]
|
||||||
|
mailgun_down:
|
||||||
|
command: >
|
||||||
|
curl -s -X POST
|
||||||
|
-F subject="Alert! {monitor_name} failed"
|
||||||
|
-F from="Minitor <minitor@minitor.mon>"
|
||||||
|
-F to=me@minitor.mon
|
||||||
|
-F text="Our monitor failed"
|
||||||
|
https://api.mailgun.net/v3/minitor.mon/messages
|
||||||
|
-u "api:${MAILGUN_API_KEY}"
|
||||||
|
sms_down:
|
||||||
|
command: >
|
||||||
|
curl -s -X POST -F "Body=Failure! {monitor_name} has failed"
|
||||||
|
-F "From=${AVAILABLE_NUMBER}" -F "To=${MY_PHONE}"
|
||||||
|
"https://api.twilio.com/2010-04-01/Accounts/${ACCOUNT_SID}/Messages"
|
||||||
|
-u "${ACCOUNT_SID}:${AUTH_TOKEN}"
|
Loading…
Reference in New Issue
Block a user