diff options
| author | Paul Buetow <paul@buetow.org> | 2023-04-17 21:43:39 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2023-04-17 21:43:39 +0300 |
| commit | 6b5ed019b50fdec4ab91922708390cb12c9a814e (patch) | |
| tree | 98785e05cb25a6c08bf687f072202fcbbcbdb7ab | |
| parent | 41a913d59e4f14b6cc61cb5eabaf0adda5c4b809 (diff) | |
can execute checks concurrently
| -rw-r--r-- | config.go | 13 | ||||
| -rw-r--r-- | gogios.json | 1 | ||||
| -rw-r--r-- | main.go | 50 | ||||
| -rw-r--r-- | state.go | 31 |
4 files changed, 68 insertions, 27 deletions
@@ -9,12 +9,13 @@ import ( ) type config struct { - EmailTo string - EmailFrom string - SMTPServer string `json:"omitempty"` - StateDir string `json:"omitempty"` - CheckTimeoutS int - Checks map[string]check + EmailTo string + EmailFrom string + SMTPServer string `json:"omitempty"` + StateDir string `json:"omitempty"` + CheckTimeoutS int + CheckConcurrency int + Checks map[string]check } func newConfig(configFile string) (config, error) { diff --git a/gogios.json b/gogios.json index a6cbace..12f13aa 100644 --- a/gogios.json +++ b/gogios.json @@ -2,6 +2,7 @@ "EmailTo": "paul", "EmailFrom": "gogios@buetow.org", "CheckTimeoutS": 10, + "CheckConcurrency": 2, "Checks": { "foo.zone HTTP IPv4": { "Plugin": "/usr/local/libexec/nagios/check_http", @@ -4,6 +4,8 @@ import ( "context" "flag" "fmt" + "log" + "sync" "time" ) @@ -21,20 +23,50 @@ func main() { notifyError(config, err) } - for name, check := range config.Checks { - ctx, cancel := context.WithTimeout(context.Background(), - time.Duration(config.CheckTimeoutS)*time.Second) - defer cancel() + type entry struct { + name string + check check + } - output, status := check.execute(ctx) - stateChanged := state.update(name, status) + limiterCh := make(chan struct{}, config.CheckConcurrency) + checkCh := make(chan entry) - if status != ok || stateChanged { - subject := fmt.Sprintf("GOGIOS %s: %s", codeToString(status), name) - notify(config, subject, output) + go func() { + for name, check := range config.Checks { + checkCh <- entry{name, check} } + close(checkCh) + }() + + var wg sync.WaitGroup + wg.Add(len(config.Checks)) + + for entry := range checkCh { + go func(name string, check check) { + limiterCh <- struct{}{} + defer func() { + <-limiterCh + wg.Done() + }() + + ctx, cancel := context.WithTimeout(context.Background(), + time.Duration(config.CheckTimeoutS)*time.Second) + 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) + } + + }(entry.name, entry.check) } + wg.Wait() + log.Println("All checks completed!") + if err := state.persist(); err != nil { notifyError(config, err) } @@ -6,56 +6,63 @@ import ( "io/ioutil" "log" "os" + "sync" ) type state struct { stateFile string Checks map[string]int + mutex sync.Mutex } func newState(config config) (state, error) { - stateFile := fmt.Sprintf("%s/state.json", config.StateDir) - state := state{stateFile, make(map[string]int)} + s := state{ + stateFile: fmt.Sprintf("%s/state.json", config.StateDir), + Checks: make(map[string]int), + } - if _, err := os.Stat(stateFile); err != nil { + if _, err := os.Stat(s.stateFile); err != nil { // OK, may be first run with no state yet. - return state, nil + return s, nil } - file, err := os.Open(stateFile) + file, err := os.Open(s.stateFile) if err != nil { - return state, err + return s, err } defer file.Close() // Read the file content bytes, err := ioutil.ReadAll(file) if err != nil { - return state, err + return s, err } // Parse the JSON content - if err := json.Unmarshal(bytes, &state.Checks); err != nil { - return state, err + if err := json.Unmarshal(bytes, &s.Checks); err != nil { + return s, err } // Clean up obsolete state information var obsolete []string - for name := range state.Checks { + for name := range s.Checks { if _, ok := config.Checks[name]; !ok { obsolete = append(obsolete, name) } } for _, name := range obsolete { - delete(state.Checks, name) + delete(s.Checks, name) log.Printf("State of %s is obsolete (removed)", name) } - return state, nil + return s, nil } func (s state) update(name string, status int) bool { + s.mutex.Lock() + defer s.mutex.Unlock() + oldStatus, ok := s.Checks[name] if !ok { log.Printf("State of %s: %d (new)", name, status) |
