diff options
| author | Paul Buetow <paul@buetow.org> | 2023-04-17 20:41:22 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2023-04-17 20:41:22 +0300 |
| commit | e40e22f69470b987bfb2c39b4a18df67f497739d (patch) | |
| tree | 73d03e14614b5b29ff07dec4ccb4cdad61234493 | |
| parent | f7456fded178b85637c82255e9c3178817f5fa68 (diff) | |
can keep state
| -rw-r--r-- | check.go | 2 | ||||
| -rw-r--r-- | config.go | 10 | ||||
| -rw-r--r-- | gogios.json | 18 | ||||
| -rw-r--r-- | main.go | 20 | ||||
| -rw-r--r-- | notify.go | 4 | ||||
| -rw-r--r-- | state.go | 78 |
6 files changed, 108 insertions, 24 deletions
@@ -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 { @@ -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"] } - ] + } } @@ -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) + } } @@ -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()) +} @@ -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) } |
