basic refactor + new prototype

This commit is contained in:
Matej Kramny
2017-02-04 16:02:22 -08:00
parent aaecc1669a
commit e910807973
6 changed files with 145 additions and 81 deletions

7
api.go Normal file
View File

@@ -0,0 +1,7 @@
package cachet
type CachetAPI struct {
Url string `json:"api_url"`
Token string `json:"api_token"`
Insecure bool `json:"insecure"`
}

View File

@@ -3,40 +3,60 @@ package main
import (
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"os/signal"
"sync"
"strings"
"github.com/Sirupsen/logrus"
cachet "github.com/castawaylabs/cachet-monitor"
docopt "github.com/docopt/docopt-go"
yaml "gopkg.in/yaml.v2"
)
var configPath string
var systemName string
var logPath string
const usage = `cachet-monitor
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() {
flag.StringVar(&configPath, "c", "/etc/cachet-monitor.config.json", "Config path")
flag.StringVar(&systemName, "name", "", "System Name")
flag.StringVar(&logPath, "log", "", "Log path")
flag.Parse()
arguments, _ := docopt.Parse(usage, nil, true, "cachet-monitor", false)
cfg, err := getConfiguration(configPath)
cfg, err := getConfiguration(arguments["--config"].(string))
if err != nil {
panic(err)
logrus.Panicf("Unable to start (reading config): %v", err)
}
if len(systemName) > 0 {
cfg.SystemName = systemName
}
if len(logPath) > 0 {
cfg.LogPath = logPath
if name := arguments["--name"]; name != nil {
cfg.SystemName = name.(string)
}
logrus.SetOutput(getLogger(arguments["--log"]))
if len(os.Getenv("CACHET_API")) > 0 {
cfg.APIUrl = os.Getenv("CACHET_API")
@@ -44,29 +64,39 @@ func main() {
if len(os.Getenv("CACHET_TOKEN")) > 0 {
cfg.APIToken = os.Getenv("CACHET_TOKEN")
}
if len(os.Getenv("CACHET_DEV")) > 0 {
logrus.SetLevel(logrus.DebugLevel)
}
if err := cfg.ValidateConfiguration(); err != nil {
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{}
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
if mon.ExpectedStatusCode > 0 {
cfg.Logger.Printf(" - Expect HTTP %d", mon.ExpectedStatusCode)
l.Infof(" - Expect HTTP %d", mon.ExpectedStatusCode)
}
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 {
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 {
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)
@@ -76,7 +106,7 @@ func main() {
signal.Notify(signals, os.Interrupt, os.Kill)
<-signals
cfg.Logger.Println("Abort: Waiting monitors to finish")
logrus.Warnf("Abort: Waiting monitors to finish")
for _, mon := range cfg.Monitors {
mon.Stop()
}
@@ -84,24 +114,17 @@ func main() {
wg.Wait()
}
func getLogger(logPath string) *log.Logger {
var logWriter = os.Stdout
var err error
func getLogger(logPath *string) *os.File {
if logPath == nil || len(*logPath) == 0 {
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)
if file, err := os.Create(logPath); err != nil {
logrus.Errorf("Unable to open file '%v' for logging: \n%v", logPath, err)
os.Exit(1)
} else {
return file
}
}
flags := log.Llongfile | log.Ldate | log.Ltime
if len(os.Getenv("CACHET_DEV")) > 0 {
flags = 0
}
return log.New(logWriter, "", flags)
}
func getConfiguration(path string) (*cachet.CachetMonitor, error) {
@@ -114,26 +137,31 @@ func getConfiguration(path string) (*cachet.CachetMonitor, error) {
// download config
response, err := http.Get(path)
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()
data, _ = ioutil.ReadAll(response.Body)
fmt.Println("Downloaded network configuration.")
logrus.Info("Downloaded network configuration.")
} else {
data, err = ioutil.ReadFile(path)
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 {
fmt.Println(err)
return nil, errors.New("Cannot parse config!")
// test file path for yml
if strings.HasSuffix(path, ".yml") || strings.HasSuffix(path, ".yaml") {
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
}

View File

@@ -1,57 +1,53 @@
package cachet
import (
"errors"
"log"
"net"
"os"
"github.com/Sirupsen/logrus"
)
type CachetMonitor struct {
Logger *log.Logger `json:"-"`
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"`
Name string `json:"system_name"`
API CachetAPI `json:"api"`
Monitors []*Monitor `json:"monitors"`
}
func (cfg *CachetMonitor) ValidateConfiguration() error {
if cfg.Logger == nil {
cfg.Logger = log.New(os.Stdout, "", log.Llongfile|log.Ldate|log.Ltime)
}
func (cfg *CachetMonitor) Validate() bool {
valid := true
if len(cfg.SystemName) == 0 {
if len(cfg.Name) == 0 {
// get hostname
cfg.SystemName = getHostname()
cfg.Name = getHostname()
}
if len(cfg.APIToken) == 0 || len(cfg.APIUrl) == 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")
if len(cfg.API.Token) == 0 || len(cfg.API.Url) == 0 {
logrus.Warnf("API URL or API Token missing.\nGet help at https://github.com/castawaylabs/cachet-monitor")
valid = false
}
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 {
if err := monitor.ValidateConfiguration(); err != nil {
return err
if err := monitor.Validate(); !valid {
valid = false
}
}
return nil
return valid
}
// getHostname returns id of the current system
func getHostname() string {
hostname, err := os.Hostname()
if err != nil || len(hostname) == 0 {
addrs, err := net.InterfaceAddrs()
if err == nil && len(hostname) > 0 {
return hostname
}
addrs, err := net.InterfaceAddrs()
if err != nil {
return "unknown"
}
@@ -59,7 +55,4 @@ func getHostname() string {
for _, addr := range addrs {
return addr.String()
}
}
return hostname
}

16
example.config.yml Normal file
View File

@@ -0,0 +1,16 @@
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>

20
http.go
View File

@@ -7,10 +7,30 @@ import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strconv"
"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) {
req, err := http.NewRequest(requestType, monitor.APIUrl+url, bytes.NewBuffer(reqBody))

View File

@@ -196,7 +196,7 @@ func (monitor *Monitor) AnalyseData() {
}
}
func (monitor *Monitor) ValidateConfiguration() error {
func (monitor *Monitor) Validate() error {
if len(monitor.ExpectedBody) > 0 {
exp, err := regexp.Compile(monitor.ExpectedBody)
if err != nil {