viper/cobra configs, psql updates

pull/508/head
hunterlong 2020-04-16 02:57:00 -07:00
parent e13b446540
commit 701fac0298
39 changed files with 1826 additions and 1493 deletions

View File

@ -43,6 +43,7 @@ notifications:
os:
- linux
script:
- "make clean newman"
- "if [[ \"$TRAVIS_BRANCH\" == \"dev\" && \"$TRAVIS_PULL_REQUEST\" = \"false\" ]]; then make dockerhub-dev; fi"
- "travis_retry make clean test-ci"
- "if [[ \"$TRAVIS_BRANCH\" == \"master\" && \"$TRAVIS_PULL_REQUEST\" = \"false\" ]]; then make coverage; fi"

View File

@ -1,3 +1,8 @@
# 0.90.28 (04-16-2020)
- Fixed postgres timestamp grouping
- Added postman (newman) API testing
- Added Viper and Cobra config/env parsing package
# 0.90.27 (04-15-2020)
- Fixed postgres database table creation process
- Modified go build process, additional ARCHs

View File

@ -291,5 +291,11 @@ snapcraft: clean compile build-bin
snapcraft push statping_${VERSION}_i386.snap --release stable
snapcraft push statping_${VERSION}_armhf.snap --release stable
.PHONY: all build build-all build-alpine test-all test test-api docker frontend up down print_details lite sentry-release snapcraft build-bin build-win build-all
postman: clean
API_SECRET=demosecret123 statping -p=8080 > /dev/null &
sleep 3
newman run -e dev/postman_environment.json dev/postman.json
killall statping
.PHONY: all build build-all build-alpine test-all test test-api docker frontend up down print_details lite sentry-release snapcraft build-bin build-win build-all postman
.SILENT: travis_s3_creds

View File

