summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2023-04-17 20:41:22 +0300
committerPaul Buetow <paul@buetow.org>2023-04-17 20:41:22 +0300
commite40e22f69470b987bfb2c39b4a18df67f497739d (patch)
tree73d03e14614b5b29ff07dec4ccb4cdad61234493
parentf7456fded178b85637c82255e9c3178817f5fa68 (diff)
can keep state
-rw-r--r--check.go2
-rw-r--r--config.go10
-rw-r--r--gogios.json18
-rw-r--r--main.go20
-rw-r--r--notify.go4
-rw-r--r--state.go78
6 files changed, 108 insertions, 24 deletions
diff --git a/check.go b/check.go
index b011eba..b98dd6a 100644
--- a/check.go
+++ b/check.go
@@ -3,7 +3,6 @@ package main
import (
"bytes"
"context"
- "log"
"os/exec"
)
@@ -19,7 +18,6 @@ func (c check) execute(ctx context.Context) (string, int) {
var bytes bytes.Buffer
cmd.Stdout = &bytes
cmd.Stderr = &bytes
- log.Println(ctx)
if err := cmd.Run(); err != nil {
if ctx.Err() == context.DeadlineExceeded {
diff --git a/config.go b/config.go
index b79e275..660b49d 100644
--- a/config.go
+++ b/config.go
@@ -14,7 +14,7 @@ type config struct {
SMTPServer string `json:"omitempty"`
StateDir string `json:"omitempty"`
CheckTimeoutS int
- Checks []check
+ Checks map[string]check
}
func newConfig(configFile string) (config, error) {
@@ -53,13 +53,5 @@ func newConfig(configFile string) (config, error) {
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/gogios.json b/gogios.json
index 2f2dbe0..a6cbace 100644
--- a/gogios.json
+++ b/gogios.json
@@ -2,16 +2,22 @@
"EmailTo": "paul",
"EmailFrom": "gogios@buetow.org",
"CheckTimeoutS": 10,
- "Checks": [
- {
- "Name": "www.foo.zone HTTP IPv4",
+ "Checks": {
+ "foo.zone HTTP IPv4": {
+ "Plugin": "/usr/local/libexec/nagios/check_http",
+ "Args": ["foo.zone", "-4"]
+ },
+ "foo.zone HTTP IPv6": {
+ "Plugin": "/usr/local/libexec/nagios/check_http",
+ "Args": ["foo.zone", "-6"]
+ },
+ "www.foo.zone HTTP IPv4": {
"Plugin": "/usr/local/libexec/nagios/check_http",
"Args": ["www.foo.zone", "-4"]
},
- {
- "Name": "www.foo.zone HTTP IPv6",
+ "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
index cf63a5f..d7df829 100644
--- a/main.go
+++ b/main.go
@@ -4,15 +4,11 @@ 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)
@@ -20,14 +16,26 @@ func main() {
panic(err)
}
- for _, check := range config.Checks {
+ state, err := newState(config)
+ if err != nil {
+ notifyError(config, err)
+ }
+
+ for name, 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 {
+ output, status := check.execute(ctx)
+ stateChanged := state.update(name, status)
+
+ if status != ok || stateChanged {
subject := fmt.Sprintf("GOGIOS %s: %s", codeToString(status), check.Name)
notify(config, subject, output)
}
}
+
+ if err := state.persist(); err != nil {
+ notifyError(config, err)
+ }
}
diff --git a/notify.go b/notify.go
index c31a242..3a295ba 100644
--- a/notify.go
+++ b/notify.go
@@ -26,3 +26,7 @@ func notify(config config, subject, body string) error {
return smtp.SendMail(config.SMTPServer, nil, config.EmailFrom,
[]string{config.EmailTo}, []byte(message))
}
+
+func notifyError(config config, err error) error {
+ return notify(config, fmt.Sprintf("GOGIOS: An error occured: %v", err), err.Error())
+}
diff --git a/state.go b/state.go
index c3c7eab..67cb1d4 100644
--- a/state.go
+++ b/state.go
@@ -1,5 +1,81 @@
package main
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+)
+
type state struct {
- Checks map[string]int
+ stateFile string
+ Checks map[string]int
+}
+
+func newState(config config) (state, error) {
+ stateFile := fmt.Sprintf("%s/state.json", config.StateDir)
+ state := state{stateFile, make(map[string]int)}
+
+ if _, err := os.Stat(stateFile); err != nil {
+ return state, err
+ }
+
+ file, err := os.Open(stateFile)
+ if err != nil {
+ return state, err
+ }
+ defer file.Close()
+
+ // Read the file content
+ bytes, err := ioutil.ReadAll(file)
+ if err != nil {
+ return state, err
+ }
+
+ // Parse the JSON content
+ if err := json.Unmarshal(bytes, &state.Checks); err != nil {
+ return state, err
+ }
+
+ // Clean up obsolete state information
+ var obsolete []string
+ for name := range state.Checks {
+ if _, ok := config.Checks[name]; !ok {
+ obsolete = append(obsolete, name)
+ }
+ }
+
+ for _, name := range obsolete {
+ delete(state.Checks, name)
+ log.Printf("State of %s is obsolete (removed)", name)
+ }
+
+ return state, nil
+}
+
+func (s state) update(name string, status int) bool {
+ oldStatus, ok := s.Checks[name]
+ if !ok {
+ log.Printf("State of %s: %d (new)", name, status)
+ s.Checks[name] = status
+ return true
+ }
+
+ if oldStatus != status {
+ log.Printf("State of %s: %d -> %d (changed)", name, oldStatus, status)
+ s.Checks[name] = status
+ return true
+ }
+
+ log.Printf("State of %s: %d (unchanged)", name, status)
+ return false
+}
+
+func (s state) persist() error {
+ jsonData, err := json.Marshal(s.Checks)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(s.stateFile, jsonData, os.ModePerm)
}