From f7456fded178b85637c82255e9c3178817f5fa68 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 17 Apr 2023 19:47:08 +0300 Subject: initial version --- check.go | 31 +++++++++++++++++++++++++++++ config.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ exitcodes.go | 21 ++++++++++++++++++++ go.mod | 3 +++ gogios.json | 17 ++++++++++++++++ main.go | 33 ++++++++++++++++++++++++++++++ notify.go | 28 ++++++++++++++++++++++++++ state.go | 5 +++++ 8 files changed, 203 insertions(+) create mode 100644 check.go create mode 100644 config.go create mode 100644 exitcodes.go create mode 100644 go.mod create mode 100644 gogios.json create mode 100644 main.go create mode 100644 notify.go create mode 100644 state.go 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" + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e8c05ec --- /dev/null +++ b/go.mod @@ -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"] + } + ] +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..cf63a5f --- /dev/null +++ b/main.go @@ -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 +} -- cgit v1.2.3