@ -4,7 +4,6 @@ import (
"bufio"
"encoding/json"
"fmt"
"github.com/joho/godotenv"
"github.com/pkg/errors"
"github.com/statping/statping/handlers"
"github.com/statping/statping/source"
@ -18,209 +17,158 @@ import (
"time"
)
// catchCLI will run functions based on the commands sent to Statping
func catchCLI(args []string) error {
func assetsCli() error {
dir := utils.Directory
runLogs := utils.InitLogs
runAssets := source.Assets
if err := utils.InitLogs(); err != nil {
return err
}
if err := source.Assets(); err != nil {
return err
}
if err := source.CreateAllAssets(dir); err != nil {
return err
}
return nil
}
switch args[0] {
case "version":
if COMMIT != "" {
fmt.Printf("%s (%s)\n", VERSION, COMMIT)
} else {
fmt.Printf("%s\n", VERSION)
}
return errors.New("end")
case "assets":
var err error
if err = runLogs(); err != nil {
return err
}
if err = runAssets(); err != nil {
return err
}
if err = source.CreateAllAssets(dir); err != nil {
return err
}
return errors.New("end")
case "sass":
if err := runLogs(); err != nil {
return err
}
if err := runAssets(); err != nil {
return err
}
if err := source.CompileSASS(source.DefaultScss...); err != nil {
return err
}
return errors.New("end")
case "update":
updateDisplay()
return errors.New("end")
case "static":
//var err error
//if err = runLogs(); err != nil {
// return err
//}
//if err = runAssets(); err != nil {
// return err
//}
//fmt.Printf("Statping v%v Exporting Static 'index.html' page...\n", VERSION)
//if _, err = core.LoadConfigFile(dir); err != nil {
// log.Errorln("config.yml file not found")
// return err
//}
//indexSource := ExportIndexHTML()
////core.CloseDB()
//if err = utils.SaveFile(dir+"/index.html", indexSource); err != nil {
// log.Errorln(err)
// return err
//}
//log.Infoln("Exported Statping index page: 'index.html'")
case "help":
HelpEcho()
return errors.New("end")
case "export":
var err error
var data []byte
if err = runLogs(); err != nil {
return err
}
if err = runAssets(); err != nil {
return err
}
config, err := configs.LoadConfigs()
if err != nil {
return err
}
if err = configs.ConnectConfigs(config); err != nil {
return err
}
if _, err := services.SelectAllServices(false); err != nil {
return err
}
if data, err = handlers.ExportSettings(); err != nil {
return fmt.Errorf("could not export settings: %v", err.Error())
}
filename := fmt.Sprintf("%s/statping-%s.json", dir, time.Now().Format("01-02-2006-1504"))
if err = utils.SaveFile(filename, data); err != nil {
return fmt.Errorf("could not write file statping-export.json: %v", err.Error())
}
log.Infoln("Statping export file saved to ", filename)
return errors.New("end")
case "import":
var err error
var data []byte
if len(args) != 2 {
return fmt.Errorf("did not include a JSON file to import\nstatping import filename.json")
}
filename := args[1]
if data, err = ioutil.ReadFile(filename); err != nil {
return err
}
var exportData handlers.ExportData
if err = json.Unmarshal(data, &exportData); err != nil {
return err
}
log.Printf("=== %s ===\n", exportData.Core.Name)
log.Printf("Services: %d\n", len(exportData.Services))
log.Printf("Checkins: %d\n", len(exportData.Checkins))
log.Printf("Groups: %d\n", len(exportData.Groups))
log.Printf("Messages: %d\n", len(exportData.Messages))
log.Printf("Users: %d\n", len(exportData.Users))
func exportCli(args []string) error {
filename := fmt.Sprintf("%s/statping-%s.json", utils.Directory, time.Now().Format("01-02-2006-1504"))
if len(args) == 1 {
filename = fmt.Sprintf("%s/%s", utils.Directory, args)
}
var data []byte
if err := utils.InitLogs(); err != nil {
return err
}
if err := source.Assets(); err != nil {
return err
}
config, err := configs.LoadConfigs()
if err != nil {
return err
}
if err = configs.ConnectConfigs(config); err != nil {
return err
}
if _, err := services.SelectAllServices(false); err != nil {
return err
}
if data, err = handlers.ExportSettings(); err != nil {
return fmt.Errorf("could not export settings: %v", err.Error())
}
if err = utils.SaveFile(filename, data); err != nil {
return fmt.Errorf("could not write file statping-export.json: %v", err.Error())
}
log.Infoln("Statping export file saved to ", filename)
return nil
}
config, err := configs.LoadConfigs()
if err != nil {
return err
}
if err = configs.ConnectConfigs(config); err != nil {
return err
}
if data, err = handlers.ExportSettings(); err != nil {
return fmt.Errorf("could not export settings: %v", err.Error())
}
func sassCli() error {
if err := utils.InitLogs(); err != nil {
return err
}
if err := source.Assets(); err != nil {
return err
}
if err := source.CompileSASS(source.DefaultScss...); err != nil {
return err
}
return nil
}
if ask("Import Core settings?") {
c := exportData.Core
if err := c.Update(); err != nil {
func onceCli() error {
if err := utils.InitLogs(); err != nil {
return err
}
if err := source.Assets(); err != nil {
return err
}
log.Infoln("Running 1 time and saving to database...")
if err := runOnce(); err != nil {
return err
}
//core.CloseDB()
fmt.Println("Check is complete.")
return nil
}
func importCli(args []string) error {
var err error
var data []byte
filename := args[1]
if data, err = ioutil.ReadFile(filename); err != nil {
return err
}
var exportData handlers.ExportData
if err = json.Unmarshal(data, &exportData); err != nil {
return err
}
log.Printf("=== %s ===\n", exportData.Core.Name)
log.Printf("Services: %d\n", len(exportData.Services))
log.Printf("Checkins: %d\n", len(exportData.Checkins))
log.Printf("Groups: %d\n", len(exportData.Groups))
log.Printf("Messages: %d\n", len(exportData.Messages))
log.Printf("Users: %d\n", len(exportData.Users))
config, err := configs.LoadConfigs()
if err != nil {
return err
}
if err = configs.ConnectConfigs(config); err != nil {
return err
}
if data, err = handlers.ExportSettings(); err != nil {
return fmt.Errorf("could not export settings: %v", err.Error())
}
if ask("Import Core settings?") {
c := exportData.Core
if err := c.Update(); err != nil {
return err
}
}
for _, s := range exportData.Groups {
if ask(fmt.Sprintf("Import Group '%s'?", s.Name)) {
s.Id = 0
if err := s.Create(); err != nil {
return err
}
}
for _, s := range exportData.Groups {
if ask(fmt.Sprintf("Import Group '%s'?", s.Name)) {
s.Id = 0
if err := s.Create(); err != nil {
return err
}
}
}
for _, s := range exportData.Services {
if ask(fmt.Sprintf("Import Service '%s'?", s.Name)) {
s.Id = 0
if err := s.Create(); err != nil {
return err
}
}
}
for _, s := range exportData.Checkins {
if ask(fmt.Sprintf("Import Checkin '%s'?", s.Name)) {
s.Id = 0
if err := s.Create(); err != nil {
return err
}
}
}
for _, s := range exportData.Messages {
if ask(fmt.Sprintf("Import Message '%s'?", s.Title)) {
s.Id = 0
if err := s.Create(); err != nil {
return err
}
}
}
for _, s := range exportData.Users {
if ask(fmt.Sprintf("Import User '%s'?", s.Username)) {
s.Id = 0
if err := s.Create(); err != nil {
return err
}
}
}
log.Infof("Import complete")
return errors.New("end")
case "run":
if err := runLogs(); err != nil {
return err
}
if err := runAssets(); err != nil {
return err
}
log.Infoln("Running 1 time and saving to database...")
runOnce()
//core.CloseDB()
fmt.Println("Check is complete.")
return errors.New("end")
case "env":
fmt.Println("Statping Environment Variable")
if err := runLogs(); err != nil {
return err
}
if err := runAssets(); err != nil {
return err
}
envs, err := godotenv.Read(".env")
if err != nil {
log.Errorln("No .env file found in current directory.")
return err
}
for k, e := range envs {
fmt.Printf("%v=%v\n", k, e)
}
default:
return nil
}
return errors.New("end")
for _, s := range exportData.Services {
if ask(fmt.Sprintf("Import Service '%s'?", s.Name)) {
s.Id = 0
if err := s.Create(); err != nil {
return err
}
}
}
for _, s := range exportData.Checkins {
if ask(fmt.Sprintf("Import Checkin '%s'?", s.Name)) {
s.Id = 0
if err := s.Create(); err != nil {
return err
}
}
}
for _, s := range exportData.Messages {
if ask(fmt.Sprintf("Import Message '%s'?", s.Title)) {
s.Id = 0
if err := s.Create(); err != nil {
return err
}
}
}
for _, s := range exportData.Users {
if ask(fmt.Sprintf("Import User '%s'?", s.Username)) {
s.Id = 0
if err := s.Create(); err != nil {
return err
}
}
}
log.Infof("Import complete")
return nil
}
func ask(format string) bool {

View File

@ -1,10 +1,12 @@
package main
import (
"bytes"
"github.com/rendon/testcli"
"github.com/statping/statping/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"io/ioutil"
"os"
"os/exec"
"testing"
@ -22,7 +24,6 @@ func init() {
func TestStartServerCommand(t *testing.T) {
t.SkipNow()
os.Setenv("DB_CONN", "sqlite")
cmd := helperCommand(nil, "")
var got = make(chan string)
commandAndSleep(cmd, time.Duration(60*time.Second), got)
@ -116,13 +117,25 @@ func TestEnvironmentVarsCommand(t *testing.T) {
}
func TestVersionCLI(t *testing.T) {
run := catchCLI([]string{"version"})
assert.EqualError(t, run, "end")
cmd := rootCmd
b := bytes.NewBufferString("")
cmd.SetOut(b)
cmd.SetArgs([]string{"version"})
cmd.Execute()
out, err := ioutil.ReadAll(b)
assert.Nil(t, err)
assert.Contains(t, string(out), VERSION)
}
func TestAssetsCLI(t *testing.T) {
catchCLI([]string{"assets"})
//assert.EqualError(t, run, "end")
cmd := rootCmd
b := bytes.NewBufferString("")
cmd.SetOut(b)
cmd.SetArgs([]string{"assets"})
cmd.Execute()
out, err := ioutil.ReadAll(b)
assert.Nil(t, err)
assert.Contains(t, string(out), VERSION)
assert.FileExists(t, dir+"/assets/css/main.css")
assert.FileExists(t, dir+"/assets/css/style.css")
assert.FileExists(t, dir+"/assets/css/vendor.css")
@ -132,8 +145,9 @@ func TestAssetsCLI(t *testing.T) {
}
func TestSassCLI(t *testing.T) {
t.SkipNow()
catchCLI([]string{"sass"})
c := testcli.Command("statping", "assets")
c.Run()
t.Log(c.Stdout())
assert.FileExists(t, dir+"/assets/css/main.css")
assert.FileExists(t, dir+"/assets/css/style.css")
assert.FileExists(t, dir+"/assets/css/vendor.css")
@ -149,22 +163,6 @@ func TestUpdateCLI(t *testing.T) {
assert.Contains(t, gg, "version")
}
func TestHelpCLI(t *testing.T) {
run := catchCLI([]string{"help"})
assert.EqualError(t, run, "end")
}
func TestRunOnceCLI(t *testing.T) {
t.SkipNow()
run := catchCLI([]string{"run"})
assert.EqualError(t, run, "end")
}
func TestEnvCLI(t *testing.T) {
run := catchCLI([]string{"env"})
assert.Error(t, run)
}
func commandAndSleep(cmd *exec.Cmd, duration time.Duration, out chan<- string) {
go func(out chan<- string) {
runCommand(cmd, out)

79
cmd/commands.go Normal file
View File

@ -0,0 +1,79 @@
package main
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "statping",
Short: "A simple Application Status Monitor that is opensource and lightweight.",
Run: func(cmd *cobra.Command, args []string) {
start()
},
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of Statping",
Run: func(cmd *cobra.Command, args []string) {
if COMMIT != "" {
fmt.Printf("%s (%s)\n", VERSION, COMMIT)
} else {
fmt.Printf("%s\n", VERSION)
}
},
}
var assetsCmd = &cobra.Command{
Use: "assets",
Short: "Dump all assets used locally to be edited",
RunE: func(cmd *cobra.Command, args []string) error {
return assetsCli()
},
}
var exportCmd = &cobra.Command{
Use: "export",
Short: "Exports your Statping settings to a 'statping-export.json' file.",
RunE: func(cmd *cobra.Command, args []string) error {
return exportCli(args)
},
}
var sassCmd = &cobra.Command{
Use: "sass",
Short: "Compile .scss files into the css directory",
RunE: func(cmd *cobra.Command, args []string) error {
return sassCli()
},
}
var onceCmd = &cobra.Command{
Use: "once",
Short: "Check all services 1 time and then quit",
RunE: func(cmd *cobra.Command, args []string) error {
return onceCli()
},
}
var importCmd = &cobra.Command{
Use: "import [.json file]",
Short: "Imports settings from a previously saved JSON file.",
RunE: func(cmd *cobra.Command, args []string) error {
return importCli(args)
},
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("requires input file (.json)")
}
return nil
},
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
exit(err)
}
}

View File

@ -1,7 +1,6 @@
package main
import (
"flag"
"fmt"
"github.com/pkg/errors"
"github.com/statping/statping/database"
@ -31,26 +30,16 @@ var (
confgs *configs.DbConfig
)
// parseFlags will parse the application flags
// -ip = 0.0.0.0 IP address for outgoing HTTP server
// -port = 8080 Port number for outgoing HTTP server
// environment variables WILL overwrite flags
func parseFlags() {
envPort := utils.Getenv("PORT", 8080).(int)
envIpAddress := utils.Getenv("IP", "0.0.0.0").(string)
envVerbose := utils.Getenv("VERBOSE", 2).(int)
//envGrpcPort := utils.Getenv("GRPC_PORT", 0).(int)
flag.StringVar(&ipAddress, "ip", envIpAddress, "IP address to run the Statping HTTP server")
flag.StringVar(&envFile, "env", "", "IP address to run the Statping HTTP server")
flag.IntVar(&port, "port", envPort, "Port to run the HTTP server")
//flag.IntVar(&grpcPort, "grpc", envGrpcPort, "Port to run the gRPC server")
flag.IntVar(&verboseMode, "verbose", envVerbose, "Run in verbose mode to see detailed logs (1 - 4)")
flag.Parse()
}
func init() {
core.New(VERSION)
utils.InitCLI()
parseFlags(rootCmd)
rootCmd.AddCommand(versionCmd)
rootCmd.AddCommand(assetsCmd)
rootCmd.AddCommand(exportCmd)
rootCmd.AddCommand(importCmd)
rootCmd.AddCommand(sassCmd)
rootCmd.AddCommand(onceCmd)
}
// exit will return an error and return an exit code 1 due to this error
@ -69,11 +58,14 @@ func Close() {
// main will run the Statping application
func main() {
Execute()
}
// main will run the Statping application
func start() {
var err error
go sigterm()
parseFlags()
if err := source.Assets(); err != nil {
exit(err)
}
@ -84,23 +76,11 @@ func main() {
log.Errorf("Statping Log Error: %v\n", err)
}
args := flag.Args()
if len(args) >= 1 {
err := catchCLI(args)
if err != nil {
if err.Error() == "end" {
os.Exit(0)
return
}
exit(err)
}
}
log.Info(fmt.Sprintf("Starting Statping v%s", VERSION))
if err := updateDisplay(); err != nil {
log.Warnln(err)
}
//if err := updateDisplay(); err != nil {
// log.Warnln(err)
//}
confgs, err = configs.LoadConfigs()
if err != nil {
@ -135,7 +115,7 @@ func main() {
exit(errors.Wrap(err, "error creating default admin user"))
}
if utils.Getenv("SAMPLE_DATA", true).(bool) {
if utils.Params.GetBool("SAMPLE_DATA") {
if err := configs.TriggerSamples(); err != nil {
exit(errors.Wrap(err, "error creating database"))
}

17
cmd/viper.go Normal file
View File

@ -0,0 +1,17 @@
package main
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func parseFlags(cmd *cobra.Command) {
cmd.PersistentFlags().StringVarP(&ipAddress, "ip", "s", "0.0.0.0", "server port")
viper.BindPFlag("ip", cmd.PersistentFlags().Lookup("ip"))
cmd.PersistentFlags().IntVarP(&port, "port", "p", 8080, "server port")
viper.BindPFlag("port", cmd.PersistentFlags().Lookup("port"))
cmd.PersistentFlags().IntVarP(&verboseMode, "verbose", "v", 2, "server port")
viper.BindPFlag("verbose", cmd.PersistentFlags().Lookup("verbose"))
}

View File

@ -156,7 +156,7 @@ type Db struct {
// Openw is a drop-in replacement for Open()
func Openw(dialect string, args ...interface{}) (db Database, err error) {
gorm.NowFunc = func() time.Time {
return time.Now().UTC()
return utils.Now()
}
gormdb, err := gorm.Open(dialect, args...)
if err != nil {
@ -167,22 +167,36 @@ func Openw(dialect string, args ...interface{}) (db Database, err error) {
}
func OpenTester() (Database, error) {
testDB := utils.Getenv("TEST_DB", "sqlite3").(string)
var dbParamsstring string
testDB := utils.Params.GetString("DB_CONN")
var dbString string
switch testDB {
case "mysql":
dbParamsstring = fmt.Sprintf("root:password123@tcp(localhost:3306)/statping?charset=utf8&parseTime=True&loc=UTC&time_zone=%%27UTC%%27")
dbString = fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=utf8&parseTime=True&loc=UTC&time_zone=%%27UTC%%27",
utils.Params.GetString("DB_HOST"),
utils.Params.GetString("DB_PASS"),
utils.Params.GetString("DB_HOST"),
utils.Params.GetInt("DB_PORT"),
utils.Params.GetString("DB_DATABASE"),
)
case "postgres":
dbParamsstring = fmt.Sprintf("host=localhost port=5432 user=root dbname=statping password=password123 timezone=UTC")
dbString = fmt.Sprintf("host=%s port=%v user=%s dbname=%s password=%s sslmode=disable timezone=UTC",
utils.Params.GetString("DB_HOST"),
utils.Params.GetInt("DB_PORT"),
utils.Params.GetString("DB_USER"),
utils.Params.GetString("DB_DATABASE"),
utils.Params.GetString("DB_PASS"))
default:
dbParamsstring = fmt.Sprintf("file:%s?mode=memory&cache=shared", utils.RandomString(12))
dbString = fmt.Sprintf("file:%s?mode=memory&cache=shared", utils.RandomString(12))
}
fmt.Println(testDB, dbParamsstring)
newDb, err := Openw(testDB, dbParamsstring)
newDb, err := Openw(testDB, dbString)
if err != nil {
return nil, err
}
newDb.DB().SetMaxOpenConns(1)
if testDB != "sqlite3" {
newDb.DB().SetMaxOpenConns(25)
}
return newDb, err
}

View File

@ -74,7 +74,7 @@ func (g *GroupQuery) GraphData(by By) ([]*TimeValue, error) {
dbQuery := g.db.MultipleSelects(
g.db.SelectByTime(g.Group),
by.String(),
).Group("timeframe")
).Group("timeframe").Order("timeframe", true)
g.db = dbQuery

View File

@ -36,7 +36,7 @@ func (it *Db) SelectByTime(increment time.Duration) string {
case "mysql":
return fmt.Sprintf("FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(created_at) / %d) * %d) AS timeframe", seconds, seconds)
case "postgres":
return fmt.Sprintf("date_trunc('%s', created_at) AS timeframe", increment)
return fmt.Sprintf("date_trunc('minute', created_at) - (CAST(EXTRACT(MINUTE FROM created_at) AS integer) %% %d) * interval '1 minute' AS timeframe", seconds)
default:
return fmt.Sprintf("datetime((strftime('%%s', created_at) / %d) * %d, 'unixepoch') as timeframe", seconds, seconds)
}

1063
dev/postman.json vendored

File diff suppressed because it is too large Load Diff

View File

@ -7,9 +7,15 @@
"value": "http://127.0.0.1:8080",
"description": "",
"enabled": true
},
{
"key": "api_key",
"value": "demosecret123",
"description": "",
"enabled": true
}
],
"_postman_variable_scope": "environment",
"_postman_exported_at": "2018-11-17T16:55:15.031Z",
"_postman_exported_using": "Postman/6.5.2"
}
}

13
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fatih/structs v1.1.0
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/getsentry/sentry-go v0.5.1
github.com/go-mail/mail v2.3.1+incompatible
github.com/gogo/protobuf v1.3.1 // indirect
@ -18,18 +19,30 @@ require (
github.com/joho/godotenv v1.3.0
github.com/kataras/iris/v12 v12.0.1
github.com/mattn/go-sqlite3 v2.0.1+incompatible
github.com/mitchellh/mapstructure v1.2.2 // indirect
github.com/pelletier/go-toml v1.7.0 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/common v0.9.1
github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f
github.com/russross/blackfriday/v2 v2.0.1
github.com/sirupsen/logrus v1.4.2
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v1.0.0
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.6.3
github.com/stretchr/testify v1.5.1
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect
golang.org/x/text v0.3.2 // indirect
golang.org/x/tools v0.0.0-20200321014904-268ba720d32c // indirect
google.golang.org/grpc v1.28.0
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.55.0 // indirect
gopkg.in/mail.v2 v2.3.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.2.8

90
go.sum
View File

@ -11,6 +11,7 @@ github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voi
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
@ -29,13 +30,19 @@ github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY=
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -47,6 +54,7 @@ github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6ps
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -59,10 +67,14 @@ github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/getsentry/sentry-go v0.5.1 h1:MIPe7ScHADsrK2vznqmhksIUFxq7m0JfTh+ZIMkI+VQ=
github.com/getsentry/sentry-go v0.5.1/go.mod h1:B8H7x8TYDPkeWPRzGpIiFO97LZP6rL8A3hEt8lUItMw=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
@ -83,11 +95,13 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -97,10 +111,12 @@ github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaW
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
@ -109,10 +125,15 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4=
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
@ -131,8 +152,10 @@ github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
@ -146,6 +169,7 @@ github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/l
github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d h1:V5Rs9ztEWdp58oayPq/ulmlqJJZeJP6pP79uP3qjcao=
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
@ -163,6 +187,8 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@ -175,10 +201,16 @@ github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
@ -188,10 +220,14 @@ github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg=
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -201,18 +237,24 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f h1:onGP+qmYmjKs7pkmi9j0mwyr97/D5wki80e74aKIOxg=
github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f/go.mod h1:cq57a4l475CeMvE7RRpSui1MEqCmhirIt1E7kl8BC2Q=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
@ -224,14 +266,36 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -239,8 +303,11 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e h1:nt2877sKfojlHCTOBXbpWjBkuWKritFaGIfgQwbQUls=
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e/go.mod h1:B4+Kq1u5FlULTjFSM707Q6e/cOHFv0z/6QRoxubDIQ8=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@ -255,12 +322,17 @@ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -285,6 +357,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
@ -301,6 +374,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -310,8 +384,16 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -334,6 +416,7 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
@ -344,15 +427,22 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gG
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ=
gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -48,12 +48,12 @@ func TestFailedHTTPServer(t *testing.T) {
func TestSetupRoutes(t *testing.T) {
form := url.Values{}
form.Add("db_host", "")
form.Add("db_user", "")
form.Add("db_password", "")
form.Add("db_database", "")
form.Add("db_connection", "sqlite")
form.Add("db_port", "")
form.Add("db_host", utils.Params.GetString("DB_HOST"))
form.Add("db_user", utils.Params.GetString("DB_USER"))
form.Add("db_password", utils.Params.GetString("DB_PASS"))
form.Add("db_database", utils.Params.GetString("DB_DATABASE"))
form.Add("db_connection", utils.Params.GetString("DB_CONN"))
form.Add("db_port", utils.Params.GetString("DB_PORT"))
form.Add("project", "Tester")
form.Add("username", "admin")
form.Add("password", "password123")
@ -82,7 +82,7 @@ func TestSetupRoutes(t *testing.T) {
Body: form.Encode(),
ExpectedStatus: 200,
HttpHeaders: []string{"Content-Type=application/x-www-form-urlencoded"},
ExpectedFiles: []string{dir + "/config.yml", dir + "/" + "statping.db"},
ExpectedFiles: []string{utils.Directory + "/config.yml"},
FuncTest: func(t *testing.T) error {
if !core.App.Setup {
return errors.New("core has not been setup")

View File

@ -44,7 +44,7 @@ func RunHTTPServer(ip string, port int) error {
log.Infoln(fmt.Sprintf("Statping Secure HTTPS Server running on https://%v:%v", ip, 443))
usingSSL = true
} else {
log.Infoln("Statping HTTP Server running on http://" + host)
log.Infoln("Statping HTTP Server running on http://" + host + basePath)
}
router = Router()
@ -172,7 +172,7 @@ func IsAdmin(r *http.Request) bool {
if !core.App.Setup {
return false
}
if utils.Getenv("GO_ENV", false).(bool) {
if utils.Params.GetString("GO_ENV") == "test" {
return true
}
claim, err := getJwtToken(r)

View File

@ -26,14 +26,14 @@ func Router() *mux.Router {
CacheStorage = NewStorage()
r := mux.NewRouter().StrictSlash(true)
authUser := utils.Getenv("AUTH_USERNAME", "").(string)
authPass := utils.Getenv("AUTH_PASSWORD", "").(string)
authUser := utils.Params.GetString("AUTH_USERNAME")
authPass := utils.Params.GetString("AUTH_PASSWORD")
if authUser != "" && authPass != "" {
r.Use(basicAuthHandler)
}
bPath := utils.Getenv("BASE_PATH", "").(string)
bPath := utils.Params.GetString("BASE_PATH")
sentryHandler := sentryhttp.New(sentryhttp.Options{})
if bPath != "" {

View File

@ -77,7 +77,7 @@ func TestApiServiceRoutes(t *testing.T) {
Name: "Statping Service Failures",
URL: "/api/services/1/failures",
Method: "GET",
ResponseLen: 125,
ResponseLen: 126,
ExpectedStatus: 200,
},
{
@ -123,7 +123,7 @@ func TestApiServiceRoutes(t *testing.T) {
Name: "Statping Service 1 Failure Data - 15 Minute",
URL: "/api/services/1/failure_data" + startEndQuery + "&group=15m",
Method: "GET",
ResponseLen: 124,
ResponseLen: 125,
ExpectedStatus: 200,
},
{

View File

@ -83,14 +83,14 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
c := &core.Core{
Name: project,
Description: description,
//ApiKey: apiKey.(string),
//ApiSecret: apiSecret.(string),
Domain: domain,
Version: core.App.Version,
Started: utils.Now(),
CreatedAt: utils.Now(),
UseCdn: null.NewNullBool(false),
Footer: null.NewNullString(""),
ApiKey: utils.Params.GetString("API_KEY"),
ApiSecret: utils.Params.GetString("API_SECRET"),
Domain: domain,
Version: core.App.Version,
Started: utils.Now(),
CreatedAt: utils.Now(),
UseCdn: null.NewNullBool(false),
Footer: null.NewNullString(""),
}
log.Infoln("Creating new Core")

View File

@ -4,6 +4,7 @@ import (
"github.com/statping/statping/database"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/null"
"github.com/statping/statping/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"os"
@ -12,7 +13,7 @@ import (
)
var (
DISCORD_URL = os.Getenv("DISCORD_URL")
DISCORD_URL = utils.Params.GetString("DISCORD_URL")
discordMessage = `{"content": "The discord notifier on Statping has been tested!"}`
)

View File

@ -8,7 +8,6 @@ import (
"github.com/statping/statping/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"os"
"testing"
"time"
)
@ -25,12 +24,12 @@ var (
var testEmail *emailOutgoing
func init() {
EMAIL_HOST = os.Getenv("EMAIL_HOST")
EMAIL_USER = os.Getenv("EMAIL_USER")
EMAIL_PASS = os.Getenv("EMAIL_PASS")
EMAIL_OUTGOING = os.Getenv("EMAIL_OUTGOING")
EMAIL_SEND_TO = os.Getenv("EMAIL_SEND_TO")
EMAIL_PORT = utils.ToInt(os.Getenv("EMAIL_PORT"))
EMAIL_HOST = utils.Params.GetString("EMAIL_HOST")
EMAIL_USER = utils.Params.GetString("EMAIL_USER")
EMAIL_PASS = utils.Params.GetString("EMAIL_PASS")
EMAIL_OUTGOING = utils.Params.GetString("EMAIL_OUTGOING")
EMAIL_SEND_TO = utils.Params.GetString("EMAIL_SEND_TO")
EMAIL_PORT = utils.ToInt(utils.Params.GetString("EMAIL_PORT"))
}
func TestEmailNotifier(t *testing.T) {

View File

@ -4,9 +4,9 @@ import (
"github.com/statping/statping/database"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/null"
"github.com/statping/statping/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"os"
"testing"
"time"
)
@ -21,7 +21,7 @@ func TestSlackNotifier(t *testing.T) {
db.AutoMigrate(&notifications.Notification{})
notifications.SetDB(db)
SLACK_URL = os.Getenv("SLACK_URL")
SLACK_URL = utils.Params.GetString("SLACK_URL")
slacker.Host = SLACK_URL
slacker.Enabled = null.NewNullBool(true)

File diff suppressed because it is too large Load Diff

View File

@ -1,78 +1,59 @@
package configs
import (
"github.com/joho/godotenv"
"github.com/pkg/errors"
"fmt"
"github.com/statping/statping/utils"
)
func (d *DbConfig) ConnectionString() string {
var conn string
postgresSSL := utils.Params.GetString("POSTGRES_SSLMODE")
switch d.DbConn {
case "sqlite", "sqlite3", "memory":
if d.DbConn == "memory" {
conn = "sqlite3"
d.DbConn = ":memory:"
return d.DbConn
} else {
conn, err := findDbFile(d)
if err != nil {
log.Errorln(err)
}
d.SqlFile = conn
log.Infof("SQL database file at: %s", d.SqlFile)
d.DbConn = "sqlite3"
return d.SqlFile
}
case "mysql":
host := fmt.Sprintf("%v:%v", d.DbHost, d.DbPort)
conn = fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8&parseTime=True&loc=UTC&time_zone=%%27UTC%%27", d.DbUser, d.DbPass, host, d.DbData)
return conn
case "postgres":
conn = fmt.Sprintf("host=%v port=%v user=%v dbname=%v password=%v timezone=UTC sslmode=%v", d.DbHost, d.DbPort, d.DbUser, d.DbData, d.DbPass, postgresSSL)
return conn
}
return conn
}
func loadConfigEnvs() (*DbConfig, error) {
var err error
log.Infof("Loading configs from environment variables")
loadDotEnvs()
dbConn := utils.Getenv("DB_CONN", "").(string)
dbHost := utils.Getenv("DB_HOST", "").(string)
dbUser := utils.Getenv("DB_USER", "").(string)
dbPass := utils.Getenv("DB_PASS", "").(string)
dbData := utils.Getenv("DB_DATABASE", "").(string)
dbPort := utils.Getenv("DB_PORT", defaultPort(dbConn)).(int)
name := utils.Getenv("NAME", "Statping").(string)
desc := utils.Getenv("DESCRIPTION", "Statping Monitoring Sample Data").(string)
user := utils.Getenv("ADMIN_USER", "admin").(string)
password := utils.Getenv("ADMIN_PASS", "admin").(string)
domain := utils.Getenv("DOMAIN", "").(string)
sqlFile := utils.Getenv("SQL_FILE", "").(string)
if dbConn != "" && dbConn != "sqlite" {
if dbHost == "" {
return nil, errors.New("Missing DB_HOST environment variable")
}
if dbPort == 0 {
return nil, errors.New("Missing DB_PORT environment variable")
}
if dbUser == "" {
return nil, errors.New("Missing DB_USER environment variable")
}
if dbPass == "" {
return nil, errors.New("Missing DB_PASS environment variable")
}
if dbData == "" {
return nil, errors.New("Missing DB_DATABASE environment variable")
}
}
p := utils.Params
config := &DbConfig{
DbConn: dbConn,
DbHost: dbHost,
DbUser: dbUser,
DbPass: dbPass,
DbData: dbData,
DbPort: dbPort,
Project: name,
Description: desc,
Domain: domain,
Email: "",
Username: user,
Password: password,
Error: nil,
DbConn: p.GetString("DB_CONN"),
DbHost: p.GetString("DB_HOST"),
DbUser: p.GetString("DB_USER"),
DbPass: p.GetString("DB_PASS"),
DbData: p.GetString("DB_DATABASE"),
DbPort: p.GetInt("DB_PORT"),
Project: p.GetString("NAME"),
Description: p.GetString("DESCRIPTION"),
Domain: p.GetString("DOMAIN"),
Email: p.GetString("EMAIL"),
Username: p.GetString("ADMIN_USER"),
Password: p.GetString("ADMIN_PASS"),
Location: utils.Directory,
SqlFile: sqlFile,
}
return config, err
}
// loadDotEnvs attempts to load database configs from a '.env' file in root directory
func loadDotEnvs() {
err := godotenv.Overload(utils.Directory + "/" + ".env")
if err == nil {
log.Warnln("Environment file '.env' found")
envs, _ := godotenv.Read(utils.Directory + "/" + ".env")
for k, e := range envs {
log.Infof("Overwriting %s=%s\n", k, e)
}
log.Warnln("These environment variables will overwrite any existing")
SqlFile: p.GetString("SQL_FILE"),
}
return config, nil
}

View File

@ -9,7 +9,6 @@ import (
func LoadConfigFile(directory string) (*DbConfig, error) {
var configs *DbConfig
log.Infof("Attempting to read config file at: %s/config.yml ", directory)
file, err := utils.OpenFile(directory + "/config.yml")
if err != nil {

View File

@ -28,6 +28,8 @@ func LoadConfigForm(r *http.Request) (*DbConfig, error) {
return nil, err
}
p := utils.Params
confg := &DbConfig{
DbConn: dbConn,
DbHost: dbHost,
@ -41,10 +43,18 @@ func LoadConfigForm(r *http.Request) (*DbConfig, error) {
Username: username,
Password: password,
Email: email,
Error: nil,
Location: utils.Directory,
}
p.Set("DB_CONN", dbConn)
p.Set("DB_HOST", dbHost)
p.Set("DB_USER", dbUser)
p.Set("DB_PORT", dbPort)
p.Set("DB_PASS", dbPass)
p.Set("DB_DATABASE", dbDatabase)
p.Set("NAME", project)
p.Set("DESCRIPTION", description)
return confg, nil
}

View File

@ -17,45 +17,19 @@ import (
"github.com/statping/statping/types/services"
"github.com/statping/statping/types/users"
"github.com/statping/statping/utils"
"os"
"time"
)
// Connect will attempt to connect to the sqlite, postgres, or mysql database
func Connect(configs *DbConfig, retry bool) error {
postgresSSL := os.Getenv("POSTGRES_SSLMODE")
var conn string
conn := configs.ConnectionString()
var err error
switch configs.DbConn {
case "sqlite", "sqlite3", "memory":
if configs.DbConn == "memory" {
conn = "sqlite3"
configs.DbConn = ":memory:"
} else {
conn = findDbFile(configs)
configs.SqlFile = conn
log.Infof("SQL database file at: %s", configs.SqlFile)
configs.DbConn = "sqlite3"
}
case "mysql":
host := fmt.Sprintf("%v:%v", configs.DbHost, configs.DbPort)
conn = fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8&parseTime=True&loc=UTC&time_zone=%%27UTC%%27", configs.DbUser, configs.DbPass, host, configs.DbData)
case "postgres":
sslMode := "disable"
if postgresSSL != "" {
sslMode = postgresSSL
}
conn = fmt.Sprintf("host=%v port=%v user=%v dbname=%v password=%v timezone=UTC sslmode=%v", configs.DbHost, configs.DbPort, configs.DbUser, configs.DbData, configs.DbPass, sslMode)
case "mssql":
host := fmt.Sprintf("%v:%v", configs.DbHost, configs.DbPort)
conn = fmt.Sprintf("sqlserver://%v:%v@%v?database=%v", configs.DbUser, configs.DbPass, host, configs.DbData)
}
log.WithFields(utils.ToFields(configs, conn)).Debugln("attempting to connect to database")
dbSession, err := database.Openw(configs.DbConn, conn)
if err != nil {
log.Debugln(fmt.Sprintf("Database connection error %s", err))
log.Errorf(fmt.Sprintf("Database connection error %s", err))
if retry {
log.Warnln(fmt.Sprintf("Database %s connection to '%s' is not available, trying again in 5 seconds...", configs.DbConn, configs.DbHost))
time.Sleep(5 * time.Second)
@ -65,20 +39,20 @@ func Connect(configs *DbConfig, retry bool) error {
}
}
apiKey := utils.Getenv("API_KEY", utils.RandomString(16)).(string)
apiSecret := utils.Getenv("API_SECRET", utils.RandomString(16)).(string)
apiKey := utils.Getenv("API_KEY", utils.RandomString(24)).(string)
apiSecret := utils.Getenv("API_SECRET", utils.RandomString(24)).(string)
configs.ApiKey = apiKey
configs.ApiSecret = apiSecret
log.WithFields(utils.ToFields(dbSession)).Debugln("connected to database")
maxOpenConn := utils.Getenv("MAX_OPEN_CONN", 25)
maxIdleConn := utils.Getenv("MAX_IDLE_CONN", 25)
maxLifeConn := utils.Getenv("MAX_LIFE_CONN", 5*time.Minute)
maxOpenConn := utils.Params.GetInt("MAX_OPEN_CONN")
maxIdleConn := utils.Params.GetInt("MAX_IDLE_CONN")
maxLifeConn := utils.Params.GetDuration("MAX_LIFE_CONN")
dbSession.DB().SetMaxOpenConns(maxOpenConn.(int))
dbSession.DB().SetMaxIdleConns(maxIdleConn.(int))
dbSession.DB().SetConnMaxLifetime(maxLifeConn.(time.Duration))
dbSession.DB().SetMaxOpenConns(maxOpenConn)
dbSession.DB().SetMaxIdleConns(maxIdleConn)
dbSession.DB().SetConnMaxLifetime(maxLifeConn)
if dbSession.DB().Ping() == nil {
if utils.VerboseMode >= 4 {

View File

@ -102,10 +102,12 @@ func (d *DbConfig) CreateDatabase() error {
log.Infoln("Creating Database Tables...")
for _, table := range DbModels {
log.Infof("Creating table '%T'", table)
if err := d.Db.CreateTable(table); err.Error() != nil {
return errors.Wrap(err.Error(), fmt.Sprintf("error creating '%T' table", table))
}
}
log.Infof("Creating table 'core'")
if err := d.Db.Table("core").CreateTable(&core.Core{}); err.Error() != nil {
return errors.Wrap(err.Error(), fmt.Sprintf("error creating 'core' table"))
}

View File

@ -1,11 +1,11 @@
package configs
import (
"fmt"
"github.com/pkg/errors"
"github.com/statping/statping/utils"
"os"
"path/filepath"
"strings"
)
var log = utils.Log
@ -18,7 +18,6 @@ func ConnectConfigs(configs *DbConfig) error {
if err := configs.Save(utils.Directory); err != nil {
return errors.Wrap(err, "error saving configuration")
}
return nil
}
@ -31,8 +30,7 @@ func LoadConfigs() (*DbConfig, error) {
return nil, errors.Errorf("Directory %s is not writable!", utils.Directory)
}
dbConn := utils.Getenv("DB_CONN", "").(string)
if dbConn != "" {
if utils.Params.GetString("DB_CONN") != "" {
configs, err := loadConfigEnvs()
if err != nil {
return LoadConfigFile(utils.Directory)
@ -43,30 +41,39 @@ func LoadConfigs() (*DbConfig, error) {
return LoadConfigFile(utils.Directory)
}
func findDbFile(configs *DbConfig) string {
func findDbFile(configs *DbConfig) (string, error) {
location := utils.Directory + "/" + SqliteFilename
if configs == nil {
return findSQLin(utils.Directory)
file, err := findSQLin(utils.Directory)
if err != nil {
return "", err
}
location = file
}
if configs.SqlFile != "" {
return configs.SqlFile
if configs != nil && configs.SqlFile != "" {
return configs.SqlFile, nil
}
return utils.Directory + "/" + SqliteFilename
return location, nil
}
func findSQLin(path string) string {
func findSQLin(path string) (string, error) {
filename := SqliteFilename
var found []string
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if filepath.Ext(path) == ".db" {
fmt.Println("DB file is now: ", info.Name())
filename = info.Name()
found = append(found, filename)
}
return nil
})
if err != nil {
log.Error(err)
return filename, err
}
return filename
if len(found) > 1 {
return filename, errors.Errorf("found multiple database files: %s", strings.Join(found, ", "))
}
return filename, nil
}

View File

@ -12,58 +12,58 @@ func init() {
os.Setenv("MIGRATION_ID", utils.ToString(latestMigration))
}
func (c *DbConfig) genericMigration(alterStr string, isPostgres bool) error {
func (d *DbConfig) genericMigration(alterStr string, isPostgres bool) error {
var extra string
extraType := "UNSIGNED INTEGER"
if isPostgres {
extra = " TYPE"
extraType = "bigint"
}
if err := c.Db.Exec(fmt.Sprintf("ALTER TABLE hits %s COLUMN latency%s BIGINT;", alterStr, extra)).Error(); err != nil {
if err := d.Db.Exec(fmt.Sprintf("ALTER TABLE hits %s COLUMN latency%s BIGINT;", alterStr, extra)).Error(); err != nil {
return err
}
if err := c.Db.Exec(fmt.Sprintf("ALTER TABLE hits %s COLUMN ping_time%s BIGINT;", alterStr, extra)).Error(); err != nil {
if err := d.Db.Exec(fmt.Sprintf("ALTER TABLE hits %s COLUMN ping_time%s BIGINT;", alterStr, extra)).Error(); err != nil {
return err
}
if err := c.Db.Exec(fmt.Sprintf("ALTER TABLE failures %s COLUMN ping_time%s BIGINT;", alterStr, extra)).Error(); err != nil {
if err := d.Db.Exec(fmt.Sprintf("ALTER TABLE failures %s COLUMN ping_time%s BIGINT;", alterStr, extra)).Error(); err != nil {
return err
}
if err := c.Db.Exec(fmt.Sprintf("UPDATE hits SET latency = CAST(latency * 1000000 AS %s);", extraType)).Error(); err != nil {
if err := d.Db.Exec(fmt.Sprintf("UPDATE hits SET latency = CAST(latency * 1000000 AS %s);", extraType)).Error(); err != nil {
return err
}
if err := c.Db.Exec(fmt.Sprintf("UPDATE hits SET ping_time = CAST(ping_time * 1000000 AS %s);", extraType)).Error(); err != nil {
if err := d.Db.Exec(fmt.Sprintf("UPDATE hits SET ping_time = CAST(ping_time * 1000000 AS %s);", extraType)).Error(); err != nil {
return err
}
if err := c.Db.Exec(fmt.Sprintf("UPDATE failures SET ping_time = CAST(ping_time * 1000000 AS %s);", extraType)).Error(); err != nil {
if err := d.Db.Exec(fmt.Sprintf("UPDATE failures SET ping_time = CAST(ping_time * 1000000 AS %s);", extraType)).Error(); err != nil {
return err
}
return nil
}
func (c *DbConfig) sqliteMigration() error {
if err := c.Db.Exec(`ALTER TABLE hits RENAME TO hits_backup;`).Error(); err != nil {
func (d *DbConfig) sqliteMigration() error {
if err := d.Db.Exec(`ALTER TABLE hits RENAME TO hits_backup;`).Error(); err != nil {
return err
}
if err := c.Db.Exec(`CREATE TABLE hits (id INTEGER PRIMARY KEY AUTOINCREMENT, service bigint, latency bigint, ping_time bigint, created_at datetime);`).Error(); err != nil {
if err := d.Db.Exec(`CREATE TABLE hits (id INTEGER PRIMARY KEY AUTOINCREMENT, service bigint, latency bigint, ping_time bigint, created_at datetime);`).Error(); err != nil {
return err
}
if err := c.Db.Exec(`INSERT INTO hits (id, service, latency, ping_time, created_at) SELECT id, service, CAST(latency * 1000000 AS bigint), CAST(ping_time * 1000000 AS bigint), created_at FROM hits_backup;`).Error(); err != nil {
if err := d.Db.Exec(`INSERT INTO hits (id, service, latency, ping_time, created_at) SELECT id, service, CAST(latency * 1000000 AS bigint), CAST(ping_time * 1000000 AS bigint), created_at FROM hits_backup;`).Error(); err != nil {
return err
}
// failures table
if err := c.Db.Exec(`ALTER TABLE failures RENAME TO failures_backup;`).Error(); err != nil {
if err := d.Db.Exec(`ALTER TABLE failures RENAME TO failures_backup;`).Error(); err != nil {
return err
}
if err := c.Db.Exec(`CREATE TABLE failures (id INTEGER PRIMARY KEY AUTOINCREMENT, issue varchar(255), method varchar(255), method_id bigint, service bigint, ping_time bigint, checkin bigint, error_code bigint, created_at datetime);`).Error(); err != nil {
if err := d.Db.Exec(`CREATE TABLE failures (id INTEGER PRIMARY KEY AUTOINCREMENT, issue varchar(255), method varchar(255), method_id bigint, service bigint, ping_time bigint, checkin bigint, error_code bigint, created_at datetime);`).Error(); err != nil {
return err
}
if err := c.Db.Exec(`INSERT INTO failures (id, issue, method, method_id, service, ping_time, checkin, created_at) SELECT id, issue, method, method_id, service, CAST(ping_time * 1000000 AS bigint), checkin, created_at FROM failures_backup;`).Error(); err != nil {
if err := d.Db.Exec(`INSERT INTO failures (id, issue, method, method_id, service, ping_time, checkin, created_at) SELECT id, issue, method, method_id, service, CAST(ping_time * 1000000 AS bigint), checkin, created_at FROM failures_backup;`).Error(); err != nil {
return err
}
if err := c.Db.Exec(`DROP TABLE hits_backup;`).Error(); err != nil {
if err := d.Db.Exec(`DROP TABLE hits_backup;`).Error(); err != nil {
return err
}
if err := c.Db.Exec(`DROP TABLE failures_backup;`).Error(); err != nil {
if err := d.Db.Exec(`DROP TABLE failures_backup;`).Error(); err != nil {
return err
}
return nil

View File

@ -1,21 +1,12 @@
package configs
import (
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
)
import "github.com/statping/statping/utils"
// Save will initially create the config.yml file
func (d *DbConfig) Save(directory string) error {
data, err := yaml.Marshal(d)
if err != nil {
return err
if err := utils.Params.SafeWriteConfigAs(directory + "/config.yml"); err != nil {
return nil
}
if err := ioutil.WriteFile(directory+"/config.yml", data, os.ModePerm); err != nil {
return err
}
d.filename = directory + "/config.yml"
return nil
}

View File

@ -20,33 +20,33 @@ import (
"github.com/statping/statping/types/users"
)
func (c *DbConfig) DatabaseChanges() error {
func (d *DbConfig) DatabaseChanges() error {
var cr core.Core
c.Db.Model(&core.Core{}).Find(&cr)
d.Db.Model(&core.Core{}).Find(&cr)
if latestMigration > cr.MigrationId {
log.Infof("Statping database is out of date, migrating to: %d", latestMigration)
switch c.Db.DbType() {
switch d.Db.DbType() {
case "mysql":
if err := c.genericMigration("MODIFY", false); err != nil {
if err := d.genericMigration("MODIFY", false); err != nil {
return err
}
case "postgres":
if err := c.genericMigration("ALTER", true); err != nil {
if err := d.genericMigration("ALTER", true); err != nil {
return err
}
default:
if err := c.sqliteMigration(); err != nil {
if err := d.sqliteMigration(); err != nil {
return err
}
}
if err := c.Db.Exec(fmt.Sprintf("UPDATE core SET migration_id = %d", latestMigration)).Error(); err != nil {
if err := d.Db.Exec(fmt.Sprintf("UPDATE core SET migration_id = %d", latestMigration)).Error(); err != nil {
return err
}
if err := c.BackupAssets(); err != nil {
if err := d.BackupAssets(); err != nil {
return err
}
}
@ -55,7 +55,7 @@ func (c *DbConfig) DatabaseChanges() error {
// BackupAssets is a temporary function (to version 0.90.*) to backup your customized theme
// to a new folder called 'assets_backup'.
func (c *DbConfig) BackupAssets() error {
func (d *DbConfig) BackupAssets() error {
if source.UsingAssets(utils.Directory) {
log.Infof("Backing up 'assets' folder to 'assets_backup'")
if err := utils.RenameDirectory(utils.Directory+"/assets", utils.Directory+"/assets_backup"); err != nil {
@ -69,12 +69,12 @@ func (c *DbConfig) BackupAssets() error {
//MigrateDatabase will migrate the database structure to current version.
//This function will NOT remove previous records, tables or columns from the database.
//If this function has an issue, it will ROLLBACK to the previous state.
func (c *DbConfig) MigrateDatabase() error {
func (d *DbConfig) MigrateDatabase() error {
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
log.Infoln("Migrating Database Tables...")
tx := c.Db.Begin()
tx := d.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
@ -99,27 +99,27 @@ func (c *DbConfig) MigrateDatabase() error {
return err
}
c.Db.Table("core").Model(&core.Core{}).Update("version", core.App.Version)
d.Db.Table("core").Model(&core.Core{}).Update("version", core.App.Version)
log.Infoln("Statping Database Tables Migrated")
if err := c.Db.Model(&hits.Hit{}).AddIndex("idx_service_hit", "service").Error(); err != nil {
if err := d.Db.Model(&hits.Hit{}).AddIndex("idx_service_hit", "service").Error(); err != nil {
log.Errorln(err)
}
if err := c.Db.Model(&hits.Hit{}).AddIndex("hit_created_at", "created_at").Error(); err != nil {
if err := d.Db.Model(&hits.Hit{}).AddIndex("hit_created_at", "created_at").Error(); err != nil {
log.Errorln(err)
}
if err := c.Db.Model(&failures.Failure{}).AddIndex("fail_created_at", "created_at").Error(); err != nil {
if err := d.Db.Model(&failures.Failure{}).AddIndex("fail_created_at", "created_at").Error(); err != nil {
log.Errorln(err)
}
if err := c.Db.Model(&failures.Failure{}).AddIndex("idx_service_fail", "service").Error(); err != nil {
if err := d.Db.Model(&failures.Failure{}).AddIndex("idx_service_fail", "service").Error(); err != nil {
log.Errorln(err)
}
if err := c.Db.Model(&failures.Failure{}).AddIndex("idx_checkin_fail", "checkin").Error(); err != nil {
if err := d.Db.Model(&failures.Failure{}).AddIndex("idx_checkin_fail", "checkin").Error(); err != nil {
log.Errorln(err)
}
log.Infoln("Database Indexes Created")

View File

@ -5,8 +5,6 @@ import (
"github.com/statping/statping/database"
"github.com/statping/statping/types/null"
"github.com/statping/statping/utils"
"os"
"time"
)
var db database.Database
@ -31,32 +29,25 @@ func Select() (*Core, error) {
}
App = &c
if os.Getenv("USE_CDN") == "true" {
if utils.Params.GetBool("USE_CDN") {
App.UseCdn = null.NewNullBool(true)
}
if os.Getenv("ALLOW_REPORTS") == "true" {
if utils.Params.GetBool("ALLOW_REPORTS") {
App.AllowReports = null.NewNullBool(true)
}
return App, q.Error()
}
func (c *Core) Create() error {
apiKey := utils.Getenv("API_KEY", utils.NewSHA256Hash()).(string)
apiSecret := utils.Getenv("API_SECRET", utils.NewSHA256Hash()).(string)
if c.ApiKey == "" || c.ApiSecret == "" {
c.ApiSecret = apiSecret
c.ApiKey = apiKey
}
newCore := &Core{
Name: c.Name,
Description: c.Description,
ConfigFile: utils.Directory + "/config.yml",
ApiKey: c.ApiKey,
ApiSecret: c.ApiSecret,
ApiKey: utils.Params.GetString("API_KEY"),
ApiSecret: utils.Params.GetString("API_SECRET"),
Version: App.Version,
Domain: c.Domain,
MigrationId: time.Now().Unix(),
MigrationId: utils.Now().Unix(),
}
q := db.Create(&newCore)
return q.Error()

View File

@ -34,7 +34,6 @@ func Samples() error {
log.Infoln(fmt.Sprintf("Adding %v Failure records to service", 400))
for fi := 0.; fi <= float64(400); fi++ {
createdAt = createdAt.Add(35 * time.Minute)
failure := &Failure{
Service: i,
Issue: "testing right here",
@ -42,6 +41,7 @@ func Samples() error {
}
tx = tx.Create(&failure)
createdAt = createdAt.Add(35 * time.Minute)
}
if err := tx.Commit().Error(); err != nil {
log.Error(err)

View File

@ -2,46 +2,40 @@ package hits
import (
"fmt"
"github.com/statping/statping/database"
"github.com/statping/statping/types"
"github.com/statping/statping/utils"
"sync"
"time"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/mattn/go-sqlite3"
"github.com/statping/statping/database"
"github.com/statping/statping/types"
"github.com/statping/statping/utils"
"time"
)
var SampleHits = 99900.
func Samples() error {
tx := db.Begin()
sg := new(sync.WaitGroup)
for i := int64(1); i <= 5; i++ {
err := createHitsAt(tx, i, sg)
if err != nil {
tx := db.Begin()
tx = createHitsAt(tx, i)
if err := tx.Commit().Error(); err != nil {
log.Error(err)
return err
}
tx = db.Begin()
}
return tx.Error()
return nil
}
func createHitsAt(db database.Database, serviceID int64, sg *sync.WaitGroup) error {
log.Infoln(fmt.Sprintf("Adding sample hit records to service #%d", serviceID))
func createHitsAt(db database.Database, serviceID int64) database.Database {
log.Infoln(fmt.Sprintf("Adding Sample records to service #%d", serviceID))
createdAt := utils.Now().Add(-3 * types.Day)
p := utils.NewPerlin(2, 2, 5, utils.Now().UnixNano())
i := 0
for hi := 0.; hi <= SampleHits; hi++ {
latency := p.Noise1D(hi / 500)
createdAt = createdAt.Add(30 * time.Second)
hit := &Hit{
Service: serviceID,
Latency: int64(latency * 10000000),
@ -50,15 +44,12 @@ func createHitsAt(db database.Database, serviceID int64, sg *sync.WaitGroup) err
}
db = db.Create(&hit)
if err := db.Error(); err != nil {
return err
}
i++
if createdAt.After(utils.Now()) {
break
}
createdAt = createdAt.Add(30 * time.Second)
}
return db.Commit().Error()
return db
}

70
utils/configs.go Normal file
View File

@ -0,0 +1,70 @@
package utils
import (
"github.com/prometheus/common/log"
"github.com/spf13/viper"
"os"
"time"
)
var (
Params *viper.Viper
)
func InitCLI() {
Params = viper.New()
setDefaults()
Params.SetConfigName("config")
Params.SetConfigType("yml")
Params.AddConfigPath(".")
err := Params.ReadInConfig()
if err != nil {
log.Debugf("config.yml Fatal error config file: %s", err)
}
Params.AddConfigPath(".")
Params.SetConfigFile(".env")
err = Params.ReadInConfig()
if err != nil {
log.Debugf(".env Fatal error config file: %s", err)
}
Params.AutomaticEnv()
if err != nil {
log.Debugf("No environment variables found: %s", err)
}
}
func setDefaults() {
defaultDir, err := os.Getwd()
if err != nil {
defaultDir = "."
}
Params.SetDefault("STATPING_DIR", defaultDir)
Directory = Params.GetString("STATPING_DIR")
Params.SetDefault("GO_ENV", "")
Params.SetDefault("DISABLE_LOGS", false)
Params.SetDefault("BASE_PATH", "")
Params.SetDefault("MAX_OPEN_CONN", 25)
Params.SetDefault("MAX_IDLE_CONN", 25)
Params.SetDefault("MAX_LIFE_CONN", 5*time.Minute)
Params.SetDefault("API_KEY", RandomString(32))
Params.SetDefault("API_SECRET", RandomString(32))
Params.SetDefault("SAMPLE_DATA", true)
Params.SetDefault("USE_CDN", false)
Params.SetDefault("ALLOW_REPORTS", false)
Params.SetDefault("AUTH_USERNAME", "")
Params.SetDefault("AUTH_PASSWORD", "")
Params.SetDefault("POSTGRES_SSLMODE", "disable")
dbConn := Params.GetString("DB_CONN")
dbInt := Params.GetInt("DB_PORT")
if dbInt == 0 && dbConn != "sqlite" && dbConn != "sqlite3" {
if dbConn == "postgres" {
Params.SetDefault("DB_PORT", 5432)
}
if dbConn == "mysql" {
Params.SetDefault("DB_PORT", 3306)
}
}
}

View File

@ -25,15 +25,9 @@ var (
// init will set the utils.Directory to the current running directory, or STATPING_DIR if it is set
func init() {
defaultDir, err := os.Getwd()
if err != nil {
defaultDir = "."
}
Directory = Getenv("STATPING_DIR", defaultDir).(string)
InitCLI()
// check if logs are disabled
disableLogs = Getenv("DISABLE_LOGS", false).(bool)
disableLogs = Params.GetBool("DISABLE_LOGS")
if disableLogs {
Log.Out = ioutil.Discard
}
@ -63,28 +57,20 @@ func (e *env) Duration() time.Duration {
}
func Getenv(key string, defaultValue interface{}) interface{} {
if val, ok := os.LookupEnv(key); ok {
if val != "" {
switch d := defaultValue.(type) {
case int, int64:
return int(ToInt(val))
case time.Duration:
dur, err := time.ParseDuration(val)
if err != nil {
return d
}
return dur
case bool:
ok, err := strconv.ParseBool(val)
if err != nil {
return d
}
return ok
default:
return val
}
if defaultValue != nil {
Params.SetDefault(key, defaultValue)
}
val := Params.Get(key)
if val != nil {
switch val.(type) {
case int, int64:
return Params.GetInt(key)
case time.Duration:
return Params.GetDuration(key)
case bool:
return Params.GetBool(key)
default:
return val
}
}
return defaultValue

View File

@ -1 +1 @@
0.90.27
0.90.28