summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2023-04-17 23:12:04 +0300
committerPaul Buetow <paul@buetow.org>2023-04-17 23:12:04 +0300
commit204c0c88104ce130bdeabfd73c735b7ef33e2f57 (patch)
tree3ff643babafedee47d40fbe6645fe2ab93d32799
parent6b5ed019b50fdec4ab91922708390cb12c9a814e (diff)
only send one report
-rw-r--r--README.md10
-rw-r--r--check.go3
-rw-r--r--main.go13
-rw-r--r--notify.go2
-rw-r--r--state.go94
5 files changed, 95 insertions, 27 deletions
diff --git a/README.md b/README.md
index 166f0d2..4c0ca65 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,11 @@
# gogios
-Minimalist monitoring system, compatible with the Nagios Check API. \ No newline at end of file
+Minimalist monitoring system, compatible with the Nagios Check API.
+
+TODO: Only send one email per state (OK, WARNING, CRITICAL,
+
+TODO: Only send one email per state (OK, WARNING, CRITICAL,
+
+TODO: Only send one email per state (OK, WARNING, CRITICAL,
+
+TODO: Only send one email per state (OK, WARNING, CRITICAL,
diff --git a/check.go b/check.go
index f728e6d..702d46b 100644
--- a/check.go
+++ b/check.go
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"os/exec"
+ "strings"
)
type check struct {
@@ -24,5 +25,5 @@ func (c check) execute(ctx context.Context) (string, int) {
}
}
- return bytes.String(), cmd.ProcessState.ExitCode()
+ return strings.TrimSuffix(bytes.String(), "\n"), cmd.ProcessState.ExitCode()
}
diff --git a/main.go b/main.go
index 6d661d7..bd13680 100644
--- a/main.go
+++ b/main.go
@@ -3,7 +3,6 @@ package main
import (
"context"
"flag"
- "fmt"
"log"
"sync"
"time"
@@ -54,13 +53,7 @@ func main() {
defer cancel()
output, status := check.execute(ctx)
- stateChanged := state.update(name, status)
-
- if status != ok || stateChanged {
- subject := fmt.Sprintf("GOGIOS %s: %s", codeToString(status), name)
- notify(config, subject, output)
- }
-
+ state.update(name, output, status)
}(entry.name, entry.check)
}
@@ -70,4 +63,8 @@ func main() {
if err := state.persist(); err != nil {
notifyError(config, err)
}
+
+ if subject, body, changed := state.report(); changed {
+ notify(config, subject, body)
+ }
}
diff --git a/notify.go b/notify.go
index 3a295ba..7b423cd 100644
--- a/notify.go
+++ b/notify.go
@@ -7,7 +7,7 @@ import (
)
func notify(config config, subject, body string) error {
- log.Println("emailNotify", subject, body)
+ log.Println("notify", subject, body)
headers := make(map[string]string)
headers["From"] = config.EmailFrom
diff --git a/state.go b/state.go
index d2be1c0..25d0ca3 100644
--- a/state.go
+++ b/state.go
@@ -6,19 +6,27 @@ import (
"io/ioutil"
"log"
"os"
+ "strings"
"sync"
)
+type checkState struct {
+ status int
+ prevStatus int
+ output string
+}
+
type state struct {
stateFile string
- Checks map[string]int
- mutex sync.Mutex
+ checks map[string]checkState
+ mutex *sync.Mutex
}
func newState(config config) (state, error) {
s := state{
stateFile: fmt.Sprintf("%s/state.json", config.StateDir),
- Checks: make(map[string]int),
+ checks: make(map[string]checkState),
+ mutex: &sync.Mutex{},
}
if _, err := os.Stat(s.stateFile); err != nil {
@@ -39,51 +47,105 @@ func newState(config config) (state, error) {
}
// Parse the JSON content
- if err := json.Unmarshal(bytes, &s.Checks); err != nil {
+ if err := json.Unmarshal(bytes, &s.checks); err != nil {
return s, err
}
// Clean up obsolete state information
var obsolete []string
- for name := range s.Checks {
+ for name := range s.checks {
if _, ok := config.Checks[name]; !ok {
obsolete = append(obsolete, name)
}
}
for _, name := range obsolete {
- delete(s.Checks, name)
+ delete(s.checks, name)
log.Printf("State of %s is obsolete (removed)", name)
}
return s, nil
}
-func (s state) update(name string, status int) bool {
+func (s state) update(name, output string, status int) {
s.mutex.Lock()
defer s.mutex.Unlock()
- oldStatus, ok := s.Checks[name]
+ prevState, ok := s.checks[name]
if !ok {
log.Printf("State of %s: %d (new)", name, status)
- s.Checks[name] = status
- return true
+ s.checks[name] = checkState{status, unknown, output}
+ return
}
- if oldStatus != status {
- log.Printf("State of %s: %d -> %d (changed)", name, oldStatus, status)
- s.Checks[name] = status
- return true
+ if prevState.status != status {
+ log.Printf("State of %s: %d -> %d (changed)", name, prevState, status)
+ s.checks[name] = checkState{status, prevState.status, output}
+ return
}
log.Printf("State of %s: %d (unchanged)", name, status)
- return false
+ s.checks[name] = checkState{status, prevState.status, output}
}
func (s state) persist() error {
- jsonData, err := json.Marshal(s.Checks)
+ jsonData, err := json.Marshal(s.checks)
if err != nil {
return err
}
return ioutil.WriteFile(s.stateFile, jsonData, os.ModePerm)
}
+
+func (s state) report() (string, string, bool) {
+ var sb strings.Builder
+ var changed bool
+
+ f := func(filter func(i int) bool) int {
+ var count int
+ for name, checkState := range s.checks {
+ if filter(checkState.status) {
+ count++
+ if checkState.status != checkState.prevStatus {
+ sb.WriteString(codeToString(checkState.prevStatus))
+ sb.WriteString("->")
+ changed = true
+ }
+ sb.WriteString(codeToString(checkState.status))
+ sb.WriteString(": ")
+ sb.WriteString(name)
+ sb.WriteString(" ==>> ")
+ sb.WriteString(checkState.output)
+ sb.WriteString("\n")
+ }
+ }
+ return count
+ }
+
+ sb.WriteString("This is the recent Googios report!\n\n")
+
+ numCriticals := f(func(i int) bool { return i == 2 })
+ if numCriticals > 0 {
+ sb.WriteString("\n")
+ }
+
+ numWarnings := f(func(i int) bool { return i == 1 })
+ if numWarnings > 0 {
+ sb.WriteString("\n")
+ }
+
+ numUnknowns := f(func(i int) bool { return i > 2 })
+ if numUnknowns > 0 {
+ sb.WriteString("\n")
+ }
+
+ numOks := f(func(i int) bool { return i == 0 })
+ if numOks > 0 {
+ sb.WriteString("\n")
+ }
+
+ sb.WriteString("Have a nice day!\n")
+ subject := fmt.Sprintf("GOGIOS Report [C:%d W:%d U:%d OK:%d]",
+ numCriticals, numWarnings, numUnknowns, numOks)
+
+ return subject, sb.String(), changed
+}