diff --git a/.gitignore b/.gitignore index 8eb58da..d969c09 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -gin-bin -example.config.local.json -.idea /config.yml /config.json +examples/ \ No newline at end of file diff --git a/cli/main.go b/cli/main.go index 03d0aff..775a315 100644 --- a/cli/main.go +++ b/cli/main.go @@ -21,7 +21,7 @@ import ( const usage = `cachet-monitor Usage: - cachet-monitor (-c PATH | --config PATH) [--log=LOGPATH] [--name=NAME] + cachet-monitor (-c PATH | --config PATH) [--log=LOGPATH] [--name=NAME] [--immediate] cachet-monitor -h | --help | --version cachet-monitor print-config @@ -38,6 +38,7 @@ Options: -c PATH.json --config PATH Path to configuration file -h --help Show this screen. --version Show version + --immediate Tick immediately (by default waits for first defined interval) print-config Print example configuration Environment varaibles: @@ -53,6 +54,10 @@ func main() { logrus.Panicf("Unable to start (reading config): %v", err) } + if immediate, ok := arguments["--immediate"]; ok { + cfg.Immediate = immediate.(bool) + } + if name := arguments["--name"]; name != nil { cfg.SystemName = name.(string) } @@ -73,6 +78,7 @@ func main() { os.Exit(1) } + logrus.Debug("Configuration valid") logrus.Infof("System: %s", cfg.SystemName) logrus.Infof("API: %s", cfg.API.URL) logrus.Infof("Monitors: %d\n", len(cfg.Monitors)) @@ -89,7 +95,7 @@ func main() { logrus.Infof("Starting Monitor #%d:", index) logrus.Infof("Features: \n - %v", strings.Join(monitor.Describe(), "\n - ")) - // go mon.Start(cfg, wg) + go monitor.ClockStart(cfg, wg) } signals := make(chan os.Signal, 1) @@ -98,7 +104,7 @@ func main() { logrus.Warnf("Abort: Waiting monitors to finish") for _, mon := range cfg.Monitors { - mon.GetMonitor().Stop() + mon.GetMonitor().ClockStop() } wg.Wait() diff --git a/config.go b/config.go index 8579469..382c621 100644 --- a/config.go +++ b/config.go @@ -15,6 +15,8 @@ type CachetMonitor struct { RawMonitors []map[string]interface{} `json:"monitors" yaml:"monitors"` Monitors []MonitorInterface `json:"-" yaml:"-"` + + Immediate bool `json:"-" yaml:"-"` } // Validate configuration diff --git a/http.go b/http.go index 12d1d36..1d4df2a 100644 --- a/http.go +++ b/http.go @@ -40,7 +40,7 @@ type HTTPMonitor struct { bodyRegexp *regexp.Regexp } -func (monitor *HTTPMonitor) do() bool { +func (monitor *HTTPMonitor) test() bool { client := &http.Client{ Timeout: time.Duration(monitor.Timeout * time.Second), } @@ -90,45 +90,30 @@ func (monitor *HTTPMonitor) do() bool { return true } -func (monitor *HTTPMonitor) Validate() []string { - errs := []string{} - if len(monitor.ExpectedBody) > 0 { - exp, err := regexp.Compile(monitor.ExpectedBody) +func (mon *HTTPMonitor) Validate() []string { + errs := mon.AbstractMonitor.Validate() + + if len(mon.ExpectedBody) > 0 { + exp, err := regexp.Compile(mon.ExpectedBody) if err != nil { errs = append(errs, "Regexp compilation failure: "+err.Error()) } else { - monitor.bodyRegexp = exp + mon.bodyRegexp = exp } } - if len(monitor.ExpectedBody) == 0 && monitor.ExpectedStatusCode == 0 { + if len(mon.ExpectedBody) == 0 && mon.ExpectedStatusCode == 0 { errs = append(errs, "Both 'expected_body' and 'expected_status_code' fields empty") } - if monitor.Interval < 1 { - monitor.Interval = DefaultInterval - } - - if monitor.Timeout < 1 { - monitor.Timeout = DefaultTimeout - } - - monitor.Method = strings.ToUpper(monitor.Method) - switch monitor.Method { + mon.Method = strings.ToUpper(mon.Method) + switch mon.Method { case "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD": break case "": - monitor.Method = "GET" + mon.Method = "GET" default: - errs = append(errs, "Unsupported check method: "+monitor.Method) - } - - if monitor.ComponentID == 0 && monitor.MetricID == 0 { - errs = append(errs, "component_id & metric_id are unset") - } - - if monitor.Threshold <= 0 { - monitor.Threshold = 100 + errs = append(errs, "Unsupported HTTP method: "+mon.Method) } return errs diff --git a/monitor.go b/monitor.go index 520e3ab..4853bcc 100644 --- a/monitor.go +++ b/monitor.go @@ -13,7 +13,11 @@ const DefaultTimeFormat = "15:04:05 Jan 2 MST" const HistorySize = 10 type MonitorInterface interface { - do() bool + ClockStart(*CachetMonitor, *sync.WaitGroup) + ClockStop() + tick() + test() bool + Validate() []string GetMonitor() *AbstractMonitor Describe() []string @@ -25,9 +29,7 @@ type AbstractMonitor struct { Target string // (default)http, tcp, dns, icmp - Type string - - // defaults true + Type string Strict bool Interval time.Duration @@ -54,16 +56,34 @@ type AbstractMonitor struct { stopC chan bool } -func (mon *AbstractMonitor) do() bool { - return true -} func (mon *AbstractMonitor) Validate() []string { - return []string{} + errs := []string{} + + if len(mon.Name) == 0 { + errs = append(errs, "Name is required") + } + + if mon.Interval < 1 { + mon.Interval = DefaultInterval + } + if mon.Timeout < 1 { + mon.Timeout = DefaultTimeout + } + + if mon.ComponentID == 0 && mon.MetricID == 0 { + errs = append(errs, "component_id & metric_id are unset") + } + + if mon.Threshold <= 0 { + mon.Threshold = 100 + } + + return errs } func (mon *AbstractMonitor) GetMonitor() *AbstractMonitor { return mon } -func (mon AbstractMonitor) Describe() []string { +func (mon *AbstractMonitor) Describe() []string { features := []string{"Type: " + mon.Type} if len(mon.Name) > 0 { @@ -73,17 +93,19 @@ func (mon AbstractMonitor) Describe() []string { return features } -func (mon *AbstractMonitor) Start(cfg *CachetMonitor, wg *sync.WaitGroup) { +func (mon *AbstractMonitor) ClockStart(cfg *CachetMonitor, wg *sync.WaitGroup) { wg.Add(1) mon.config = cfg mon.stopC = make(chan bool) - mon.Tick() + if cfg.Immediate { + mon.tick() + } ticker := time.NewTicker(mon.Interval * time.Second) for { select { case <-ticker.C: - mon.Tick() + mon.tick() case <-mon.stopC: wg.Done() return @@ -91,41 +113,35 @@ func (mon *AbstractMonitor) Start(cfg *CachetMonitor, wg *sync.WaitGroup) { } } -func (monitor *AbstractMonitor) Stop() { - if monitor.Stopped() { - return - } - - close(monitor.stopC) -} - -func (monitor *AbstractMonitor) Stopped() bool { +func (mon *AbstractMonitor) ClockStop() { select { - case <-monitor.stopC: - return true + case <-mon.stopC: + return default: - return false + close(mon.stopC) } } -func (monitor *AbstractMonitor) Tick() { +func (mon *AbstractMonitor) test() bool { return false } + +func (mon *AbstractMonitor) tick() { reqStart := getMs() - up := monitor.do() + up := mon.test() lag := getMs() - reqStart - if len(monitor.history) == HistorySize-1 { - logrus.Warnf("%v is now saturated\n", monitor.Name) + if len(mon.history) == HistorySize-1 { + logrus.Warnf("%v is now saturated\n", mon.Name) } - if len(monitor.history) >= HistorySize { - monitor.history = monitor.history[len(monitor.history)-(HistorySize-1):] + if len(mon.history) >= HistorySize { + mon.history = mon.history[len(mon.history)-(HistorySize-1):] } - monitor.history = append(monitor.history, up) - monitor.AnalyseData() + mon.history = append(mon.history, up) + mon.AnalyseData() // report lag - if up && monitor.MetricID > 0 { + if up && mon.MetricID > 0 { logrus.Infof("%v", lag) - // monitor.SendMetric(lag) + // mon.SendMetric(lag) } }