mirror of https://github.com/statping/statping
608 lines
16 KiB
Go
608 lines
16 KiB
Go
// 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 core
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/hunterlong/statping/core/notifier"
|
|
"github.com/hunterlong/statping/types"
|
|
"github.com/hunterlong/statping/utils"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
sampleStart = time.Now().Add((-24 * 7) * time.Hour).UTC()
|
|
SampleHits = 9900.
|
|
)
|
|
|
|
// InsertSampleData will create the example/dummy services for a brand new Statping installation
|
|
func InsertSampleData() error {
|
|
log.Infoln("Inserting Sample Data...")
|
|
|
|
insertSampleGroups()
|
|
createdOn := time.Now().Add(((-24 * 30) * 3) * time.Hour).UTC()
|
|
s1 := ReturnService(&types.Service{
|
|
Name: "Google",
|
|
Domain: "https://google.com",
|
|
ExpectedStatus: 200,
|
|
Interval: 10,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 10,
|
|
Order: 1,
|
|
GroupId: 1,
|
|
Permalink: types.NewNullString("google"),
|
|
VerifySSL: types.NewNullBool(true),
|
|
CreatedAt: createdOn,
|
|
})
|
|
s2 := ReturnService(&types.Service{
|
|
Name: "Statping Github",
|
|
Domain: "https://github.com/hunterlong/statping",
|
|
ExpectedStatus: 200,
|
|
Interval: 30,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 20,
|
|
Order: 2,
|
|
Permalink: types.NewNullString("statping_github"),
|
|
VerifySSL: types.NewNullBool(true),
|
|
CreatedAt: createdOn,
|
|
})
|
|
s3 := ReturnService(&types.Service{
|
|
Name: "JSON Users Test",
|
|
Domain: "https://jsonplaceholder.typicode.com/users",
|
|
ExpectedStatus: 200,
|
|
Interval: 60,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 30,
|
|
Order: 3,
|
|
Public: types.NewNullBool(true),
|
|
VerifySSL: types.NewNullBool(true),
|
|
GroupId: 2,
|
|
CreatedAt: createdOn,
|
|
})
|
|
s4 := ReturnService(&types.Service{
|
|
Name: "JSON API Tester",
|
|
Domain: "https://jsonplaceholder.typicode.com/posts",
|
|
ExpectedStatus: 201,
|
|
Expected: types.NewNullString(`(title)": "((\\"|[statping])*)"`),
|
|
Interval: 30,
|
|
Type: "http",
|
|
Method: "POST",
|
|
PostData: types.NewNullString(`{ "title": "statping", "body": "bar", "userId": 19999 }`),
|
|
Timeout: 30,
|
|
Order: 4,
|
|
Public: types.NewNullBool(true),
|
|
VerifySSL: types.NewNullBool(true),
|
|
GroupId: 2,
|
|
CreatedAt: createdOn,
|
|
})
|
|
s5 := ReturnService(&types.Service{
|
|
Name: "Google DNS",
|
|
Domain: "8.8.8.8",
|
|
Interval: 20,
|
|
Type: "tcp",
|
|
Port: 53,
|
|
Timeout: 120,
|
|
Order: 5,
|
|
Public: types.NewNullBool(true),
|
|
GroupId: 1,
|
|
CreatedAt: createdOn,
|
|
})
|
|
|
|
s1.Create(false)
|
|
s2.Create(false)
|
|
s3.Create(false)
|
|
s4.Create(false)
|
|
s5.Create(false)
|
|
|
|
insertMessages()
|
|
|
|
insertSampleIncidents()
|
|
|
|
log.Infoln("Sample data has finished importing")
|
|
|
|
return nil
|
|
}
|
|
|
|
func insertSampleIncidents() error {
|
|
incident1 := &Incident{&types.Incident{
|
|
Title: "Github Downtime",
|
|
Description: "This is an example of a incident for a service.",
|
|
ServiceId: 2,
|
|
}}
|
|
_, err := incident1.Create()
|
|
|
|
incidentUpdate1 := &IncidentUpdate{&types.IncidentUpdate{
|
|
IncidentId: incident1.Id,
|
|
Message: "Github's page for Statping seems to be sending a 501 error.",
|
|
Type: "Investigating",
|
|
}}
|
|
_, err = incidentUpdate1.Create()
|
|
|
|
incidentUpdate2 := &IncidentUpdate{&types.IncidentUpdate{
|
|
IncidentId: incident1.Id,
|
|
Message: "Problem is continuing and we are looking at the issues.",
|
|
Type: "Update",
|
|
}}
|
|
_, err = incidentUpdate2.Create()
|
|
|
|
incidentUpdate3 := &IncidentUpdate{&types.IncidentUpdate{
|
|
IncidentId: incident1.Id,
|
|
Message: "Github is now back online and everything is working.",
|
|
Type: "Resolved",
|
|
}}
|
|
_, err = incidentUpdate3.Create()
|
|
|
|
return err
|
|
}
|
|
|
|
func insertSampleGroups() error {
|
|
group1 := &Group{&types.Group{
|
|
Name: "Main Services",
|
|
Public: types.NewNullBool(true),
|
|
Order: 2,
|
|
}}
|
|
_, err := group1.Create()
|
|
group2 := &Group{&types.Group{
|
|
Name: "Linked Services",
|
|
Public: types.NewNullBool(false),
|
|
Order: 1,
|
|
}}
|
|
_, err = group2.Create()
|
|
group3 := &Group{&types.Group{
|
|
Name: "Empty Group",
|
|
Public: types.NewNullBool(false),
|
|
Order: 3,
|
|
}}
|
|
_, err = group3.Create()
|
|
return err
|
|
}
|
|
|
|
// insertSampleCheckins will create 2 checkins with 60 successful hits per Checkin
|
|
func insertSampleCheckins() error {
|
|
s1 := SelectService(1)
|
|
checkin1 := ReturnCheckin(&types.Checkin{
|
|
ServiceId: s1.Id,
|
|
Interval: 300,
|
|
GracePeriod: 300,
|
|
})
|
|
checkin1.Update()
|
|
|
|
s2 := SelectService(1)
|
|
checkin2 := ReturnCheckin(&types.Checkin{
|
|
ServiceId: s2.Id,
|
|
Interval: 900,
|
|
GracePeriod: 300,
|
|
})
|
|
checkin2.Update()
|
|
|
|
checkTime := time.Now().UTC().Add(-24 * time.Hour)
|
|
for i := 0; i <= 60; i++ {
|
|
checkHit := ReturnCheckinHit(&types.CheckinHit{
|
|
Checkin: checkin1.Id,
|
|
From: "192.168.0.1",
|
|
CreatedAt: checkTime.UTC(),
|
|
})
|
|
checkHit.Create()
|
|
checkTime = checkTime.Add(10 * time.Minute)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// InsertSampleHits will create a couple new hits for the sample services
|
|
func InsertSampleHits() error {
|
|
tx := hitsDB().Begin()
|
|
sg := new(sync.WaitGroup)
|
|
for i := int64(1); i <= 5; i++ {
|
|
sg.Add(1)
|
|
service := SelectService(i)
|
|
seed := time.Now().UnixNano()
|
|
log.Infoln(fmt.Sprintf("Adding %v sample hit records to service %v", SampleHits, service.Name))
|
|
createdAt := sampleStart
|
|
p := utils.NewPerlin(2., 2., 10, seed)
|
|
go func() {
|
|
defer sg.Done()
|
|
for hi := 0.; hi <= float64(SampleHits); hi++ {
|
|
latency := p.Noise1D(hi / 500)
|
|
createdAt = createdAt.Add(60 * time.Second)
|
|
hit := &types.Hit{
|
|
Service: service.Id,
|
|
CreatedAt: createdAt,
|
|
Latency: latency,
|
|
}
|
|
tx = tx.Create(&hit)
|
|
}
|
|
}()
|
|
}
|
|
sg.Wait()
|
|
err := tx.Commit().Error()
|
|
if err != nil {
|
|
log.Errorln(err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// insertSampleCore will create a new Core for the seed
|
|
func insertSampleCore() error {
|
|
core := &types.Core{
|
|
Name: "Statping Sample Data",
|
|
Description: "This data is only used to testing",
|
|
ApiKey: "sample",
|
|
ApiSecret: "samplesecret",
|
|
Domain: "http://localhost:8080",
|
|
Version: "test",
|
|
CreatedAt: time.Now().UTC(),
|
|
UseCdn: types.NewNullBool(false),
|
|
}
|
|
query := coreDB().Create(core)
|
|
return query.Error()
|
|
}
|
|
|
|
// insertSampleUsers will create 2 admin users for a seed database
|
|
func insertSampleUsers() error {
|
|
u2 := ReturnUser(&types.User{
|
|
Username: "testadmin",
|
|
Password: "password123",
|
|
Email: "info@betatude.com",
|
|
Admin: types.NewNullBool(true),
|
|
})
|
|
|
|
u3 := ReturnUser(&types.User{
|
|
Username: "testadmin2",
|
|
Password: "password123",
|
|
Email: "info@adminhere.com",
|
|
Admin: types.NewNullBool(true),
|
|
})
|
|
|
|
_, err := u2.Create()
|
|
_, err = u3.Create()
|
|
return err
|
|
}
|
|
|
|
func insertMessages() error {
|
|
m1 := ReturnMessage(&types.Message{
|
|
Title: "Routine Downtime",
|
|
Description: "This is an example a upcoming message for a service!",
|
|
ServiceId: 1,
|
|
StartOn: time.Now().UTC().Add(15 * time.Minute),
|
|
EndOn: time.Now().UTC().Add(2 * time.Hour),
|
|
})
|
|
if _, err := m1.Create(); err != nil {
|
|
return err
|
|
}
|
|
m2 := ReturnMessage(&types.Message{
|
|
Title: "Server Reboot",
|
|
Description: "This is another example a upcoming message for a service!",
|
|
ServiceId: 3,
|
|
StartOn: time.Now().UTC().Add(15 * time.Minute),
|
|
EndOn: time.Now().UTC().Add(2 * time.Hour),
|
|
})
|
|
if _, err := m2.Create(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// InsertLargeSampleData will create the example/dummy services for testing the Statping server
|
|
func InsertLargeSampleData() error {
|
|
if err := insertSampleCore(); err != nil {
|
|
return err
|
|
}
|
|
if err := InsertSampleData(); err != nil {
|
|
return err
|
|
}
|
|
if err := insertSampleUsers(); err != nil {
|
|
return err
|
|
}
|
|
if err := insertSampleCheckins(); err != nil {
|
|
return err
|
|
}
|
|
if err := insertMessages(); err != nil {
|
|
return err
|
|
}
|
|
createdOn := time.Now().UTC().Add((-24 * 90) * time.Hour)
|
|
s6 := ReturnService(&types.Service{
|
|
Name: "JSON Lint",
|
|
Domain: "https://jsonlint.com",
|
|
ExpectedStatus: 200,
|
|
Interval: 15,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 10,
|
|
Order: 6,
|
|
CreatedAt: createdOn,
|
|
})
|
|
|
|
s7 := ReturnService(&types.Service{
|
|
Name: "Demo Page",
|
|
Domain: "https://demo.statping.com",
|
|
ExpectedStatus: 200,
|
|
Interval: 30,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 15,
|
|
Order: 7,
|
|
CreatedAt: createdOn,
|
|
})
|
|
|
|
s8 := ReturnService(&types.Service{
|
|
Name: "Golang",
|
|
Domain: "https://golang.org",
|
|
ExpectedStatus: 200,
|
|
Interval: 15,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 10,
|
|
Order: 8,
|
|
})
|
|
|
|
s9 := ReturnService(&types.Service{
|
|
Name: "Santa Monica",
|
|
Domain: "https://www.santamonica.com",
|
|
ExpectedStatus: 200,
|
|
Interval: 15,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 10,
|
|
Order: 9,
|
|
CreatedAt: createdOn,
|
|
})
|
|
|
|
s10 := ReturnService(&types.Service{
|
|
Name: "Oeschs Die Dritten",
|
|
Domain: "https://www.oeschs-die-dritten.ch/en/",
|
|
ExpectedStatus: 200,
|
|
Interval: 15,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 10,
|
|
Order: 10,
|
|
CreatedAt: createdOn,
|
|
})
|
|
|
|
s11 := ReturnService(&types.Service{
|
|
Name: "XS Project - Bochka, Bass, Kolbaser",
|
|
Domain: "https://www.youtube.com/watch?v=VLW1ieY4Izw",
|
|
ExpectedStatus: 200,
|
|
Interval: 60,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 20,
|
|
Order: 11,
|
|
CreatedAt: createdOn,
|
|
})
|
|
|
|
s12 := ReturnService(&types.Service{
|
|
Name: "Github",
|
|
Domain: "https://github.com/hunterlong",
|
|
ExpectedStatus: 200,
|
|
Interval: 60,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 20,
|
|
Order: 12,
|
|
CreatedAt: createdOn,
|
|
})
|
|
|
|
s13 := ReturnService(&types.Service{
|
|
Name: "Failing URL",
|
|
Domain: "http://thisdomainisfakeanditsgoingtofail.com",
|
|
ExpectedStatus: 200,
|
|
Interval: 45,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 10,
|
|
Order: 13,
|
|
CreatedAt: createdOn,
|
|
})
|
|
|
|
s14 := ReturnService(&types.Service{
|
|
Name: "Oesch's die Dritten - Die Jodelsprache",
|
|
Domain: "https://www.youtube.com/watch?v=k3GTxRt4iao",
|
|
ExpectedStatus: 200,
|
|
Interval: 60,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 12,
|
|
Order: 14,
|
|
CreatedAt: createdOn,
|
|
})
|
|
|
|
s15 := ReturnService(&types.Service{
|
|
Name: "Gorm",
|
|
Domain: "http://gorm.io/",
|
|
ExpectedStatus: 200,
|
|
Interval: 30,
|
|
Type: "http",
|
|
Method: "GET",
|
|
Timeout: 12,
|
|
Order: 15,
|
|
CreatedAt: createdOn,
|
|
})
|
|
|
|
s6.Create(false)
|
|
s7.Create(false)
|
|
s8.Create(false)
|
|
s9.Create(false)
|
|
s10.Create(false)
|
|
s11.Create(false)
|
|
s12.Create(false)
|
|
s13.Create(false)
|
|
s14.Create(false)
|
|
s15.Create(false)
|
|
|
|
var dayAgo = time.Now().UTC().Add((-24 * 90) * time.Hour)
|
|
|
|
insertHitRecords(dayAgo, 5450)
|
|
|
|
insertFailureRecords(dayAgo, 730)
|
|
|
|
return nil
|
|
}
|
|
|
|
// insertFailureRecords will create failures for 15 services from seed
|
|
func insertFailureRecords(since time.Time, amount int) {
|
|
for i := int64(14); i <= 15; i++ {
|
|
service := SelectService(i)
|
|
log.Infoln(fmt.Sprintf("Adding %v Failure records to service %v", amount, service.Name))
|
|
createdAt := since
|
|
|
|
for fi := 1; fi <= amount; fi++ {
|
|
createdAt = createdAt.Add(2 * time.Minute)
|
|
|
|
failure := &types.Failure{
|
|
Service: service.Id,
|
|
Issue: "testing right here",
|
|
CreatedAt: createdAt,
|
|
}
|
|
|
|
service.CreateFailure(failure)
|
|
}
|
|
}
|
|
}
|
|
|
|
// insertHitRecords will create successful Hit records for 15 services
|
|
func insertHitRecords(since time.Time, amount int) {
|
|
for i := int64(1); i <= 15; i++ {
|
|
service := SelectService(i)
|
|
log.Infoln(fmt.Sprintf("Adding %v hit records to service %v", amount, service.Name))
|
|
createdAt := since
|
|
p := utils.NewPerlin(2, 2, 5, time.Now().UnixNano())
|
|
for hi := 1; hi <= amount; hi++ {
|
|
latency := p.Noise1D(float64(hi / 10))
|
|
createdAt = createdAt.Add(1 * time.Minute)
|
|
hit := &types.Hit{
|
|
Service: service.Id,
|
|
CreatedAt: createdAt.UTC(),
|
|
Latency: latency,
|
|
}
|
|
service.CreateHit(hit)
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TmpRecords is used for testing Statping. It will create a SQLite database file
|
|
// with sample data and store it in the /tmp folder to be used by the tests.
|
|
func TmpRecords(dbFile string) error {
|
|
var sqlFile = utils.Directory + "/" + dbFile
|
|
utils.CreateDirectory(utils.Directory + "/tmp")
|
|
var tmpSqlFile = utils.Directory + "/tmp/" + types.SqliteFilename
|
|
SampleHits = 480
|
|
|
|
var err error
|
|
CoreApp = NewCore()
|
|
CoreApp.Name = "Tester"
|
|
configs := &types.DbConfig{
|
|
DbConn: "sqlite",
|
|
Project: "Tester",
|
|
Location: utils.Directory,
|
|
SqlFile: sqlFile,
|
|
}
|
|
log.Infoln("saving config.yml in: " + utils.Directory)
|
|
if configs, err = CoreApp.SaveConfig(configs); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("loading config.yml from: " + utils.Directory)
|
|
if configs, err = LoadConfigFile(utils.Directory); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("connecting to database")
|
|
|
|
exists := utils.FileExists(tmpSqlFile)
|
|
if exists {
|
|
log.Infoln(tmpSqlFile + " was found, copying the temp database to " + sqlFile)
|
|
if err := utils.DeleteFile(sqlFile); err != nil {
|
|
log.Infoln(sqlFile + " was not found")
|
|
}
|
|
if err := utils.CopyFile(tmpSqlFile, sqlFile); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("loading config.yml from: " + utils.Directory)
|
|
|
|
if err := CoreApp.Connect(false, utils.Directory); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("selecting the Core variable")
|
|
if _, err := SelectCore(); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("inserting notifiers into database")
|
|
if err := InsertNotifierDB(); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("inserting integrations into database")
|
|
if err := InsertIntegratorDB(); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("loading all services")
|
|
if _, err := CoreApp.SelectAllServices(false); err != nil {
|
|
return err
|
|
}
|
|
if err := AttachNotifiers(); err != nil {
|
|
return err
|
|
}
|
|
if err := AddIntegrations(); err != nil {
|
|
return err
|
|
}
|
|
CoreApp.Notifications = notifier.AllCommunications
|
|
return nil
|
|
}
|
|
|
|
log.Infoln(tmpSqlFile + " not found, creating a new database...")
|
|
|
|
if err := CoreApp.Connect(false, utils.Directory); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("creating database")
|
|
if err := CoreApp.CreateDatabase(); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("migrating database")
|
|
if err := CoreApp.MigrateDatabase(); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("insert large sample data into database")
|
|
if err := InsertLargeSampleData(); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("selecting the Core variable")
|
|
if CoreApp, err = SelectCore(); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("inserting notifiers into database")
|
|
if err := InsertNotifierDB(); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("inserting integrations into database")
|
|
if err := InsertIntegratorDB(); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("loading all services")
|
|
if _, err := CoreApp.SelectAllServices(false); err != nil {
|
|
return err
|
|
}
|
|
log.Infoln("copying sql database file to: " + tmpSqlFile)
|
|
if err := utils.CopyFile(sqlFile, tmpSqlFile); err != nil {
|
|
return err
|
|
}
|
|
return err
|
|
}
|