diff options
| author | Paul Buetow <paul@buetow.org> | 2023-04-17 19:47:08 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2023-04-17 19:47:08 +0300 |
| commit | f7456fded178b85637c82255e9c3178817f5fa68 (patch) | |
| tree | 78d4a0e2d03793f3a1ce5ae6a14fa7684b59b598 | |
| parent | e96f62acfd73d1c6b9a540927ada2ac8c81cffc1 (diff) | |
initial version
| -rw-r--r-- | check.go | 31 | ||||
| -rw-r--r-- | config.go | 65 | ||||
| -rw-r--r-- | exitcodes.go | 21 | ||||
| -rw-r--r-- | go.mod | 3 | ||||
| -rw-r--r-- | gogios.json | 17 | ||||
| -rw-r--r-- | main.go | 33 | ||||
| -rw-r--r-- | notify.go | 28 | ||||
| -rw-r--r-- | state.go | 5 |
8 files changed, 203 insertions, 0 deletions
diff --git a/check.go b/check.go new file mode 100644 index 0000000..b011eba --- /dev/null +++ b/check.go @@ -0,0 +1,31 @@ +package main + +import ( + "bytes" + "context" + "log" + "os/exec" +) + +type check struct { + Name string + Plugin string + Args []string +} + +func (c check) execute(ctx context.Context) (string, int) { + cmd := exec.CommandContext(ctx, c.Plugin, c.Args...) + + var bytes bytes.Buffer + cmd.Stdout = &bytes + cmd.Stderr = &bytes + log.Println(ctx) + + if err := cmd.Run(); err != nil { + if ctx.Err() == context.DeadlineExceeded { + return "Check command timed out", critical + } + } + + return bytes.String(), cmd.ProcessState.ExitCode() +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..b79e275 --- /dev/null +++ b/config.go @@ -0,0 +1,65 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" +) + +type config struct { + EmailTo string + EmailFrom string + SMTPServer string `json:"omitempty"` + StateDir string `json:"omitempty"` + CheckTimeoutS int + Checks []check +} + +func newConfig(configFile string) (config, error) { + var config config + + // Open the file + file, err := os.Open(configFile) + if err != nil { + return config, err + } + defer file.Close() + + // Read the file content + bytes, err := ioutil.ReadAll(file) + if err != nil { + return config, err + } + + // Parse the JSON content + err = json.Unmarshal(bytes, &config) + if err != nil { + return config, err + } + + if config.SMTPServer == "" { + hostname, err := os.Hostname() + if err != nil { + panic(err) + } + config.SMTPServer = fmt.Sprintf("%s:25", hostname) + log.Println("Set SMTPServer to " + config.SMTPServer) + } + + if config.StateDir == "" { + config.StateDir = "." + log.Println("Set StateDir to " + config.StateDir) + } + + dedup := make(map[string]interface{}) + for _, check := range config.Checks { + if _, ok := dedup[check.Name]; ok { + return config, fmt.Errorf("Duplicate definition of check %s", check.Name) + } + dedup[check.Name] = struct{}{} + } + + return config, nil +} diff --git a/exitcodes.go b/exitcodes.go new file mode 100644 index 0000000..18e3e19 --- /dev/null +++ b/exitcodes.go @@ -0,0 +1,21 @@ +package main + +const ( + ok = 0 + warning = 1 + critical = 2 + unknown = 3 +) + +func codeToString(state int) string { + switch state { + case 0: + return "OK" + case 1: + return "WARNING" + case 2: + return "CRITICAL" + default: + return "UNKNOWN" + } +} @@ -0,0 +1,3 @@ +module codeberg.org/snonux/gogios + +go 1.19 diff --git a/gogios.json b/gogios.json new file mode 100644 index 0000000..2f2dbe0 --- /dev/null +++ b/gogios.json @@ -0,0 +1,17 @@ +{ + "EmailTo": "paul", + "EmailFrom": "gogios@buetow.org", + "CheckTimeoutS": 10, + "Checks": [ + { + "Name": "www.foo.zone HTTP IPv4", + "Plugin": "/usr/local/libexec/nagios/check_http", + "Args": ["www.foo.zone", "-4"] + }, + { + "Name": "www.foo.zone HTTP IPv6", + "Plugin": "/usr/local/libexec/nagios/check_http", + "Args": ["www.foo.zone", "-6"] + } + ] +} @@ -0,0 +1,33 @@ +package main + +import ( + "context" + "flag" + "fmt" + "log" + "time" +) + +func main() { + log.Println("Welcome to Gogios!") + + configFile := flag.String("cfg", "/etc/gogios.json", "The config file") + + flag.Parse() + + config, err := newConfig(*configFile) + if err != nil { + panic(err) + } + + for _, check := range config.Checks { + ctx, cancel := context.WithTimeout(context.Background(), + time.Duration(config.CheckTimeoutS)*time.Second) + defer cancel() + + if output, status := check.execute(ctx); status != ok { + subject := fmt.Sprintf("GOGIOS %s: %s", codeToString(status), check.Name) + notify(config, subject, output) + } + } +} diff --git a/notify.go b/notify.go new file mode 100644 index 0000000..c31a242 --- /dev/null +++ b/notify.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "log" + "net/smtp" +) + +func notify(config config, subject, body string) error { + log.Println("emailNotify", subject, body) + + headers := make(map[string]string) + headers["From"] = config.EmailFrom + headers["To"] = config.EmailTo + headers["Subject"] = subject + headers["MIME-Version"] = "1.0" + headers["Content-Type"] = "text/plain; charset=\"utf-8\"" + + header := "" + for k, v := range headers { + header += fmt.Sprintf("%s: %s\r\n", k, v) + } + + message := header + "\r\n" + body + log.Println("Using SMTP server", config.SMTPServer) + return smtp.SendMail(config.SMTPServer, nil, config.EmailFrom, + []string{config.EmailTo}, []byte(message)) +} diff --git a/state.go b/state.go new file mode 100644 index 0000000..c3c7eab --- /dev/null +++ b/state.go @@ -0,0 +1,5 @@ +package main + +type state struct { + Checks map[string]int +} |
