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"
|
"fmt"
|
||||||
"github.com/hunterlong/statping/core"
|
"github.com/hunterlong/statping/core"
|
||||||
"github.com/hunterlong/statping/handlers"
|
"github.com/hunterlong/statping/handlers"
|
||||||
"github.com/hunterlong/statping/plugin"
|
|
||||||
"github.com/hunterlong/statping/source"
|
"github.com/hunterlong/statping/source"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
|
@ -72,13 +71,6 @@ func catchCLI(args []string) error {
|
||||||
case "update":
|
case "update":
|
||||||
updateDisplay()
|
updateDisplay()
|
||||||
return errors.New("end")
|
return errors.New("end")
|
||||||
case "test":
|
|
||||||
cmd := args[1]
|
|
||||||
switch cmd {
|
|
||||||
case "plugins":
|
|
||||||
plugin.LoadPlugins()
|
|
||||||
}
|
|
||||||
return errors.New("end")
|
|
||||||
case "static":
|
case "static":
|
||||||
var err error
|
var err error
|
||||||
if err = runLogs(); err != nil {
|
if err = runLogs(); err != nil {
|
||||||
|
@ -88,7 +80,7 @@ func catchCLI(args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("Statping v%v Exporting Static 'index.html' page...\n", VERSION)
|
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")
|
log.Errorln("config.yml file not found")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -111,7 +103,7 @@ func catchCLI(args []string) error {
|
||||||
if err = runAssets(); err != nil {
|
if err = runAssets(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if core.CoreApp.Config, err = core.LoadConfigFile(dir); err != nil {
|
if _, err = core.LoadConfigFile(dir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = core.CoreApp.Connect(false, dir); err != nil {
|
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
|
// runOnce will initialize the Statping application and check each service 1 time, will not run HTTP server
|
||||||
func runOnce() {
|
func runOnce() {
|
||||||
var err error
|
var err error
|
||||||
core.CoreApp.Config, err = core.LoadConfigFile(utils.Directory)
|
_, err = core.LoadConfigFile(utils.Directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("config.yml file not found")
|
log.Errorln("config.yml file not found")
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ func TestExportCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateCommand(t *testing.T) {
|
func TestUpdateCommand(t *testing.T) {
|
||||||
|
t.SkipNow()
|
||||||
cmd := helperCommand(nil, "version")
|
cmd := helperCommand(nil, "version")
|
||||||
var got = make(chan string)
|
var got = make(chan string)
|
||||||
commandAndSleep(cmd, time.Duration(15*time.Second), got)
|
commandAndSleep(cmd, time.Duration(15*time.Second), got)
|
||||||
|
@ -93,6 +94,7 @@ func TestUpdateCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetsCommand(t *testing.T) {
|
func TestAssetsCommand(t *testing.T) {
|
||||||
|
t.SkipNow()
|
||||||
c := testcli.Command("statping", "assets")
|
c := testcli.Command("statping", "assets")
|
||||||
c.Run()
|
c.Run()
|
||||||
t.Log(c.Stdout())
|
t.Log(c.Stdout())
|
||||||
|
|
39
cmd/main.go
39
cmd/main.go
|
@ -22,7 +22,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statping/core"
|
"github.com/hunterlong/statping/core"
|
||||||
"github.com/hunterlong/statping/handlers"
|
"github.com/hunterlong/statping/handlers"
|
||||||
"github.com/hunterlong/statping/plugin"
|
|
||||||
"github.com/hunterlong/statping/source"
|
"github.com/hunterlong/statping/source"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"os"
|
"os"
|
||||||
|
@ -49,22 +48,17 @@ func init() {
|
||||||
// parseFlags will parse the application flags
|
// parseFlags will parse the application flags
|
||||||
// -ip = 0.0.0.0 IP address for outgoing HTTP server
|
// -ip = 0.0.0.0 IP address for outgoing HTTP server
|
||||||
// -port = 8080 Port number for outgoing HTTP server
|
// -port = 8080 Port number for outgoing HTTP server
|
||||||
|
// environment variables WILL overwrite flags
|
||||||
func parseFlags() {
|
func parseFlags() {
|
||||||
flag.StringVar(&ipAddress, "ip", "0.0.0.0", "IP address to run the Statping HTTP server")
|
envPort := utils.Getenv("PORT", 8080).(int)
|
||||||
flag.StringVar(&envFile, "env", "", "IP address to run the Statping HTTP server")
|
envIpAddress := utils.Getenv("IP", "0.0.0.0").(string)
|
||||||
flag.IntVar(&port, "port", 8080, "Port to run the HTTP server")
|
envVerbose := utils.Getenv("VERBOSE", 2).(int)
|
||||||
flag.IntVar(&verboseMode, "verbose", 2, "Run in verbose mode to see detailed logs (1 - 4)")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if os.Getenv("PORT") != "" {
|
flag.StringVar(&ipAddress, "ip", envIpAddress, "IP address to run the Statping HTTP server")
|
||||||
port = int(utils.ToInt(os.Getenv("PORT")))
|
flag.StringVar(&envFile, "env", "", "IP address to run the Statping HTTP server")
|
||||||
}
|
flag.IntVar(&port, "port", envPort, "Port to run the HTTP server")
|
||||||
if os.Getenv("IP") != "" {
|
flag.IntVar(&verboseMode, "verbose", envVerbose, "Run in verbose mode to see detailed logs (1 - 4)")
|
||||||
ipAddress = os.Getenv("IP")
|
flag.Parse()
|
||||||
}
|
|
||||||
if os.Getenv("VERBOSE") != "" {
|
|
||||||
verboseMode = int(utils.ToInt(os.Getenv("VERBOSE")))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// main will run the Statping application
|
// main will run the Statping application
|
||||||
|
@ -93,7 +87,7 @@ func main() {
|
||||||
log.Info(fmt.Sprintf("Starting Statping v%v", VERSION))
|
log.Info(fmt.Sprintf("Starting Statping v%v", VERSION))
|
||||||
updateDisplay()
|
updateDisplay()
|
||||||
|
|
||||||
configs, err := core.LoadConfigFile(utils.Directory)
|
_, err = core.LoadConfigFile(utils.Directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
core.CoreApp.Setup = false
|
core.CoreApp.Setup = false
|
||||||
|
@ -108,16 +102,16 @@ func main() {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.CoreApp.Config = configs
|
|
||||||
if err := mainProcess(); err != nil {
|
if err := mainProcess(); err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close will gracefully stop the database connection, and log file
|
// Close will gracefully stop the database connection, and log file
|
||||||
func Close() {
|
func Close() {
|
||||||
core.CloseDB()
|
|
||||||
utils.CloseLogs()
|
utils.CloseLogs()
|
||||||
|
core.CloseDB()
|
||||||
}
|
}
|
||||||
|
|
||||||
// sigterm will attempt to close the database connections gracefully
|
// 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))
|
log.Errorln(fmt.Sprintf("could not connect to database: %v", err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
core.CoreApp.MigrateDatabase()
|
if err := core.CoreApp.MigrateDatabase(); err != nil {
|
||||||
core.InitApp()
|
return err
|
||||||
|
}
|
||||||
|
if err := core.InitApp(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if core.CoreApp.Setup {
|
if core.CoreApp.Setup {
|
||||||
plugin.LoadPlugins()
|
|
||||||
if err := handlers.RunHTTPServer(ipAddress, port); err != nil {
|
if err := handlers.RunHTTPServer(ipAddress, port); err != nil {
|
||||||
log.Fatalln(err)
|
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
|
// checkServices will start the checking go routine for each service
|
||||||
func checkServices() {
|
func checkServices() {
|
||||||
log.Infoln(fmt.Sprintf("Starting monitoring process for %v Services", len(CoreApp.services)))
|
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 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
|
// 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.
|
// if record param is set to true, it will add a record into the database.
|
||||||
func CheckService(srv database.Servicer, record bool) {
|
func CheckService(srv *Service, record bool) {
|
||||||
switch srv.Model().Type {
|
switch srv.Type {
|
||||||
case "http":
|
case "http":
|
||||||
CheckHttp(srv, record)
|
CheckHttp(srv, record)
|
||||||
case "tcp", "udp":
|
case "tcp", "udp":
|
||||||
|
@ -54,8 +55,7 @@ func CheckService(srv database.Servicer, record bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckQueue is the main go routine for checking a service
|
// CheckQueue is the main go routine for checking a service
|
||||||
func ServiceCheckQueue(srv database.Servicer, record bool) {
|
func ServiceCheckQueue(s *Service, record bool) {
|
||||||
s := srv.Model()
|
|
||||||
s.Checkpoint = time.Now()
|
s.Checkpoint = time.Now()
|
||||||
s.SleepDuration = (time.Duration(s.Id) * 100) * time.Millisecond
|
s.SleepDuration = (time.Duration(s.Id) * 100) * time.Millisecond
|
||||||
CheckLoop:
|
CheckLoop:
|
||||||
|
@ -65,11 +65,11 @@ CheckLoop:
|
||||||
log.Infoln(fmt.Sprintf("Stopping service: %v", s.Name))
|
log.Infoln(fmt.Sprintf("Stopping service: %v", s.Name))
|
||||||
break CheckLoop
|
break CheckLoop
|
||||||
case <-time.After(s.SleepDuration):
|
case <-time.After(s.SleepDuration):
|
||||||
CheckService(srv, record)
|
CheckService(s, record)
|
||||||
s.Checkpoint = s.Checkpoint.Add(srv.Interval())
|
s.Checkpoint = s.Checkpoint.Add(s.Duration())
|
||||||
sleep := s.Checkpoint.Sub(time.Now())
|
sleep := s.Checkpoint.Sub(time.Now())
|
||||||
if !s.Online {
|
if !s.Online {
|
||||||
s.SleepDuration = srv.Interval()
|
s.SleepDuration = s.Duration()
|
||||||
} else {
|
} else {
|
||||||
s.SleepDuration = sleep
|
s.SleepDuration = sleep
|
||||||
}
|
}
|
||||||
|
@ -78,19 +78,7 @@ CheckLoop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// duration returns the amount of duration for a service to check its status
|
func parseHost(s *Service) string {
|
||||||
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()
|
|
||||||
if s.Type == "tcp" || s.Type == "udp" {
|
if s.Type == "tcp" || s.Type == "udp" {
|
||||||
return s.Domain
|
return s.Domain
|
||||||
} else {
|
} 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
|
// 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) {
|
func dnsCheck(s *Service) (float64, error) {
|
||||||
s := srv.Model()
|
|
||||||
var err error
|
var err error
|
||||||
t1 := time.Now()
|
t1 := time.Now()
|
||||||
host := parseHost(srv)
|
host := parseHost(s)
|
||||||
if s.Type == "tcp" {
|
if s.Type == "tcp" {
|
||||||
_, err = net.LookupHost(host)
|
_, err = net.LookupHost(host)
|
||||||
} else {
|
} else {
|
||||||
|
@ -126,8 +113,7 @@ func isIPv6(address string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkIcmp will send a ICMP ping packet to the service
|
// checkIcmp will send a ICMP ping packet to the service
|
||||||
func CheckIcmp(srv database.Servicer, record bool) *types.Service {
|
func CheckIcmp(s *Service, record bool) *types.Service {
|
||||||
s := srv.Model()
|
|
||||||
p := fastping.NewPinger()
|
p := fastping.NewPinger()
|
||||||
resolveIP := "ip4:icmp"
|
resolveIP := "ip4:icmp"
|
||||||
if isIPv6(s.Domain) {
|
if isIPv6(s.Domain) {
|
||||||
|
@ -135,8 +121,8 @@ func CheckIcmp(srv database.Servicer, record bool) *types.Service {
|
||||||
}
|
}
|
||||||
ra, err := net.ResolveIPAddr(resolveIP, s.Domain)
|
ra, err := net.ResolveIPAddr(resolveIP, s.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordFailure(srv, fmt.Sprintf("Could not send ICMP to service %v, %v", s.Domain, err))
|
recordFailure(s, fmt.Sprintf("Could not send ICMP to service %v, %v", s.Domain, err))
|
||||||
return s
|
return s.Service
|
||||||
}
|
}
|
||||||
p.AddIPAddr(ra)
|
p.AddIPAddr(ra)
|
||||||
p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
|
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()
|
err = p.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recordFailure(srv, fmt.Sprintf("Issue running ICMP to service %v, %v", s.Domain, err))
|
recordFailure(s, fmt.Sprintf("Issue running ICMP to service %v, %v", s.Domain, err))
|
||||||
return s
|
return s.Service
|
||||||
}
|
}
|
||||||
s.LastResponse = ""
|
s.LastResponse = ""
|
||||||
return s
|
return s.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkTcp will check a TCP service
|
// checkTcp will check a TCP service
|
||||||
func CheckTcp(srv database.Servicer, record bool) *types.Service {
|
func CheckTcp(s *Service, record bool) *types.Service {
|
||||||
s := srv.Model()
|
dnsLookup, err := dnsCheck(s)
|
||||||
dnsLookup, err := dnsCheck(srv)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if record {
|
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
|
s.PingTime = dnsLookup
|
||||||
t1 := time.Now()
|
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)
|
conn, err := net.DialTimeout(s.Type, domain, time.Duration(s.Timeout)*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if record {
|
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 err := conn.Close(); err != nil {
|
||||||
if record {
|
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()
|
t2 := time.Now()
|
||||||
s.Latency = t2.Sub(t1).Seconds()
|
s.Latency = t2.Sub(t1).Seconds()
|
||||||
|
@ -190,18 +175,17 @@ func CheckTcp(srv database.Servicer, record bool) *types.Service {
|
||||||
if record {
|
if record {
|
||||||
recordSuccess(s)
|
recordSuccess(s)
|
||||||
}
|
}
|
||||||
return s
|
return s.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkHttp will check a HTTP service
|
// checkHttp will check a HTTP service
|
||||||
func CheckHttp(srv database.Servicer, record bool) *types.Service {
|
func CheckHttp(s *Service, record bool) *types.Service {
|
||||||
s := srv.Model()
|
dnsLookup, err := dnsCheck(s)
|
||||||
dnsLookup, err := dnsCheck(srv)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if record {
|
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
|
s.PingTime = dnsLookup
|
||||||
t1 := time.Now()
|
t1 := time.Now()
|
||||||
|
@ -224,9 +208,9 @@ func CheckHttp(srv database.Servicer, record bool) *types.Service {
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if record {
|
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()
|
t2 := time.Now()
|
||||||
s.Latency = t2.Sub(t1).Seconds()
|
s.Latency = t2.Sub(t1).Seconds()
|
||||||
|
@ -240,25 +224,25 @@ func CheckHttp(srv database.Servicer, record bool) *types.Service {
|
||||||
}
|
}
|
||||||
if !match {
|
if !match {
|
||||||
if record {
|
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 s.ExpectedStatus != res.StatusCode {
|
||||||
if record {
|
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 {
|
if record {
|
||||||
recordSuccess(s)
|
recordSuccess(s)
|
||||||
}
|
}
|
||||||
return s
|
return s.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// recordSuccess will create a new 'hit' record in the database for a successful/online 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()
|
s.LastOnline = time.Now().UTC()
|
||||||
hit := &types.Hit{
|
hit := &types.Hit{
|
||||||
Service: s.Id,
|
Service: s.Id,
|
||||||
|
@ -268,14 +252,13 @@ func recordSuccess(s *types.Service) {
|
||||||
}
|
}
|
||||||
database.Create(hit)
|
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))
|
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.Online = true
|
||||||
s.SuccessNotified = true
|
s.SuccessNotified = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// recordFailure will create a new 'Failure' record in the database for a offline service
|
// recordFailure will create a new 'Failure' record in the database for a offline service
|
||||||
func recordFailure(srv database.Servicer, issue string) {
|
func recordFailure(s *Service, issue string) {
|
||||||
s := srv.Model()
|
|
||||||
fail := &types.Failure{
|
fail := &types.Failure{
|
||||||
Service: s.Id,
|
Service: s.Id,
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
|
@ -288,6 +271,6 @@ func recordFailure(srv database.Servicer, issue string) {
|
||||||
database.Create(fail)
|
database.Create(fail)
|
||||||
s.Online = false
|
s.Online = false
|
||||||
s.SuccessNotified = false
|
s.SuccessNotified = false
|
||||||
s.DownText = srv.DowntimeText()
|
s.DownText = s.DowntimeText()
|
||||||
notifier.OnFailure(s, fail)
|
notifier.OnFailure(s.Service, fail)
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ func AllCheckins() []*database.CheckinObj {
|
||||||
// SelectCheckin will find a Checkin based on the API supplied
|
// SelectCheckin will find a Checkin based on the API supplied
|
||||||
func SelectCheckin(api string) *Checkin {
|
func SelectCheckin(api string) *Checkin {
|
||||||
for _, s := range Services() {
|
for _, s := range Services() {
|
||||||
for _, c := range s.AllCheckins() {
|
for _, c := range s.Checkins() {
|
||||||
if c.ApiKey == api {
|
if c.ApiKey == api {
|
||||||
return &Checkin{c}
|
return &Checkin{c}
|
||||||
}
|
}
|
||||||
|
@ -140,19 +140,17 @@ func (c *Checkin) GetFailures(count int) []*types.Failure {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create will create a new Checkin
|
// Create will create a new Checkin
|
||||||
func (c *Checkin) Delete() error {
|
func (c *Checkin) Delete() {
|
||||||
c.Close()
|
c.Close()
|
||||||
i := c.index()
|
i := c.index()
|
||||||
service := c.Service()
|
srv := c.Service()
|
||||||
slice := service.Checkins
|
slice := srv.Service.Checkins
|
||||||
service.Checkins = append(slice[:i], slice[i+1:]...)
|
srv.Service.Checkins = append(slice[:i], slice[i+1:]...)
|
||||||
row := Database(c).Delete(&c)
|
|
||||||
return row.Error()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// index returns a checkin index int for updating the *checkin.Service slice
|
// index returns a checkin index int for updating the *checkin.Service slice
|
||||||
func (c *Checkin) index() int {
|
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 {
|
if c.Id == checkin.Model().Id {
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
132
core/configs.go
132
core/configs.go
|
@ -23,7 +23,6 @@ import (
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrorResponse is used for HTTP errors to show to User
|
// 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
|
// LoadConfigFile will attempt to load the 'config.yml' file in a specific directory
|
||||||
func LoadConfigFile(directory string) (*types.DbConfig, error) {
|
func LoadConfigFile(directory string) (*DbConfig, error) {
|
||||||
var configs *types.DbConfig
|
var configs *DbConfig
|
||||||
if os.Getenv("DB_CONN") != "" {
|
|
||||||
log.Warnln("DB_CONN environment variable was found, waiting for database...")
|
dbConn := utils.Getenv("DB_CONN", "")
|
||||||
|
|
||||||
|
if dbConn != "" {
|
||||||
|
log.Infof("DB_CONN=%s environment variable was found, waiting for database...", dbConn)
|
||||||
return LoadUsingEnv()
|
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")
|
file, err := ioutil.ReadFile(directory + "/config.yml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
CoreApp.Setup = false
|
CoreApp.Setup = false
|
||||||
|
@ -49,54 +51,49 @@ func LoadConfigFile(directory string) (*types.DbConfig, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.WithFields(utils.ToFields(configs)).Debugln("read config file: " + directory + "/config.yml")
|
log.WithFields(utils.ToFields(configs)).Debugln("read config file: " + directory + "/config.yml")
|
||||||
CoreApp.Config = configs
|
CoreApp.Config = configs.DbConfig
|
||||||
return configs, err
|
return configs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadUsingEnv will attempt to load database configs based on environment variables. If DB_CONN is set if will force this function.
|
// 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()
|
Configs, err := EnvToConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Configs, err
|
return Configs, err
|
||||||
}
|
}
|
||||||
CoreApp.Name = os.Getenv("NAME")
|
|
||||||
if Configs.Domain == "" {
|
CoreApp.Name = utils.Getenv("NAME", "").(string)
|
||||||
CoreApp.Domain = Configs.LocalIP
|
CoreApp.Domain = utils.Getenv("DOMAIN", Configs.LocalIP).(string)
|
||||||
} else {
|
CoreApp.UseCdn = types.NewNullBool(utils.Getenv("USE_CDN", false).(bool))
|
||||||
CoreApp.Domain = os.Getenv("DOMAIN")
|
|
||||||
}
|
|
||||||
CoreApp.UseCdn = types.NewNullBool(os.Getenv("USE_CDN") == "true")
|
|
||||||
|
|
||||||
err = CoreApp.Connect(true, utils.Directory)
|
err = CoreApp.Connect(true, utils.Directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if _, err := CoreApp.SaveConfig(Configs); err != nil {
|
if err := Configs.Save(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
exists := DbSession.HasTable("core")
|
exists := DbSession.HasTable("core")
|
||||||
if !exists {
|
if !exists {
|
||||||
log.Infoln(fmt.Sprintf("Core database does not exist, creating now!"))
|
log.Infoln(fmt.Sprintf("Core database does not exist, creating now!"))
|
||||||
CoreApp.DropDatabase()
|
if err := CoreApp.DropDatabase(); err != nil {
|
||||||
CoreApp.CreateDatabase()
|
return nil, err
|
||||||
CoreApp, err = CoreApp.InsertCore(Configs)
|
}
|
||||||
|
if err := CoreApp.CreateDatabase(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
CoreApp, err = Configs.InsertCore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
username := os.Getenv("ADMIN_USER")
|
username := utils.Getenv("ADMIN_USER", "admin").(string)
|
||||||
if username == "" {
|
password := utils.Getenv("ADMIN_PASSWORD", "admin").(string)
|
||||||
username = "admin"
|
|
||||||
}
|
|
||||||
password := os.Getenv("ADMIN_PASSWORD")
|
|
||||||
if password == "" {
|
|
||||||
password = "admin"
|
|
||||||
}
|
|
||||||
|
|
||||||
admin := &types.User{
|
admin := &types.User{
|
||||||
Username: username,
|
Username: username,
|
||||||
Password: password,
|
Password: utils.HashPassword(password),
|
||||||
Email: "info@admin.com",
|
Email: "info@admin.com",
|
||||||
Admin: types.NewNullBool(true),
|
Admin: types.NewNullBool(true),
|
||||||
}
|
}
|
||||||
|
@ -128,67 +125,56 @@ func defaultPort(db string) int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvToConfig converts environment variables to a DbConfig variable
|
// EnvToConfig converts environment variables to a DbConfig variable
|
||||||
func EnvToConfig() (*types.DbConfig, error) {
|
func EnvToConfig() (*DbConfig, error) {
|
||||||
var err error
|
var err error
|
||||||
if os.Getenv("DB_CONN") == "" {
|
|
||||||
return nil, errors.New("Missing DB_CONN environment variable")
|
dbConn := utils.Getenv("DB_CONN", "").(string)
|
||||||
}
|
dbHost := utils.Getenv("DB_HOST", "").(string)
|
||||||
if os.Getenv("DB_CONN") != "sqlite" {
|
dbUser := utils.Getenv("DB_USER", "").(string)
|
||||||
if os.Getenv("DB_HOST") == "" {
|
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")
|
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")
|
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")
|
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")
|
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")
|
CoreApp.Config = &types.DbConfig{
|
||||||
if adminUser == "" {
|
DbConn: dbConn,
|
||||||
adminUser = "admin"
|
DbHost: dbHost,
|
||||||
}
|
DbUser: dbUser,
|
||||||
|
DbPass: dbPass,
|
||||||
adminPass := os.Getenv("ADMIN_PASS")
|
DbData: dbData,
|
||||||
if adminPass == "" {
|
DbPort: dbPort,
|
||||||
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,
|
|
||||||
Project: name,
|
Project: name,
|
||||||
Description: description,
|
Description: desc,
|
||||||
Domain: os.Getenv("DOMAIN"),
|
Domain: domain,
|
||||||
Email: "",
|
Email: "",
|
||||||
Username: adminUser,
|
Username: user,
|
||||||
Password: adminPass,
|
Password: password,
|
||||||
Error: nil,
|
Error: nil,
|
||||||
Location: utils.Directory,
|
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
|
// 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 {
|
type Core struct {
|
||||||
*types.Core
|
*types.Core
|
||||||
services []database.Servicer
|
services []*Service
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -63,18 +63,31 @@ func (c *Core) ToCore() *types.Core {
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitApp will initialize Statping
|
// InitApp will initialize Statping
|
||||||
func InitApp() {
|
func InitApp() error {
|
||||||
SelectCore()
|
if _, err := SelectCore(); err != nil {
|
||||||
InsertNotifierDB()
|
return err
|
||||||
InsertIntegratorDB()
|
}
|
||||||
SelectAllServices(true)
|
if err := InsertNotifierDB(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := InsertIntegratorDB(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := SelectAllServices(true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
checkServices()
|
checkServices()
|
||||||
AttachNotifiers()
|
if err := AttachNotifiers(); err != nil {
|
||||||
AddIntegrations()
|
return err
|
||||||
|
}
|
||||||
|
if err := AddIntegrations(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
CoreApp.Notifications = notifier.AllCommunications
|
CoreApp.Notifications = notifier.AllCommunications
|
||||||
CoreApp.Integrations = integrations.Integrations
|
CoreApp.Integrations = integrations.Integrations
|
||||||
go DatabaseMaintence()
|
database.StartMaintenceRoutine()
|
||||||
CoreApp.Setup = true
|
CoreApp.Setup = true
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertNotifierDB inject the Statping database instance to the Notifier package
|
// 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)
|
// 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
|
// Sort interface for resroting the Services in order
|
||||||
func (c ServiceOrder) Len() int { return len(c) }
|
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) 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 (
|
import (
|
||||||
"github.com/hunterlong/statping/source"
|
"github.com/hunterlong/statping/source"
|
||||||
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -39,26 +40,27 @@ func init() {
|
||||||
|
|
||||||
func TestNewCore(t *testing.T) {
|
func TestNewCore(t *testing.T) {
|
||||||
err := TmpRecords("core.db")
|
err := TmpRecords("core.db")
|
||||||
|
t.Log(err)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.NotNil(t, CoreApp)
|
require.NotNil(t, CoreApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDbConfig_Save(t *testing.T) {
|
func TestDbConfig_Save(t *testing.T) {
|
||||||
t.SkipNow()
|
if skipNewDb {
|
||||||
//if skipNewDb {
|
t.SkipNow()
|
||||||
// t.SkipNow()
|
}
|
||||||
//}
|
|
||||||
//var err error
|
config := &DbConfig{&types.DbConfig{
|
||||||
//Configs = &DbConfig{
|
DbConn: "sqlite",
|
||||||
// DbConn: "sqlite",
|
Project: "Tester",
|
||||||
// Project: "Tester",
|
Location: dir,
|
||||||
// Location: dir,
|
}}
|
||||||
//}
|
|
||||||
//Configs, err = Configs.Save()
|
err := config.Save()
|
||||||
//assert.Nil(t, err)
|
require.Nil(t, err)
|
||||||
//assert.Equal(t, "sqlite", Configs.DbConn)
|
assert.Equal(t, "sqlite", CoreApp.Config.DbConn)
|
||||||
//assert.NotEmpty(t, Configs.ApiKey)
|
assert.NotEmpty(t, CoreApp.Config.ApiKey)
|
||||||
//assert.NotEmpty(t, Configs.ApiSecret)
|
assert.NotEmpty(t, CoreApp.Config.ApiSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadDbConfig(t *testing.T) {
|
func TestLoadDbConfig(t *testing.T) {
|
||||||
|
@ -73,7 +75,6 @@ func TestDbConnection(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDropDatabase(t *testing.T) {
|
func TestDropDatabase(t *testing.T) {
|
||||||
t.SkipNow()
|
|
||||||
if skipNewDb {
|
if skipNewDb {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
|
@ -82,7 +83,6 @@ func TestDropDatabase(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSeedSchemaDatabase(t *testing.T) {
|
func TestSeedSchemaDatabase(t *testing.T) {
|
||||||
t.SkipNow()
|
|
||||||
if skipNewDb {
|
if skipNewDb {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,6 @@ func TestMigrateDatabase(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSeedDatabase(t *testing.T) {
|
func TestSeedDatabase(t *testing.T) {
|
||||||
t.SkipNow()
|
|
||||||
err := InsertLargeSampleData()
|
err := InsertLargeSampleData()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
@ -115,7 +114,6 @@ func TestSelectCore(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsertNotifierDB(t *testing.T) {
|
func TestInsertNotifierDB(t *testing.T) {
|
||||||
t.SkipNow()
|
|
||||||
if skipNewDb {
|
if skipNewDb {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,14 +39,12 @@ var (
|
||||||
|
|
||||||
func init() {
|
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{}}
|
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
|
// 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 {
|
func Database(obj interface{}) database.Database {
|
||||||
switch obj.(type) {
|
switch obj.(type) {
|
||||||
|
@ -140,18 +138,18 @@ func CloseDB() {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// InsertCore create the single row for the Core settings in Statping
|
// 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{
|
CoreApp = &Core{Core: &types.Core{
|
||||||
Name: db.Project,
|
Name: d.Project,
|
||||||
Description: db.Description,
|
Description: d.Description,
|
||||||
ConfigFile: "config.yml",
|
ConfigFile: "config.yml",
|
||||||
ApiKey: utils.NewSHA1Hash(9),
|
ApiKey: utils.NewSHA1Hash(9),
|
||||||
ApiSecret: utils.NewSHA1Hash(16),
|
ApiSecret: utils.NewSHA1Hash(16),
|
||||||
Domain: db.Domain,
|
Domain: d.Domain,
|
||||||
MigrationId: time.Now().Unix(),
|
MigrationId: time.Now().Unix(),
|
||||||
Config: db,
|
Config: d.DbConfig,
|
||||||
}}
|
}}
|
||||||
query := Database(CoreApp).Create(&CoreApp)
|
query := DbSession.Create(CoreApp.Core)
|
||||||
return CoreApp, query.Error()
|
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")
|
log.WithFields(utils.ToFields(dbSession)).Debugln("connected to database")
|
||||||
|
|
||||||
dbSession.DB().SetMaxOpenConns(5)
|
maxOpenConn := utils.Getenv("MAX_OPEN_CONN", 5)
|
||||||
dbSession.DB().SetMaxIdleConns(5)
|
maxIdleConn := utils.Getenv("MAX_IDLE_CONN", 5)
|
||||||
dbSession.DB().SetConnMaxLifetime(1 * time.Minute)
|
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 {
|
if dbSession.DB().Ping() == nil {
|
||||||
DbSession = dbSession
|
DbSession = dbSession
|
||||||
|
@ -239,26 +241,6 @@ func (c *Core) waitForDb() error {
|
||||||
return c.Connect(true, utils.Directory)
|
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
|
// Update will save the config.yml file
|
||||||
func (c *Core) UpdateConfig() error {
|
func (c *Core) UpdateConfig() error {
|
||||||
var err error
|
var err error
|
||||||
|
@ -278,25 +260,25 @@ func (c *Core) UpdateConfig() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save will initially create the config.yml file
|
// 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")
|
config, err := os.Create(utils.Directory + "/config.yml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
defer config.Close()
|
defer config.Close()
|
||||||
log.WithFields(utils.ToFields(configs)).Debugln("saving config file at: " + utils.Directory + "/config.yml")
|
log.WithFields(utils.ToFields(d)).Debugln("saving config file at: " + utils.Directory + "/config.yml")
|
||||||
c.Config = configs
|
CoreApp.Config = d.DbConfig
|
||||||
c.Config.ApiKey = utils.NewSHA1Hash(16)
|
CoreApp.Config.ApiKey = utils.NewSHA1Hash(16)
|
||||||
c.Config.ApiSecret = utils.NewSHA1Hash(16)
|
CoreApp.Config.ApiSecret = utils.NewSHA1Hash(16)
|
||||||
data, err := yaml.Marshal(configs)
|
data, err := yaml.Marshal(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
config.WriteString(string(data))
|
config.WriteString(string(data))
|
||||||
log.WithFields(utils.ToFields(configs)).Infoln("saved config file at: " + utils.Directory + "/config.yml")
|
log.WithFields(utils.ToFields(d)).Infoln("saved config file at: " + utils.Directory + "/config.yml")
|
||||||
return c.Config, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateCore will initialize the global variable 'CoreApp". This global variable contains most of Statping app.
|
// 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
|
// DropDatabase will DROP each table Statping created
|
||||||
func (c *Core) DropDatabase() error {
|
func (c *Core) DropDatabase() error {
|
||||||
log.Infoln("Dropping Database Tables...")
|
log.Infoln("Dropping Database Tables...")
|
||||||
err := DbSession.DropTableIfExists("checkins")
|
tables := []string{"checkins", "checkin_hits", "notifications", "core", "failures", "hits", "services", "users", "messages", "incidents", "incident_updates"}
|
||||||
err = DbSession.DropTableIfExists("checkin_hits")
|
for _, t := range tables {
|
||||||
err = DbSession.DropTableIfExists("notifications")
|
if err := DbSession.DropTableIfExists(t); err != nil {
|
||||||
err = DbSession.DropTableIfExists("core")
|
return err.Error()
|
||||||
err = DbSession.DropTableIfExists("failures")
|
}
|
||||||
err = DbSession.DropTableIfExists("hits")
|
}
|
||||||
err = DbSession.DropTableIfExists("services")
|
return nil
|
||||||
err = DbSession.DropTableIfExists("users")
|
|
||||||
err = DbSession.DropTableIfExists("messages")
|
|
||||||
err = DbSession.DropTableIfExists("incidents")
|
|
||||||
err = DbSession.DropTableIfExists("incident_updates")
|
|
||||||
return err.Error()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateDatabase will CREATE TABLES for each of the Statping elements
|
// CreateDatabase will CREATE TABLES for each of the Statping elements
|
||||||
|
|
|
@ -49,10 +49,10 @@ func ExportSettings() ([]byte, error) {
|
||||||
Core: CoreApp.Core,
|
Core: CoreApp.Core,
|
||||||
Notifiers: CoreApp.Notifications,
|
Notifiers: CoreApp.Notifications,
|
||||||
//Checkins: database.AllCheckins(),
|
//Checkins: database.AllCheckins(),
|
||||||
Users: users,
|
Users: users,
|
||||||
//Services: CoreApp.Services,
|
//Services: CoreApp.Services,
|
||||||
//Groups: SelectGroups(true, true),
|
//Groups: SelectGroups(true, true),
|
||||||
Messages: messages,
|
Messages: messages,
|
||||||
}
|
}
|
||||||
export, err := json.Marshal(data)
|
export, err := json.Marshal(data)
|
||||||
return export, err
|
return export, err
|
||||||
|
|
|
@ -19,7 +19,9 @@ import (
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Failure struct{}
|
type Failure struct {
|
||||||
|
*types.Failure
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
limitedFailures = 32
|
limitedFailures = 32
|
||||||
|
|
|
@ -7,46 +7,45 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Group struct {
|
type Group struct {
|
||||||
database.Grouper
|
*types.Group
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectGroups returns all groups
|
// SelectGroups returns all groups
|
||||||
func SelectGroups(includeAll bool, auth bool) []database.Grouper {
|
func SelectGroups(includeAll bool, auth bool) []*Group {
|
||||||
var validGroups []database.Grouper
|
var validGroups []*Group
|
||||||
|
|
||||||
groups := database.AllGroups()
|
groups := database.AllGroups()
|
||||||
|
|
||||||
for _, g := range groups {
|
for _, g := range groups {
|
||||||
if !g.Model().Public.Bool {
|
if !g.Public.Bool {
|
||||||
if auth {
|
if auth {
|
||||||
validGroups = append(validGroups, g)
|
validGroups = append(validGroups, &Group{g.Group})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
validGroups = append(validGroups, g)
|
validGroups = append(validGroups, &Group{g.Group})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Sort(GroupOrder(validGroups))
|
sort.Sort(GroupOrder(validGroups))
|
||||||
if includeAll {
|
if includeAll {
|
||||||
emptyGroup := &Group{}
|
validGroups = append(validGroups, &Group{})
|
||||||
validGroups = append(validGroups, emptyGroup)
|
|
||||||
}
|
}
|
||||||
return validGroups
|
return validGroups
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectGroup returns a *core.Group
|
// SelectGroup returns a *core.Group
|
||||||
func SelectGroup(id int64) *types.Group {
|
func SelectGroup(id int64) *Group {
|
||||||
for _, g := range SelectGroups(true, true) {
|
for _, g := range SelectGroups(true, true) {
|
||||||
if g.Model().Id == id {
|
if g.Id == id {
|
||||||
return g.Model()
|
return g
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupOrder will reorder the groups based on 'order_id' (Order)
|
// 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
|
// Sort interface for resorting the Groups in order
|
||||||
func (c GroupOrder) Len() int { return len(c) }
|
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) 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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/hunterlong/statping/database"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||||
|
@ -57,7 +58,7 @@ var core = &types.Core{
|
||||||
func injectDatabase() {
|
func injectDatabase() {
|
||||||
sqlPath := dir + "/notifier.db"
|
sqlPath := dir + "/notifier.db"
|
||||||
utils.DeleteFile(sqlPath)
|
utils.DeleteFile(sqlPath)
|
||||||
db, _ = types.Openw("sqlite3", sqlPath)
|
db, _ = database.Openw("sqlite3", sqlPath)
|
||||||
db.CreateTable(&Notification{})
|
db.CreateTable(&Notification{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
152
core/sample.go
152
core/sample.go
|
@ -34,7 +34,10 @@ var (
|
||||||
func InsertSampleData() error {
|
func InsertSampleData() error {
|
||||||
log.Infoln("Inserting Sample Data...")
|
log.Infoln("Inserting Sample Data...")
|
||||||
|
|
||||||
insertSampleGroups()
|
if err := insertSampleGroups(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
createdOn := time.Now().Add(((-24 * 30) * 3) * time.Hour).UTC()
|
createdOn := time.Now().Add(((-24 * 30) * 3) * time.Hour).UTC()
|
||||||
s1 := &types.Service{
|
s1 := &types.Service{
|
||||||
Name: "Google",
|
Name: "Google",
|
||||||
|
@ -106,15 +109,33 @@ func InsertSampleData() error {
|
||||||
CreatedAt: createdOn,
|
CreatedAt: createdOn,
|
||||||
}
|
}
|
||||||
|
|
||||||
database.Create(s1)
|
if _, err := database.Create(s1); err != nil {
|
||||||
database.Create(s2)
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
database.Create(s3)
|
}
|
||||||
database.Create(s4)
|
if _, err := database.Create(s2); err != nil {
|
||||||
database.Create(s5)
|
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")
|
log.Infoln("Sample data has finished importing")
|
||||||
|
|
||||||
|
@ -128,7 +149,7 @@ func insertSampleIncidents() error {
|
||||||
ServiceId: 2,
|
ServiceId: 2,
|
||||||
}
|
}
|
||||||
if _, err := database.Create(incident1); err != nil {
|
if _, err := database.Create(incident1); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateIncidentUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
incidentUpdate1 := &types.IncidentUpdate{
|
incidentUpdate1 := &types.IncidentUpdate{
|
||||||
|
@ -137,7 +158,7 @@ func insertSampleIncidents() error {
|
||||||
Type: "Investigating",
|
Type: "Investigating",
|
||||||
}
|
}
|
||||||
if _, err := database.Create(incidentUpdate1); err != nil {
|
if _, err := database.Create(incidentUpdate1); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateIncidentUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
incidentUpdate2 := &types.IncidentUpdate{
|
incidentUpdate2 := &types.IncidentUpdate{
|
||||||
|
@ -146,7 +167,7 @@ func insertSampleIncidents() error {
|
||||||
Type: "Update",
|
Type: "Update",
|
||||||
}
|
}
|
||||||
if _, err := database.Create(incidentUpdate2); err != nil {
|
if _, err := database.Create(incidentUpdate2); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateIncidentUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
incidentUpdate3 := &types.IncidentUpdate{
|
incidentUpdate3 := &types.IncidentUpdate{
|
||||||
|
@ -155,7 +176,7 @@ func insertSampleIncidents() error {
|
||||||
Type: "Resolved",
|
Type: "Resolved",
|
||||||
}
|
}
|
||||||
if _, err := database.Create(incidentUpdate3); err != nil {
|
if _, err := database.Create(incidentUpdate3); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateIncidentUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -168,7 +189,7 @@ func insertSampleGroups() error {
|
||||||
Order: 2,
|
Order: 2,
|
||||||
}
|
}
|
||||||
if _, err := database.Create(group1); err != nil {
|
if _, err := database.Create(group1); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
group2 := &types.Group{
|
group2 := &types.Group{
|
||||||
|
@ -177,7 +198,7 @@ func insertSampleGroups() error {
|
||||||
Order: 1,
|
Order: 1,
|
||||||
}
|
}
|
||||||
if _, err := database.Create(group2); err != nil {
|
if _, err := database.Create(group2); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
group3 := &types.Group{
|
group3 := &types.Group{
|
||||||
|
@ -186,7 +207,7 @@ func insertSampleGroups() error {
|
||||||
Order: 3,
|
Order: 3,
|
||||||
}
|
}
|
||||||
if _, err := database.Create(group3); err != nil {
|
if _, err := database.Create(group3); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateGroup)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -195,24 +216,24 @@ func insertSampleGroups() error {
|
||||||
func insertSampleCheckins() error {
|
func insertSampleCheckins() error {
|
||||||
s1 := SelectService(1)
|
s1 := SelectService(1)
|
||||||
checkin1 := &types.Checkin{
|
checkin1 := &types.Checkin{
|
||||||
ServiceId: s1.Model().Id,
|
ServiceId: s1.Id,
|
||||||
Interval: 300,
|
Interval: 300,
|
||||||
GracePeriod: 300,
|
GracePeriod: 300,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(checkin1); err != nil {
|
if _, err := database.Create(checkin1); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
}
|
}
|
||||||
|
|
||||||
s2 := SelectService(1)
|
s2 := SelectService(1)
|
||||||
checkin2 := &types.Checkin{
|
checkin2 := &types.Checkin{
|
||||||
ServiceId: s2.Model().Id,
|
ServiceId: s2.Id,
|
||||||
Interval: 900,
|
Interval: 900,
|
||||||
GracePeriod: 300,
|
GracePeriod: 300,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(checkin2); err != nil {
|
if _, err := database.Create(checkin2); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateCheckinHit)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTime := time.Now().UTC().Add(-24 * time.Hour)
|
checkTime := time.Now().UTC().Add(-24 * time.Hour)
|
||||||
|
@ -224,7 +245,7 @@ func insertSampleCheckins() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(checkHit); err != nil {
|
if _, err := database.Create(checkHit); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateCheckinHit)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTime = checkTime.Add(10 * time.Minute)
|
checkTime = checkTime.Add(10 * time.Minute)
|
||||||
|
@ -240,7 +261,7 @@ func InsertSampleHits() error {
|
||||||
sg.Add(1)
|
sg.Add(1)
|
||||||
service := SelectService(i)
|
service := SelectService(i)
|
||||||
seed := time.Now().UnixNano()
|
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
|
createdAt := sampleStart
|
||||||
p := utils.NewPerlin(2., 2., 10, seed)
|
p := utils.NewPerlin(2., 2., 10, seed)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -249,7 +270,7 @@ func InsertSampleHits() error {
|
||||||
latency := p.Noise1D(hi / 500)
|
latency := p.Noise1D(hi / 500)
|
||||||
createdAt = createdAt.Add(60 * time.Second)
|
createdAt = createdAt.Add(60 * time.Second)
|
||||||
hit := &types.Hit{
|
hit := &types.Hit{
|
||||||
Service: service.Model().Id,
|
Service: service.Id,
|
||||||
CreatedAt: createdAt,
|
CreatedAt: createdAt,
|
||||||
Latency: latency,
|
Latency: latency,
|
||||||
}
|
}
|
||||||
|
@ -258,11 +279,11 @@ func InsertSampleHits() error {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
sg.Wait()
|
sg.Wait()
|
||||||
err := tx.Commit().Error()
|
if err := tx.Commit().Error(); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
|
return types.ErrWrap(err, types.ErrorCreateSampleHits)
|
||||||
}
|
}
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertSampleCore will create a new Core for the seed
|
// insertSampleCore will create a new Core for the seed
|
||||||
|
@ -276,11 +297,14 @@ func insertSampleCore() error {
|
||||||
Version: "test",
|
Version: "test",
|
||||||
CreatedAt: time.Now().UTC(),
|
CreatedAt: time.Now().UTC(),
|
||||||
UseCdn: types.NewNullBool(false),
|
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
|
// insertSampleUsers will create 2 admin users for a seed database
|
||||||
|
@ -293,7 +317,7 @@ func insertSampleUsers() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(u2); err != nil {
|
if _, err := database.Create(u2); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
u3 := &types.User{
|
u3 := &types.User{
|
||||||
|
@ -304,7 +328,7 @@ func insertSampleUsers() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(u3); err != nil {
|
if _, err := database.Create(u3); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -320,7 +344,7 @@ func insertMessages() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(m1); err != nil {
|
if _, err := database.Create(m1); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
m2 := &types.Message{
|
m2 := &types.Message{
|
||||||
|
@ -332,7 +356,7 @@ func insertMessages() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(m2); err != nil {
|
if _, err := database.Create(m2); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateMessage)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -368,7 +392,7 @@ func InsertLargeSampleData() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(s6); err != nil {
|
if _, err := database.Create(s6); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
}
|
}
|
||||||
|
|
||||||
s7 := &types.Service{
|
s7 := &types.Service{
|
||||||
|
@ -384,7 +408,7 @@ func InsertLargeSampleData() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(s7); err != nil {
|
if _, err := database.Create(s7); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
}
|
}
|
||||||
|
|
||||||
s8 := &types.Service{
|
s8 := &types.Service{
|
||||||
|
@ -399,7 +423,7 @@ func InsertLargeSampleData() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(s8); err != nil {
|
if _, err := database.Create(s8); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
}
|
}
|
||||||
|
|
||||||
s9 := &types.Service{
|
s9 := &types.Service{
|
||||||
|
@ -415,7 +439,7 @@ func InsertLargeSampleData() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(s9); err != nil {
|
if _, err := database.Create(s9); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
}
|
}
|
||||||
|
|
||||||
s10 := &types.Service{
|
s10 := &types.Service{
|
||||||
|
@ -431,7 +455,7 @@ func InsertLargeSampleData() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(s10); err != nil {
|
if _, err := database.Create(s10); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
}
|
}
|
||||||
|
|
||||||
s11 := &types.Service{
|
s11 := &types.Service{
|
||||||
|
@ -447,7 +471,7 @@ func InsertLargeSampleData() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(s11); err != nil {
|
if _, err := database.Create(s11); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
}
|
}
|
||||||
|
|
||||||
s12 := &types.Service{
|
s12 := &types.Service{
|
||||||
|
@ -463,7 +487,7 @@ func InsertLargeSampleData() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(s12); err != nil {
|
if _, err := database.Create(s12); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
}
|
}
|
||||||
|
|
||||||
s13 := &types.Service{
|
s13 := &types.Service{
|
||||||
|
@ -479,7 +503,7 @@ func InsertLargeSampleData() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(s13); err != nil {
|
if _, err := database.Create(s13); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
}
|
}
|
||||||
|
|
||||||
s14 := &types.Service{
|
s14 := &types.Service{
|
||||||
|
@ -495,7 +519,7 @@ func InsertLargeSampleData() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(s14); err != nil {
|
if _, err := database.Create(s14); err != nil {
|
||||||
return err
|
return types.ErrWrap(err, types.ErrorCreateService)
|
||||||
}
|
}
|
||||||
|
|
||||||
s15 := &types.Service{
|
s15 := &types.Service{
|
||||||
|
@ -511,7 +535,7 @@ func InsertLargeSampleData() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Create(s15); err != nil {
|
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)
|
var dayAgo = time.Now().UTC().Add((-24 * 90) * time.Hour)
|
||||||
|
@ -527,14 +551,14 @@ func InsertLargeSampleData() error {
|
||||||
func insertFailureRecords(since time.Time, amount int) {
|
func insertFailureRecords(since time.Time, amount int) {
|
||||||
for i := int64(14); i <= 15; i++ {
|
for i := int64(14); i <= 15; i++ {
|
||||||
service := SelectService(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
|
createdAt := since
|
||||||
|
|
||||||
for fi := 1; fi <= amount; fi++ {
|
for fi := 1; fi <= amount; fi++ {
|
||||||
createdAt = createdAt.Add(2 * time.Minute)
|
createdAt = createdAt.Add(2 * time.Minute)
|
||||||
|
|
||||||
failure := &types.Failure{
|
failure := &types.Failure{
|
||||||
Service: service.Model().Id,
|
Service: service.Id,
|
||||||
Issue: "testing right here",
|
Issue: "testing right here",
|
||||||
CreatedAt: createdAt,
|
CreatedAt: createdAt,
|
||||||
}
|
}
|
||||||
|
@ -545,32 +569,36 @@ func insertFailureRecords(since time.Time, amount int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertHitRecords will create successful Hit records for 15 services
|
// 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++ {
|
for i := int64(1); i <= 15; i++ {
|
||||||
service := SelectService(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
|
createdAt := since
|
||||||
p := utils.NewPerlin(2, 2, 5, time.Now().UnixNano())
|
p := utils.NewPerlin(2, 2, 5, time.Now().UnixNano())
|
||||||
for hi := 1; hi <= amount; hi++ {
|
for hi := 1; hi <= amount; hi++ {
|
||||||
latency := p.Noise1D(float64(hi / 10))
|
latency := p.Noise1D(float64(hi / 10))
|
||||||
createdAt = createdAt.Add(1 * time.Minute)
|
createdAt = createdAt.Add(1 * time.Minute)
|
||||||
hit := &types.Hit{
|
hit := &types.Hit{
|
||||||
Service: service.Model().Id,
|
Service: service.Id,
|
||||||
CreatedAt: createdAt.UTC(),
|
CreatedAt: createdAt.UTC(),
|
||||||
Latency: latency,
|
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
|
// 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.
|
// with sample data and store it in the /tmp folder to be used by the tests.
|
||||||
func TmpRecords(dbFile string) error {
|
func TmpRecords(dbFile string) error {
|
||||||
var sqlFile = utils.Directory + "/" + dbFile
|
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
|
var tmpSqlFile = utils.Directory + "/tmp/" + types.SqliteFilename
|
||||||
SampleHits = 480
|
SampleHits = 480
|
||||||
|
|
||||||
|
@ -578,19 +606,19 @@ func TmpRecords(dbFile string) error {
|
||||||
CoreApp = NewCore()
|
CoreApp = NewCore()
|
||||||
CoreApp.Name = "Tester"
|
CoreApp.Name = "Tester"
|
||||||
CoreApp.Setup = true
|
CoreApp.Setup = true
|
||||||
configs := &types.DbConfig{
|
configs := &DbConfig{&types.DbConfig{
|
||||||
DbConn: "sqlite",
|
DbConn: "sqlite",
|
||||||
Project: "Tester",
|
Project: "Tester",
|
||||||
Location: utils.Directory,
|
Location: utils.Directory,
|
||||||
SqlFile: sqlFile,
|
SqlFile: sqlFile,
|
||||||
}
|
}}
|
||||||
log.Infoln("saving config.yml in: " + utils.Directory)
|
log.Infoln("saving config.yml in: " + utils.Directory)
|
||||||
if configs, err = CoreApp.SaveConfig(configs); err != nil {
|
if err := configs.Save(); err != nil {
|
||||||
return err
|
log.Error(err)
|
||||||
}
|
}
|
||||||
log.Infoln("loading config.yml from: " + utils.Directory)
|
log.Infoln("loading config.yml from: " + utils.Directory)
|
||||||
if configs, err = LoadConfigFile(utils.Directory); err != nil {
|
if configs, err = LoadConfigFile(utils.Directory); err != nil {
|
||||||
return err
|
log.Error(err)
|
||||||
}
|
}
|
||||||
log.Infoln("connecting to database")
|
log.Infoln("connecting to database")
|
||||||
|
|
||||||
|
@ -598,37 +626,37 @@ func TmpRecords(dbFile string) error {
|
||||||
if exists {
|
if exists {
|
||||||
log.Infoln(tmpSqlFile + " was found, copying the temp database to " + sqlFile)
|
log.Infoln(tmpSqlFile + " was found, copying the temp database to " + sqlFile)
|
||||||
if err := utils.DeleteFile(sqlFile); err != nil {
|
if err := utils.DeleteFile(sqlFile); err != nil {
|
||||||
log.Infoln(sqlFile + " was not found")
|
log.Error(err)
|
||||||
}
|
}
|
||||||
if err := utils.CopyFile(tmpSqlFile, sqlFile); err != nil {
|
if err := utils.CopyFile(tmpSqlFile, sqlFile); err != nil {
|
||||||
return err
|
log.Error(err)
|
||||||
}
|
}
|
||||||
log.Infoln("loading config.yml from: " + utils.Directory)
|
log.Infoln("loading config.yml from: " + utils.Directory)
|
||||||
|
|
||||||
if err := CoreApp.Connect(false, utils.Directory); err != nil {
|
if err := CoreApp.Connect(false, utils.Directory); err != nil {
|
||||||
return err
|
log.Error(err)
|
||||||
}
|
}
|
||||||
log.Infoln("selecting the Core variable")
|
log.Infoln("selecting the Core variable")
|
||||||
if _, err := SelectCore(); err != nil {
|
if _, err := SelectCore(); err != nil {
|
||||||
return err
|
log.Error(err)
|
||||||
}
|
}
|
||||||
log.Infoln("inserting notifiers into database")
|
log.Infoln("inserting notifiers into database")
|
||||||
if err := InsertNotifierDB(); err != nil {
|
if err := InsertNotifierDB(); err != nil {
|
||||||
return err
|
log.Error(err)
|
||||||
}
|
}
|
||||||
log.Infoln("inserting integrations into database")
|
log.Infoln("inserting integrations into database")
|
||||||
if err := InsertIntegratorDB(); err != nil {
|
if err := InsertIntegratorDB(); err != nil {
|
||||||
return err
|
log.Error(err)
|
||||||
}
|
}
|
||||||
log.Infoln("loading all services")
|
log.Infoln("loading all services")
|
||||||
if _, err := SelectAllServices(false); err != nil {
|
if _, err := SelectAllServices(false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := AttachNotifiers(); err != nil {
|
if err := AttachNotifiers(); err != nil {
|
||||||
return err
|
log.Error(err)
|
||||||
}
|
}
|
||||||
if err := AddIntegrations(); err != nil {
|
if err := AddIntegrations(); err != nil {
|
||||||
return err
|
log.Error(err)
|
||||||
}
|
}
|
||||||
CoreApp.Notifications = notifier.AllCommunications
|
CoreApp.Notifications = notifier.AllCommunications
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -25,20 +25,19 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
*types.Service
|
*database.ServiceObj
|
||||||
}
|
}
|
||||||
|
|
||||||
type Servicer interface{}
|
func Services() []*Service {
|
||||||
|
|
||||||
func Services() []database.Servicer {
|
|
||||||
return CoreApp.services
|
return CoreApp.services
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectService returns a *core.Service from in memory
|
// SelectService returns a *core.Service from in memory
|
||||||
func SelectService(id int64) database.Servicer {
|
func SelectService(id int64) *Service {
|
||||||
for _, s := range Services() {
|
for _, s := range Services() {
|
||||||
if s.Model().Id == id {
|
if s.Id == id {
|
||||||
fmt.Println("service: ", s.Model())
|
s.UpdateStats()
|
||||||
|
fmt.Println("service: ", s.Name, s.Stats)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +46,7 @@ func SelectService(id int64) database.Servicer {
|
||||||
|
|
||||||
// CheckinProcess runs the checkin routine for each checkin attached to service
|
// CheckinProcess runs the checkin routine for each checkin attached to service
|
||||||
func CheckinProcess(s database.Servicer) {
|
func CheckinProcess(s database.Servicer) {
|
||||||
for _, c := range s.AllCheckins() {
|
for _, c := range s.Checkins() {
|
||||||
c.Start()
|
c.Start()
|
||||||
go CheckinRoutine(c)
|
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
|
// SelectAllServices returns a slice of *core.Service to be store on []*core.Services
|
||||||
// should only be called once on startup.
|
// should only be called once on startup.
|
||||||
func SelectAllServices(start bool) ([]*database.ServiceObj, error) {
|
func SelectAllServices(start bool) ([]*Service, error) {
|
||||||
srvs := database.Services()
|
srvs := database.Services()
|
||||||
for _, s := range srvs {
|
|
||||||
fmt.Println("services: ", s.Id, s.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range srvs {
|
for _, s := range srvs {
|
||||||
if start {
|
if start {
|
||||||
service := s.Model()
|
s.Start()
|
||||||
service.Start()
|
|
||||||
CheckinProcess(s)
|
CheckinProcess(s)
|
||||||
}
|
}
|
||||||
//fails := service.Service (limitedFailures)
|
|
||||||
//for _, f := range fails {
|
fails := s.Failures().Last(limitedFailures)
|
||||||
// service.Failures = append(service.Failures, f)
|
s.Service.Failures = fails
|
||||||
//}
|
|
||||||
for _, c := range s.AllCheckins() {
|
for _, c := range s.Checkins() {
|
||||||
s.Checkins = append(s.Checkins, c)
|
s.Service.Checkins = append(s.Service.Checkins, c.Checkin)
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect initial service stats
|
// collect initial service stats
|
||||||
s.Service.Stats = s.UpdateStats()
|
s.UpdateStats()
|
||||||
CoreApp.services = append(CoreApp.services, s)
|
CoreApp.services = append(CoreApp.services, &Service{s})
|
||||||
}
|
}
|
||||||
reorderServices()
|
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'
|
// reorderServices will sort the services based on 'order_id'
|
||||||
|
@ -87,24 +90,10 @@ func reorderServices() {
|
||||||
sort.Sort(ServiceOrder(CoreApp.services))
|
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
|
// 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 {
|
for k, service := range CoreApp.services {
|
||||||
if s.Model().Id == service.Model().Id {
|
if s == service.Id {
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,14 +101,13 @@ func index(s database.Servicer) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateService will update a service in the []*core.Services slice
|
// updateService will update a service in the []*core.Services slice
|
||||||
func updateService(s database.Servicer) {
|
func updateService(s *Service) {
|
||||||
CoreApp.services[index(s)] = s
|
CoreApp.services[index(s.Id)] = s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete will remove a service from the database, it will also end the service checking go routine
|
// Delete will remove a service from the database, it will also end the service checking go routine
|
||||||
func Delete(srv database.Servicer) error {
|
func (s *Service) Delete() error {
|
||||||
i := index(srv)
|
i := index(s.Id)
|
||||||
s := srv.Model()
|
|
||||||
err := database.Delete(s)
|
err := database.Delete(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(fmt.Sprintf("Failed to delete service %v. %v", s.Name, err))
|
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
|
slice := CoreApp.services
|
||||||
CoreApp.services = append(slice[:i], slice[i+1:]...)
|
CoreApp.services = append(slice[:i], slice[i+1:]...)
|
||||||
reorderServices()
|
reorderServices()
|
||||||
notifier.OnDeletedService(s)
|
notifier.OnDeletedService(s.Service)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update will update a service in the database, the service's checking routine can be restarted by passing true
|
// 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 {
|
func Update(s *Service, restart bool) error {
|
||||||
s := srv.Model()
|
|
||||||
err := database.Update(s)
|
err := database.Update(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(fmt.Sprintf("Failed to update service %v. %v", s.Name, err))
|
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 {
|
if restart {
|
||||||
s.Close()
|
s.Close()
|
||||||
s.Start()
|
s.Start()
|
||||||
s.SleepDuration = time.Duration(s.Interval) * time.Second
|
s.SleepDuration = s.Duration()
|
||||||
go ServiceCheckQueue(srv, true)
|
go ServiceCheckQueue(s, true)
|
||||||
}
|
}
|
||||||
reorderServices()
|
reorderServices()
|
||||||
updateService(srv)
|
updateService(s)
|
||||||
notifier.OnUpdatedService(s)
|
notifier.OnUpdatedService(s.Service)
|
||||||
return err
|
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))
|
log.Errorln(fmt.Sprintf("Failed to create service %v #%v: %v", s.Name, s.Id, err))
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
service := &Service{s}
|
||||||
s.Start()
|
s.Start()
|
||||||
go ServiceCheckQueue(srv, check)
|
CoreApp.services = append(CoreApp.services, service)
|
||||||
CoreApp.services = append(CoreApp.services, srv)
|
go ServiceCheckQueue(service, check)
|
||||||
reorderServices()
|
reorderServices()
|
||||||
notifier.OnNewService(s)
|
notifier.OnNewService(s.Service)
|
||||||
return s.Id, nil
|
return s.Id, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,10 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hunterlong/statping/database"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -29,13 +30,12 @@ var (
|
||||||
|
|
||||||
func TestCreateCheckin(t *testing.T) {
|
func TestCreateCheckin(t *testing.T) {
|
||||||
service := SelectService(1)
|
service := SelectService(1)
|
||||||
testCheckin = ReturnCheckin(&types.Checkin{
|
checkin := &types.Checkin{
|
||||||
ServiceId: service.Id,
|
ServiceId: service.Id,
|
||||||
Interval: 10,
|
Interval: 10,
|
||||||
GracePeriod: 5,
|
GracePeriod: 5,
|
||||||
ApiKey: utils.RandomString(7),
|
}
|
||||||
})
|
id, err := database.Create(checkin)
|
||||||
id, err := testCheckin.Create()
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotZero(t, id)
|
assert.NotZero(t, id)
|
||||||
assert.NotEmpty(t, testCheckin.ApiKey)
|
assert.NotEmpty(t, testCheckin.ApiKey)
|
||||||
|
@ -46,13 +46,13 @@ func TestCreateCheckin(t *testing.T) {
|
||||||
|
|
||||||
func TestSelectCheckin(t *testing.T) {
|
func TestSelectCheckin(t *testing.T) {
|
||||||
service := SelectService(1)
|
service := SelectService(1)
|
||||||
checkins := service.AllCheckins()
|
checkins := service.Checkins()
|
||||||
assert.NotNil(t, checkins)
|
assert.NotNil(t, checkins)
|
||||||
assert.Equal(t, 1, len(checkins))
|
assert.Equal(t, 1, len(checkins))
|
||||||
testCheckin = checkins[0]
|
c := checkins[0]
|
||||||
assert.Equal(t, int64(10), testCheckin.Interval)
|
assert.Equal(t, int64(10), c.Interval)
|
||||||
assert.Equal(t, int64(5), testCheckin.GracePeriod)
|
assert.Equal(t, int64(5), c.GracePeriod)
|
||||||
assert.Equal(t, 7, len(testCheckin.ApiKey))
|
assert.Equal(t, 7, len(c.ApiKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateCheckin(t *testing.T) {
|
func TestUpdateCheckin(t *testing.T) {
|
||||||
|
@ -63,7 +63,7 @@ func TestUpdateCheckin(t *testing.T) {
|
||||||
assert.NotZero(t, id)
|
assert.NotZero(t, id)
|
||||||
assert.NotEmpty(t, testCheckin.ApiKey)
|
assert.NotEmpty(t, testCheckin.ApiKey)
|
||||||
service := SelectService(1)
|
service := SelectService(1)
|
||||||
checkin := service.AllCheckins()[0]
|
checkin := service.Checkins()[0]
|
||||||
assert.Equal(t, int64(60), checkin.Interval)
|
assert.Equal(t, int64(60), checkin.Interval)
|
||||||
assert.Equal(t, int64(15), checkin.GracePeriod)
|
assert.Equal(t, int64(15), checkin.GracePeriod)
|
||||||
t.Log(testCheckin.Expected())
|
t.Log(testCheckin.Expected())
|
||||||
|
@ -72,15 +72,16 @@ func TestUpdateCheckin(t *testing.T) {
|
||||||
|
|
||||||
func TestCreateCheckinHits(t *testing.T) {
|
func TestCreateCheckinHits(t *testing.T) {
|
||||||
service := SelectService(1)
|
service := SelectService(1)
|
||||||
checkins := service.AllCheckins()
|
checkins := service.Checkins()
|
||||||
assert.Equal(t, 1, len(checkins))
|
assert.Equal(t, 1, len(checkins))
|
||||||
created := time.Now().UTC().Add(-60 * time.Second)
|
created := time.Now().UTC().Add(-60 * time.Second)
|
||||||
hit := ReturnCheckinHit(&types.CheckinHit{
|
hit := &types.CheckinHit{
|
||||||
Checkin: testCheckin.Id,
|
Checkin: testCheckin.Id,
|
||||||
From: "192.168.1.1",
|
From: "192.168.1.1",
|
||||||
CreatedAt: created,
|
CreatedAt: created,
|
||||||
})
|
}
|
||||||
hit.Create()
|
_, err := database.Create(hit)
|
||||||
|
require.Nil(t, err)
|
||||||
hits := testCheckin.AllHits()
|
hits := testCheckin.AllHits()
|
||||||
assert.Equal(t, 1, len(hits))
|
assert.Equal(t, 1, len(hits))
|
||||||
}
|
}
|
||||||
|
@ -88,13 +89,13 @@ func TestCreateCheckinHits(t *testing.T) {
|
||||||
func TestSelectCheckinMethods(t *testing.T) {
|
func TestSelectCheckinMethods(t *testing.T) {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
service := SelectService(1)
|
service := SelectService(1)
|
||||||
checkins := service.AllCheckins()
|
checkins := service.Checkins()
|
||||||
assert.NotNil(t, checkins)
|
assert.NotNil(t, checkins)
|
||||||
lastHit := testCheckin.Last()
|
|
||||||
assert.Equal(t, float64(60), testCheckin.Period().Seconds())
|
assert.Equal(t, float64(60), testCheckin.Period().Seconds())
|
||||||
assert.Equal(t, float64(15), testCheckin.Grace().Seconds())
|
assert.Equal(t, float64(15), testCheckin.Grace().Seconds())
|
||||||
t.Log(testCheckin.Expected())
|
t.Log(testCheckin.Expected())
|
||||||
|
|
||||||
|
lastHit := checkins[0]
|
||||||
assert.True(t, testCheckin.Expected().Seconds() < -5)
|
assert.True(t, testCheckin.Expected().Seconds() < -5)
|
||||||
assert.False(t, lastHit.CreatedAt.IsZero())
|
assert.False(t, lastHit.CreatedAt.IsZero())
|
||||||
assert.Equal(t, "A minute ago", lastHit.Ago())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hunterlong/statping/database"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -28,7 +30,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSelectHTTPService(t *testing.T) {
|
func TestSelectHTTPService(t *testing.T) {
|
||||||
services, err := CoreApp.SelectAllServices(false)
|
services, err := SelectAllServices(false)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 15, len(services))
|
assert.Equal(t, 15, len(services))
|
||||||
assert.Equal(t, "Google", services[0].Name)
|
assert.Equal(t, "Google", services[0].Name)
|
||||||
|
@ -36,12 +38,11 @@ func TestSelectHTTPService(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectAllServices(t *testing.T) {
|
func TestSelectAllServices(t *testing.T) {
|
||||||
services := CoreApp.Services
|
services := CoreApp.services
|
||||||
for _, s := range services {
|
for _, s := range services {
|
||||||
service := s.(*Service)
|
CheckService(s, false)
|
||||||
service.Check(false)
|
assert.False(t, s.IsRunning())
|
||||||
assert.False(t, service.IsRunning())
|
t.Logf("ID: %v %v\n", s.Id, s.Name)
|
||||||
t.Logf("ID: %v %v\n", service.Id, service.Name)
|
|
||||||
}
|
}
|
||||||
assert.Equal(t, 15, len(services))
|
assert.Equal(t, 15, len(services))
|
||||||
}
|
}
|
||||||
|
@ -54,7 +55,7 @@ func TestServiceDowntime(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectTCPService(t *testing.T) {
|
func TestSelectTCPService(t *testing.T) {
|
||||||
services := CoreApp.Services
|
services := CoreApp.services
|
||||||
assert.Equal(t, 15, len(services))
|
assert.Equal(t, 15, len(services))
|
||||||
service := SelectService(5)
|
service := SelectService(5)
|
||||||
assert.NotNil(t, service)
|
assert.NotNil(t, service)
|
||||||
|
@ -67,8 +68,10 @@ func TestUpdateService(t *testing.T) {
|
||||||
assert.Equal(t, "Google", service.Name)
|
assert.Equal(t, "Google", service.Name)
|
||||||
service.Name = "Updated Google"
|
service.Name = "Updated Google"
|
||||||
service.Interval = 5
|
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
|
// check if updating pointer array shutdown any other service
|
||||||
service = SelectService(1)
|
service = SelectService(1)
|
||||||
assert.Equal(t, "Updated Google", service.Name)
|
assert.Equal(t, "Updated Google", service.Name)
|
||||||
|
@ -76,19 +79,20 @@ func TestUpdateService(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateAllServices(t *testing.T) {
|
func TestUpdateAllServices(t *testing.T) {
|
||||||
services, err := CoreApp.SelectAllServices(false)
|
services, err := SelectAllServices(false)
|
||||||
assert.Nil(t, err)
|
require.Nil(t, err)
|
||||||
for k, srv := range services {
|
for k, srv := range services {
|
||||||
srv.Name = "Changed " + srv.Name
|
srv.Name = "Changed " + srv.Name
|
||||||
srv.Interval = k + 3
|
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) {
|
func TestServiceHTTPCheck(t *testing.T) {
|
||||||
service := SelectService(1)
|
service := SelectService(1)
|
||||||
service.Check(true)
|
CheckService(service, true)
|
||||||
assert.Equal(t, "Changed Updated Google", service.Name)
|
assert.Equal(t, "Changed Updated Google", service.Name)
|
||||||
assert.True(t, service.Online)
|
assert.True(t, service.Online)
|
||||||
}
|
}
|
||||||
|
@ -104,7 +108,7 @@ func TestCheckHTTPService(t *testing.T) {
|
||||||
|
|
||||||
func TestServiceTCPCheck(t *testing.T) {
|
func TestServiceTCPCheck(t *testing.T) {
|
||||||
service := SelectService(5)
|
service := SelectService(5)
|
||||||
service.Check(true)
|
CheckService(service, true)
|
||||||
assert.Equal(t, "Changed Google DNS", service.Name)
|
assert.Equal(t, "Changed Google DNS", service.Name)
|
||||||
assert.True(t, service.Online)
|
assert.True(t, service.Online)
|
||||||
}
|
}
|
||||||
|
@ -130,23 +134,17 @@ func TestServiceOnline24Hours(t *testing.T) {
|
||||||
func TestServiceAvgUptime(t *testing.T) {
|
func TestServiceAvgUptime(t *testing.T) {
|
||||||
since := utils.Now().Add(-24 * time.Hour).Add(-10 * time.Minute)
|
since := utils.Now().Add(-24 * time.Hour).Add(-10 * time.Minute)
|
||||||
service := SelectService(1)
|
service := SelectService(1)
|
||||||
assert.NotEqual(t, "0.00", service.AvgUptime(since))
|
assert.NotEqual(t, "0.00", service.AvgTime())
|
||||||
service2 := SelectService(5)
|
service2 := SelectService(5)
|
||||||
assert.Equal(t, "100", service2.AvgUptime(since))
|
assert.Equal(t, "100", service2.AvgTime())
|
||||||
service3 := SelectService(13)
|
service3 := SelectService(13)
|
||||||
assert.NotEqual(t, "0", service3.AvgUptime(since))
|
assert.NotEqual(t, "0", service3.AvgUptime(since))
|
||||||
service4 := SelectService(15)
|
service4 := SelectService(15)
|
||||||
assert.NotEqual(t, "0", service4.AvgUptime(since))
|
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) {
|
func TestCreateService(t *testing.T) {
|
||||||
s := ReturnService(&types.Service{
|
s := &types.Service{
|
||||||
Name: "That'll do 🐢",
|
Name: "That'll do 🐢",
|
||||||
Domain: "https://www.youtube.com/watch?v=rjQtzV9IZ0Q",
|
Domain: "https://www.youtube.com/watch?v=rjQtzV9IZ0Q",
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
|
@ -155,12 +153,11 @@ func TestCreateService(t *testing.T) {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 20,
|
Timeout: 20,
|
||||||
GroupId: 1,
|
GroupId: 1,
|
||||||
})
|
}
|
||||||
var err error
|
obj, err := database.Create(s)
|
||||||
newServiceId, err = s.Create(false)
|
require.Nil(t, err)
|
||||||
assert.Nil(t, err)
|
assert.NotZero(t, obj.Id)
|
||||||
assert.NotZero(t, newServiceId)
|
newService := SelectService(obj.Id)
|
||||||
newService := SelectService(newServiceId)
|
|
||||||
assert.Equal(t, "That'll do 🐢", newService.Name)
|
assert.Equal(t, "That'll do 🐢", newService.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +167,7 @@ func TestViewNewService(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateFailingHTTPService(t *testing.T) {
|
func TestCreateFailingHTTPService(t *testing.T) {
|
||||||
s := ReturnService(&types.Service{
|
s := &types.Service{
|
||||||
Name: "Bad URL",
|
Name: "Bad URL",
|
||||||
Domain: "http://localhost/iamnothere",
|
Domain: "http://localhost/iamnothere",
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
|
@ -179,12 +176,11 @@ func TestCreateFailingHTTPService(t *testing.T) {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 5,
|
Timeout: 5,
|
||||||
GroupId: 1,
|
GroupId: 1,
|
||||||
})
|
}
|
||||||
var err error
|
obj, err := database.Create(s)
|
||||||
newServiceId, err = s.Create(false)
|
require.Nil(t, err)
|
||||||
assert.Nil(t, err)
|
assert.NotZero(t, obj.Id)
|
||||||
assert.NotZero(t, newServiceId)
|
newService := SelectService(obj.Id)
|
||||||
newService := SelectService(newServiceId)
|
|
||||||
assert.Equal(t, "Bad URL", newService.Name)
|
assert.Equal(t, "Bad URL", newService.Name)
|
||||||
t.Log("new service ID: ", newServiceId)
|
t.Log("new service ID: ", newServiceId)
|
||||||
}
|
}
|
||||||
|
@ -192,13 +188,13 @@ func TestCreateFailingHTTPService(t *testing.T) {
|
||||||
func TestServiceFailedCheck(t *testing.T) {
|
func TestServiceFailedCheck(t *testing.T) {
|
||||||
service := SelectService(17)
|
service := SelectService(17)
|
||||||
assert.Equal(t, "Bad URL", service.Name)
|
assert.Equal(t, "Bad URL", service.Name)
|
||||||
service.Check(false)
|
CheckService(service, false)
|
||||||
assert.Equal(t, "Bad URL", service.Name)
|
assert.Equal(t, "Bad URL", service.Name)
|
||||||
assert.False(t, service.Online)
|
assert.False(t, service.Online)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateFailingTCPService(t *testing.T) {
|
func TestCreateFailingTCPService(t *testing.T) {
|
||||||
s := ReturnService(&types.Service{
|
s := &types.Service{
|
||||||
Name: "Bad TCP",
|
Name: "Bad TCP",
|
||||||
Domain: "localhost",
|
Domain: "localhost",
|
||||||
Port: 5050,
|
Port: 5050,
|
||||||
|
@ -206,50 +202,51 @@ func TestCreateFailingTCPService(t *testing.T) {
|
||||||
Type: "tcp",
|
Type: "tcp",
|
||||||
Timeout: 5,
|
Timeout: 5,
|
||||||
GroupId: 1,
|
GroupId: 1,
|
||||||
})
|
}
|
||||||
var err error
|
var err error
|
||||||
newServiceId, err = s.Create(false)
|
obj, err := database.Create(s)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotZero(t, newServiceId)
|
assert.NotZero(t, obj.Id)
|
||||||
newService := SelectService(newServiceId)
|
newService := SelectService(obj.Id)
|
||||||
assert.Equal(t, "Bad TCP", newService.Name)
|
assert.Equal(t, "Bad TCP", newService.Name)
|
||||||
t.Log("new failing tcp service ID: ", newServiceId)
|
t.Log("new failing tcp service ID: ", newServiceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceFailedTCPCheck(t *testing.T) {
|
func TestServiceFailedTCPCheck(t *testing.T) {
|
||||||
service := SelectService(newServiceId)
|
service := SelectService(newServiceId)
|
||||||
service.Check(false)
|
CheckService(service, false)
|
||||||
assert.Equal(t, "Bad TCP", service.Name)
|
assert.Equal(t, "Bad TCP", service.Name)
|
||||||
assert.False(t, service.Online)
|
assert.False(t, service.Online)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateServiceFailure(t *testing.T) {
|
func TestCreateServiceFailure(t *testing.T) {
|
||||||
fail := &types.Failure{
|
|
||||||
Issue: "This is not an issue, but it would container HTTP response errors.",
|
|
||||||
Method: "http",
|
|
||||||
}
|
|
||||||
service := SelectService(8)
|
service := SelectService(8)
|
||||||
id, err := service.CreateFailure(fail)
|
fail := &types.Failure{
|
||||||
|
Issue: "This is not an issue, but it would container HTTP response errors.",
|
||||||
|
Method: "http",
|
||||||
|
Service: service.Id,
|
||||||
|
}
|
||||||
|
obj, err := database.Create(fail)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotZero(t, id)
|
assert.NotZero(t, obj.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteService(t *testing.T) {
|
func TestDeleteService(t *testing.T) {
|
||||||
service := SelectService(newServiceId)
|
service := SelectService(newServiceId)
|
||||||
|
|
||||||
count, err := CoreApp.SelectAllServices(false)
|
count, err := SelectAllServices(false)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 18, len(count))
|
assert.Equal(t, 18, len(count))
|
||||||
|
|
||||||
err = service.Delete()
|
err = service.Delete()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
services := CoreApp.Services
|
services := CoreApp.services
|
||||||
assert.Equal(t, 17, len(services))
|
assert.Equal(t, 17, len(services))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceCloseRoutine(t *testing.T) {
|
func TestServiceCloseRoutine(t *testing.T) {
|
||||||
s := ReturnService(new(types.Service))
|
s := new(Service)
|
||||||
s.Name = "example"
|
s.Name = "example"
|
||||||
s.Domain = "https://google.com"
|
s.Domain = "https://google.com"
|
||||||
s.Type = "http"
|
s.Type = "http"
|
||||||
|
@ -260,7 +257,7 @@ func TestServiceCloseRoutine(t *testing.T) {
|
||||||
assert.True(t, s.IsRunning())
|
assert.True(t, s.IsRunning())
|
||||||
t.Log(s.Checkpoint)
|
t.Log(s.Checkpoint)
|
||||||
t.Log(s.SleepDuration)
|
t.Log(s.SleepDuration)
|
||||||
go s.CheckQueue(false)
|
go ServiceCheckQueue(s, false)
|
||||||
t.Log(s.Checkpoint)
|
t.Log(s.Checkpoint)
|
||||||
t.Log(s.SleepDuration)
|
t.Log(s.SleepDuration)
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
@ -274,7 +271,7 @@ func TestServiceCloseRoutine(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceCheckQueue(t *testing.T) {
|
func TestServiceCheckQueue(t *testing.T) {
|
||||||
s := ReturnService(new(types.Service))
|
s := new(Service)
|
||||||
s.Name = "example"
|
s.Name = "example"
|
||||||
s.Domain = "https://google.com"
|
s.Domain = "https://google.com"
|
||||||
s.Type = "http"
|
s.Type = "http"
|
||||||
|
@ -283,7 +280,7 @@ func TestServiceCheckQueue(t *testing.T) {
|
||||||
s.Interval = 1
|
s.Interval = 1
|
||||||
s.Start()
|
s.Start()
|
||||||
assert.True(t, s.IsRunning())
|
assert.True(t, s.IsRunning())
|
||||||
go s.CheckQueue(false)
|
go ServiceCheckQueue(s, false)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
@ -300,14 +297,14 @@ func TestServiceCheckQueue(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDNScheckService(t *testing.T) {
|
func TestDNScheckService(t *testing.T) {
|
||||||
s := ReturnService(new(types.Service))
|
s := new(Service)
|
||||||
s.Name = "example"
|
s.Name = "example"
|
||||||
s.Domain = "http://localhost:9000"
|
s.Domain = "http://localhost:9000"
|
||||||
s.Type = "http"
|
s.Type = "http"
|
||||||
s.Method = "GET"
|
s.Method = "GET"
|
||||||
s.ExpectedStatus = 200
|
s.ExpectedStatus = 200
|
||||||
s.Interval = 1
|
s.Interval = 1
|
||||||
amount, err := s.dnsCheck()
|
amount, err := dnsCheck(s)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotZero(t, amount)
|
assert.NotZero(t, amount)
|
||||||
}
|
}
|
||||||
|
@ -317,25 +314,13 @@ func TestSelectServiceLink(t *testing.T) {
|
||||||
assert.Equal(t, "google", service.Permalink.String)
|
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) {
|
func TestGroup_Create(t *testing.T) {
|
||||||
group := &Group{&types.Group{
|
group := &types.Group{
|
||||||
Name: "Testing",
|
Name: "Testing",
|
||||||
}}
|
}
|
||||||
newGroupId, err := group.Create()
|
obj, err := database.Create(group)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotZero(t, newGroupId)
|
assert.NotZero(t, obj.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGroup_Services(t *testing.T) {
|
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 will return the User and a boolean if authentication was correct.
|
||||||
// AuthUser accepts username, and password as a string
|
// AuthUser accepts username, and password as a string
|
||||||
func AuthUser(username, password string) (*User, bool) {
|
func AuthUser(username, password string) (*types.User, bool) {
|
||||||
user, err := SelectUsername(username)
|
user, err := database.UserByUsername(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnln(fmt.Errorf("user %v not found", username))
|
log.Warnln(fmt.Errorf("user %v not found", username))
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println(username, password)
|
||||||
|
|
||||||
|
fmt.Println(username, user.Password)
|
||||||
|
|
||||||
if CheckHash(password, user.Password) {
|
if CheckHash(password, user.Password) {
|
||||||
user.UpdatedAt = time.Now().UTC()
|
user.UpdatedAt = time.Now().UTC()
|
||||||
user.Update()
|
database.Update(user)
|
||||||
return user, true
|
return user.User, true
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,26 +16,26 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hunterlong/statping/database"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateUser(t *testing.T) {
|
func TestCreateUser(t *testing.T) {
|
||||||
user := ReturnUser(&types.User{
|
user := &types.User{
|
||||||
Username: "hunter",
|
Username: "hunter",
|
||||||
Password: "password123",
|
Password: "password123",
|
||||||
Email: "test@email.com",
|
Email: "test@email.com",
|
||||||
Admin: types.NewNullBool(true),
|
Admin: types.NewNullBool(true),
|
||||||
})
|
}
|
||||||
userId, err := user.Create()
|
obj, err := database.Create(user)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotZero(t, userId)
|
assert.NotZero(t, obj.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectAllUsers(t *testing.T) {
|
func TestSelectAllUsers(t *testing.T) {
|
||||||
users, err := SelectAllUsers()
|
users := SelectAllUsers()
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, 3, len(users))
|
assert.Equal(t, 3, len(users))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,20 +66,19 @@ func TestUpdateUser(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateUser2(t *testing.T) {
|
func TestCreateUser2(t *testing.T) {
|
||||||
user := ReturnUser(&types.User{
|
user := &types.User{
|
||||||
Username: "hunterlong",
|
Username: "hunterlong",
|
||||||
Password: "password123",
|
Password: "password123",
|
||||||
Email: "User@email.com",
|
Email: "User@email.com",
|
||||||
Admin: types.NewNullBool(true),
|
Admin: types.NewNullBool(true),
|
||||||
})
|
}
|
||||||
userId, err := user.Create()
|
obj, err := database.Create(user)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotZero(t, userId)
|
assert.NotZero(t, obj.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectAllUsersAgain(t *testing.T) {
|
func TestSelectAllUsersAgain(t *testing.T) {
|
||||||
users, err := SelectAllUsers()
|
users := SelectAllUsers()
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, 4, len(users))
|
assert.Equal(t, 4, len(users))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package database
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
|
"github.com/hunterlong/statping/utils"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,6 +21,21 @@ type Checkiner interface {
|
||||||
Object() *CheckinObj
|
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) {
|
func Checkin(id int64) (*CheckinObj, error) {
|
||||||
var checkin types.Checkin
|
var checkin types.Checkin
|
||||||
query := database.Checkins().Where("id = ?", id)
|
query := database.Checkins().Where("id = ?", id)
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CrudObject interface {
|
||||||
|
Create()
|
||||||
|
}
|
||||||
|
|
||||||
type Object struct {
|
type Object struct {
|
||||||
Id int64
|
Id int64
|
||||||
model interface{}
|
model interface{}
|
||||||
|
@ -25,41 +28,35 @@ func wrapObject(id int64, model interface{}, db Database) *Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
func modelId(model interface{}) int64 {
|
func modelId(model interface{}) int64 {
|
||||||
fmt.Printf("%T\n", model)
|
|
||||||
iface := reflect.ValueOf(model)
|
|
||||||
field := iface.Elem().FieldByName("Id")
|
|
||||||
return field.Int()
|
|
||||||
}
|
|
||||||
|
|
||||||
func toModel(model interface{}) Database {
|
|
||||||
switch model.(type) {
|
switch model.(type) {
|
||||||
case *types.Core:
|
case *types.Core:
|
||||||
return database.Model(&types.Core{}).Table("core")
|
return 0
|
||||||
default:
|
default:
|
||||||
return database.Model(&model)
|
iface := reflect.ValueOf(model)
|
||||||
|
field := iface.Elem().FieldByName("Id")
|
||||||
|
return field.Int()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Create(data interface{}) (*Object, error) {
|
func Create(data interface{}) (*Object, error) {
|
||||||
model := toModel(data)
|
model := database.Model(&data)
|
||||||
query := model.Create(data)
|
if err := model.Create(data).Error(); err != nil {
|
||||||
if query.Error() != nil {
|
return nil, err
|
||||||
return nil, query.Error()
|
|
||||||
}
|
}
|
||||||
obj := &Object{
|
obj := &Object{
|
||||||
Id: modelId(data),
|
Id: modelId(data),
|
||||||
model: data,
|
model: data,
|
||||||
db: model,
|
db: model,
|
||||||
}
|
}
|
||||||
return obj, query.Error()
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Update(data interface{}) error {
|
func Update(data interface{}) error {
|
||||||
model := toModel(data)
|
model := database.Model(&data)
|
||||||
return model.Update(&data).Error()
|
return model.Update(&data).Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Delete(data interface{}) error {
|
func Delete(data interface{}) error {
|
||||||
model := toModel(data)
|
model := database.Model(&data)
|
||||||
return model.Delete(data).Error()
|
return model.Delete(data).Error()
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,8 +113,6 @@ type Database interface {
|
||||||
|
|
||||||
Requests(*http.Request, isObject) Database
|
Requests(*http.Request, isObject) Database
|
||||||
|
|
||||||
GroupQuery(query *GroupQuery, by By) GroupByer
|
|
||||||
|
|
||||||
Objects
|
Objects
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,6 +191,9 @@ type Db struct {
|
||||||
|
|
||||||
// Openw is a drop-in replacement for Open()
|
// Openw is a drop-in replacement for Open()
|
||||||
func Openw(dialect string, args ...interface{}) (db Database, err error) {
|
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...)
|
gormdb, err := gorm.Open(dialect, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -34,10 +34,10 @@ func (f *FailureObj) DeleteAll() error {
|
||||||
return query.Error()
|
return query.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FailureObj) Last(amount int) *types.Failure {
|
func (f *FailureObj) Last(amount int) []*types.Failure {
|
||||||
var fail types.Failure
|
var fail []*types.Failure
|
||||||
f.o.db.Limit(amount).Last(&fail)
|
f.o.db.Limit(amount).Find(&fail)
|
||||||
return &fail
|
return fail
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FailureObj) Count() int {
|
func (f *FailureObj) Count() int {
|
||||||
|
|
|
@ -15,7 +15,7 @@ type GroupBy struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GroupByer interface {
|
type GroupByer interface {
|
||||||
ToTimeValue(interface{}) (*TimeVar, error)
|
ToTimeValue() (*TimeVar, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type By string
|
type By string
|
||||||
|
@ -25,7 +25,6 @@ func (b By) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GroupQuery struct {
|
type GroupQuery struct {
|
||||||
db Database
|
|
||||||
Start time.Time
|
Start time.Time
|
||||||
End time.Time
|
End time.Time
|
||||||
Group string
|
Group string
|
||||||
|
@ -33,6 +32,8 @@ type GroupQuery struct {
|
||||||
Limit int
|
Limit int
|
||||||
Offset int
|
Offset int
|
||||||
FillEmpty bool
|
FillEmpty bool
|
||||||
|
|
||||||
|
db Database
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b GroupQuery) Find(data interface{}) error {
|
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 {
|
type TimeVar struct {
|
||||||
g *GroupBy
|
g *GroupQuery
|
||||||
data []*TimeValue
|
data []*TimeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TimeVar) ToValues() []*TimeValue {
|
func (t *TimeVar) ToValues() ([]*TimeValue, error) {
|
||||||
return t.data
|
return t.data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupBy) toFloatRows() []*TimeValue {
|
func (g *GroupQuery) toFloatRows() []*TimeValue {
|
||||||
rows, err := g.db.Rows()
|
rows, err := g.db.Rows()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -77,7 +69,12 @@ func (g *GroupBy) toFloatRows() []*TimeValue {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var timeframe time.Time
|
var timeframe time.Time
|
||||||
amount := float64(0)
|
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())
|
newTs := types.FixedTime(timeframe, g.duration())
|
||||||
data = append(data, &TimeValue{
|
data = append(data, &TimeValue{
|
||||||
Timeframe: newTs,
|
Timeframe: newTs,
|
||||||
|
@ -87,17 +84,41 @@ func (g *GroupBy) toFloatRows() []*TimeValue {
|
||||||
return data
|
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()
|
rows, err := g.db.Rows()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var data []*TimeValue
|
var data []*TimeValue
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var timeframe time.Time
|
var timeframe string
|
||||||
amount := float64(0)
|
amount := float64(0)
|
||||||
rows.Scan(&timeframe, &amount)
|
if err := rows.Scan(&timeframe, &amount); err != nil {
|
||||||
newTs := types.FixedTime(timeframe, g.duration())
|
log.Error(err, timeframe)
|
||||||
|
}
|
||||||
|
trueTime, _ := g.db.ParseTime(timeframe)
|
||||||
|
newTs := types.FixedTime(trueTime, g.duration())
|
||||||
data = append(data, &TimeValue{
|
data = append(data, &TimeValue{
|
||||||
Timeframe: newTs,
|
Timeframe: newTs,
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
|
@ -106,7 +127,7 @@ func (g *GroupBy) ToTimeValue(dbType interface{}) (*TimeVar, error) {
|
||||||
return &TimeVar{g, data}, nil
|
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)
|
timeMap := make(map[string]float64)
|
||||||
var validSet []*TimeValue
|
var validSet []*TimeValue
|
||||||
dur := t.g.duration()
|
dur := t.g.duration()
|
||||||
|
@ -132,11 +153,11 @@ func (t *TimeVar) FillMissing(current, end time.Time) []*TimeValue {
|
||||||
currentStr = types.FixedTime(current, t.g.duration())
|
currentStr = types.FixedTime(current, t.g.duration())
|
||||||
}
|
}
|
||||||
|
|
||||||
return validSet
|
return validSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupBy) duration() time.Duration {
|
func (g *GroupQuery) duration() time.Duration {
|
||||||
switch g.query.Group {
|
switch g.Group {
|
||||||
case "second":
|
case "second":
|
||||||
return types.Second
|
return types.Second
|
||||||
case "minute":
|
case "minute":
|
||||||
|
@ -175,6 +196,8 @@ func ParseQueries(r *http.Request, o isObject) *GroupQuery {
|
||||||
limit = 10000
|
limit = 10000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db := o.object().db
|
||||||
|
|
||||||
query := &GroupQuery{
|
query := &GroupQuery{
|
||||||
Start: time.Unix(startField, 0).UTC(),
|
Start: time.Unix(startField, 0).UTC(),
|
||||||
End: time.Unix(endField, 0).UTC(),
|
End: time.Unix(endField, 0).UTC(),
|
||||||
|
@ -183,10 +206,9 @@ func ParseQueries(r *http.Request, o isObject) *GroupQuery {
|
||||||
Limit: int(limit),
|
Limit: int(limit),
|
||||||
Offset: int(offset),
|
Offset: int(offset),
|
||||||
FillEmpty: fill,
|
FillEmpty: fill,
|
||||||
|
db: db,
|
||||||
}
|
}
|
||||||
|
|
||||||
db := o.object().db
|
|
||||||
|
|
||||||
if query.Limit != 0 {
|
if query.Limit != 0 {
|
||||||
db = db.Limit(query.Limit)
|
db = db.Limit(query.Limit)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,25 @@ func (h *HitObj) All() []*types.Hit {
|
||||||
return fails
|
return fails
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HitObj) Last(amount int) *types.Hit {
|
func (s *ServiceObj) CreateHit(hit *types.Hit) *HitObj {
|
||||||
var hits types.Hit
|
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)
|
h.o.db.Limit(amount).Find(&hits)
|
||||||
return &hits
|
return hits
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HitObj) Since(t time.Time) []*types.Hit {
|
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 {
|
type Servicer interface {
|
||||||
Hits() *HitObj
|
|
||||||
Failures() *FailureObj
|
Failures() *FailureObj
|
||||||
AllCheckins() []*CheckinObj
|
Checkins() []*CheckinObj
|
||||||
Model() *types.Service
|
|
||||||
Interval() time.Duration
|
|
||||||
DowntimeText() string
|
DowntimeText() string
|
||||||
|
UpdateStats()
|
||||||
|
Model() *ServiceObj
|
||||||
|
|
||||||
Hittable
|
Hittable
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hittable interface {
|
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) {
|
func Service(id int64) (*ServiceObj, error) {
|
||||||
|
@ -52,7 +56,7 @@ func Services() []*ServiceObj {
|
||||||
return wrapServices(services, db)
|
return wrapServices(services, db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceObj) AllCheckins() []*CheckinObj {
|
func (s *ServiceObj) Checkins() []*CheckinObj {
|
||||||
var checkins []*types.Checkin
|
var checkins []*types.Checkin
|
||||||
query := database.Checkins().Where("service = ?", s.Id)
|
query := database.Checkins().Where("service = ?", s.Id)
|
||||||
query.Find(&checkins)
|
query.Find(&checkins)
|
||||||
|
@ -61,7 +65,10 @@ func (s *ServiceObj) AllCheckins() []*CheckinObj {
|
||||||
|
|
||||||
func (s *ServiceObj) DowntimeText() string {
|
func (s *ServiceObj) DowntimeText() string {
|
||||||
last := s.Failures().Last(1)
|
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
|
// ParseError returns a human readable error for a Failure
|
||||||
|
@ -116,16 +123,7 @@ func parseError(f *types.Failure) string {
|
||||||
return f.Issue
|
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 {
|
func (s *ServiceObj) Hits() *HitObj {
|
||||||
fmt.Println("hits")
|
|
||||||
query := database.Hits().Where("service = ?", s.Id)
|
query := database.Hits().Where("service = ?", s.Id)
|
||||||
return &HitObj{wrapObject(s.Id, nil, query)}
|
return &HitObj{wrapObject(s.Id, nil, query)}
|
||||||
}
|
}
|
||||||
|
@ -149,34 +147,27 @@ func (s *ServiceObj) object() *Object {
|
||||||
return s.o
|
return s.o
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceObj) UpdateStats() *types.Stats {
|
func (s *ServiceObj) UpdateStats() {
|
||||||
s.Online24Hours = s.OnlineDaysPercent(1)
|
s.Online24Hours = s.OnlineDaysPercent(1)
|
||||||
s.Online7Days = s.OnlineDaysPercent(7)
|
s.Online7Days = s.OnlineDaysPercent(7)
|
||||||
s.AvgResponse = s.AvgTime()
|
s.AvgResponse = s.AvgTime()
|
||||||
s.FailuresLast24Hours = len(s.Failures().Since(time.Now().Add(-time.Hour * 24)))
|
s.FailuresLast24Hours = len(s.Failures().Since(time.Now().UTC().Add(-time.Hour * 24)))
|
||||||
return s.Stats
|
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
|
// AvgTime will return the average amount of time for a service to response back successfully
|
||||||
func (s *ServiceObj) AvgTime() float64 {
|
func (s *ServiceObj) AvgTime() float64 {
|
||||||
var sum []float64
|
sum := s.Hits().Sum()
|
||||||
database.Hits().
|
return sum
|
||||||
Select("AVG(latency) as amount").
|
}
|
||||||
Where("service = ?", s.Id).Pluck("amount", &sum).Debug()
|
|
||||||
|
|
||||||
sumTotal := float64(0)
|
// AvgUptime will return the average amount of time for a service to response back successfully
|
||||||
for _, v := range sum {
|
func (s *ServiceObj) AvgUptime(since time.Time) float64 {
|
||||||
sumTotal += v
|
sum := s.Hits().Sum()
|
||||||
}
|
return sum
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnlineDaysPercent returns the service's uptime percent within last 24 hours
|
// 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 {
|
func (s *ServiceObj) Downtime() time.Duration {
|
||||||
hits := s.Hits().Last(1)
|
hits := s.Hits().Last(1)
|
||||||
fail := s.Failures().Last(1)
|
fail := s.Failures().Last(1)
|
||||||
if fail == nil {
|
if len(fail) == 0 {
|
||||||
return time.Duration(0)
|
return time.Duration(0)
|
||||||
}
|
}
|
||||||
if hits == nil {
|
if len(fail) == 0 {
|
||||||
return time.Now().UTC().Sub(fail.CreatedAt.UTC())
|
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
|
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-mail/mail v2.3.1+incompatible
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||||
github.com/gorilla/mux v1.7.3
|
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/jinzhu/gorm v1.9.11
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.3.0
|
||||||
github.com/lib/pq v1.2.0 // indirect
|
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 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
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.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.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/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 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
|
|
@ -33,6 +33,7 @@ func init() {
|
||||||
|
|
||||||
func TestResetDatabase(t *testing.T) {
|
func TestResetDatabase(t *testing.T) {
|
||||||
err := core.TmpRecords("handlers.db")
|
err := core.TmpRecords("handlers.db")
|
||||||
|
t.Log(err)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.NotNil(t, core.CoreApp)
|
require.NotNil(t, core.CoreApp)
|
||||||
require.NotNil(t, core.CoreApp.Config)
|
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)
|
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := checkin.Delete()
|
|
||||||
if err != nil {
|
if err := database.Delete(checkin); err != nil {
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkin.Delete()
|
||||||
|
|
||||||
sendJsonAction(checkin, "delete", w, r)
|
sendJsonAction(checkin, "delete", w, r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/hunterlong/statping/core"
|
"github.com/hunterlong/statping/core"
|
||||||
"github.com/hunterlong/statping/source"
|
"github.com/hunterlong/statping/source"
|
||||||
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"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)
|
expirationTime := time.Now().Add(72 * time.Hour)
|
||||||
jwtClaim := JwtClaim{
|
jwtClaim := JwtClaim{
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/hunterlong/statping/core"
|
"github.com/hunterlong/statping/core"
|
||||||
"github.com/hunterlong/statping/database"
|
"github.com/hunterlong/statping/database"
|
||||||
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -32,11 +33,19 @@ func apiAllGroupHandler(r *http.Request) interface{} {
|
||||||
return groups
|
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
|
// apiGroupHandler will show a single group
|
||||||
func apiGroupHandler(w http.ResponseWriter, r *http.Request) {
|
func apiGroupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
group := core.SelectGroup(utils.ToInt(vars["id"]))
|
group := core.SelectGroup(utils.ToInt(vars["id"]))
|
||||||
if group == nil {
|
if group.Id == 0 {
|
||||||
sendErrorJson(errors.New("group not found"), w, r)
|
sendErrorJson(errors.New("group not found"), w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -47,7 +56,7 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
func apiGroupUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
func apiGroupUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
group := core.SelectGroup(utils.ToInt(vars["id"]))
|
group := core.SelectGroup(utils.ToInt(vars["id"]))
|
||||||
if group == nil {
|
if group.Id == 0 {
|
||||||
sendErrorJson(errors.New("group not found"), w, r)
|
sendErrorJson(errors.New("group not found"), w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -82,7 +91,7 @@ func apiCreateGroupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
func apiGroupDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
func apiGroupDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
group := core.SelectGroup(utils.ToInt(vars["id"]))
|
group := core.SelectGroup(utils.ToInt(vars["id"]))
|
||||||
if group == nil {
|
if group.Id == 0 {
|
||||||
sendErrorJson(errors.New("group not found"), w, r)
|
sendErrorJson(errors.New("group not found"), w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ func prometheusHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if !v.Online {
|
if !v.Online {
|
||||||
online = 0
|
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_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_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)
|
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/source"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -41,15 +40,18 @@ func Router() *mux.Router {
|
||||||
CacheStorage = NewStorage()
|
CacheStorage = NewStorage()
|
||||||
r := mux.NewRouter().StrictSlash(true)
|
r := mux.NewRouter().StrictSlash(true)
|
||||||
|
|
||||||
if os.Getenv("AUTH_USERNAME") != "" && os.Getenv("AUTH_PASSWORD") != "" {
|
authUser := utils.Getenv("AUTH_USERNAME", "").(string)
|
||||||
authUser = os.Getenv("AUTH_USERNAME")
|
authPass := utils.Getenv("AUTH_PASSWORD", "").(string)
|
||||||
authPass = os.Getenv("AUTH_PASSWORD")
|
|
||||||
|
if authUser != "" && authPass != "" {
|
||||||
r.Use(basicAuthHandler)
|
r.Use(basicAuthHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("BASE_PATH") != "" {
|
bPath := utils.Getenv("BASE_PATH", "").(string)
|
||||||
basePath = "/" + os.Getenv("BASE_PATH") + "/"
|
|
||||||
r = r.PathPrefix("/" + os.Getenv("BASE_PATH")).Subrouter()
|
if bPath != "" {
|
||||||
|
basePath = "/" + bPath + "/"
|
||||||
|
r = r.PathPrefix("/" + bPath).Subrouter()
|
||||||
r.Handle("", http.HandlerFunc(indexHandler))
|
r.Handle("", http.HandlerFunc(indexHandler))
|
||||||
} else {
|
} else {
|
||||||
r.Handle("/", http.HandlerFunc(indexHandler))
|
r.Handle("/", http.HandlerFunc(indexHandler))
|
||||||
|
|
|
@ -30,35 +30,37 @@ type scope struct {
|
||||||
func (s scope) MarshalJSON() ([]byte, error) {
|
func (s scope) MarshalJSON() ([]byte, error) {
|
||||||
svc := reflect.ValueOf(s.data)
|
svc := reflect.ValueOf(s.data)
|
||||||
if svc.Kind() == reflect.Slice {
|
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++ {
|
for i := 0; i < svc.Len(); i++ {
|
||||||
objIndex := svc.Index(i)
|
objIndex := svc.Index(i)
|
||||||
if objIndex.Kind() == reflect.Ptr {
|
alldata[i] = SafeJson(objIndex, s.scope)
|
||||||
objIndex = objIndex.Elem()
|
|
||||||
}
|
|
||||||
alldata = append(alldata, SafeJson(objIndex.Interface(), s.scope))
|
|
||||||
}
|
}
|
||||||
return json.Marshal(alldata)
|
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{})
|
thisData := make(map[string]interface{})
|
||||||
t := reflect.TypeOf(input)
|
if val.Kind() == reflect.Interface && !val.IsNil() {
|
||||||
elem := reflect.ValueOf(input)
|
elm := val.Elem()
|
||||||
d, _ := json.Marshal(input)
|
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
|
for i := 0; i < val.NumField(); i++ {
|
||||||
json.Unmarshal(d, &raw)
|
valueField := val.Field(i)
|
||||||
|
typeField := val.Type().Field(i)
|
||||||
|
tagVal := typeField.Tag
|
||||||
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
tag := tagVal.Get("scope")
|
||||||
field := t.Field(i)
|
|
||||||
|
|
||||||
tag := field.Tag.Get("scope")
|
|
||||||
tags := strings.Split(tag, ",")
|
tags := strings.Split(tag, ",")
|
||||||
|
|
||||||
jTags := field.Tag.Get("json")
|
jTags := tagVal.Get("json")
|
||||||
jsonTag := strings.Split(jTags, ",")
|
jsonTag := strings.Split(jTags, ",")
|
||||||
|
|
||||||
if len(jsonTag) == 0 {
|
if len(jsonTag) == 0 {
|
||||||
|
@ -69,21 +71,19 @@ func SafeJson(input interface{}, scope string) map[string]interface{} {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
trueValue := elem.Field(i).Interface()
|
|
||||||
|
|
||||||
if len(jsonTag) == 2 {
|
if len(jsonTag) == 2 {
|
||||||
if jsonTag[1] == "omitempty" && trueValue == "" {
|
if jsonTag[1] == "omitempty" && valueField.Interface() == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
thisData[jsonTag[0]] = trueValue
|
thisData[jsonTag[0]] = valueField.Interface()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if forTag(tags, scope) {
|
if forTag(tags, scope) {
|
||||||
thisData[jsonTag[0]] = trueValue
|
thisData[jsonTag[0]] = valueField.Interface()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return thisData
|
return thisData
|
||||||
|
|
|
@ -46,11 +46,11 @@ func reorderServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func apiServiceHandler(r *http.Request) interface{} {
|
func apiServiceHandler(r *http.Request) interface{} {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
servicer := core.SelectService(utils.ToInt(vars["id"])).Model()
|
servicer := core.SelectService(utils.ToInt(vars["id"]))
|
||||||
if servicer == nil {
|
if servicer == nil {
|
||||||
return errors.New("service not found")
|
return errors.New("service not found")
|
||||||
}
|
}
|
||||||
return *servicer
|
return servicer.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
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())
|
groupQuery := database.ParseQueries(r, service.Hits())
|
||||||
|
|
||||||
obj := core.GraphData(groupQuery, &types.Hit{}, database.ByAverage("latency"))
|
objs, err := groupQuery.GraphData(database.ByAverage("latency"))
|
||||||
returnJson(obj, w, r)
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
returnJson(objs, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiServiceFailureDataHandler(w http.ResponseWriter, r *http.Request) {
|
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)
|
sendErrorJson(errors.New("service data not found"), w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
groupQuery := database.ParseQueries(r, service.Hits())
|
|
||||||
|
|
||||||
obj := core.GraphData(groupQuery, &types.Failure{}, database.ByCount)
|
groupQuery := database.ParseQueries(r, service.Failures())
|
||||||
returnJson(obj, w, r)
|
|
||||||
|
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) {
|
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)
|
sendErrorJson(errors.New("service data not found"), w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
groupQuery := database.ParseQueries(r, service.Hits())
|
groupQuery := database.ParseQueries(r, service.Hits())
|
||||||
|
|
||||||
obj := core.GraphData(groupQuery, &types.Hit{}, database.ByAverage("ping_time"))
|
objs, err := groupQuery.GraphData(database.ByAverage("ping_time"))
|
||||||
returnJson(obj, w, r)
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
returnJson(objs, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
type dataXy struct {
|
type dataXy struct {
|
||||||
|
@ -190,10 +206,11 @@ func apiAllServicesHandler(r *http.Request) interface{} {
|
||||||
return joinServices(services)
|
return joinServices(services)
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinServices(srvs []database.Servicer) []*types.Service {
|
func joinServices(srvs []*core.Service) []*types.Service {
|
||||||
var services []*types.Service
|
var services []*types.Service
|
||||||
for _, v := range srvs {
|
for _, v := range srvs {
|
||||||
services = append(services, v.Model())
|
v.UpdateStats()
|
||||||
|
services = append(services, v.Service)
|
||||||
}
|
}
|
||||||
return services
|
return services
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
sample, _ := strconv.ParseBool(r.PostForm.Get("sample_data"))
|
sample, _ := strconv.ParseBool(r.PostForm.Get("sample_data"))
|
||||||
dir := utils.Directory
|
dir := utils.Directory
|
||||||
|
|
||||||
config := &types.DbConfig{
|
config := &core.DbConfig{DbConfig: &types.DbConfig{
|
||||||
DbConn: dbConn,
|
DbConn: dbConn,
|
||||||
DbHost: dbHost,
|
DbHost: dbHost,
|
||||||
DbUser: dbUser,
|
DbUser: dbUser,
|
||||||
|
@ -67,11 +67,11 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
Email: email,
|
Email: email,
|
||||||
Error: nil,
|
Error: nil,
|
||||||
Location: utils.Directory,
|
Location: utils.Directory,
|
||||||
}
|
}}
|
||||||
|
|
||||||
log.WithFields(utils.ToFields(core.CoreApp, config)).Debugln("new configs posted")
|
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)
|
log.Errorln(err)
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
return
|
return
|
||||||
|
@ -100,7 +100,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
core.CoreApp, err = core.CoreApp.InsertCore(config)
|
core.CoreApp, err = config.InsertCore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
|
@ -130,7 +130,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
Config *types.DbConfig `json:"config"`
|
Config *types.DbConfig `json:"config"`
|
||||||
}{
|
}{
|
||||||
"okokok",
|
"okokok",
|
||||||
config,
|
config.DbConfig,
|
||||||
}
|
}
|
||||||
returnJson(out, w, r)
|
returnJson(out, w, r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package notifiers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hunterlong/statping/core/notifier"
|
"github.com/hunterlong/statping/core/notifier"
|
||||||
|
"github.com/hunterlong/statping/database"
|
||||||
"github.com/hunterlong/statping/source"
|
"github.com/hunterlong/statping/source"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
|
@ -80,5 +81,5 @@ func injectDatabase() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
db.CreateTable(¬ifier.Notification{})
|
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 {
|
if _, err := os.Stat(folder + "/assets"); err == nil {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
if os.Getenv("USE_ASSETS") == "true" {
|
useAssets := utils.Getenv("USE_ASSETS", false).(bool)
|
||||||
|
|
||||||
|
if useAssets {
|
||||||
log.Infoln("Environment variable USE_ASSETS was found.")
|
log.Infoln("Environment variable USE_ASSETS was found.")
|
||||||
if err := CreateAllAssets(folder); err != nil {
|
if err := CreateAllAssets(folder); err != nil {
|
||||||
log.Warnln(err)
|
log.Warnln(err)
|
||||||
|
|
|
@ -36,15 +36,6 @@ type Checkin struct {
|
||||||
Failures []*Failure `gorm:"-" json:"failures"`
|
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
|
// CheckinHit is a successful response from a Checkin
|
||||||
type CheckinHit struct {
|
type CheckinHit struct {
|
||||||
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
||||||
|
|
|
@ -29,29 +29,31 @@ type AllNotifiers interface{}
|
||||||
// will be saved into 1 row in the 'core' table. You can use the core.CoreApp
|
// will be saved into 1 row in the 'core' table. You can use the core.CoreApp
|
||||||
// global variable to interact with the attributes to the application, such as services.
|
// global variable to interact with the attributes to the application, such as services.
|
||||||
type Core struct {
|
type Core struct {
|
||||||
Name string `gorm:"not null;column:name" json:"name"`
|
Name string `gorm:"not null;column:name" json:"name"`
|
||||||
Description string `gorm:"not null;column:description" json:"description,omitempty"`
|
Description string `gorm:"not null;column:description" json:"description,omitempty"`
|
||||||
ConfigFile string `gorm:"column:config" json:"-"`
|
ConfigFile string `gorm:"column:config" json:"-"`
|
||||||
ApiKey string `gorm:"column:api_key" json:"api_key" scope:"admin"`
|
ApiKey string `gorm:"column:api_key" json:"api_key" scope:"admin"`
|
||||||
ApiSecret string `gorm:"column:api_secret" json:"api_secret" scope:"admin"`
|
ApiSecret string `gorm:"column:api_secret" json:"api_secret" scope:"admin"`
|
||||||
Style string `gorm:"not null;column:style" json:"style,omitempty"`
|
Style string `gorm:"not null;column:style" json:"style,omitempty"`
|
||||||
Footer NullString `gorm:"column:footer" json:"footer"`
|
Footer NullString `gorm:"column:footer" json:"footer"`
|
||||||
Domain string `gorm:"not null;column:domain" json:"domain"`
|
Domain string `gorm:"not null;column:domain" json:"domain"`
|
||||||
Version string `gorm:"column:version" json:"version"`
|
Version string `gorm:"column:version" json:"version"`
|
||||||
Setup bool `gorm:"-" json:"setup"`
|
Setup bool `gorm:"-" json:"setup"`
|
||||||
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
|
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
|
||||||
UseCdn NullBool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
|
UseCdn NullBool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
|
||||||
Timezone float32 `gorm:"column:timezone;default:-8.0" json:"timezone,omitempty"`
|
Timezone float32 `gorm:"column:timezone;default:-8.0" json:"timezone,omitempty"`
|
||||||
LoggedIn bool `gorm:"-" json:"logged_in"`
|
LoggedIn bool `gorm:"-" json:"logged_in"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||||
Started time.Time `gorm:"-" json:"started_on"`
|
Started time.Time `gorm:"-" json:"started_on"`
|
||||||
Plugins []*Info `gorm:"-" json:"-"`
|
Plugins []*Info `gorm:"-" json:"-"`
|
||||||
Repos []PluginJSON `gorm:"-" json:"-"`
|
Notifications []AllNotifiers `gorm:"-" json:"-"`
|
||||||
AllPlugins []PluginActions `gorm:"-" json:"-"`
|
Config *DbConfig `gorm:"-" json:"-"`
|
||||||
Notifications []AllNotifiers `gorm:"-" json:"-"`
|
Integrations []Integrator `gorm:"-" json:"-"`
|
||||||
Config *DbConfig `gorm:"-" json:"-"`
|
}
|
||||||
Integrations []Integrator `gorm:"-" json:"-"`
|
|
||||||
|
func (Core) TableName() string {
|
||||||
|
return "core"
|
||||||
}
|
}
|
||||||
|
|
||||||
type Servicer interface {
|
type Servicer interface {
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,46 +21,46 @@ import (
|
||||||
|
|
||||||
// Service is the main struct for Services
|
// Service is the main struct for Services
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
||||||
Name string `gorm:"column:name" json:"name"`
|
Name string `gorm:"column:name" json:"name"`
|
||||||
Domain string `gorm:"column:domain" json:"domain" private:"true" scope:"user,admin"`
|
Domain string `gorm:"column:domain" json:"domain" private:"true" scope:"user,admin"`
|
||||||
Expected NullString `gorm:"column:expected" json:"expected" scope:"user,admin"`
|
Expected NullString `gorm:"column:expected" json:"expected" scope:"user,admin"`
|
||||||
ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status" scope:"user,admin"`
|
ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status" scope:"user,admin"`
|
||||||
Interval int `gorm:"default:30;column:check_interval" json:"check_interval" scope:"user,admin"`
|
Interval int `gorm:"default:30;column:check_interval" json:"check_interval" scope:"user,admin"`
|
||||||
Type string `gorm:"column:check_type" json:"type" scope:"user,admin"`
|
Type string `gorm:"column:check_type" json:"type" scope:"user,admin"`
|
||||||
Method string `gorm:"column:method" json:"method" scope:"user,admin"`
|
Method string `gorm:"column:method" json:"method" scope:"user,admin"`
|
||||||
PostData NullString `gorm:"column:post_data" json:"post_data" scope:"user,admin"`
|
PostData NullString `gorm:"column:post_data" json:"post_data" scope:"user,admin"`
|
||||||
Port int `gorm:"not null;column:port" json:"port" scope:"user,admin"`
|
Port int `gorm:"not null;column:port" json:"port" scope:"user,admin"`
|
||||||
Timeout int `gorm:"default:30;column:timeout" json:"timeout" scope:"user,admin"`
|
Timeout int `gorm:"default:30;column:timeout" json:"timeout" scope:"user,admin"`
|
||||||
Order int `gorm:"default:0;column:order_id" json:"order_id"`
|
Order int `gorm:"default:0;column:order_id" json:"order_id"`
|
||||||
VerifySSL NullBool `gorm:"default:false;column:verify_ssl" json:"verify_ssl" scope:"user,admin"`
|
VerifySSL NullBool `gorm:"default:false;column:verify_ssl" json:"verify_ssl" scope:"user,admin"`
|
||||||
Public NullBool `gorm:"default:true;column:public" json:"public"`
|
Public NullBool `gorm:"default:true;column:public" json:"public"`
|
||||||
GroupId int `gorm:"default:0;column:group_id" json:"group_id"`
|
GroupId int `gorm:"default:0;column:group_id" json:"group_id"`
|
||||||
Headers NullString `gorm:"column:headers" json:"headers" scope:"user,admin"`
|
Headers NullString `gorm:"column:headers" json:"headers" scope:"user,admin"`
|
||||||
Permalink NullString `gorm:"column:permalink" json:"permalink"`
|
Permalink NullString `gorm:"column:permalink" json:"permalink"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||||
Online bool `gorm:"-" json:"online"`
|
Online bool `gorm:"-" json:"online"`
|
||||||
Latency float64 `gorm:"-" json:"latency"`
|
Latency float64 `gorm:"-" json:"latency"`
|
||||||
PingTime float64 `gorm:"-" json:"ping_time"`
|
PingTime float64 `gorm:"-" json:"ping_time"`
|
||||||
Online24Hours float32 `gorm:"-" json:"online_24_hours"`
|
Online24Hours float32 `gorm:"-" json:"online_24_hours"`
|
||||||
Online7Days float32 `gorm:"-" json:"online_7_days"`
|
Online7Days float32 `gorm:"-" json:"online_7_days"`
|
||||||
AvgResponse float64 `gorm:"-" json:"avg_response"`
|
AvgResponse float64 `gorm:"-" json:"avg_response"`
|
||||||
FailuresLast24Hours int `gorm:"-" json:"failures_24_hours"`
|
FailuresLast24Hours int `gorm:"-" json:"failures_24_hours"`
|
||||||
Running chan bool `gorm:"-" json:"-"`
|
Running chan bool `gorm:"-" json:"-"`
|
||||||
Checkpoint time.Time `gorm:"-" json:"-"`
|
Checkpoint time.Time `gorm:"-" json:"-"`
|
||||||
SleepDuration time.Duration `gorm:"-" json:"-"`
|
SleepDuration time.Duration `gorm:"-" json:"-"`
|
||||||
LastResponse string `gorm:"-" json:"-"`
|
LastResponse string `gorm:"-" json:"-"`
|
||||||
AllowNotifications NullBool `gorm:"default:true;column:allow_notifications" json:"allow_notifications" scope:"user,admin"`
|
AllowNotifications NullBool `gorm:"default:true;column:allow_notifications" json:"allow_notifications" scope:"user,admin"`
|
||||||
UserNotified bool `gorm:"-" json:"-"` // True if the User was already notified about a Downtime
|
UserNotified bool `gorm:"-" json:"-"` // True if the User was already notified about a Downtime
|
||||||
UpdateNotify NullBool `gorm:"default:true;column:notify_all_changes" json:"notify_all_changes" scope:"user,admin"` // This Variable is a simple copy of `core.CoreApp.UpdateNotify.Bool`
|
UpdateNotify NullBool `gorm:"default:true;column:notify_all_changes" json:"notify_all_changes" scope:"user,admin"` // This Variable is a simple copy of `core.CoreApp.UpdateNotify.Bool`
|
||||||
DownText string `gorm:"-" json:"-"` // Contains the current generated Downtime Text
|
DownText string `gorm:"-" json:"-"` // Contains the current generated Downtime Text
|
||||||
SuccessNotified bool `gorm:"-" json:"-"` // Is 'true' if the user has already be informed that the Services now again available
|
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"`
|
LastStatusCode int `gorm:"-" json:"status_code"`
|
||||||
LastOnline time.Time `gorm:"-" json:"last_success"`
|
LastOnline time.Time `gorm:"-" json:"last_success"`
|
||||||
Failures []Failure `gorm:"-" json:"failures,omitempty" scope:"user,admin"`
|
Failures []*Failure `gorm:"-" json:"failures,omitempty" scope:"user,admin"`
|
||||||
Checkins []CheckinInterface `gorm:"-" json:"checkins,omitempty" scope:"user,admin"`
|
Checkins []*Checkin `gorm:"-" json:"checkins,omitempty" scope:"user,admin"`
|
||||||
Stats *Stats `gorm:"-" json:"stats,omitempty"`
|
Stats *Stats `gorm:"-" json:"stats,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckinInterface interface {
|
type CheckinInterface interface {
|
||||||
|
@ -72,8 +72,8 @@ type Stater interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
Failures uint64 `gorm:"-" json:"failures,omitempty"`
|
Failures int `gorm:"-" json:"failures"`
|
||||||
Hits uint64 `gorm:"-" json:"hits,omitempty"`
|
Hits int `gorm:"-" json:"hits"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeforeCreate for Service will set CreatedAt to UTC
|
// BeforeCreate for Service will set CreatedAt to UTC
|
||||||
|
@ -85,6 +85,10 @@ func (s *Service) BeforeCreate() (err error) {
|
||||||
return
|
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
|
// Start will create a channel for the service checking go routine
|
||||||
func (s *Service) Start() {
|
func (s *Service) Start() {
|
||||||
s.Running = make(chan bool)
|
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
|
// init will set the utils.Directory to the current running directory, or STATPING_DIR if it is set
|
||||||
func init() {
|
func init() {
|
||||||
if os.Getenv("STATPING_DIR") != "" {
|
defaultDir, err := os.Getwd()
|
||||||
Directory = os.Getenv("STATPING_DIR")
|
if err != nil {
|
||||||
} else {
|
defaultDir = "."
|
||||||
dir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
Directory = "."
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Directory = dir
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Directory = Getenv("STATPING_DIR", defaultDir).(string)
|
||||||
|
|
||||||
// check if logs are disabled
|
// check if logs are disabled
|
||||||
logger := os.Getenv("DISABLE_LOGS")
|
disableLogs = Getenv("DISABLE_LOGS", false).(bool)
|
||||||
disableLogs, _ = strconv.ParseBool(logger)
|
|
||||||
if disableLogs {
|
if disableLogs {
|
||||||
Log.Out = ioutil.Discard
|
Log.Out = ioutil.Discard
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Debugln("current working directory: ", Directory)
|
Log.Debugln("current working directory: ", Directory)
|
||||||
Log.AddHook(new(hook))
|
Log.AddHook(new(hook))
|
||||||
Log.SetNoLock()
|
Log.SetNoLock()
|
||||||
checkVerboseMode()
|
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{} {
|
func SliceConvert(g []*interface{}) []interface{} {
|
||||||
var arr []interface{}
|
var arr []interface{}
|
||||||
for _, v := range g {
|
for _, v := range g {
|
||||||
|
|
Loading…
Reference in New Issue