Skip to content
Snippets Groups Projects
Commit e9108079 authored by Matej Kramny's avatar Matej Kramny
Browse files

basic refactor + new prototype

parent aaecc166
No related branches found
No related tags found
No related merge requests found
api.go 0 → 100644
package cachet
type CachetAPI struct {
Url string `json:"api_url"`
Token string `json:"api_token"`
Insecure bool `json:"insecure"`
}
...@@ -3,40 +3,60 @@ package main ...@@ -3,40 +3,60 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"flag"
"fmt"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"os/signal" "os/signal"
"sync" "sync"
"strings"
"github.com/Sirupsen/logrus"
cachet "github.com/castawaylabs/cachet-monitor" cachet "github.com/castawaylabs/cachet-monitor"
docopt "github.com/docopt/docopt-go"
yaml "gopkg.in/yaml.v2"
) )
var configPath string const usage = `cachet-monitor
var systemName string
var logPath string Usage:
cachet-monitor (-c PATH | --config PATH) [--log=LOGPATH] [--name=NAME]
cachet-monitor -h | --help | --version
cachet-monitor print-config
Arguments:
PATH path to config.yml
LOGPATH path to log output (defaults to STDOUT)
NAME name of this logger
Examples:
cachet-monitor -c /root/cachet-monitor.yml
cachet-monitor -c /root/cachet-monitor.yml --log=/var/log/cachet-monitor.log --name="development machine"
Options:
-c PATH.yml --config PATH Path to configuration file
-h --help Show this screen.
--version Show version
print-config Print example configuration
Environment varaibles:
CACHET_API override API url from configuration
CACHET_TOKEN override API token from configuration
CACHET_DEV set to enable dev logging`
func main() { func main() {
flag.StringVar(&configPath, "c", "/etc/cachet-monitor.config.json", "Config path") arguments, _ := docopt.Parse(usage, nil, true, "cachet-monitor", false)
flag.StringVar(&systemName, "name", "", "System Name")
flag.StringVar(&logPath, "log", "", "Log path")
flag.Parse()
cfg, err := getConfiguration(configPath) cfg, err := getConfiguration(arguments["--config"].(string))
if err != nil { if err != nil {
panic(err) logrus.Panicf("Unable to start (reading config): %v", err)
} }
if len(systemName) > 0 { if name := arguments["--name"]; name != nil {
cfg.SystemName = systemName cfg.SystemName = name.(string)
}
if len(logPath) > 0 {
cfg.LogPath = logPath
} }
logrus.SetOutput(getLogger(arguments["--log"]))
if len(os.Getenv("CACHET_API")) > 0 { if len(os.Getenv("CACHET_API")) > 0 {
cfg.APIUrl = os.Getenv("CACHET_API") cfg.APIUrl = os.Getenv("CACHET_API")
...@@ -44,29 +64,39 @@ func main() { ...@@ -44,29 +64,39 @@ func main() {
if len(os.Getenv("CACHET_TOKEN")) > 0 { if len(os.Getenv("CACHET_TOKEN")) > 0 {
cfg.APIToken = os.Getenv("CACHET_TOKEN") cfg.APIToken = os.Getenv("CACHET_TOKEN")
} }
if len(os.Getenv("CACHET_DEV")) > 0 {
logrus.SetLevel(logrus.DebugLevel)
}
if err := cfg.ValidateConfiguration(); err != nil { if err := cfg.ValidateConfiguration(); err != nil {
panic(err) panic(err)
} }
cfg.Logger.Printf("System: %s\nAPI: %s\nMonitors: %d\n\n", cfg.SystemName, cfg.APIUrl, len(cfg.Monitors)) logrus.Infof("System: %s\nAPI: %s\nMonitors: %d\n\n", cfg.SystemName, cfg.APIUrl, len(cfg.Monitors))
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
for _, mon := range cfg.Monitors { for _, mon := range cfg.Monitors {
cfg.Logger.Printf(" Starting %s: %d seconds check interval\n - %v %s (%d second/s timeout)", mon.Name, mon.CheckInterval, mon.Method, mon.URL, mon.HttpTimeout) l := logrus.WithFields(logrus.Fields{
"name": mon.Name,
"interval": mon.CheckInterval,
"method": mon.Method,
"url": mon.URL,
"timeout": mon.HttpTimeout,
})
l.Info(" Starting monitor")
// print features // print features
if mon.ExpectedStatusCode > 0 { if mon.ExpectedStatusCode > 0 {
cfg.Logger.Printf(" - Expect HTTP %d", mon.ExpectedStatusCode) l.Infof(" - Expect HTTP %d", mon.ExpectedStatusCode)
} }
if len(mon.ExpectedBody) > 0 { if len(mon.ExpectedBody) > 0 {
cfg.Logger.Printf(" - Expect Body to match \"%v\"", mon.ExpectedBody) l.Infof(" - Expect Body to match \"%v\"", mon.ExpectedBody)
} }
if mon.MetricID > 0 { if mon.MetricID > 0 {
cfg.Logger.Printf(" - Log lag to metric id %d\n", mon.MetricID) l.Infof(" - Log lag to metric id %d\n", mon.MetricID)
} }
if mon.ComponentID > 0 { if mon.ComponentID > 0 {
cfg.Logger.Printf(" - Update component id %d\n\n", mon.ComponentID) l.Infof(" - Update component id %d\n\n", mon.ComponentID)
} }
go mon.Start(cfg, wg) go mon.Start(cfg, wg)
...@@ -76,7 +106,7 @@ func main() { ...@@ -76,7 +106,7 @@ func main() {
signal.Notify(signals, os.Interrupt, os.Kill) signal.Notify(signals, os.Interrupt, os.Kill)
<-signals <-signals
cfg.Logger.Println("Abort: Waiting monitors to finish") logrus.Warnf("Abort: Waiting monitors to finish")
for _, mon := range cfg.Monitors { for _, mon := range cfg.Monitors {
mon.Stop() mon.Stop()
} }
...@@ -84,24 +114,17 @@ func main() { ...@@ -84,24 +114,17 @@ func main() {
wg.Wait() wg.Wait()
} }
func getLogger(logPath string) *log.Logger { func getLogger(logPath *string) *os.File {
var logWriter = os.Stdout if logPath == nil || len(*logPath) == 0 {
var err error return os.Stdout
if len(logPath) > 0 {
logWriter, err = os.Create(logPath)
if err != nil {
fmt.Printf("Unable to open file '%v' for logging\n", logPath)
os.Exit(1)
}
} }
flags := log.Llongfile | log.Ldate | log.Ltime if file, err := os.Create(logPath); err != nil {
if len(os.Getenv("CACHET_DEV")) > 0 { logrus.Errorf("Unable to open file '%v' for logging: \n%v", logPath, err)
flags = 0 os.Exit(1)
} else {
return file
} }
return log.New(logWriter, "", flags)
} }
func getConfiguration(path string) (*cachet.CachetMonitor, error) { func getConfiguration(path string) (*cachet.CachetMonitor, error) {
...@@ -114,26 +137,31 @@ func getConfiguration(path string) (*cachet.CachetMonitor, error) { ...@@ -114,26 +137,31 @@ func getConfiguration(path string) (*cachet.CachetMonitor, error) {
// download config // download config
response, err := http.Get(path) response, err := http.Get(path)
if err != nil { if err != nil {
return nil, errors.New("Cannot download network config: " + err.Error()) logrus.Warn("Unable to download network configuration")
return nil, err
} }
defer response.Body.Close() defer response.Body.Close()
data, _ = ioutil.ReadAll(response.Body) data, _ = ioutil.ReadAll(response.Body)
fmt.Println("Downloaded network configuration.") logrus.Info("Downloaded network configuration.")
} else { } else {
data, err = ioutil.ReadFile(path) data, err = ioutil.ReadFile(path)
if err != nil { if err != nil {
return nil, errors.New("Config file '" + path + "' missing!") return nil, errors.New("Unable to open file: '" + path + "'")
} }
} }
if err := json.Unmarshal(data, &cfg); err != nil { // test file path for yml
fmt.Println(err) if strings.HasSuffix(path, ".yml") || strings.HasSuffix(path, ".yaml") {
return nil, errors.New("Cannot parse config!") err = yaml.Unmarshal(data, &cfg)
} else {
err = json.Unmarshal(data, &cfg)
} }
cfg.Logger = getLogger(cfg.LogPath) if err != nil {
logrus.Warnf("Unable to parse configuration file")
}
return &cfg, nil return &cfg, err
} }
package cachet package cachet
import ( import (
"errors"
"log"
"net" "net"
"os" "os"
"github.com/Sirupsen/logrus"
) )
type CachetMonitor struct { type CachetMonitor struct {
Logger *log.Logger `json:"-"` Name string `json:"system_name"`
API CachetAPI `json:"api"`
APIUrl string `json:"api_url"`
APIToken string `json:"api_token"`
SystemName string `json:"system_name"`
LogPath string `json:"log_path"`
InsecureAPI bool `json:"insecure_api"`
Monitors []*Monitor `json:"monitors"` Monitors []*Monitor `json:"monitors"`
} }
func (cfg *CachetMonitor) ValidateConfiguration() error { func (cfg *CachetMonitor) Validate() bool {
if cfg.Logger == nil { valid := true
cfg.Logger = log.New(os.Stdout, "", log.Llongfile|log.Ldate|log.Ltime)
}
if len(cfg.SystemName) == 0 { if len(cfg.Name) == 0 {
// get hostname // get hostname
cfg.SystemName = getHostname() cfg.Name = getHostname()
} }
if len(cfg.APIToken) == 0 || len(cfg.APIUrl) == 0 { if len(cfg.API.Token) == 0 || len(cfg.API.Url) == 0 {
return errors.New("API URL or API Token not set. cachet-monitor won't be able to report incidents.\n\nPlease set:\n CACHET_API and CACHET_TOKEN environment variable to override settings.\n\nGet help at https://github.com/castawaylabs/cachet-monitor\n") logrus.Warnf("API URL or API Token missing.\nGet help at https://github.com/castawaylabs/cachet-monitor")
valid = false
} }
if len(cfg.Monitors) == 0 { if len(cfg.Monitors) == 0 {
return errors.New("No monitors defined!\nSee sample configuration: https://github.com/castawaylabs/cachet-monitor/blob/master/example.config.json\n") logrus.Warnf("No monitors defined!\nSee help for example configuration")
valid = false
} }
for _, monitor := range cfg.Monitors { for _, monitor := range cfg.Monitors {
if err := monitor.ValidateConfiguration(); err != nil { if err := monitor.Validate(); !valid {
return err valid = false
} }
} }
return nil return valid
} }
// getHostname returns id of the current system // getHostname returns id of the current system
func getHostname() string { func getHostname() string {
hostname, err := os.Hostname() hostname, err := os.Hostname()
if err != nil || len(hostname) == 0 { if err == nil && len(hostname) > 0 {
addrs, err := net.InterfaceAddrs() return hostname
}
addrs, err := net.InterfaceAddrs()
if err != nil { if err != nil {
return "unknown" return "unknown"
} }
...@@ -60,6 +56,3 @@ func getHostname() string { ...@@ -60,6 +56,3 @@ func getHostname() string {
return addr.String() return addr.String()
} }
} }
return hostname
}
apiurl: https://demo.cachethq.io/api/v1
apitoken: 9yMHsdioQosnyVK4iCVR
insecureapi: true
monitors:
- name: google
type: http
url: https://google.com
stricttls: true
threshold: 80
componentid: 1
interval: 10
timeout: 5
expectedstatuscode: 200
headers:
- name: Authorization
value: Basic <xyz>
\ No newline at end of file
...@@ -7,10 +7,30 @@ import ( ...@@ -7,10 +7,30 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"regexp"
"strconv" "strconv"
"time" "time"
) )
type HttpMonitor struct {
URL string `json:"url"`
Method string `json:"method"`
StrictTLS bool `json:"strict_tls"`
CheckInterval time.Duration `json:"interval"`
HttpTimeout time.Duration `json:"timeout"`
// Threshold = percentage
Threshold float32 `json:"threshold"`
ExpectedStatusCode int `json:"expected_status_code"`
// compiled to Regexp
ExpectedBody string `json:"expected_body"`
bodyRegexp *regexp.Regexp
}
type TCPMonitor struct{}
type ICMPMonitor struct{}
type DNSMonitor struct{}
func (monitor *CachetMonitor) makeRequest(requestType string, url string, reqBody []byte) (*http.Response, []byte, error) { func (monitor *CachetMonitor) makeRequest(requestType string, url string, reqBody []byte) (*http.Response, []byte, error) {
req, err := http.NewRequest(requestType, monitor.APIUrl+url, bytes.NewBuffer(reqBody)) req, err := http.NewRequest(requestType, monitor.APIUrl+url, bytes.NewBuffer(reqBody))
......
...@@ -196,7 +196,7 @@ func (monitor *Monitor) AnalyseData() { ...@@ -196,7 +196,7 @@ func (monitor *Monitor) AnalyseData() {
} }
} }
func (monitor *Monitor) ValidateConfiguration() error { func (monitor *Monitor) Validate() error {
if len(monitor.ExpectedBody) > 0 { if len(monitor.ExpectedBody) > 0 {
exp, err := regexp.Compile(monitor.ExpectedBody) exp, err := regexp.Compile(monitor.ExpectedBody)
if err != nil { if err != nil {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment