mirror of https://github.com/statping/statping
vue
parent
7ac26c2841
commit
5e1b92a313
14
cmd/cli.go
14
cmd/cli.go
|
@ -21,7 +21,6 @@ import (
|
|||
"fmt"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/handlers"
|
||||
"github.com/hunterlong/statping/plugin"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
|
@ -72,13 +71,6 @@ func catchCLI(args []string) error {
|
|||
case "update":
|
||||
updateDisplay()
|
||||
return errors.New("end")
|
||||
case "test":
|
||||
cmd := args[1]
|
||||
switch cmd {
|
||||
case "plugins":
|
||||
plugin.LoadPlugins()
|
||||
}
|
||||
return errors.New("end")
|
||||
case "static":
|
||||
var err error
|
||||
if err = runLogs(); err != nil {
|
||||
|
@ -88,7 +80,7 @@ func catchCLI(args []string) error {
|
|||
return err
|
||||
}
|
||||
fmt.Printf("Statping v%v Exporting Static 'index.html' page...\n", VERSION)
|
||||
if core.CoreApp.Config, err = core.LoadConfigFile(dir); err != nil {
|
||||
if _, err = core.LoadConfigFile(dir); err != nil {
|
||||
log.Errorln("config.yml file not found")
|
||||
return err
|
||||
}
|
||||
|
@ -111,7 +103,7 @@ func catchCLI(args []string) error {
|
|||
if err = runAssets(); err != nil {
|
||||
return err
|
||||
}
|
||||
if core.CoreApp.Config, err = core.LoadConfigFile(dir); err != nil {
|
||||
if _, err = core.LoadConfigFile(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = core.CoreApp.Connect(false, dir); err != nil {
|
||||
|
@ -207,7 +199,7 @@ func updateDisplay() error {
|
|||
// runOnce will initialize the Statping application and check each service 1 time, will not run HTTP server
|
||||
func runOnce() {
|
||||
var err error
|
||||
core.CoreApp.Config, err = core.LoadConfigFile(utils.Directory)
|
||||
_, err = core.LoadConfigFile(utils.Directory)
|
||||
if err != nil {
|
||||
log.Errorln("config.yml file not found")
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ func TestExportCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUpdateCommand(t *testing.T) {
|
||||
t.SkipNow()
|
||||
cmd := helperCommand(nil, "version")
|
||||
var got = make(chan string)
|
||||
commandAndSleep(cmd, time.Duration(15*time.Second), got)
|
||||
|
@ -93,6 +94,7 @@ func TestUpdateCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAssetsCommand(t *testing.T) {
|
||||
t.SkipNow()
|
||||
c := testcli.Command("statping", "assets")
|
||||
c.Run()
|
||||
t.Log(c.Stdout())
|
||||
|
|
39
cmd/main.go
39
cmd/main.go
|
@ -22,7 +22,6 @@ import (
|
|||
"fmt"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/handlers"
|
||||
"github.com/hunterlong/statping/plugin"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/joho/godotenv"
|
||||
"os"
|
||||
|
@ -49,22 +48,17 @@ func init() {
|
|||
// 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() {
|
||||
flag.StringVar(&ipAddress, "ip", "0.0.0.0", "IP address to run the Statping HTTP server")
|
||||
flag.StringVar(&envFile, "env", "", "IP address to run the Statping HTTP server")
|
||||
flag.IntVar(&port, "port", 8080, "Port to run the HTTP server")
|
||||
flag.IntVar(&verboseMode, "verbose", 2, "Run in verbose mode to see detailed logs (1 - 4)")
|
||||
flag.Parse()
|
||||
envPort := utils.Getenv("PORT", 8080).(int)
|
||||
envIpAddress := utils.Getenv("IP", "0.0.0.0").(string)
|
||||
envVerbose := utils.Getenv("VERBOSE", 2).(int)
|
||||
|
||||
if os.Getenv("PORT") != "" {
|
||||
port = int(utils.ToInt(os.Getenv("PORT")))
|
||||
}
|
||||
if os.Getenv("IP") != "" {
|
||||
ipAddress = os.Getenv("IP")
|
||||
}
|
||||
if os.Getenv("VERBOSE") != "" {
|
||||
verboseMode = int(utils.ToInt(os.Getenv("VERBOSE")))
|
||||
}
|
||||
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(&verboseMode, "verbose", envVerbose, "Run in verbose mode to see detailed logs (1 - 4)")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
// main will run the Statping application
|
||||
|
@ -93,7 +87,7 @@ func main() {
|
|||
log.Info(fmt.Sprintf("Starting Statping v%v", VERSION))
|
||||
updateDisplay()
|
||||
|
||||
configs, err := core.LoadConfigFile(utils.Directory)
|
||||
_, err = core.LoadConfigFile(utils.Directory)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
core.CoreApp.Setup = false
|
||||
|
@ -108,16 +102,16 @@ func main() {
|
|||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
core.CoreApp.Config = configs
|
||||
if err := mainProcess(); err != nil {
|
||||
log.Fatalln(err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
// Close will gracefully stop the database connection, and log file
|
||||
func Close() {
|
||||
core.CloseDB()
|
||||
utils.CloseLogs()
|
||||
core.CloseDB()
|
||||
}
|
||||
|
||||
// sigterm will attempt to close the database connections gracefully
|
||||
|
@ -147,10 +141,13 @@ func mainProcess() error {
|
|||
log.Errorln(fmt.Sprintf("could not connect to database: %v", err))
|
||||
return err
|
||||
}
|
||||
core.CoreApp.MigrateDatabase()
|
||||
core.InitApp()
|
||||
if err := core.CoreApp.MigrateDatabase(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := core.InitApp(); err != nil {
|
||||
return err
|
||||
}
|
||||
if core.CoreApp.Setup {
|
||||
plugin.LoadPlugins()
|
||||
if err := handlers.RunHTTPServer(ipAddress, port); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
|
103
core/checker.go
103
core/checker.go
|
@ -34,16 +34,17 @@ import (
|
|||
// checkServices will start the checking go routine for each service
|
||||
func checkServices() {
|
||||
log.Infoln(fmt.Sprintf("Starting monitoring process for %v Services", len(CoreApp.services)))
|
||||
for _, ser := range CoreApp.services {
|
||||
for _, s := range CoreApp.services {
|
||||
//go CheckinRoutine()
|
||||
go ServiceCheckQueue(ser, true)
|
||||
time.Sleep(200 * time.Millisecond) // short delay so requests don't run all at the same time.
|
||||
go ServiceCheckQueue(s, true)
|
||||
}
|
||||
}
|
||||
|
||||
// Check will run checkHttp for HTTP services and checkTcp for TCP services
|
||||
// if record param is set to true, it will add a record into the database.
|
||||
func CheckService(srv database.Servicer, record bool) {
|
||||
switch srv.Model().Type {
|
||||
func CheckService(srv *Service, record bool) {
|
||||
switch srv.Type {
|
||||
case "http":
|
||||
CheckHttp(srv, record)
|
||||
case "tcp", "udp":
|
||||
|
@ -54,8 +55,7 @@ func CheckService(srv database.Servicer, record bool) {
|
|||
}
|
||||
|
||||
// CheckQueue is the main go routine for checking a service
|
||||
func ServiceCheckQueue(srv database.Servicer, record bool) {
|
||||
s := srv.Model()
|
||||
func ServiceCheckQueue(s *Service, record bool) {
|
||||
s.Checkpoint = time.Now()
|
||||
s.SleepDuration = (time.Duration(s.Id) * 100) * time.Millisecond
|
||||
CheckLoop:
|
||||
|
@ -65,11 +65,11 @@ CheckLoop:
|
|||
log.Infoln(fmt.Sprintf("Stopping service: %v", s.Name))
|
||||
break CheckLoop
|
||||
case <-time.After(s.SleepDuration):
|
||||
CheckService(srv, record)
|
||||
s.Checkpoint = s.Checkpoint.Add(srv.Interval())
|
||||
CheckService(s, record)
|
||||
s.Checkpoint = s.Checkpoint.Add(s.Duration())
|
||||
sleep := s.Checkpoint.Sub(time.Now())
|
||||
if !s.Online {
|
||||
s.SleepDuration = srv.Interval()
|
||||
s.SleepDuration = s.Duration()
|
||||
} else {
|
||||
s.SleepDuration = sleep
|
||||
}
|
||||
|
@ -78,19 +78,7 @@ CheckLoop:
|
|||
}
|
||||
}
|
||||
|
||||
// duration returns the amount of duration for a service to check its status
|
||||
func duration(s database.Servicer) time.Duration {
|
||||
var amount time.Duration
|
||||
if s.Interval() >= 10000 {
|
||||
amount = s.Interval() * time.Microsecond
|
||||
} else {
|
||||
amount = s.Interval() * time.Second
|
||||
}
|
||||
return amount
|
||||
}
|
||||
|
||||
func parseHost(srv database.Servicer) string {
|
||||
s := srv.Model()
|
||||
func parseHost(s *Service) string {
|
||||
if s.Type == "tcp" || s.Type == "udp" {
|
||||
return s.Domain
|
||||
} else {
|
||||
|
@ -103,11 +91,10 @@ func parseHost(srv database.Servicer) string {
|
|||
}
|
||||
|
||||
// dnsCheck will check the domain name and return a float64 for the amount of time the DNS check took
|
||||
func dnsCheck(srv database.Servicer) (float64, error) {
|
||||
s := srv.Model()
|
||||
func dnsCheck(s *Service) (float64, error) {
|
||||
var err error
|
||||
t1 := time.Now()
|
||||
host := parseHost(srv)
|
||||
host := parseHost(s)
|
||||
if s.Type == "tcp" {
|
||||
_, err = net.LookupHost(host)
|
||||
} else {
|
||||
|
@ -126,8 +113,7 @@ func isIPv6(address string) bool {
|
|||
}
|
||||
|
||||
// checkIcmp will send a ICMP ping packet to the service
|
||||
func CheckIcmp(srv database.Servicer, record bool) *types.Service {
|
||||
s := srv.Model()
|
||||
func CheckIcmp(s *Service, record bool) *types.Service {
|
||||
p := fastping.NewPinger()
|
||||
resolveIP := "ip4:icmp"
|
||||
if isIPv6(s.Domain) {
|
||||
|
@ -135,8 +121,8 @@ func CheckIcmp(srv database.Servicer, record bool) *types.Service {
|
|||
}
|
||||
ra, err := net.ResolveIPAddr(resolveIP, s.Domain)
|
||||
if err != nil {
|
||||
recordFailure(srv, fmt.Sprintf("Could not send ICMP to service %v, %v", s.Domain, err))
|
||||
return s
|
||||
recordFailure(s, fmt.Sprintf("Could not send ICMP to service %v, %v", s.Domain, err))
|
||||
return s.Service
|
||||
}
|
||||
p.AddIPAddr(ra)
|
||||
p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
|
||||
|
@ -145,22 +131,21 @@ func CheckIcmp(srv database.Servicer, record bool) *types.Service {
|
|||
}
|
||||
err = p.Run()
|
||||
if err != nil {
|
||||
recordFailure(srv, fmt.Sprintf("Issue running ICMP to service %v, %v", s.Domain, err))
|
||||
return s
|
||||
recordFailure(s, fmt.Sprintf("Issue running ICMP to service %v, %v", s.Domain, err))
|
||||
return s.Service
|
||||
}
|
||||
s.LastResponse = ""
|
||||
return s
|
||||
return s.Service
|
||||
}
|
||||
|
||||
// checkTcp will check a TCP service
|
||||
func CheckTcp(srv database.Servicer, record bool) *types.Service {
|
||||
s := srv.Model()
|
||||
dnsLookup, err := dnsCheck(srv)
|
||||
func CheckTcp(s *Service, record bool) *types.Service {
|
||||
dnsLookup, err := dnsCheck(s)
|
||||
if err != nil {
|
||||
if record {
|
||||
recordFailure(srv, fmt.Sprintf("Could not get IP address for TCP service %v, %v", s.Domain, err))
|
||||
recordFailure(s, fmt.Sprintf("Could not get IP address for TCP service %v, %v", s.Domain, err))
|
||||
}
|
||||
return s
|
||||
return s.Service
|
||||
}
|
||||
s.PingTime = dnsLookup
|
||||
t1 := time.Now()
|
||||
|
@ -174,15 +159,15 @@ func CheckTcp(srv database.Servicer, record bool) *types.Service {
|
|||
conn, err := net.DialTimeout(s.Type, domain, time.Duration(s.Timeout)*time.Second)
|
||||
if err != nil {
|
||||
if record {
|
||||
recordFailure(srv, fmt.Sprintf("Dial Error %v", err))
|
||||
recordFailure(s, fmt.Sprintf("Dial Error %v", err))
|
||||
}
|
||||
return s
|
||||
return s.Service
|
||||
}
|
||||
if err := conn.Close(); err != nil {
|
||||
if record {
|
||||
recordFailure(srv, fmt.Sprintf("%v Socket Close Error %v", strings.ToUpper(s.Type), err))
|
||||
recordFailure(s, fmt.Sprintf("%v Socket Close Error %v", strings.ToUpper(s.Type), err))
|
||||
}
|
||||
return s
|
||||
return s.Service
|
||||
}
|
||||
t2 := time.Now()
|
||||
s.Latency = t2.Sub(t1).Seconds()
|
||||
|
@ -190,18 +175,17 @@ func CheckTcp(srv database.Servicer, record bool) *types.Service {
|
|||
if record {
|
||||
recordSuccess(s)
|
||||
}
|
||||
return s
|
||||
return s.Service
|
||||
}
|
||||
|
||||
// checkHttp will check a HTTP service
|
||||
func CheckHttp(srv database.Servicer, record bool) *types.Service {
|
||||
s := srv.Model()
|
||||
dnsLookup, err := dnsCheck(srv)
|
||||
func CheckHttp(s *Service, record bool) *types.Service {
|
||||
dnsLookup, err := dnsCheck(s)
|
||||
if err != nil {
|
||||
if record {
|
||||
recordFailure(srv, fmt.Sprintf("Could not get IP address for domain %v, %v", s.Domain, err))
|
||||
recordFailure(s, fmt.Sprintf("Could not get IP address for domain %v, %v", s.Domain, err))
|
||||
}
|
||||
return s
|
||||
return s.Service
|
||||
}
|
||||
s.PingTime = dnsLookup
|
||||
t1 := time.Now()
|
||||
|
@ -224,9 +208,9 @@ func CheckHttp(srv database.Servicer, record bool) *types.Service {
|
|||
}
|
||||
if err != nil {
|
||||
if record {
|
||||
recordFailure(srv, fmt.Sprintf("HTTP Error %v", err))
|
||||
recordFailure(s, fmt.Sprintf("HTTP Error %v", err))
|
||||
}
|
||||
return s
|
||||
return s.Service
|
||||
}
|
||||
t2 := time.Now()
|
||||
s.Latency = t2.Sub(t1).Seconds()
|
||||
|
@ -240,25 +224,25 @@ func CheckHttp(srv database.Servicer, record bool) *types.Service {
|
|||
}
|
||||
if !match {
|
||||
if record {
|
||||
recordFailure(srv, fmt.Sprintf("HTTP Response Body did not match '%v'", s.Expected))
|
||||
recordFailure(s, fmt.Sprintf("HTTP Response Body did not match '%v'", s.Expected))
|
||||
}
|
||||
return s
|
||||
return s.Service
|
||||
}
|
||||
}
|
||||
if s.ExpectedStatus != res.StatusCode {
|
||||
if record {
|
||||
recordFailure(srv, fmt.Sprintf("HTTP Status Code %v did not match %v", res.StatusCode, s.ExpectedStatus))
|
||||
recordFailure(s, fmt.Sprintf("HTTP Status Code %v did not match %v", res.StatusCode, s.ExpectedStatus))
|
||||
}
|
||||
return s
|
||||
return s.Service
|
||||
}
|
||||
if record {
|
||||
recordSuccess(s)
|
||||
}
|
||||
return s
|
||||
return s.Service
|
||||
}
|
||||
|
||||
// recordSuccess will create a new 'hit' record in the database for a successful/online service
|
||||
func recordSuccess(s *types.Service) {
|
||||
func recordSuccess(s *Service) {
|
||||
s.LastOnline = time.Now().UTC()
|
||||
hit := &types.Hit{
|
||||
Service: s.Id,
|
||||
|
@ -268,14 +252,13 @@ func recordSuccess(s *types.Service) {
|
|||
}
|
||||
database.Create(hit)
|
||||
log.WithFields(utils.ToFields(hit, s)).Infoln(fmt.Sprintf("Service %v Successful Response: %0.2f ms | Lookup in: %0.2f ms", s.Name, hit.Latency*1000, hit.PingTime*1000))
|
||||
notifier.OnSuccess(s)
|
||||
notifier.OnSuccess(s.Service)
|
||||
s.Online = true
|
||||
s.SuccessNotified = true
|
||||
}
|
||||
|
||||
// recordFailure will create a new 'Failure' record in the database for a offline service
|
||||
func recordFailure(srv database.Servicer, issue string) {
|
||||
s := srv.Model()
|
||||
func recordFailure(s *Service, issue string) {
|
||||
fail := &types.Failure{
|
||||
Service: s.Id,
|
||||
Issue: issue,
|
||||
|
@ -288,6 +271,6 @@ func recordFailure(srv database.Servicer, issue string) {
|
|||
database.Create(fail)
|
||||
s.Online = false
|
||||
s.SuccessNotified = false
|
||||
s.DownText = srv.DowntimeText()
|
||||
notifier.OnFailure(s, fail)
|
||||
s.DownText = s.DowntimeText()
|
||||
notifier.OnFailure(s.Service, fail)
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ func AllCheckins() []*database.CheckinObj {
|
|||
// SelectCheckin will find a Checkin based on the API supplied
|
||||
func SelectCheckin(api string) *Checkin {
|
||||
for _, s := range Services() {
|
||||
for _, c := range s.AllCheckins() {
|
||||
for _, c := range s.Checkins() {
|
||||
if c.ApiKey == api {
|
||||
return &Checkin{c}
|
||||
}
|
||||
|
@ -140,19 +140,17 @@ func (c *Checkin) GetFailures(count int) []*types.Failure {
|
|||
}
|
||||
|
||||
// Create will create a new Checkin
|
||||
func (c *Checkin) Delete() error {
|
||||
func (c *Checkin) Delete() {
|
||||
c.Close()
|
||||
i := c.index()
|
||||
service := c.Service()
|
||||
slice := service.Checkins
|
||||
service.Checkins = append(slice[:i], slice[i+1:]...)
|
||||
row := Database(c).Delete(&c)
|
||||
return row.Error()
|
||||
srv := c.Service()
|
||||
slice := srv.Service.Checkins
|
||||
srv.Service.Checkins = append(slice[:i], slice[i+1:]...)
|
||||
}
|
||||
|
||||
// index returns a checkin index int for updating the *checkin.Service slice
|
||||
func (c *Checkin) index() int {
|
||||
for k, checkin := range c.Service().Checkins {
|
||||
for k, checkin := range c.Service().Checkins() {
|
||||
if c.Id == checkin.Model().Id {
|
||||
return k
|
||||
}
|
||||
|
|
132
core/configs.go
132
core/configs.go
|
@ -23,7 +23,6 @@ import (
|
|||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ErrorResponse is used for HTTP errors to show to User
|
||||
|
@ -32,13 +31,16 @@ type ErrorResponse struct {
|
|||
}
|
||||
|
||||
// LoadConfigFile will attempt to load the 'config.yml' file in a specific directory
|
||||
func LoadConfigFile(directory string) (*types.DbConfig, error) {
|
||||
var configs *types.DbConfig
|
||||
if os.Getenv("DB_CONN") != "" {
|
||||
log.Warnln("DB_CONN environment variable was found, waiting for database...")
|
||||
func LoadConfigFile(directory string) (*DbConfig, error) {
|
||||
var configs *DbConfig
|
||||
|
||||
dbConn := utils.Getenv("DB_CONN", "")
|
||||
|
||||
if dbConn != "" {
|
||||
log.Infof("DB_CONN=%s environment variable was found, waiting for database...", dbConn)
|
||||
return LoadUsingEnv()
|
||||
}
|
||||
log.Debugln("attempting to read config file at: " + directory + "/config.yml")
|
||||
log.Debugln("Attempting to read config file at: " + directory + "/config.yml")
|
||||
file, err := ioutil.ReadFile(directory + "/config.yml")
|
||||
if err != nil {
|
||||
CoreApp.Setup = false
|
||||
|
@ -49,54 +51,49 @@ func LoadConfigFile(directory string) (*types.DbConfig, error) {
|
|||
return nil, err
|
||||
}
|
||||
log.WithFields(utils.ToFields(configs)).Debugln("read config file: " + directory + "/config.yml")
|
||||
CoreApp.Config = configs
|
||||
CoreApp.Config = configs.DbConfig
|
||||
return configs, err
|
||||
}
|
||||
|
||||
// LoadUsingEnv will attempt to load database configs based on environment variables. If DB_CONN is set if will force this function.
|
||||
func LoadUsingEnv() (*types.DbConfig, error) {
|
||||
func LoadUsingEnv() (*DbConfig, error) {
|
||||
Configs, err := EnvToConfig()
|
||||
if err != nil {
|
||||
return Configs, err
|
||||
}
|
||||
CoreApp.Name = os.Getenv("NAME")
|
||||
if Configs.Domain == "" {
|
||||
CoreApp.Domain = Configs.LocalIP
|
||||
} else {
|
||||
CoreApp.Domain = os.Getenv("DOMAIN")
|
||||
}
|
||||
CoreApp.UseCdn = types.NewNullBool(os.Getenv("USE_CDN") == "true")
|
||||
|
||||
CoreApp.Name = utils.Getenv("NAME", "").(string)
|
||||
CoreApp.Domain = utils.Getenv("DOMAIN", Configs.LocalIP).(string)
|
||||
CoreApp.UseCdn = types.NewNullBool(utils.Getenv("USE_CDN", false).(bool))
|
||||
|
||||
err = CoreApp.Connect(true, utils.Directory)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
if _, err := CoreApp.SaveConfig(Configs); err != nil {
|
||||
if err := Configs.Save(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exists := DbSession.HasTable("core")
|
||||
if !exists {
|
||||
log.Infoln(fmt.Sprintf("Core database does not exist, creating now!"))
|
||||
CoreApp.DropDatabase()
|
||||
CoreApp.CreateDatabase()
|
||||
CoreApp, err = CoreApp.InsertCore(Configs)
|
||||
if err := CoreApp.DropDatabase(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := CoreApp.CreateDatabase(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
CoreApp, err = Configs.InsertCore()
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
|
||||
username := os.Getenv("ADMIN_USER")
|
||||
if username == "" {
|
||||
username = "admin"
|
||||
}
|
||||
password := os.Getenv("ADMIN_PASSWORD")
|
||||
if password == "" {
|
||||
password = "admin"
|
||||
}
|
||||
username := utils.Getenv("ADMIN_USER", "admin").(string)
|
||||
password := utils.Getenv("ADMIN_PASSWORD", "admin").(string)
|
||||
|
||||
admin := &types.User{
|
||||
Username: username,
|
||||
Password: password,
|
||||
Password: utils.HashPassword(password),
|
||||
Email: "info@admin.com",
|
||||
Admin: types.NewNullBool(true),
|
||||
}
|
||||
|
@ -128,67 +125,56 @@ func defaultPort(db string) int64 {
|
|||
}
|
||||
|
||||
// EnvToConfig converts environment variables to a DbConfig variable
|
||||
func EnvToConfig() (*types.DbConfig, error) {
|
||||
func EnvToConfig() (*DbConfig, error) {
|
||||
var err error
|
||||
if os.Getenv("DB_CONN") == "" {
|
||||
return nil, errors.New("Missing DB_CONN environment variable")
|
||||
}
|
||||
if os.Getenv("DB_CONN") != "sqlite" {
|
||||
if os.Getenv("DB_HOST") == "" {
|
||||
|
||||
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)).(int64)
|
||||
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 != "sqlite" {
|
||||
if dbHost == "" {
|
||||
return nil, errors.New("Missing DB_HOST environment variable")
|
||||
}
|
||||
if os.Getenv("DB_USER") == "" {
|
||||
if dbUser == "" {
|
||||
return nil, errors.New("Missing DB_USER environment variable")
|
||||
}
|
||||
if os.Getenv("DB_PASS") == "" {
|
||||
if dbPass == "" {
|
||||
return nil, errors.New("Missing DB_PASS environment variable")
|
||||
}
|
||||
if os.Getenv("DB_DATABASE") == "" {
|
||||
if dbData == "" {
|
||||
return nil, errors.New("Missing DB_DATABASE environment variable")
|
||||
}
|
||||
}
|
||||
port := utils.ToInt(os.Getenv("DB_PORT"))
|
||||
if port == 0 {
|
||||
port = defaultPort(os.Getenv("DB_PORT"))
|
||||
}
|
||||
name := os.Getenv("NAME")
|
||||
if name == "" {
|
||||
name = "Statping"
|
||||
}
|
||||
description := os.Getenv("DESCRIPTION")
|
||||
if description == "" {
|
||||
description = "Statping Monitoring Sample Data"
|
||||
}
|
||||
|
||||
adminUser := os.Getenv("ADMIN_USER")
|
||||
if adminUser == "" {
|
||||
adminUser = "admin"
|
||||
}
|
||||
|
||||
adminPass := os.Getenv("ADMIN_PASS")
|
||||
if adminPass == "" {
|
||||
adminPass = "admin"
|
||||
}
|
||||
|
||||
configs := &types.DbConfig{
|
||||
DbConn: os.Getenv("DB_CONN"),
|
||||
DbHost: os.Getenv("DB_HOST"),
|
||||
DbUser: os.Getenv("DB_USER"),
|
||||
DbPass: os.Getenv("DB_PASS"),
|
||||
DbData: os.Getenv("DB_DATABASE"),
|
||||
DbPort: port,
|
||||
CoreApp.Config = &types.DbConfig{
|
||||
DbConn: dbConn,
|
||||
DbHost: dbHost,
|
||||
DbUser: dbUser,
|
||||
DbPass: dbPass,
|
||||
DbData: dbData,
|
||||
DbPort: dbPort,
|
||||
Project: name,
|
||||
Description: description,
|
||||
Domain: os.Getenv("DOMAIN"),
|
||||
Description: desc,
|
||||
Domain: domain,
|
||||
Email: "",
|
||||
Username: adminUser,
|
||||
Password: adminPass,
|
||||
Username: user,
|
||||
Password: password,
|
||||
Error: nil,
|
||||
Location: utils.Directory,
|
||||
SqlFile: os.Getenv("SQL_FILE"),
|
||||
SqlFile: sqlFile,
|
||||
}
|
||||
CoreApp.Config = configs
|
||||
return configs, err
|
||||
|
||||
return &DbConfig{CoreApp.Config}, err
|
||||
}
|
||||
|
||||
// SampleData runs all the sample data for a new Statping installation
|
||||
|
|
35
core/core.go
35
core/core.go
|
@ -35,7 +35,7 @@ type PluginRepos types.PluginRepos
|
|||
|
||||
type Core struct {
|
||||
*types.Core
|
||||
services []database.Servicer
|
||||
services []*Service
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -63,18 +63,31 @@ func (c *Core) ToCore() *types.Core {
|
|||
}
|
||||
|
||||
// InitApp will initialize Statping
|
||||
func InitApp() {
|
||||
SelectCore()
|
||||
InsertNotifierDB()
|
||||
InsertIntegratorDB()
|
||||
SelectAllServices(true)
|
||||
func InitApp() error {
|
||||
if _, err := SelectCore(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := InsertNotifierDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := InsertIntegratorDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := SelectAllServices(true); err != nil {
|
||||
return err
|
||||
}
|
||||
checkServices()
|
||||
AttachNotifiers()
|
||||
AddIntegrations()
|
||||
if err := AttachNotifiers(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := AddIntegrations(); err != nil {
|
||||
return err
|
||||
}
|
||||
CoreApp.Notifications = notifier.AllCommunications
|
||||
CoreApp.Integrations = integrations.Integrations
|
||||
go DatabaseMaintence()
|
||||
database.StartMaintenceRoutine()
|
||||
CoreApp.Setup = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertNotifierDB inject the Statping database instance to the Notifier package
|
||||
|
@ -214,9 +227,9 @@ func AddIntegrations() error {
|
|||
}
|
||||
|
||||
// ServiceOrder will reorder the services based on 'order_id' (Order)
|
||||
type ServiceOrder []database.Servicer
|
||||
type ServiceOrder []*Service
|
||||
|
||||
// Sort interface for resroting the Services in order
|
||||
func (c ServiceOrder) Len() int { return len(c) }
|
||||
func (c ServiceOrder) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
func (c ServiceOrder) Less(i, j int) bool { return c[i].Model().Order < c[j].Model().Order }
|
||||
func (c ServiceOrder) Less(i, j int) bool { return c[i].Order < c[j].Order }
|
||||
|
|
|
@ -17,6 +17,7 @@ package core
|
|||
|
||||
import (
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -39,26 +40,27 @@ func init() {
|
|||
|
||||
func TestNewCore(t *testing.T) {
|
||||
err := TmpRecords("core.db")
|
||||
t.Log(err)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, CoreApp)
|
||||
}
|
||||
|
||||
func TestDbConfig_Save(t *testing.T) {
|
||||
if skipNewDb {
|
||||
t.SkipNow()
|
||||
//if skipNewDb {
|
||||
// t.SkipNow()
|
||||
//}
|
||||
//var err error
|
||||
//Configs = &DbConfig{
|
||||
// DbConn: "sqlite",
|
||||
// Project: "Tester",
|
||||
// Location: dir,
|
||||
//}
|
||||
//Configs, err = Configs.Save()
|
||||
//assert.Nil(t, err)
|
||||
//assert.Equal(t, "sqlite", Configs.DbConn)
|
||||
//assert.NotEmpty(t, Configs.ApiKey)
|
||||
//assert.NotEmpty(t, Configs.ApiSecret)
|
||||
}
|
||||
|
||||
config := &DbConfig{&types.DbConfig{
|
||||
DbConn: "sqlite",
|
||||
Project: "Tester",
|
||||
Location: dir,
|
||||
}}
|
||||
|
||||
err := config.Save()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "sqlite", CoreApp.Config.DbConn)
|
||||
assert.NotEmpty(t, CoreApp.Config.ApiKey)
|
||||
assert.NotEmpty(t, CoreApp.Config.ApiSecret)
|
||||
}
|
||||
|
||||
func TestLoadDbConfig(t *testing.T) {
|
||||
|
@ -73,7 +75,6 @@ func TestDbConnection(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDropDatabase(t *testing.T) {
|
||||
t.SkipNow()
|
||||
if skipNewDb {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
@ -82,7 +83,6 @@ func TestDropDatabase(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSeedSchemaDatabase(t *testing.T) {
|
||||
t.SkipNow()
|
||||
if skipNewDb {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
@ -97,7 +97,6 @@ func TestMigrateDatabase(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSeedDatabase(t *testing.T) {
|
||||
t.SkipNow()
|
||||
err := InsertLargeSampleData()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
@ -115,7 +114,6 @@ func TestSelectCore(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInsertNotifierDB(t *testing.T) {
|
||||
t.SkipNow()
|
||||
if skipNewDb {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
|
|
@ -39,14 +39,12 @@ var (
|
|||
|
||||
func init() {
|
||||
DbModels = []interface{}{&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Message{}, &types.Group{}, &types.Checkin{}, &types.CheckinHit{}, ¬ifier.Notification{}, &types.Incident{}, &types.IncidentUpdate{}, &types.Integration{}}
|
||||
|
||||
gorm.NowFunc = func() time.Time {
|
||||
return time.Now().UTC()
|
||||
}
|
||||
}
|
||||
|
||||
// DbConfig stores the config.yml file for the statup configuration
|
||||
type DbConfig types.DbConfig
|
||||
type DbConfig struct {
|
||||
*types.DbConfig
|
||||
}
|
||||
|
||||
func Database(obj interface{}) database.Database {
|
||||
switch obj.(type) {
|
||||
|
@ -140,18 +138,18 @@ func CloseDB() {
|
|||
//}
|
||||
|
||||
// InsertCore create the single row for the Core settings in Statping
|
||||
func (c *Core) InsertCore(db *types.DbConfig) (*Core, error) {
|
||||
func (d *DbConfig) InsertCore() (*Core, error) {
|
||||
CoreApp = &Core{Core: &types.Core{
|
||||
Name: db.Project,
|
||||
Description: db.Description,
|
||||
Name: d.Project,
|
||||
Description: d.Description,
|
||||
ConfigFile: "config.yml",
|
||||
ApiKey: utils.NewSHA1Hash(9),
|
||||
ApiSecret: utils.NewSHA1Hash(16),
|
||||
Domain: db.Domain,
|
||||
Domain: d.Domain,
|
||||
MigrationId: time.Now().Unix(),
|
||||
Config: db,
|
||||
Config: d.DbConfig,
|
||||
}}
|
||||
query := Database(CoreApp).Create(&CoreApp)
|
||||
query := DbSession.Create(CoreApp.Core)
|
||||
return CoreApp, query.Error()
|
||||
}
|
||||
|
||||
|
@ -219,9 +217,13 @@ func (c *Core) Connect(retry bool, location string) error {
|
|||
}
|
||||
log.WithFields(utils.ToFields(dbSession)).Debugln("connected to database")
|
||||
|
||||
dbSession.DB().SetMaxOpenConns(5)
|
||||
dbSession.DB().SetMaxIdleConns(5)
|
||||
dbSession.DB().SetConnMaxLifetime(1 * time.Minute)
|
||||
maxOpenConn := utils.Getenv("MAX_OPEN_CONN", 5)
|
||||
maxIdleConn := utils.Getenv("MAX_IDLE_CONN", 5)
|
||||
maxLifeConn := utils.Getenv("MAX_LIFE_CONN", 2*time.Minute)
|
||||
|
||||
dbSession.DB().SetMaxOpenConns(maxOpenConn.(int))
|
||||
dbSession.DB().SetMaxIdleConns(maxIdleConn.(int))
|
||||
dbSession.DB().SetConnMaxLifetime(maxLifeConn.(time.Duration))
|
||||
|
||||
if dbSession.DB().Ping() == nil {
|
||||
DbSession = dbSession
|
||||
|
@ -239,26 +241,6 @@ func (c *Core) waitForDb() error {
|
|||
return c.Connect(true, utils.Directory)
|
||||
}
|
||||
|
||||
// DatabaseMaintence will automatically delete old records from 'failures' and 'hits'
|
||||
// this function is currently set to delete records 7+ days old every 60 minutes
|
||||
func DatabaseMaintence() {
|
||||
for range time.Tick(60 * time.Minute) {
|
||||
log.Infoln("Checking for database records older than 3 months...")
|
||||
since := time.Now().AddDate(0, -3, 0).UTC()
|
||||
DeleteAllSince("failures", since)
|
||||
DeleteAllSince("hits", since)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAllSince will delete a specific table's records based on a time.
|
||||
func DeleteAllSince(table string, date time.Time) {
|
||||
sql := fmt.Sprintf("DELETE FROM %v WHERE created_at < '%v';", table, date.Format("2006-01-02"))
|
||||
db := DbSession.Exec(sql)
|
||||
if db.Error() != nil {
|
||||
log.Warnln(db.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Update will save the config.yml file
|
||||
func (c *Core) UpdateConfig() error {
|
||||
var err error
|
||||
|
@ -278,25 +260,25 @@ func (c *Core) UpdateConfig() error {
|
|||
}
|
||||
|
||||
// Save will initially create the config.yml file
|
||||
func (c *Core) SaveConfig(configs *types.DbConfig) (*types.DbConfig, error) {
|
||||
func (d *DbConfig) Save() error {
|
||||
config, err := os.Create(utils.Directory + "/config.yml")
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer config.Close()
|
||||
log.WithFields(utils.ToFields(configs)).Debugln("saving config file at: " + utils.Directory + "/config.yml")
|
||||
c.Config = configs
|
||||
c.Config.ApiKey = utils.NewSHA1Hash(16)
|
||||
c.Config.ApiSecret = utils.NewSHA1Hash(16)
|
||||
data, err := yaml.Marshal(configs)
|
||||
log.WithFields(utils.ToFields(d)).Debugln("saving config file at: " + utils.Directory + "/config.yml")
|
||||
CoreApp.Config = d.DbConfig
|
||||
CoreApp.Config.ApiKey = utils.NewSHA1Hash(16)
|
||||
CoreApp.Config.ApiSecret = utils.NewSHA1Hash(16)
|
||||
data, err := yaml.Marshal(d)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
config.WriteString(string(data))
|
||||
log.WithFields(utils.ToFields(configs)).Infoln("saved config file at: " + utils.Directory + "/config.yml")
|
||||
return c.Config, err
|
||||
log.WithFields(utils.ToFields(d)).Infoln("saved config file at: " + utils.Directory + "/config.yml")
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateCore will initialize the global variable 'CoreApp". This global variable contains most of Statping app.
|
||||
|
@ -324,18 +306,13 @@ func (c *Core) CreateCore() *Core {
|
|||
// DropDatabase will DROP each table Statping created
|
||||
func (c *Core) DropDatabase() error {
|
||||
log.Infoln("Dropping Database Tables...")
|
||||
err := DbSession.DropTableIfExists("checkins")
|
||||
err = DbSession.DropTableIfExists("checkin_hits")
|
||||
err = DbSession.DropTableIfExists("notifications")
|
||||
err = DbSession.DropTableIfExists("core")
|
||||
err = DbSession.DropTableIfExists("failures")
|
||||
err = DbSession.DropTableIfExists("hits")
|
||||
err = DbSession.DropTableIfExists("services")
|
||||
err = DbSession.DropTableIfExists("users")
|
||||
err = DbSession.DropTableIfExists("messages")
|
||||
err = DbSession.DropTableIfExists("incidents")
|
||||
err = DbSession.DropTableIfExists("incident_updates")
|
||||
tables := []string{"checkins", "checkin_hits", "notifications", "core", "failures", "hits", "services", "users", "messages", "incidents", "incident_updates"}
|
||||
for _, t := range tables {
|
||||
if err := DbSession.DropTableIfExists(t); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateDatabase will CREATE TABLES for each of the Statping elements
|
||||
|
|
|
@ -19,7 +19,9 @@ import (
|
|||
"github.com/hunterlong/statping/types"
|
||||
)
|
||||
|
||||
type Failure struct{}
|
||||
type Failure struct {
|
||||
*types.Failure
|
||||
}
|
||||
|
||||
const (
|
||||
limitedFailures = 32
|
||||
|
|
|
@ -7,46 +7,45 @@ import (
|
|||
)
|
||||
|
||||
type Group struct {
|
||||
database.Grouper
|
||||
*types.Group
|
||||
}
|
||||
|
||||
// SelectGroups returns all groups
|
||||
func SelectGroups(includeAll bool, auth bool) []database.Grouper {
|
||||
var validGroups []database.Grouper
|
||||
func SelectGroups(includeAll bool, auth bool) []*Group {
|
||||
var validGroups []*Group
|
||||
|
||||
groups := database.AllGroups()
|
||||
|
||||
for _, g := range groups {
|
||||
if !g.Model().Public.Bool {
|
||||
if !g.Public.Bool {
|
||||
if auth {
|
||||
validGroups = append(validGroups, g)
|
||||
validGroups = append(validGroups, &Group{g.Group})
|
||||
}
|
||||
} else {
|
||||
validGroups = append(validGroups, g)
|
||||
validGroups = append(validGroups, &Group{g.Group})
|
||||
}
|
||||
}
|
||||
sort.Sort(GroupOrder(validGroups))
|
||||
if includeAll {
|
||||
emptyGroup := &Group{}
|
||||
validGroups = append(validGroups, emptyGroup)
|
||||
validGroups = append(validGroups, &Group{})
|
||||
}
|
||||
return validGroups
|
||||
}
|
||||
|
||||
// SelectGroup returns a *core.Group
|
||||
func SelectGroup(id int64) *types.Group {
|
||||
func SelectGroup(id int64) *Group {
|
||||
for _, g := range SelectGroups(true, true) {
|
||||
if g.Model().Id == id {
|
||||
return g.Model()
|
||||
if g.Id == id {
|
||||
return g
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GroupOrder will reorder the groups based on 'order_id' (Order)
|
||||
type GroupOrder []database.Grouper
|
||||
type GroupOrder []*Group
|
||||
|
||||
// Sort interface for resorting the Groups in order
|
||||
func (c GroupOrder) Len() int { return len(c) }
|
||||
func (c GroupOrder) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
func (c GroupOrder) Less(i, j int) bool { return c[i].Model().Order < c[j].Model().Order }
|
||||
func (c GroupOrder) Less(i, j int) bool { return c[i].Order < c[j].Order }
|
||||
|
|
|
@ -17,6 +17,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
|
@ -57,7 +58,7 @@ var core = &types.Core{
|
|||
func injectDatabase() {
|
||||
sqlPath := dir + "/notifier.db"
|
||||
utils.DeleteFile(sqlPath)
|
||||
db, _ = types.Openw("sqlite3", sqlPath)
|
||||
db, _ = database.Openw("sqlite3", sqlPath)
|
||||
db.CreateTable(&Notification{})
|
||||
}
|
||||
|
||||
|
|
152
core/sample.go
152
core/sample.go
|
@ -34,7 +34,10 @@ var (
|
|||
func InsertSampleData() error {
|
||||
log.Infoln("Inserting Sample Data...")
|
||||
|
||||
insertSampleGroups()
|
||||
if err := insertSampleGroups(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
createdOn := time.Now().Add(((-24 * 30) * 3) * time.Hour).UTC()
|
||||
s1 := &types.Service{
|
||||
Name: "Google",
|
||||
|
@ -106,15 +109,33 @@ func InsertSampleData() error {
|
|||
CreatedAt: createdOn,
|
||||
}
|
||||
|
||||
database.Create(s1)
|
||||
database.Create(s2)
|
||||
database.Create(s3)
|
||||
database.Create(s4)
|
||||
database.Create(s5)
|
||||
if _, err := database.Create(s1); err != nil {
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
if _, err := database.Create(s2); err != nil {
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
if _, err := database.Create(s3); err != nil {
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
if _, err := database.Create(s4); err != nil {
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
if _, err := database.Create(s5); err != nil {
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
insertMessages()
|
||||
if _, err := SelectAllServices(false); err != nil {
|
||||
return types.ErrWrap(err, types.ErrorServiceSelection)
|
||||
}
|
||||
|
||||
insertSampleIncidents()
|
||||
if err := insertMessages(); err != nil {
|
||||
return types.ErrWrap(err, types.ErrorCreateMessage)
|
||||
}
|
||||
|
||||
if err := insertSampleIncidents(); err != nil {
|
||||
return types.ErrWrap(err, types.ErrorCreateIncident)
|
||||
}
|
||||
|
||||
log.Infoln("Sample data has finished importing")
|
||||
|
||||
|
@ -128,7 +149,7 @@ func insertSampleIncidents() error {
|
|||
ServiceId: 2,
|
||||
}
|
||||
if _, err := database.Create(incident1); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateIncidentUp)
|
||||
}
|
||||
|
||||
incidentUpdate1 := &types.IncidentUpdate{
|
||||
|
@ -137,7 +158,7 @@ func insertSampleIncidents() error {
|
|||
Type: "Investigating",
|
||||
}
|
||||
if _, err := database.Create(incidentUpdate1); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateIncidentUp)
|
||||
}
|
||||
|
||||
incidentUpdate2 := &types.IncidentUpdate{
|
||||
|
@ -146,7 +167,7 @@ func insertSampleIncidents() error {
|
|||
Type: "Update",
|
||||
}
|
||||
if _, err := database.Create(incidentUpdate2); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateIncidentUp)
|
||||
}
|
||||
|
||||
incidentUpdate3 := &types.IncidentUpdate{
|
||||
|
@ -155,7 +176,7 @@ func insertSampleIncidents() error {
|
|||
Type: "Resolved",
|
||||
}
|
||||
if _, err := database.Create(incidentUpdate3); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateIncidentUp)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -168,7 +189,7 @@ func insertSampleGroups() error {
|
|||
Order: 2,
|
||||
}
|
||||
if _, err := database.Create(group1); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateGroup)
|
||||
}
|
||||
|
||||
group2 := &types.Group{
|
||||
|
@ -177,7 +198,7 @@ func insertSampleGroups() error {
|
|||
Order: 1,
|
||||
}
|
||||
if _, err := database.Create(group2); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateGroup)
|
||||
}
|
||||
|
||||
group3 := &types.Group{
|
||||
|
@ -186,7 +207,7 @@ func insertSampleGroups() error {
|
|||
Order: 3,
|
||||
}
|
||||
if _, err := database.Create(group3); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateGroup)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -195,24 +216,24 @@ func insertSampleGroups() error {
|
|||
func insertSampleCheckins() error {
|
||||
s1 := SelectService(1)
|
||||
checkin1 := &types.Checkin{
|
||||
ServiceId: s1.Model().Id,
|
||||
ServiceId: s1.Id,
|
||||
Interval: 300,
|
||||
GracePeriod: 300,
|
||||
}
|
||||
|
||||
if _, err := database.Create(checkin1); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
s2 := SelectService(1)
|
||||
checkin2 := &types.Checkin{
|
||||
ServiceId: s2.Model().Id,
|
||||
ServiceId: s2.Id,
|
||||
Interval: 900,
|
||||
GracePeriod: 300,
|
||||
}
|
||||
|
||||
if _, err := database.Create(checkin2); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateCheckinHit)
|
||||
}
|
||||
|
||||
checkTime := time.Now().UTC().Add(-24 * time.Hour)
|
||||
|
@ -224,7 +245,7 @@ func insertSampleCheckins() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(checkHit); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateCheckinHit)
|
||||
}
|
||||
|
||||
checkTime = checkTime.Add(10 * time.Minute)
|
||||
|
@ -240,7 +261,7 @@ func InsertSampleHits() error {
|
|||
sg.Add(1)
|
||||
service := SelectService(i)
|
||||
seed := time.Now().UnixNano()
|
||||
log.Infoln(fmt.Sprintf("Adding %v sample hit records to service %v", SampleHits, service.Model().Name))
|
||||
log.Infoln(fmt.Sprintf("Adding %v sample hit records to service %v", SampleHits, service.Name))
|
||||
createdAt := sampleStart
|
||||
p := utils.NewPerlin(2., 2., 10, seed)
|
||||
go func() {
|
||||
|
@ -249,7 +270,7 @@ func InsertSampleHits() error {
|
|||
latency := p.Noise1D(hi / 500)
|
||||
createdAt = createdAt.Add(60 * time.Second)
|
||||
hit := &types.Hit{
|
||||
Service: service.Model().Id,
|
||||
Service: service.Id,
|
||||
CreatedAt: createdAt,
|
||||
Latency: latency,
|
||||
}
|
||||
|
@ -258,11 +279,11 @@ func InsertSampleHits() error {
|
|||
}()
|
||||
}
|
||||
sg.Wait()
|
||||
err := tx.Commit().Error()
|
||||
if err != nil {
|
||||
if err := tx.Commit().Error(); err != nil {
|
||||
log.Errorln(err)
|
||||
return types.ErrWrap(err, types.ErrorCreateSampleHits)
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// insertSampleCore will create a new Core for the seed
|
||||
|
@ -276,11 +297,14 @@ func insertSampleCore() error {
|
|||
Version: "test",
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UseCdn: types.NewNullBool(false),
|
||||
Footer: types.NewNullString(""),
|
||||
}
|
||||
|
||||
_, err := database.Create(core)
|
||||
if _, err := database.Create(core); err != nil {
|
||||
return types.ErrWrap(err, types.ErrorCreateCore)
|
||||
}
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// insertSampleUsers will create 2 admin users for a seed database
|
||||
|
@ -293,7 +317,7 @@ func insertSampleUsers() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(u2); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateUser)
|
||||
}
|
||||
|
||||
u3 := &types.User{
|
||||
|
@ -304,7 +328,7 @@ func insertSampleUsers() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(u3); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateUser)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -320,7 +344,7 @@ func insertMessages() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(m1); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateMessage)
|
||||
}
|
||||
|
||||
m2 := &types.Message{
|
||||
|
@ -332,7 +356,7 @@ func insertMessages() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(m2); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -368,7 +392,7 @@ func InsertLargeSampleData() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(s6); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
s7 := &types.Service{
|
||||
|
@ -384,7 +408,7 @@ func InsertLargeSampleData() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(s7); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
s8 := &types.Service{
|
||||
|
@ -399,7 +423,7 @@ func InsertLargeSampleData() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(s8); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
s9 := &types.Service{
|
||||
|
@ -415,7 +439,7 @@ func InsertLargeSampleData() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(s9); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
s10 := &types.Service{
|
||||
|
@ -431,7 +455,7 @@ func InsertLargeSampleData() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(s10); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
s11 := &types.Service{
|
||||
|
@ -447,7 +471,7 @@ func InsertLargeSampleData() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(s11); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
s12 := &types.Service{
|
||||
|
@ -463,7 +487,7 @@ func InsertLargeSampleData() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(s12); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
s13 := &types.Service{
|
||||
|
@ -479,7 +503,7 @@ func InsertLargeSampleData() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(s13); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
s14 := &types.Service{
|
||||
|
@ -495,7 +519,7 @@ func InsertLargeSampleData() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(s14); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
s15 := &types.Service{
|
||||
|
@ -511,7 +535,7 @@ func InsertLargeSampleData() error {
|
|||
}
|
||||
|
||||
if _, err := database.Create(s15); err != nil {
|
||||
return err
|
||||
return types.ErrWrap(err, types.ErrorCreateService)
|
||||
}
|
||||
|
||||
var dayAgo = time.Now().UTC().Add((-24 * 90) * time.Hour)
|
||||
|
@ -527,14 +551,14 @@ func InsertLargeSampleData() error {
|
|||
func insertFailureRecords(since time.Time, amount int) {
|
||||
for i := int64(14); i <= 15; i++ {
|
||||
service := SelectService(i)
|
||||
log.Infoln(fmt.Sprintf("Adding %v Failure records to service %v", amount, service.Model().Name))
|
||||
log.Infoln(fmt.Sprintf("Adding %v Failure records to service %v", amount, service.Name))
|
||||
createdAt := since
|
||||
|
||||
for fi := 1; fi <= amount; fi++ {
|
||||
createdAt = createdAt.Add(2 * time.Minute)
|
||||
|
||||
failure := &types.Failure{
|
||||
Service: service.Model().Id,
|
||||
Service: service.Id,
|
||||
Issue: "testing right here",
|
||||
CreatedAt: createdAt,
|
||||
}
|
||||
|
@ -545,32 +569,36 @@ func insertFailureRecords(since time.Time, amount int) {
|
|||
}
|
||||
|
||||
// insertHitRecords will create successful Hit records for 15 services
|
||||
func insertHitRecords(since time.Time, amount int) {
|
||||
func insertHitRecords(since time.Time, amount int) error {
|
||||
for i := int64(1); i <= 15; i++ {
|
||||
service := SelectService(i)
|
||||
log.Infoln(fmt.Sprintf("Adding %v hit records to service %v", amount, service.Model().Name))
|
||||
log.Infoln(fmt.Sprintf("Adding %v hit records to service %v", amount, service.Name))
|
||||
createdAt := since
|
||||
p := utils.NewPerlin(2, 2, 5, time.Now().UnixNano())
|
||||
for hi := 1; hi <= amount; hi++ {
|
||||
latency := p.Noise1D(float64(hi / 10))
|
||||
createdAt = createdAt.Add(1 * time.Minute)
|
||||
hit := &types.Hit{
|
||||
Service: service.Model().Id,
|
||||
Service: service.Id,
|
||||
CreatedAt: createdAt.UTC(),
|
||||
Latency: latency,
|
||||
}
|
||||
database.Create(hit)
|
||||
if _, err := database.Create(hit); err != nil {
|
||||
return types.ErrWrap(err, types.ErrorCreateHit, service.Id)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TmpRecords is used for testing Statping. It will create a SQLite database file
|
||||
// with sample data and store it in the /tmp folder to be used by the tests.
|
||||
func TmpRecords(dbFile string) error {
|
||||
var sqlFile = utils.Directory + "/" + dbFile
|
||||
utils.CreateDirectory(utils.Directory + "/tmp")
|
||||
if err := utils.CreateDirectory(utils.Directory + "/tmp"); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
var tmpSqlFile = utils.Directory + "/tmp/" + types.SqliteFilename
|
||||
SampleHits = 480
|
||||
|
||||
|
@ -578,19 +606,19 @@ func TmpRecords(dbFile string) error {
|
|||
CoreApp = NewCore()
|
||||
CoreApp.Name = "Tester"
|
||||
CoreApp.Setup = true
|
||||
configs := &types.DbConfig{
|
||||
configs := &DbConfig{&types.DbConfig{
|
||||
DbConn: "sqlite",
|
||||
Project: "Tester",
|
||||
Location: utils.Directory,
|
||||
SqlFile: sqlFile,
|
||||
}
|
||||
}}
|
||||
log.Infoln("saving config.yml in: " + utils.Directory)
|
||||
if configs, err = CoreApp.SaveConfig(configs); err != nil {
|
||||
return err
|
||||
if err := configs.Save(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
log.Infoln("loading config.yml from: " + utils.Directory)
|
||||
if configs, err = LoadConfigFile(utils.Directory); err != nil {
|
||||
return err
|
||||
log.Error(err)
|
||||
}
|
||||
log.Infoln("connecting to database")
|
||||
|
||||
|
@ -598,37 +626,37 @@ func TmpRecords(dbFile string) error {
|
|||
if exists {
|
||||
log.Infoln(tmpSqlFile + " was found, copying the temp database to " + sqlFile)
|
||||
if err := utils.DeleteFile(sqlFile); err != nil {
|
||||
log.Infoln(sqlFile + " was not found")
|
||||
log.Error(err)
|
||||
}
|
||||
if err := utils.CopyFile(tmpSqlFile, sqlFile); err != nil {
|
||||
return err
|
||||
log.Error(err)
|
||||
}
|
||||
log.Infoln("loading config.yml from: " + utils.Directory)
|
||||
|
||||
if err := CoreApp.Connect(false, utils.Directory); err != nil {
|
||||
return err
|
||||
log.Error(err)
|
||||
}
|
||||
log.Infoln("selecting the Core variable")
|
||||
if _, err := SelectCore(); err != nil {
|
||||
return err
|
||||
log.Error(err)
|
||||
}
|
||||
log.Infoln("inserting notifiers into database")
|
||||
if err := InsertNotifierDB(); err != nil {
|
||||
return err
|
||||
log.Error(err)
|
||||
}
|
||||
log.Infoln("inserting integrations into database")
|
||||
if err := InsertIntegratorDB(); err != nil {
|
||||
return err
|
||||
log.Error(err)
|
||||
}
|
||||
log.Infoln("loading all services")
|
||||
if _, err := SelectAllServices(false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := AttachNotifiers(); err != nil {
|
||||
return err
|
||||
log.Error(err)
|
||||
}
|
||||
if err := AddIntegrations(); err != nil {
|
||||
return err
|
||||
log.Error(err)
|
||||
}
|
||||
CoreApp.Notifications = notifier.AllCommunications
|
||||
return nil
|
||||
|
|
|
@ -25,20 +25,19 @@ import (
|
|||
)
|
||||
|
||||
type Service struct {
|
||||
*types.Service
|
||||
*database.ServiceObj
|
||||
}
|
||||
|
||||
type Servicer interface{}
|
||||
|
||||
func Services() []database.Servicer {
|
||||
func Services() []*Service {
|
||||
return CoreApp.services
|
||||
}
|
||||
|
||||
// SelectService returns a *core.Service from in memory
|
||||
func SelectService(id int64) database.Servicer {
|
||||
func SelectService(id int64) *Service {
|
||||
for _, s := range Services() {
|
||||
if s.Model().Id == id {
|
||||
fmt.Println("service: ", s.Model())
|
||||
if s.Id == id {
|
||||
s.UpdateStats()
|
||||
fmt.Println("service: ", s.Name, s.Stats)
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +46,7 @@ func SelectService(id int64) database.Servicer {
|
|||
|
||||
// CheckinProcess runs the checkin routine for each checkin attached to service
|
||||
func CheckinProcess(s database.Servicer) {
|
||||
for _, c := range s.AllCheckins() {
|
||||
for _, c := range s.Checkins() {
|
||||
c.Start()
|
||||
go CheckinRoutine(c)
|
||||
}
|
||||
|
@ -55,31 +54,35 @@ func CheckinProcess(s database.Servicer) {
|
|||
|
||||
// SelectAllServices returns a slice of *core.Service to be store on []*core.Services
|
||||
// should only be called once on startup.
|
||||
func SelectAllServices(start bool) ([]*database.ServiceObj, error) {
|
||||
func SelectAllServices(start bool) ([]*Service, error) {
|
||||
srvs := database.Services()
|
||||
for _, s := range srvs {
|
||||
fmt.Println("services: ", s.Id, s.Name)
|
||||
}
|
||||
|
||||
for _, s := range srvs {
|
||||
if start {
|
||||
service := s.Model()
|
||||
service.Start()
|
||||
s.Start()
|
||||
CheckinProcess(s)
|
||||
}
|
||||
//fails := service.Service (limitedFailures)
|
||||
//for _, f := range fails {
|
||||
// service.Failures = append(service.Failures, f)
|
||||
//}
|
||||
for _, c := range s.AllCheckins() {
|
||||
s.Checkins = append(s.Checkins, c)
|
||||
|
||||
fails := s.Failures().Last(limitedFailures)
|
||||
s.Service.Failures = fails
|
||||
|
||||
for _, c := range s.Checkins() {
|
||||
s.Service.Checkins = append(s.Service.Checkins, c.Checkin)
|
||||
}
|
||||
|
||||
// collect initial service stats
|
||||
s.Service.Stats = s.UpdateStats()
|
||||
CoreApp.services = append(CoreApp.services, s)
|
||||
s.UpdateStats()
|
||||
CoreApp.services = append(CoreApp.services, &Service{s})
|
||||
}
|
||||
reorderServices()
|
||||
return srvs, nil
|
||||
return CoreApp.services, nil
|
||||
}
|
||||
|
||||
func wrapFailures(f []*types.Failure) []*Failure {
|
||||
var fails []*Failure
|
||||
for _, v := range f {
|
||||
fails = append(fails, &Failure{v})
|
||||
}
|
||||
return fails
|
||||
}
|
||||
|
||||
// reorderServices will sort the services based on 'order_id'
|
||||
|
@ -87,24 +90,10 @@ func reorderServices() {
|
|||
sort.Sort(ServiceOrder(CoreApp.services))
|
||||
}
|
||||
|
||||
// GraphData will return all hits or failures
|
||||
func GraphData(q *database.GroupQuery, dbType interface{}, by database.By) []*database.TimeValue {
|
||||
dbQuery, err := q.Database().GroupQuery(q, by).ToTimeValue(dbType)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil
|
||||
}
|
||||
if q.FillEmpty {
|
||||
return dbQuery.FillMissing(q.Start, q.End)
|
||||
}
|
||||
return dbQuery.ToValues()
|
||||
}
|
||||
|
||||
// index returns a services index int for updating the []*core.Services slice
|
||||
func index(s database.Servicer) int {
|
||||
func index(s int64) int {
|
||||
for k, service := range CoreApp.services {
|
||||
if s.Model().Id == service.Model().Id {
|
||||
if s == service.Id {
|
||||
return k
|
||||
}
|
||||
}
|
||||
|
@ -112,14 +101,13 @@ func index(s database.Servicer) int {
|
|||
}
|
||||
|
||||
// updateService will update a service in the []*core.Services slice
|
||||
func updateService(s database.Servicer) {
|
||||
CoreApp.services[index(s)] = s
|
||||
func updateService(s *Service) {
|
||||
CoreApp.services[index(s.Id)] = s
|
||||
}
|
||||
|
||||
// Delete will remove a service from the database, it will also end the service checking go routine
|
||||
func Delete(srv database.Servicer) error {
|
||||
i := index(srv)
|
||||
s := srv.Model()
|
||||
func (s *Service) Delete() error {
|
||||
i := index(s.Id)
|
||||
err := database.Delete(s)
|
||||
if err != nil {
|
||||
log.Errorln(fmt.Sprintf("Failed to delete service %v. %v", s.Name, err))
|
||||
|
@ -129,13 +117,12 @@ func Delete(srv database.Servicer) error {
|
|||
slice := CoreApp.services
|
||||
CoreApp.services = append(slice[:i], slice[i+1:]...)
|
||||
reorderServices()
|
||||
notifier.OnDeletedService(s)
|
||||
notifier.OnDeletedService(s.Service)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update will update a service in the database, the service's checking routine can be restarted by passing true
|
||||
func Update(srv database.Servicer, restart bool) error {
|
||||
s := srv.Model()
|
||||
func Update(s *Service, restart bool) error {
|
||||
err := database.Update(s)
|
||||
if err != nil {
|
||||
log.Errorln(fmt.Sprintf("Failed to update service %v. %v", s.Name, err))
|
||||
|
@ -151,12 +138,12 @@ func Update(srv database.Servicer, restart bool) error {
|
|||
if restart {
|
||||
s.Close()
|
||||
s.Start()
|
||||
s.SleepDuration = time.Duration(s.Interval) * time.Second
|
||||
go ServiceCheckQueue(srv, true)
|
||||
s.SleepDuration = s.Duration()
|
||||
go ServiceCheckQueue(s, true)
|
||||
}
|
||||
reorderServices()
|
||||
updateService(srv)
|
||||
notifier.OnUpdatedService(s)
|
||||
updateService(s)
|
||||
notifier.OnUpdatedService(s.Service)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -169,10 +156,11 @@ func Create(srv database.Servicer, check bool) (int64, error) {
|
|||
log.Errorln(fmt.Sprintf("Failed to create service %v #%v: %v", s.Name, s.Id, err))
|
||||
return 0, err
|
||||
}
|
||||
service := &Service{s}
|
||||
s.Start()
|
||||
go ServiceCheckQueue(srv, check)
|
||||
CoreApp.services = append(CoreApp.services, srv)
|
||||
CoreApp.services = append(CoreApp.services, service)
|
||||
go ServiceCheckQueue(service, check)
|
||||
reorderServices()
|
||||
notifier.OnNewService(s)
|
||||
notifier.OnNewService(s.Service)
|
||||
return s.Id, nil
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
@ -29,13 +30,12 @@ var (
|
|||
|
||||
func TestCreateCheckin(t *testing.T) {
|
||||
service := SelectService(1)
|
||||
testCheckin = ReturnCheckin(&types.Checkin{
|
||||
checkin := &types.Checkin{
|
||||
ServiceId: service.Id,
|
||||
Interval: 10,
|
||||
GracePeriod: 5,
|
||||
ApiKey: utils.RandomString(7),
|
||||
})
|
||||
id, err := testCheckin.Create()
|
||||
}
|
||||
id, err := database.Create(checkin)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, id)
|
||||
assert.NotEmpty(t, testCheckin.ApiKey)
|
||||
|
@ -46,13 +46,13 @@ func TestCreateCheckin(t *testing.T) {
|
|||
|
||||
func TestSelectCheckin(t *testing.T) {
|
||||
service := SelectService(1)
|
||||
checkins := service.AllCheckins()
|
||||
checkins := service.Checkins()
|
||||
assert.NotNil(t, checkins)
|
||||
assert.Equal(t, 1, len(checkins))
|
||||
testCheckin = checkins[0]
|
||||
assert.Equal(t, int64(10), testCheckin.Interval)
|
||||
assert.Equal(t, int64(5), testCheckin.GracePeriod)
|
||||
assert.Equal(t, 7, len(testCheckin.ApiKey))
|
||||
c := checkins[0]
|
||||
assert.Equal(t, int64(10), c.Interval)
|
||||
assert.Equal(t, int64(5), c.GracePeriod)
|
||||
assert.Equal(t, 7, len(c.ApiKey))
|
||||
}
|
||||
|
||||
func TestUpdateCheckin(t *testing.T) {
|
||||
|
@ -63,7 +63,7 @@ func TestUpdateCheckin(t *testing.T) {
|
|||
assert.NotZero(t, id)
|
||||
assert.NotEmpty(t, testCheckin.ApiKey)
|
||||
service := SelectService(1)
|
||||
checkin := service.AllCheckins()[0]
|
||||
checkin := service.Checkins()[0]
|
||||
assert.Equal(t, int64(60), checkin.Interval)
|
||||
assert.Equal(t, int64(15), checkin.GracePeriod)
|
||||
t.Log(testCheckin.Expected())
|
||||
|
@ -72,15 +72,16 @@ func TestUpdateCheckin(t *testing.T) {
|
|||
|
||||
func TestCreateCheckinHits(t *testing.T) {
|
||||
service := SelectService(1)
|
||||
checkins := service.AllCheckins()
|
||||
checkins := service.Checkins()
|
||||
assert.Equal(t, 1, len(checkins))
|
||||
created := time.Now().UTC().Add(-60 * time.Second)
|
||||
hit := ReturnCheckinHit(&types.CheckinHit{
|
||||
hit := &types.CheckinHit{
|
||||
Checkin: testCheckin.Id,
|
||||
From: "192.168.1.1",
|
||||
CreatedAt: created,
|
||||
})
|
||||
hit.Create()
|
||||
}
|
||||
_, err := database.Create(hit)
|
||||
require.Nil(t, err)
|
||||
hits := testCheckin.AllHits()
|
||||
assert.Equal(t, 1, len(hits))
|
||||
}
|
||||
|
@ -88,13 +89,13 @@ func TestCreateCheckinHits(t *testing.T) {
|
|||
func TestSelectCheckinMethods(t *testing.T) {
|
||||
time.Sleep(5 * time.Second)
|
||||
service := SelectService(1)
|
||||
checkins := service.AllCheckins()
|
||||
checkins := service.Checkins()
|
||||
assert.NotNil(t, checkins)
|
||||
lastHit := testCheckin.Last()
|
||||
assert.Equal(t, float64(60), testCheckin.Period().Seconds())
|
||||
assert.Equal(t, float64(15), testCheckin.Grace().Seconds())
|
||||
t.Log(testCheckin.Expected())
|
||||
|
||||
lastHit := checkins[0]
|
||||
assert.True(t, testCheckin.Expected().Seconds() < -5)
|
||||
assert.False(t, lastHit.CreatedAt.IsZero())
|
||||
assert.Equal(t, "A minute ago", lastHit.Ago())
|
||||
}
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
@ -28,7 +30,7 @@ var (
|
|||
)
|
||||
|
||||
func TestSelectHTTPService(t *testing.T) {
|
||||
services, err := CoreApp.SelectAllServices(false)
|
||||
services, err := SelectAllServices(false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 15, len(services))
|
||||
assert.Equal(t, "Google", services[0].Name)
|
||||
|
@ -36,12 +38,11 @@ func TestSelectHTTPService(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSelectAllServices(t *testing.T) {
|
||||
services := CoreApp.Services
|
||||
services := CoreApp.services
|
||||
for _, s := range services {
|
||||
service := s.(*Service)
|
||||
service.Check(false)
|
||||
assert.False(t, service.IsRunning())
|
||||
t.Logf("ID: %v %v\n", service.Id, service.Name)
|
||||
CheckService(s, false)
|
||||
assert.False(t, s.IsRunning())
|
||||
t.Logf("ID: %v %v\n", s.Id, s.Name)
|
||||
}
|
||||
assert.Equal(t, 15, len(services))
|
||||
}
|
||||
|
@ -54,7 +55,7 @@ func TestServiceDowntime(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSelectTCPService(t *testing.T) {
|
||||
services := CoreApp.Services
|
||||
services := CoreApp.services
|
||||
assert.Equal(t, 15, len(services))
|
||||
service := SelectService(5)
|
||||
assert.NotNil(t, service)
|
||||
|
@ -67,8 +68,10 @@ func TestUpdateService(t *testing.T) {
|
|||
assert.Equal(t, "Google", service.Name)
|
||||
service.Name = "Updated Google"
|
||||
service.Interval = 5
|
||||
err := service.Update(true)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err := database.Update(service)
|
||||
require.Nil(t, err)
|
||||
|
||||
// check if updating pointer array shutdown any other service
|
||||
service = SelectService(1)
|
||||
assert.Equal(t, "Updated Google", service.Name)
|
||||
|
@ -76,19 +79,20 @@ func TestUpdateService(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUpdateAllServices(t *testing.T) {
|
||||
services, err := CoreApp.SelectAllServices(false)
|
||||
assert.Nil(t, err)
|
||||
services, err := SelectAllServices(false)
|
||||
require.Nil(t, err)
|
||||
for k, srv := range services {
|
||||
srv.Name = "Changed " + srv.Name
|
||||
srv.Interval = k + 3
|
||||
err := srv.Update(true)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err := database.Update(srv)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceHTTPCheck(t *testing.T) {
|
||||
service := SelectService(1)
|
||||
service.Check(true)
|
||||
CheckService(service, true)
|
||||
assert.Equal(t, "Changed Updated Google", service.Name)
|
||||
assert.True(t, service.Online)
|
||||
}
|
||||
|
@ -104,7 +108,7 @@ func TestCheckHTTPService(t *testing.T) {
|
|||
|
||||
func TestServiceTCPCheck(t *testing.T) {
|
||||
service := SelectService(5)
|
||||
service.Check(true)
|
||||
CheckService(service, true)
|
||||
assert.Equal(t, "Changed Google DNS", service.Name)
|
||||
assert.True(t, service.Online)
|
||||
}
|
||||
|
@ -130,23 +134,17 @@ func TestServiceOnline24Hours(t *testing.T) {
|
|||
func TestServiceAvgUptime(t *testing.T) {
|
||||
since := utils.Now().Add(-24 * time.Hour).Add(-10 * time.Minute)
|
||||
service := SelectService(1)
|
||||
assert.NotEqual(t, "0.00", service.AvgUptime(since))
|
||||
assert.NotEqual(t, "0.00", service.AvgTime())
|
||||
service2 := SelectService(5)
|
||||
assert.Equal(t, "100", service2.AvgUptime(since))
|
||||
assert.Equal(t, "100", service2.AvgTime())
|
||||
service3 := SelectService(13)
|
||||
assert.NotEqual(t, "0", service3.AvgUptime(since))
|
||||
service4 := SelectService(15)
|
||||
assert.NotEqual(t, "0", service4.AvgUptime(since))
|
||||
}
|
||||
|
||||
func TestServiceSum(t *testing.T) {
|
||||
service := SelectService(5)
|
||||
sum := service.Sum()
|
||||
assert.NotZero(t, sum)
|
||||
}
|
||||
|
||||
func TestCreateService(t *testing.T) {
|
||||
s := ReturnService(&types.Service{
|
||||
s := &types.Service{
|
||||
Name: "That'll do 🐢",
|
||||
Domain: "https://www.youtube.com/watch?v=rjQtzV9IZ0Q",
|
||||
ExpectedStatus: 200,
|
||||
|
@ -155,12 +153,11 @@ func TestCreateService(t *testing.T) {
|
|||
Method: "GET",
|
||||
Timeout: 20,
|
||||
GroupId: 1,
|
||||
})
|
||||
var err error
|
||||
newServiceId, err = s.Create(false)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, newServiceId)
|
||||
newService := SelectService(newServiceId)
|
||||
}
|
||||
obj, err := database.Create(s)
|
||||
require.Nil(t, err)
|
||||
assert.NotZero(t, obj.Id)
|
||||
newService := SelectService(obj.Id)
|
||||
assert.Equal(t, "That'll do 🐢", newService.Name)
|
||||
}
|
||||
|
||||
|
@ -170,7 +167,7 @@ func TestViewNewService(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateFailingHTTPService(t *testing.T) {
|
||||
s := ReturnService(&types.Service{
|
||||
s := &types.Service{
|
||||
Name: "Bad URL",
|
||||
Domain: "http://localhost/iamnothere",
|
||||
ExpectedStatus: 200,
|
||||
|
@ -179,12 +176,11 @@ func TestCreateFailingHTTPService(t *testing.T) {
|
|||
Method: "GET",
|
||||
Timeout: 5,
|
||||
GroupId: 1,
|
||||
})
|
||||
var err error
|
||||
newServiceId, err = s.Create(false)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, newServiceId)
|
||||
newService := SelectService(newServiceId)
|
||||
}
|
||||
obj, err := database.Create(s)
|
||||
require.Nil(t, err)
|
||||
assert.NotZero(t, obj.Id)
|
||||
newService := SelectService(obj.Id)
|
||||
assert.Equal(t, "Bad URL", newService.Name)
|
||||
t.Log("new service ID: ", newServiceId)
|
||||
}
|
||||
|
@ -192,13 +188,13 @@ func TestCreateFailingHTTPService(t *testing.T) {
|
|||
func TestServiceFailedCheck(t *testing.T) {
|
||||
service := SelectService(17)
|
||||
assert.Equal(t, "Bad URL", service.Name)
|
||||
service.Check(false)
|
||||
CheckService(service, false)
|
||||
assert.Equal(t, "Bad URL", service.Name)
|
||||
assert.False(t, service.Online)
|
||||
}
|
||||
|
||||
func TestCreateFailingTCPService(t *testing.T) {
|
||||
s := ReturnService(&types.Service{
|
||||
s := &types.Service{
|
||||
Name: "Bad TCP",
|
||||
Domain: "localhost",
|
||||
Port: 5050,
|
||||
|
@ -206,50 +202,51 @@ func TestCreateFailingTCPService(t *testing.T) {
|
|||
Type: "tcp",
|
||||
Timeout: 5,
|
||||
GroupId: 1,
|
||||
})
|
||||
}
|
||||
var err error
|
||||
newServiceId, err = s.Create(false)
|
||||
obj, err := database.Create(s)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, newServiceId)
|
||||
newService := SelectService(newServiceId)
|
||||
assert.NotZero(t, obj.Id)
|
||||
newService := SelectService(obj.Id)
|
||||
assert.Equal(t, "Bad TCP", newService.Name)
|
||||
t.Log("new failing tcp service ID: ", newServiceId)
|
||||
}
|
||||
|
||||
func TestServiceFailedTCPCheck(t *testing.T) {
|
||||
service := SelectService(newServiceId)
|
||||
service.Check(false)
|
||||
CheckService(service, false)
|
||||
assert.Equal(t, "Bad TCP", service.Name)
|
||||
assert.False(t, service.Online)
|
||||
}
|
||||
|
||||
func TestCreateServiceFailure(t *testing.T) {
|
||||
service := SelectService(8)
|
||||
fail := &types.Failure{
|
||||
Issue: "This is not an issue, but it would container HTTP response errors.",
|
||||
Method: "http",
|
||||
Service: service.Id,
|
||||
}
|
||||
service := SelectService(8)
|
||||
id, err := service.CreateFailure(fail)
|
||||
obj, err := database.Create(fail)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, id)
|
||||
assert.NotZero(t, obj.Id)
|
||||
}
|
||||
|
||||
func TestDeleteService(t *testing.T) {
|
||||
service := SelectService(newServiceId)
|
||||
|
||||
count, err := CoreApp.SelectAllServices(false)
|
||||
count, err := SelectAllServices(false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 18, len(count))
|
||||
|
||||
err = service.Delete()
|
||||
assert.Nil(t, err)
|
||||
|
||||
services := CoreApp.Services
|
||||
services := CoreApp.services
|
||||
assert.Equal(t, 17, len(services))
|
||||
}
|
||||
|
||||
func TestServiceCloseRoutine(t *testing.T) {
|
||||
s := ReturnService(new(types.Service))
|
||||
s := new(Service)
|
||||
s.Name = "example"
|
||||
s.Domain = "https://google.com"
|
||||
s.Type = "http"
|
||||
|
@ -260,7 +257,7 @@ func TestServiceCloseRoutine(t *testing.T) {
|
|||
assert.True(t, s.IsRunning())
|
||||
t.Log(s.Checkpoint)
|
||||
t.Log(s.SleepDuration)
|
||||
go s.CheckQueue(false)
|
||||
go ServiceCheckQueue(s, false)
|
||||
t.Log(s.Checkpoint)
|
||||
t.Log(s.SleepDuration)
|
||||
time.Sleep(5 * time.Second)
|
||||
|
@ -274,7 +271,7 @@ func TestServiceCloseRoutine(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestServiceCheckQueue(t *testing.T) {
|
||||
s := ReturnService(new(types.Service))
|
||||
s := new(Service)
|
||||
s.Name = "example"
|
||||
s.Domain = "https://google.com"
|
||||
s.Type = "http"
|
||||
|
@ -283,7 +280,7 @@ func TestServiceCheckQueue(t *testing.T) {
|
|||
s.Interval = 1
|
||||
s.Start()
|
||||
assert.True(t, s.IsRunning())
|
||||
go s.CheckQueue(false)
|
||||
go ServiceCheckQueue(s, false)
|
||||
|
||||
go func() {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
@ -300,14 +297,14 @@ func TestServiceCheckQueue(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDNScheckService(t *testing.T) {
|
||||
s := ReturnService(new(types.Service))
|
||||
s := new(Service)
|
||||
s.Name = "example"
|
||||
s.Domain = "http://localhost:9000"
|
||||
s.Type = "http"
|
||||
s.Method = "GET"
|
||||
s.ExpectedStatus = 200
|
||||
s.Interval = 1
|
||||
amount, err := s.dnsCheck()
|
||||
amount, err := dnsCheck(s)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, amount)
|
||||
}
|
||||
|
@ -317,25 +314,13 @@ func TestSelectServiceLink(t *testing.T) {
|
|||
assert.Equal(t, "google", service.Permalink.String)
|
||||
}
|
||||
|
||||
func TestDbtimestamp(t *testing.T) {
|
||||
CoreApp.Config.DbConn = "mysql"
|
||||
query := Dbtimestamp("minute", "latency")
|
||||
assert.Equal(t, "CONCAT(date_format(created_at, '%Y-%m-%d %H:00:00')) AS timeframe, AVG(latency) AS value", query)
|
||||
CoreApp.Config.DbConn = "postgres"
|
||||
query = Dbtimestamp("minute", "latency")
|
||||
assert.Equal(t, "date_trunc('minute', created_at) AS timeframe, AVG(latency) AS value", query)
|
||||
CoreApp.Config.DbConn = "sqlite"
|
||||
query = Dbtimestamp("minute", "latency")
|
||||
assert.Equal(t, "datetime((strftime('%s', created_at) / 60) * 60, 'unixepoch') AS timeframe, AVG(latency) as value", query)
|
||||
}
|
||||
|
||||
func TestGroup_Create(t *testing.T) {
|
||||
group := &Group{&types.Group{
|
||||
group := &types.Group{
|
||||
Name: "Testing",
|
||||
}}
|
||||
newGroupId, err := group.Create()
|
||||
}
|
||||
obj, err := database.Create(group)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, newGroupId)
|
||||
assert.NotZero(t, obj.Id)
|
||||
}
|
||||
|
||||
func TestGroup_Services(t *testing.T) {
|
||||
|
|
|
@ -96,16 +96,21 @@ func SelectAllUsers() []*types.User {
|
|||
|
||||
// AuthUser will return the User and a boolean if authentication was correct.
|
||||
// AuthUser accepts username, and password as a string
|
||||
func AuthUser(username, password string) (*User, bool) {
|
||||
user, err := SelectUsername(username)
|
||||
func AuthUser(username, password string) (*types.User, bool) {
|
||||
user, err := database.UserByUsername(username)
|
||||
if err != nil {
|
||||
log.Warnln(fmt.Errorf("user %v not found", username))
|
||||
return nil, false
|
||||
}
|
||||
|
||||
fmt.Println(username, password)
|
||||
|
||||
fmt.Println(username, user.Password)
|
||||
|
||||
if CheckHash(password, user.Password) {
|
||||
user.UpdatedAt = time.Now().UTC()
|
||||
user.Update()
|
||||
return user, true
|
||||
database.Update(user)
|
||||
return user.User, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
|
|
@ -16,26 +16,26 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
user := ReturnUser(&types.User{
|
||||
user := &types.User{
|
||||
Username: "hunter",
|
||||
Password: "password123",
|
||||
Email: "test@email.com",
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
userId, err := user.Create()
|
||||
}
|
||||
obj, err := database.Create(user)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, userId)
|
||||
assert.NotZero(t, obj.Id)
|
||||
}
|
||||
|
||||
func TestSelectAllUsers(t *testing.T) {
|
||||
users, err := SelectAllUsers()
|
||||
assert.Nil(t, err)
|
||||
users := SelectAllUsers()
|
||||
assert.Equal(t, 3, len(users))
|
||||
}
|
||||
|
||||
|
@ -66,20 +66,19 @@ func TestUpdateUser(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateUser2(t *testing.T) {
|
||||
user := ReturnUser(&types.User{
|
||||
user := &types.User{
|
||||
Username: "hunterlong",
|
||||
Password: "password123",
|
||||
Email: "User@email.com",
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
userId, err := user.Create()
|
||||
}
|
||||
obj, err := database.Create(user)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, userId)
|
||||
assert.NotZero(t, obj.Id)
|
||||
}
|
||||
|
||||
func TestSelectAllUsersAgain(t *testing.T) {
|
||||
users, err := SelectAllUsers()
|
||||
assert.Nil(t, err)
|
||||
users := SelectAllUsers()
|
||||
assert.Equal(t, 4, len(users))
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package database
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -20,6 +21,21 @@ type Checkiner interface {
|
|||
Object() *CheckinObj
|
||||
}
|
||||
|
||||
func (c *CheckinObj) BeforeCreate() (err error) {
|
||||
c.ApiKey = utils.RandomString(7)
|
||||
if c.CreatedAt.IsZero() {
|
||||
c.CreatedAt = time.Now().UTC()
|
||||
c.UpdatedAt = time.Now().UTC()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CheckinObj) BeforeDelete(tx Database) (err error) {
|
||||
q := tx.Services().Where("id = ?", c.ServiceId).
|
||||
Update("group_id", 0)
|
||||
return q.Error()
|
||||
}
|
||||
|
||||
func Checkin(id int64) (*CheckinObj, error) {
|
||||
var checkin types.Checkin
|
||||
query := database.Checkins().Where("id = ?", id)
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type CrudObject interface {
|
||||
Create()
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
Id int64
|
||||
model interface{}
|
||||
|
@ -25,41 +28,35 @@ func wrapObject(id int64, model interface{}, db Database) *Object {
|
|||
}
|
||||
|
||||
func modelId(model interface{}) int64 {
|
||||
fmt.Printf("%T\n", model)
|
||||
switch model.(type) {
|
||||
case *types.Core:
|
||||
return 0
|
||||
default:
|
||||
iface := reflect.ValueOf(model)
|
||||
field := iface.Elem().FieldByName("Id")
|
||||
return field.Int()
|
||||
}
|
||||
|
||||
func toModel(model interface{}) Database {
|
||||
switch model.(type) {
|
||||
case *types.Core:
|
||||
return database.Model(&types.Core{}).Table("core")
|
||||
default:
|
||||
return database.Model(&model)
|
||||
}
|
||||
}
|
||||
|
||||
func Create(data interface{}) (*Object, error) {
|
||||
model := toModel(data)
|
||||
query := model.Create(data)
|
||||
if query.Error() != nil {
|
||||
return nil, query.Error()
|
||||
model := database.Model(&data)
|
||||
if err := model.Create(data).Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj := &Object{
|
||||
Id: modelId(data),
|
||||
model: data,
|
||||
db: model,
|
||||
}
|
||||
return obj, query.Error()
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func Update(data interface{}) error {
|
||||
model := toModel(data)
|
||||
model := database.Model(&data)
|
||||
return model.Update(&data).Error()
|
||||
}
|
||||
|
||||
func Delete(data interface{}) error {
|
||||
model := toModel(data)
|
||||
model := database.Model(&data)
|
||||
return model.Delete(data).Error()
|
||||
}
|
||||
|
|
|
@ -113,8 +113,6 @@ type Database interface {
|
|||
|
||||
Requests(*http.Request, isObject) Database
|
||||
|
||||
GroupQuery(query *GroupQuery, by By) GroupByer
|
||||
|
||||
Objects
|
||||
}
|
||||
|
||||
|
@ -193,6 +191,9 @@ 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()
|
||||
}
|
||||
gormdb, err := gorm.Open(dialect, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -34,10 +34,10 @@ func (f *FailureObj) DeleteAll() error {
|
|||
return query.Error()
|
||||
}
|
||||
|
||||
func (f *FailureObj) Last(amount int) *types.Failure {
|
||||
var fail types.Failure
|
||||
f.o.db.Limit(amount).Last(&fail)
|
||||
return &fail
|
||||
func (f *FailureObj) Last(amount int) []*types.Failure {
|
||||
var fail []*types.Failure
|
||||
f.o.db.Limit(amount).Find(&fail)
|
||||
return fail
|
||||
}
|
||||
|
||||
func (f *FailureObj) Count() int {
|
||||
|
|
|
@ -15,7 +15,7 @@ type GroupBy struct {
|
|||
}
|
||||
|
||||
type GroupByer interface {
|
||||
ToTimeValue(interface{}) (*TimeVar, error)
|
||||
ToTimeValue() (*TimeVar, error)
|
||||
}
|
||||
|
||||
type By string
|
||||
|
@ -25,7 +25,6 @@ func (b By) String() string {
|
|||
}
|
||||
|
||||
type GroupQuery struct {
|
||||
db Database
|
||||
Start time.Time
|
||||
End time.Time
|
||||
Group string
|
||||
|
@ -33,6 +32,8 @@ type GroupQuery struct {
|
|||
Limit int
|
||||
Offset int
|
||||
FillEmpty bool
|
||||
|
||||
db Database
|
||||
}
|
||||
|
||||
func (b GroupQuery) Find(data interface{}) error {
|
||||
|
@ -50,25 +51,16 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func (db *Db) GroupQuery(q *GroupQuery, by By) GroupByer {
|
||||
dbQuery := db.MultipleSelects(
|
||||
db.SelectByTime(q.Group),
|
||||
by.String(),
|
||||
).Group("timeframe")
|
||||
|
||||
return &GroupBy{dbQuery, q}
|
||||
}
|
||||
|
||||
type TimeVar struct {
|
||||
g *GroupBy
|
||||
g *GroupQuery
|
||||
data []*TimeValue
|
||||
}
|
||||
|
||||
func (t *TimeVar) ToValues() []*TimeValue {
|
||||
return t.data
|
||||
func (t *TimeVar) ToValues() ([]*TimeValue, error) {
|
||||
return t.data, nil
|
||||
}
|
||||
|
||||
func (g *GroupBy) toFloatRows() []*TimeValue {
|
||||
func (g *GroupQuery) toFloatRows() []*TimeValue {
|
||||
rows, err := g.db.Rows()
|
||||
if err != nil {
|
||||
return nil
|
||||
|
@ -77,7 +69,12 @@ func (g *GroupBy) toFloatRows() []*TimeValue {
|
|||
for rows.Next() {
|
||||
var timeframe time.Time
|
||||
amount := float64(0)
|
||||
rows.Scan(&timeframe, &amount)
|
||||
if err := rows.Scan(&timeframe, &amount); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
|
||||
fmt.Println("float rows: ", timeframe, amount)
|
||||
|
||||
newTs := types.FixedTime(timeframe, g.duration())
|
||||
data = append(data, &TimeValue{
|
||||
Timeframe: newTs,
|
||||
|
@ -87,17 +84,41 @@ func (g *GroupBy) toFloatRows() []*TimeValue {
|
|||
return data
|
||||
}
|
||||
|
||||
func (g *GroupBy) ToTimeValue(dbType interface{}) (*TimeVar, error) {
|
||||
// GraphData will return all hits or failures
|
||||
func (g *GroupQuery) GraphData(by By) ([]*TimeValue, error) {
|
||||
|
||||
dbQuery := g.db.MultipleSelects(
|
||||
g.db.SelectByTime(g.Group),
|
||||
by.String(),
|
||||
).Group("timeframe")
|
||||
|
||||
g.db = dbQuery
|
||||
|
||||
caller, err := g.ToTimeValue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g.FillEmpty {
|
||||
return caller.FillMissing(g.Start, g.End)
|
||||
}
|
||||
return caller.ToValues()
|
||||
}
|
||||
|
||||
func (g *GroupQuery) ToTimeValue() (*TimeVar, error) {
|
||||
rows, err := g.db.Rows()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data []*TimeValue
|
||||
for rows.Next() {
|
||||
var timeframe time.Time
|
||||
var timeframe string
|
||||
amount := float64(0)
|
||||
rows.Scan(&timeframe, &amount)
|
||||
newTs := types.FixedTime(timeframe, g.duration())
|
||||
if err := rows.Scan(&timeframe, &amount); err != nil {
|
||||
log.Error(err, timeframe)
|
||||
}
|
||||
trueTime, _ := g.db.ParseTime(timeframe)
|
||||
newTs := types.FixedTime(trueTime, g.duration())
|
||||
data = append(data, &TimeValue{
|
||||
Timeframe: newTs,
|
||||
Amount: amount,
|
||||
|
@ -106,7 +127,7 @@ func (g *GroupBy) ToTimeValue(dbType interface{}) (*TimeVar, error) {
|
|||
return &TimeVar{g, data}, nil
|
||||
}
|
||||
|
||||
func (t *TimeVar) FillMissing(current, end time.Time) []*TimeValue {
|
||||
func (t *TimeVar) FillMissing(current, end time.Time) ([]*TimeValue, error) {
|
||||
timeMap := make(map[string]float64)
|
||||
var validSet []*TimeValue
|
||||
dur := t.g.duration()
|
||||
|
@ -132,11 +153,11 @@ func (t *TimeVar) FillMissing(current, end time.Time) []*TimeValue {
|
|||
currentStr = types.FixedTime(current, t.g.duration())
|
||||
}
|
||||
|
||||
return validSet
|
||||
return validSet, nil
|
||||
}
|
||||
|
||||
func (g *GroupBy) duration() time.Duration {
|
||||
switch g.query.Group {
|
||||
func (g *GroupQuery) duration() time.Duration {
|
||||
switch g.Group {
|
||||
case "second":
|
||||
return types.Second
|
||||
case "minute":
|
||||
|
@ -175,6 +196,8 @@ func ParseQueries(r *http.Request, o isObject) *GroupQuery {
|
|||
limit = 10000
|
||||
}
|
||||
|
||||
db := o.object().db
|
||||
|
||||
query := &GroupQuery{
|
||||
Start: time.Unix(startField, 0).UTC(),
|
||||
End: time.Unix(endField, 0).UTC(),
|
||||
|
@ -183,10 +206,9 @@ func ParseQueries(r *http.Request, o isObject) *GroupQuery {
|
|||
Limit: int(limit),
|
||||
Offset: int(offset),
|
||||
FillEmpty: fill,
|
||||
db: db,
|
||||
}
|
||||
|
||||
db := o.object().db
|
||||
|
||||
if query.Limit != 0 {
|
||||
db = db.Limit(query.Limit)
|
||||
}
|
||||
|
|
|
@ -15,10 +15,25 @@ func (h *HitObj) All() []*types.Hit {
|
|||
return fails
|
||||
}
|
||||
|
||||
func (h *HitObj) Last(amount int) *types.Hit {
|
||||
var hits types.Hit
|
||||
func (s *ServiceObj) CreateHit(hit *types.Hit) *HitObj {
|
||||
hit.Service = s.Id
|
||||
database.Create(hit)
|
||||
return &HitObj{wrapObject(hit.Id, hit, database.Hits().Where("id = ?", hit.Id))}
|
||||
}
|
||||
|
||||
func (h *HitObj) Sum() float64 {
|
||||
result := struct {
|
||||
amount float64
|
||||
}{0}
|
||||
|
||||
h.o.db.Select("AVG(latency) as amount").Scan(&result).Debug()
|
||||
return result.amount
|
||||
}
|
||||
|
||||
func (h *HitObj) Last(amount int) []*types.Hit {
|
||||
var hits []*types.Hit
|
||||
h.o.db.Limit(amount).Find(&hits)
|
||||
return &hits
|
||||
return hits
|
||||
}
|
||||
|
||||
func (h *HitObj) Since(t time.Time) []*types.Hit {
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
log = utils.Log
|
||||
removeRowsAfter = types.Month * 6
|
||||
maintenceDuration = types.Hour
|
||||
)
|
||||
|
||||
func StartMaintenceRoutine() {
|
||||
dur := os.Getenv("REMOVE_AFTER")
|
||||
var removeDur time.Duration
|
||||
|
||||
if dur != "" {
|
||||
parsedDur, err := time.ParseDuration(dur)
|
||||
if err != nil {
|
||||
log.Errorf("could not parse duration: %s, using default: %s", dur, removeRowsAfter.String())
|
||||
removeDur = removeRowsAfter
|
||||
} else {
|
||||
removeDur = parsedDur
|
||||
}
|
||||
} else {
|
||||
removeDur = removeRowsAfter
|
||||
}
|
||||
|
||||
log.Infof("Service Failure and Hit records will be automatically removed after %s", removeDur.String())
|
||||
go databaseMaintence(removeDur)
|
||||
}
|
||||
|
||||
// databaseMaintence will automatically delete old records from 'failures' and 'hits'
|
||||
// this function is currently set to delete records 7+ days old every 60 minutes
|
||||
func databaseMaintence(dur time.Duration) {
|
||||
deleteAfter := time.Now().UTC().Add(dur)
|
||||
|
||||
for range time.Tick(maintenceDuration) {
|
||||
log.Infof("Deleting failures older than %s", dur.String())
|
||||
DeleteAllSince("failures", deleteAfter)
|
||||
|
||||
log.Infof("Deleting hits older than %s", dur.String())
|
||||
DeleteAllSince("hits", deleteAfter)
|
||||
|
||||
maintenceDuration = types.Hour
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAllSince will delete a specific table's records based on a time.
|
||||
func DeleteAllSince(table string, date time.Time) {
|
||||
sql := fmt.Sprintf("DELETE FROM %v WHERE created_at < '%v';", table, database.FormatTime(date))
|
||||
db := database.Exec(sql)
|
||||
if db.Error() != nil {
|
||||
log.Warnln(db.Error())
|
||||
}
|
||||
}
|
|
@ -16,18 +16,22 @@ type ServiceObj struct {
|
|||
}
|
||||
|
||||
type Servicer interface {
|
||||
Hits() *HitObj
|
||||
Failures() *FailureObj
|
||||
AllCheckins() []*CheckinObj
|
||||
Model() *types.Service
|
||||
Interval() time.Duration
|
||||
Checkins() []*CheckinObj
|
||||
DowntimeText() string
|
||||
UpdateStats()
|
||||
Model() *ServiceObj
|
||||
|
||||
Hittable
|
||||
}
|
||||
|
||||
type Hittable interface {
|
||||
CreateHit(*types.Hit) (int64, error)
|
||||
Hits() *HitObj
|
||||
CreateHit(hit *types.Hit) *HitObj
|
||||
}
|
||||
|
||||
func (s *ServiceObj) Model() *ServiceObj {
|
||||
return s
|
||||
}
|
||||
|
||||
func Service(id int64) (*ServiceObj, error) {
|
||||
|
@ -52,7 +56,7 @@ func Services() []*ServiceObj {
|
|||
return wrapServices(services, db)
|
||||
}
|
||||
|
||||
func (s *ServiceObj) AllCheckins() []*CheckinObj {
|
||||
func (s *ServiceObj) Checkins() []*CheckinObj {
|
||||
var checkins []*types.Checkin
|
||||
query := database.Checkins().Where("service = ?", s.Id)
|
||||
query.Find(&checkins)
|
||||
|
@ -61,7 +65,10 @@ func (s *ServiceObj) AllCheckins() []*CheckinObj {
|
|||
|
||||
func (s *ServiceObj) DowntimeText() string {
|
||||
last := s.Failures().Last(1)
|
||||
return parseError(last)
|
||||
if len(last) == 0 {
|
||||
return ""
|
||||
}
|
||||
return parseError(last[0])
|
||||
}
|
||||
|
||||
// ParseError returns a human readable error for a Failure
|
||||
|
@ -116,16 +123,7 @@ func parseError(f *types.Failure) string {
|
|||
return f.Issue
|
||||
}
|
||||
|
||||
func (s *ServiceObj) Interval() time.Duration {
|
||||
return time.Duration(s.Service.Interval) * time.Second
|
||||
}
|
||||
|
||||
func (s *ServiceObj) Model() *types.Service {
|
||||
return s.Service
|
||||
}
|
||||
|
||||
func (s *ServiceObj) Hits() *HitObj {
|
||||
fmt.Println("hits")
|
||||
query := database.Hits().Where("service = ?", s.Id)
|
||||
return &HitObj{wrapObject(s.Id, nil, query)}
|
||||
}
|
||||
|
@ -149,34 +147,27 @@ func (s *ServiceObj) object() *Object {
|
|||
return s.o
|
||||
}
|
||||
|
||||
func (s *ServiceObj) UpdateStats() *types.Stats {
|
||||
func (s *ServiceObj) UpdateStats() {
|
||||
s.Online24Hours = s.OnlineDaysPercent(1)
|
||||
s.Online7Days = s.OnlineDaysPercent(7)
|
||||
s.AvgResponse = s.AvgTime()
|
||||
s.FailuresLast24Hours = len(s.Failures().Since(time.Now().Add(-time.Hour * 24)))
|
||||
return s.Stats
|
||||
s.FailuresLast24Hours = len(s.Failures().Since(time.Now().UTC().Add(-time.Hour * 24)))
|
||||
s.Stats = &types.Stats{
|
||||
Failures: s.Failures().Count(),
|
||||
Hits: s.Hits().Count(),
|
||||
}
|
||||
}
|
||||
|
||||
// AvgTime will return the average amount of time for a service to response back successfully
|
||||
func (s *ServiceObj) AvgTime() float64 {
|
||||
var sum []float64
|
||||
database.Hits().
|
||||
Select("AVG(latency) as amount").
|
||||
Where("service = ?", s.Id).Pluck("amount", &sum).Debug()
|
||||
sum := s.Hits().Sum()
|
||||
return sum
|
||||
}
|
||||
|
||||
sumTotal := float64(0)
|
||||
for _, v := range sum {
|
||||
sumTotal += v
|
||||
}
|
||||
|
||||
total := s.Hits().Count()
|
||||
|
||||
if total == 0 {
|
||||
return 0
|
||||
}
|
||||
avg := sumTotal / float64(total) * 100
|
||||
f, _ := strconv.ParseFloat(fmt.Sprintf("%0.0f", avg*10), 32)
|
||||
return f
|
||||
// AvgUptime will return the average amount of time for a service to response back successfully
|
||||
func (s *ServiceObj) AvgUptime(since time.Time) float64 {
|
||||
sum := s.Hits().Sum()
|
||||
return sum
|
||||
}
|
||||
|
||||
// OnlineDaysPercent returns the service's uptime percent within last 24 hours
|
||||
|
@ -211,12 +202,12 @@ func (s *ServiceObj) OnlineSince(ago time.Time) float32 {
|
|||
func (s *ServiceObj) Downtime() time.Duration {
|
||||
hits := s.Hits().Last(1)
|
||||
fail := s.Failures().Last(1)
|
||||
if fail == nil {
|
||||
if len(fail) == 0 {
|
||||
return time.Duration(0)
|
||||
}
|
||||
if hits == nil {
|
||||
return time.Now().UTC().Sub(fail.CreatedAt.UTC())
|
||||
if len(fail) == 0 {
|
||||
return time.Now().UTC().Sub(fail[0].CreatedAt.UTC())
|
||||
}
|
||||
since := fail.CreatedAt.UTC().Sub(hits.CreatedAt.UTC())
|
||||
since := fail[0].CreatedAt.UTC().Sub(hits[0].CreatedAt.UTC())
|
||||
return since
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -18,8 +18,6 @@ require (
|
|||
github.com/go-mail/mail v2.3.1+incompatible
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||
github.com/gorilla/mux v1.7.3
|
||||
github.com/gorilla/websocket v1.4.1 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.3 // indirect
|
||||
github.com/jinzhu/gorm v1.9.11
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/lib/pq v1.2.0 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -86,11 +86,7 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
|
|||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
|
|
|
@ -33,6 +33,7 @@ func init() {
|
|||
|
||||
func TestResetDatabase(t *testing.T) {
|
||||
err := core.TmpRecords("handlers.db")
|
||||
t.Log(err)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, core.CoreApp)
|
||||
require.NotNil(t, core.CoreApp.Config)
|
||||
|
|
|
@ -95,10 +95,13 @@ func checkinDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||
return
|
||||
}
|
||||
err := checkin.Delete()
|
||||
if err != nil {
|
||||
|
||||
if err := database.Delete(checkin); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
checkin.Delete()
|
||||
|
||||
sendJsonAction(checkin, "delete", w, r)
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -183,7 +184,7 @@ func removeJwtToken(w http.ResponseWriter) {
|
|||
})
|
||||
}
|
||||
|
||||
func setJwtToken(user *core.User, w http.ResponseWriter) (JwtClaim, string) {
|
||||
func setJwtToken(user *types.User, w http.ResponseWriter) (JwtClaim, string) {
|
||||
expirationTime := time.Now().Add(72 * time.Hour)
|
||||
jwtClaim := JwtClaim{
|
||||
Username: user.Username,
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
@ -32,11 +33,19 @@ func apiAllGroupHandler(r *http.Request) interface{} {
|
|||
return groups
|
||||
}
|
||||
|
||||
func flattenGroups(groups []*core.Group) []*types.Group {
|
||||
var groupers []*types.Group
|
||||
for _, g := range groups {
|
||||
groupers = append(groupers, g.Group)
|
||||
}
|
||||
return groupers
|
||||
}
|
||||
|
||||
// apiGroupHandler will show a single group
|
||||
func apiGroupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
group := core.SelectGroup(utils.ToInt(vars["id"]))
|
||||
if group == nil {
|
||||
if group.Id == 0 {
|
||||
sendErrorJson(errors.New("group not found"), w, r)
|
||||
return
|
||||
}
|
||||
|
@ -47,7 +56,7 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
func apiGroupUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
group := core.SelectGroup(utils.ToInt(vars["id"]))
|
||||
if group == nil {
|
||||
if group.Id == 0 {
|
||||
sendErrorJson(errors.New("group not found"), w, r)
|
||||
return
|
||||
}
|
||||
|
@ -82,7 +91,7 @@ func apiCreateGroupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
func apiGroupDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
group := core.SelectGroup(utils.ToInt(vars["id"]))
|
||||
if group == nil {
|
||||
if group.Id == 0 {
|
||||
sendErrorJson(errors.New("group not found"), w, r)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ func prometheusHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if !v.Online {
|
||||
online = 0
|
||||
}
|
||||
met := fmt.Sprintf("statping_service_failures{id=\"%v\" name=\"%v\"} %v\n", v.Id, v.Name, len(v.Failures))
|
||||
met := fmt.Sprintf("statping_service_failures{id=\"%v\" name=\"%v\"} %v\n", v.Id, v.Name, v.Failures().Count())
|
||||
met += fmt.Sprintf("statping_service_latency{id=\"%v\" name=\"%v\"} %0.0f\n", v.Id, v.Name, (v.Latency * 100))
|
||||
met += fmt.Sprintf("statping_service_online{id=\"%v\" name=\"%v\"} %v\n", v.Id, v.Name, online)
|
||||
met += fmt.Sprintf("statping_service_status_code{id=\"%v\" name=\"%v\"} %v\n", v.Id, v.Name, v.LastStatusCode)
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -41,15 +40,18 @@ func Router() *mux.Router {
|
|||
CacheStorage = NewStorage()
|
||||
r := mux.NewRouter().StrictSlash(true)
|
||||
|
||||
if os.Getenv("AUTH_USERNAME") != "" && os.Getenv("AUTH_PASSWORD") != "" {
|
||||
authUser = os.Getenv("AUTH_USERNAME")
|
||||
authPass = os.Getenv("AUTH_PASSWORD")
|
||||
authUser := utils.Getenv("AUTH_USERNAME", "").(string)
|
||||
authPass := utils.Getenv("AUTH_PASSWORD", "").(string)
|
||||
|
||||
if authUser != "" && authPass != "" {
|
||||
r.Use(basicAuthHandler)
|
||||
}
|
||||
|
||||
if os.Getenv("BASE_PATH") != "" {
|
||||
basePath = "/" + os.Getenv("BASE_PATH") + "/"
|
||||
r = r.PathPrefix("/" + os.Getenv("BASE_PATH")).Subrouter()
|
||||
bPath := utils.Getenv("BASE_PATH", "").(string)
|
||||
|
||||
if bPath != "" {
|
||||
basePath = "/" + bPath + "/"
|
||||
r = r.PathPrefix("/" + bPath).Subrouter()
|
||||
r.Handle("", http.HandlerFunc(indexHandler))
|
||||
} else {
|
||||
r.Handle("/", http.HandlerFunc(indexHandler))
|
||||
|
|
|
@ -30,35 +30,37 @@ type scope struct {
|
|||
func (s scope) MarshalJSON() ([]byte, error) {
|
||||
svc := reflect.ValueOf(s.data)
|
||||
if svc.Kind() == reflect.Slice {
|
||||
alldata := make([]map[string]interface{}, 0)
|
||||
alldata := make([]map[string]interface{}, svc.Len())
|
||||
for i := 0; i < svc.Len(); i++ {
|
||||
objIndex := svc.Index(i)
|
||||
if objIndex.Kind() == reflect.Ptr {
|
||||
objIndex = objIndex.Elem()
|
||||
}
|
||||
alldata = append(alldata, SafeJson(objIndex.Interface(), s.scope))
|
||||
alldata[i] = SafeJson(objIndex, s.scope)
|
||||
}
|
||||
return json.Marshal(alldata)
|
||||
}
|
||||
return json.Marshal(SafeJson(svc.Interface(), s.scope))
|
||||
return json.Marshal(SafeJson(svc, s.scope))
|
||||
}
|
||||
|
||||
func SafeJson(input interface{}, scope string) map[string]interface{} {
|
||||
func SafeJson(val reflect.Value, scope string) map[string]interface{} {
|
||||
thisData := make(map[string]interface{})
|
||||
t := reflect.TypeOf(input)
|
||||
elem := reflect.ValueOf(input)
|
||||
d, _ := json.Marshal(input)
|
||||
if val.Kind() == reflect.Interface && !val.IsNil() {
|
||||
elm := val.Elem()
|
||||
if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
|
||||
val = elm
|
||||
}
|
||||
}
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
var raw map[string]*json.RawMessage
|
||||
json.Unmarshal(d, &raw)
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
valueField := val.Field(i)
|
||||
typeField := val.Type().Field(i)
|
||||
tagVal := typeField.Tag
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
|
||||
tag := field.Tag.Get("scope")
|
||||
tag := tagVal.Get("scope")
|
||||
tags := strings.Split(tag, ",")
|
||||
|
||||
jTags := field.Tag.Get("json")
|
||||
jTags := tagVal.Get("json")
|
||||
jsonTag := strings.Split(jTags, ",")
|
||||
|
||||
if len(jsonTag) == 0 {
|
||||
|
@ -69,21 +71,19 @@ func SafeJson(input interface{}, scope string) map[string]interface{} {
|
|||
continue
|
||||
}
|
||||
|
||||
trueValue := elem.Field(i).Interface()
|
||||
|
||||
if len(jsonTag) == 2 {
|
||||
if jsonTag[1] == "omitempty" && trueValue == "" {
|
||||
if jsonTag[1] == "omitempty" && valueField.Interface() == "" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if tag == "" {
|
||||
thisData[jsonTag[0]] = trueValue
|
||||
thisData[jsonTag[0]] = valueField.Interface()
|
||||
continue
|
||||
}
|
||||
|
||||
if forTag(tags, scope) {
|
||||
thisData[jsonTag[0]] = trueValue
|
||||
thisData[jsonTag[0]] = valueField.Interface()
|
||||
}
|
||||
}
|
||||
return thisData
|
||||
|
|
|
@ -46,11 +46,11 @@ func reorderServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func apiServiceHandler(r *http.Request) interface{} {
|
||||
vars := mux.Vars(r)
|
||||
servicer := core.SelectService(utils.ToInt(vars["id"])).Model()
|
||||
servicer := core.SelectService(utils.ToInt(vars["id"]))
|
||||
if servicer == nil {
|
||||
return errors.New("service not found")
|
||||
}
|
||||
return *servicer
|
||||
return servicer.Service
|
||||
}
|
||||
|
||||
func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -130,8 +130,12 @@ func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
groupQuery := database.ParseQueries(r, service.Hits())
|
||||
|
||||
obj := core.GraphData(groupQuery, &types.Hit{}, database.ByAverage("latency"))
|
||||
returnJson(obj, w, r)
|
||||
objs, err := groupQuery.GraphData(database.ByAverage("latency"))
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
returnJson(objs, w, r)
|
||||
}
|
||||
|
||||
func apiServiceFailureDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -141,10 +145,16 @@ func apiServiceFailureDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(errors.New("service data not found"), w, r)
|
||||
return
|
||||
}
|
||||
groupQuery := database.ParseQueries(r, service.Hits())
|
||||
|
||||
obj := core.GraphData(groupQuery, &types.Failure{}, database.ByCount)
|
||||
returnJson(obj, w, r)
|
||||
groupQuery := database.ParseQueries(r, service.Failures())
|
||||
|
||||
objs, err := groupQuery.GraphData(database.ByCount)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
returnJson(objs, w, r)
|
||||
}
|
||||
|
||||
func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -154,10 +164,16 @@ func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(errors.New("service data not found"), w, r)
|
||||
return
|
||||
}
|
||||
|
||||
groupQuery := database.ParseQueries(r, service.Hits())
|
||||
|
||||
obj := core.GraphData(groupQuery, &types.Hit{}, database.ByAverage("ping_time"))
|
||||
returnJson(obj, w, r)
|
||||
objs, err := groupQuery.GraphData(database.ByAverage("ping_time"))
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
returnJson(objs, w, r)
|
||||
}
|
||||
|
||||
type dataXy struct {
|
||||
|
@ -190,10 +206,11 @@ func apiAllServicesHandler(r *http.Request) interface{} {
|
|||
return joinServices(services)
|
||||
}
|
||||
|
||||
func joinServices(srvs []database.Servicer) []*types.Service {
|
||||
func joinServices(srvs []*core.Service) []*types.Service {
|
||||
var services []*types.Service
|
||||
for _, v := range srvs {
|
||||
services = append(services, v.Model())
|
||||
v.UpdateStats()
|
||||
services = append(services, v.Service)
|
||||
}
|
||||
return services
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sample, _ := strconv.ParseBool(r.PostForm.Get("sample_data"))
|
||||
dir := utils.Directory
|
||||
|
||||
config := &types.DbConfig{
|
||||
config := &core.DbConfig{DbConfig: &types.DbConfig{
|
||||
DbConn: dbConn,
|
||||
DbHost: dbHost,
|
||||
DbUser: dbUser,
|
||||
|
@ -67,11 +67,11 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Email: email,
|
||||
Error: nil,
|
||||
Location: utils.Directory,
|
||||
}
|
||||
}}
|
||||
|
||||
log.WithFields(utils.ToFields(core.CoreApp, config)).Debugln("new configs posted")
|
||||
|
||||
if _, err := core.CoreApp.SaveConfig(config); err != nil {
|
||||
if err := config.Save(); err != nil {
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
@ -100,7 +100,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
core.CoreApp, err = core.CoreApp.InsertCore(config)
|
||||
core.CoreApp, err = config.InsertCore()
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
|
@ -130,7 +130,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Config *types.DbConfig `json:"config"`
|
||||
}{
|
||||
"okokok",
|
||||
config,
|
||||
config.DbConfig,
|
||||
}
|
||||
returnJson(out, w, r)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/hunterlong/statping/core/notifier"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
|
@ -80,5 +81,5 @@ func injectDatabase() {
|
|||
panic(err)
|
||||
}
|
||||
db.CreateTable(¬ifier.Notification{})
|
||||
notifier.SetDB(&types.Db{db})
|
||||
notifier.SetDB(&database.Db{db, "sqlite3"})
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
// Package plugin contains the interfaces to build your own Golang Plugin that will receive triggers on Statping events.
|
||||
package plugin
|
116
plugin/plugin.go
116
plugin/plugin.go
|
@ -1,116 +0,0 @@
|
|||
// Statping
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statping
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"plugin"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//
|
||||
// STATPING PLUGIN INTERFACE
|
||||
//
|
||||
// v0.1
|
||||
//
|
||||
// https://statping.com
|
||||
//
|
||||
//
|
||||
// An expandable plugin framework that will still
|
||||
// work even if there's an update or addition.
|
||||
//
|
||||
|
||||
var (
|
||||
AllPlugins []*types.PluginObject
|
||||
dir string
|
||||
log = utils.Log.WithField("type", "plugin")
|
||||
)
|
||||
|
||||
func init() {
|
||||
utils.InitLogs()
|
||||
dir = utils.Directory
|
||||
}
|
||||
|
||||
func LoadPlugin(file string) error {
|
||||
log.Infoln(fmt.Sprintf("opening file %v", file))
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fSplit := strings.Split(f.Name(), "/")
|
||||
fileBin := fSplit[len(fSplit)-1]
|
||||
|
||||
log.Infoln(fmt.Sprintf("Attempting to load plugin '%v'", fileBin))
|
||||
ext := strings.Split(fileBin, ".")
|
||||
if len(ext) != 2 {
|
||||
log.Errorln(fmt.Sprintf("Plugin '%v' must end in .so extension", fileBin))
|
||||
return fmt.Errorf("Plugin '%v' must end in .so extension %v", fileBin, len(ext))
|
||||
}
|
||||
if ext[1] != "so" {
|
||||
log.Errorln(fmt.Sprintf("Plugin '%v' must end in .so extension", fileBin))
|
||||
return fmt.Errorf("Plugin '%v' must end in .so extension", fileBin)
|
||||
}
|
||||
plug, err := plugin.Open(file)
|
||||
if err != nil {
|
||||
log.Errorln(fmt.Sprintf("Plugin '%v' could not load correctly. %v", fileBin, err))
|
||||
return err
|
||||
}
|
||||
symPlugin, err := plug.Lookup("Plugin")
|
||||
if err != nil {
|
||||
log.Errorln(fmt.Sprintf("Plugin '%v' could not locate Plugin variable. %v", fileBin, err))
|
||||
return err
|
||||
}
|
||||
plugActions, ok := symPlugin.(types.PluginActions)
|
||||
if !ok {
|
||||
log.Errorln(fmt.Sprintf("Plugin %v was not type PluginObject", f.Name()))
|
||||
return fmt.Errorf("Plugin %v was not type PluginActions %v", f.Name(), plugActions.GetInfo())
|
||||
}
|
||||
info := plugActions.GetInfo()
|
||||
err = plugActions.OnLoad()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infoln(fmt.Sprintf("Plugin %v loaded from %v", info.Name, f.Name()))
|
||||
core.CoreApp.AllPlugins = append(core.CoreApp.AllPlugins, plugActions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadPlugins() {
|
||||
pluginDir := dir + "/plugins"
|
||||
log.Infoln(fmt.Sprintf("Loading any available Plugins from /plugins directory"))
|
||||
if _, err := os.Stat(pluginDir); os.IsNotExist(err) {
|
||||
os.Mkdir(pluginDir, os.ModePerm)
|
||||
}
|
||||
files, err := ioutil.ReadDir(pluginDir)
|
||||
if err != nil {
|
||||
log.Warnln(fmt.Sprintf("Plugins directory was not found. Error: %v", err))
|
||||
return
|
||||
}
|
||||
for _, f := range files {
|
||||
err := LoadPlugin(f.Name())
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
log.Infoln(fmt.Sprintf("Loaded %v Plugins", len(core.CoreApp.Plugins)))
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
// Statping
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statping
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
example types.PluginActions
|
||||
)
|
||||
|
||||
func init() {
|
||||
utils.InitLogs()
|
||||
source.Assets()
|
||||
}
|
||||
|
||||
func TestLoadPlugin(t *testing.T) {
|
||||
//err := LoadPlugin(dir+"/plugins/example.so")
|
||||
//assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
//err := Add(example)
|
||||
//assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestSelect(t *testing.T) {
|
||||
//err := example.GetInfo()
|
||||
//assert.Equal(t, "", err.Name)
|
||||
}
|
||||
|
||||
//func TestAddRoute(t *testing.T) {
|
||||
// example.AddRoute("/plugin_example", "GET", setupHandler)
|
||||
//}
|
|
@ -80,7 +80,9 @@ func UsingAssets(folder string) bool {
|
|||
if _, err := os.Stat(folder + "/assets"); err == nil {
|
||||
return true
|
||||
} else {
|
||||
if os.Getenv("USE_ASSETS") == "true" {
|
||||
useAssets := utils.Getenv("USE_ASSETS", false).(bool)
|
||||
|
||||
if useAssets {
|
||||
log.Infoln("Environment variable USE_ASSETS was found.")
|
||||
if err := CreateAllAssets(folder); err != nil {
|
||||
log.Warnln(err)
|
||||
|
|
|
@ -36,15 +36,6 @@ type Checkin struct {
|
|||
Failures []*Failure `gorm:"-" json:"failures"`
|
||||
}
|
||||
|
||||
// BeforeCreate for Checkin will set CreatedAt to UTC
|
||||
func (c *Checkin) BeforeCreate() (err error) {
|
||||
if c.CreatedAt.IsZero() {
|
||||
c.CreatedAt = time.Now().UTC()
|
||||
c.UpdatedAt = time.Now().UTC()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CheckinHit is a successful response from a Checkin
|
||||
type CheckinHit struct {
|
||||
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
||||
|
|
|
@ -47,13 +47,15 @@ type Core struct {
|
|||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
Started time.Time `gorm:"-" json:"started_on"`
|
||||
Plugins []*Info `gorm:"-" json:"-"`
|
||||
Repos []PluginJSON `gorm:"-" json:"-"`
|
||||
AllPlugins []PluginActions `gorm:"-" json:"-"`
|
||||
Notifications []AllNotifiers `gorm:"-" json:"-"`
|
||||
Config *DbConfig `gorm:"-" json:"-"`
|
||||
Integrations []Integrator `gorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
func (Core) TableName() string {
|
||||
return "core"
|
||||
}
|
||||
|
||||
type Servicer interface {
|
||||
Model() *Service
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorServiceSelection = returnErr("error selecting services")
|
||||
|
||||
// create errors
|
||||
ErrorCreateService = returnErr("error creating service")
|
||||
ErrorCreateMessage = returnErr("error creating messages")
|
||||
ErrorCreateIncident = returnErr("error creating incident")
|
||||
ErrorCreateUser = returnErr("error creating user")
|
||||
ErrorCreateIncidentUp = returnErr("error creating incident update")
|
||||
ErrorCreateGroup = returnErr("error creating group")
|
||||
ErrorCreateCheckinHit = returnErr("error creating checkin hit")
|
||||
ErrorCreateSampleHits = returnErr("error creating sample hits")
|
||||
ErrorCreateCore = returnErr("error creating core")
|
||||
ErrorCreateHit = returnErr("error creating hit for service %v")
|
||||
|
||||
ErrorDirCreate = returnErr("error creating directory %s")
|
||||
|
||||
ErrorFileCopy = returnErr("error copying file %s to %s")
|
||||
|
||||
ErrorConfig = returnErr("error with configuration")
|
||||
ErrorConnection = returnErr("error with connection")
|
||||
|
||||
ErrorNotFound = returnErrCode("item was not found", http.StatusNotFound)
|
||||
ErrorJSONParse = returnErrCode("could not parse JSON request", http.StatusBadRequest)
|
||||
)
|
||||
|
||||
type Errorer interface {
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
err error
|
||||
code int
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
func (e Error) String() string {
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
func returnErrCode(str string, code int) error {
|
||||
return Error{
|
||||
err: errors.New(str),
|
||||
code: code,
|
||||
}
|
||||
}
|
||||
|
||||
func returnErr(str string) Error {
|
||||
return Error{
|
||||
err: errors.New(str),
|
||||
}
|
||||
}
|
||||
|
||||
func convertError(val interface{}) string {
|
||||
switch v := val.(type) {
|
||||
case *Error:
|
||||
return v.Error()
|
||||
case string:
|
||||
return v
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type errorer interface {
|
||||
Error() string
|
||||
}
|
||||
|
||||
func ErrWrap(err errorer, format interface{}, args ...interface{}) Error {
|
||||
return Error{
|
||||
err: errors.Wrapf(err, convertError(format), args...),
|
||||
code: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func Err(err errorer, format interface{}) Error {
|
||||
return Error{
|
||||
err: errors.Wrap(err, convertError(format)),
|
||||
code: 0,
|
||||
}
|
||||
}
|
|
@ -58,8 +58,8 @@ type Service struct {
|
|||
SuccessNotified bool `gorm:"-" json:"-"` // Is 'true' if the user has already be informed that the Services now again available
|
||||
LastStatusCode int `gorm:"-" json:"status_code"`
|
||||
LastOnline time.Time `gorm:"-" json:"last_success"`
|
||||
Failures []Failure `gorm:"-" json:"failures,omitempty" scope:"user,admin"`
|
||||
Checkins []CheckinInterface `gorm:"-" json:"checkins,omitempty" scope:"user,admin"`
|
||||
Failures []*Failure `gorm:"-" json:"failures,omitempty" scope:"user,admin"`
|
||||
Checkins []*Checkin `gorm:"-" json:"checkins,omitempty" scope:"user,admin"`
|
||||
Stats *Stats `gorm:"-" json:"stats,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -72,8 +72,8 @@ type Stater interface {
|
|||
}
|
||||
|
||||
type Stats struct {
|
||||
Failures uint64 `gorm:"-" json:"failures,omitempty"`
|
||||
Hits uint64 `gorm:"-" json:"hits,omitempty"`
|
||||
Failures int `gorm:"-" json:"failures"`
|
||||
Hits int `gorm:"-" json:"hits"`
|
||||
}
|
||||
|
||||
// BeforeCreate for Service will set CreatedAt to UTC
|
||||
|
@ -85,6 +85,10 @@ func (s *Service) BeforeCreate() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (s *Service) Duration() time.Duration {
|
||||
return time.Duration(s.Interval) * time.Second
|
||||
}
|
||||
|
||||
// Start will create a channel for the service checking go routine
|
||||
func (s *Service) Start() {
|
||||
s.Running = make(chan bool)
|
||||
|
|
|
@ -44,29 +44,54 @@ var (
|
|||
|
||||
// init will set the utils.Directory to the current running directory, or STATPING_DIR if it is set
|
||||
func init() {
|
||||
if os.Getenv("STATPING_DIR") != "" {
|
||||
Directory = os.Getenv("STATPING_DIR")
|
||||
} else {
|
||||
dir, err := os.Getwd()
|
||||
defaultDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
Directory = "."
|
||||
return
|
||||
}
|
||||
Directory = dir
|
||||
defaultDir = "."
|
||||
}
|
||||
|
||||
Directory = Getenv("STATPING_DIR", defaultDir).(string)
|
||||
|
||||
// check if logs are disabled
|
||||
logger := os.Getenv("DISABLE_LOGS")
|
||||
disableLogs, _ = strconv.ParseBool(logger)
|
||||
disableLogs = Getenv("DISABLE_LOGS", false).(bool)
|
||||
if disableLogs {
|
||||
Log.Out = ioutil.Discard
|
||||
return
|
||||
}
|
||||
|
||||
Log.Debugln("current working directory: ", Directory)
|
||||
Log.AddHook(new(hook))
|
||||
Log.SetNoLock()
|
||||
checkVerboseMode()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func SliceConvert(g []*interface{}) []interface{} {
|
||||
var arr []interface{}
|
||||
for _, v := range g {
|
||||
|
|
Loading…
Reference in New Issue