// Statping // Copyright (C) 2018. Hunter Long and the project contributors // Written by Hunter Long 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 . package core import ( "fmt" "github.com/hunterlong/statping/core/notifier" "github.com/hunterlong/statping/database" "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...") if err := insertSampleGroups(); err != nil { return err } createdOn := time.Now().Add(((-24 * 30) * 3) * time.Hour).UTC() s1 := &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 := &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 := &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 := &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 := &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, } if _, err := database.Create(s1); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } if _, err := database.Create(s2); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } if _, err := database.Create(s3); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } if _, err := database.Create(s4); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } if _, err := database.Create(s5); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } if _, err := SelectAllServices(false); err != nil { return types.ErrWrap(err, types.ErrorServiceSelection) } 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") return nil } func insertSampleIncidents() error { incident1 := &types.Incident{ Title: "Github Downtime", Description: "This is an example of a incident for a service.", ServiceId: 2, } if _, err := database.Create(incident1); err != nil { return types.ErrWrap(err, types.ErrorCreateIncidentUp) } incidentUpdate1 := &types.IncidentUpdate{ IncidentId: incident1.Id, Message: "Github's page for Statping seems to be sending a 501 error.", Type: "Investigating", } if _, err := database.Create(incidentUpdate1); err != nil { return types.ErrWrap(err, types.ErrorCreateIncidentUp) } incidentUpdate2 := &types.IncidentUpdate{ IncidentId: incident1.Id, Message: "Problem is continuing and we are looking at the issues.", Type: "Update", } if _, err := database.Create(incidentUpdate2); err != nil { return types.ErrWrap(err, types.ErrorCreateIncidentUp) } incidentUpdate3 := &types.IncidentUpdate{ IncidentId: incident1.Id, Message: "Github is now back online and everything is working.", Type: "Resolved", } if _, err := database.Create(incidentUpdate3); err != nil { return types.ErrWrap(err, types.ErrorCreateIncidentUp) } return nil } func insertSampleGroups() error { group1 := &types.Group{ Name: "Main Services", Public: types.NewNullBool(true), Order: 2, } if _, err := database.Create(group1); err != nil { return types.ErrWrap(err, types.ErrorCreateGroup) } group2 := &types.Group{ Name: "Linked Services", Public: types.NewNullBool(false), Order: 1, } if _, err := database.Create(group2); err != nil { return types.ErrWrap(err, types.ErrorCreateGroup) } group3 := &types.Group{ Name: "Empty Group", Public: types.NewNullBool(false), Order: 3, } if _, err := database.Create(group3); err != nil { return types.ErrWrap(err, types.ErrorCreateGroup) } return nil } // insertSampleCheckins will create 2 checkins with 60 successful hits per Checkin func insertSampleCheckins() error { s1 := SelectService(1) checkin1 := &types.Checkin{ ServiceId: s1.Id, Interval: 300, GracePeriod: 300, } if _, err := database.Create(checkin1); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } s2 := SelectService(1) checkin2 := &types.Checkin{ ServiceId: s2.Id, Interval: 900, GracePeriod: 300, } if _, err := database.Create(checkin2); err != nil { return types.ErrWrap(err, types.ErrorCreateCheckinHit) } checkTime := time.Now().UTC().Add(-24 * time.Hour) for i := 0; i <= 60; i++ { checkHit := &types.CheckinHit{ Checkin: checkin1.Id, From: "192.168.0.1", CreatedAt: checkTime.UTC(), } if _, err := database.Create(checkHit); err != nil { return types.ErrWrap(err, types.ErrorCreateCheckinHit) } checkTime = checkTime.Add(10 * time.Minute) } return nil } // InsertSampleHits will create a couple new hits for the sample services func InsertSampleHits() error { tx := Database(&Hit{}).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() if err := tx.Commit().Error(); err != nil { log.Errorln(err) return types.ErrWrap(err, types.ErrorCreateSampleHits) } return nil } // 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), Footer: types.NewNullString(""), } if _, err := database.Create(core); err != nil { return types.ErrWrap(err, types.ErrorCreateCore) } return nil } // insertSampleUsers will create 2 admin users for a seed database func insertSampleUsers() error { u2 := &types.User{ Username: "testadmin", Password: "password123", Email: "info@betatude.com", Admin: types.NewNullBool(true), } if _, err := database.Create(u2); err != nil { return types.ErrWrap(err, types.ErrorCreateUser) } u3 := &types.User{ Username: "testadmin2", Password: "password123", Email: "info@adminhere.com", Admin: types.NewNullBool(true), } if _, err := database.Create(u3); err != nil { return types.ErrWrap(err, types.ErrorCreateUser) } return nil } func insertMessages() error { m1 := &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 := database.Create(m1); err != nil { return types.ErrWrap(err, types.ErrorCreateMessage) } m2 := &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 := database.Create(m2); err != nil { return types.ErrWrap(err, types.ErrorCreateMessage) } 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 := &types.Service{ Name: "JSON Lint", Domain: "https://jsonlint.com", ExpectedStatus: 200, Interval: 15, Type: "http", Method: "GET", Timeout: 10, Order: 6, CreatedAt: createdOn, } if _, err := database.Create(s6); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } s7 := &types.Service{ Name: "Demo Page", Domain: "https://demo.statping.com", ExpectedStatus: 200, Interval: 30, Type: "http", Method: "GET", Timeout: 15, Order: 7, CreatedAt: createdOn, } if _, err := database.Create(s7); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } s8 := &types.Service{ Name: "Golang", Domain: "https://golang.org", ExpectedStatus: 200, Interval: 15, Type: "http", Method: "GET", Timeout: 10, Order: 8, } if _, err := database.Create(s8); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } s9 := &types.Service{ Name: "Santa Monica", Domain: "https://www.santamonica.com", ExpectedStatus: 200, Interval: 15, Type: "http", Method: "GET", Timeout: 10, Order: 9, CreatedAt: createdOn, } if _, err := database.Create(s9); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } s10 := &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, } if _, err := database.Create(s10); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } s11 := &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, } if _, err := database.Create(s11); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } s12 := &types.Service{ Name: "Github", Domain: "https://github.com/hunterlong", ExpectedStatus: 200, Interval: 60, Type: "http", Method: "GET", Timeout: 20, Order: 12, CreatedAt: createdOn, } if _, err := database.Create(s12); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } s13 := &types.Service{ Name: "Failing URL", Domain: "http://thisdomainisfakeanditsgoingtofail.com", ExpectedStatus: 200, Interval: 45, Type: "http", Method: "GET", Timeout: 10, Order: 13, CreatedAt: createdOn, } if _, err := database.Create(s13); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } s14 := &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, } if _, err := database.Create(s14); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } s15 := &types.Service{ Name: "Gorm", Domain: "http://gorm.io/", ExpectedStatus: 200, Interval: 30, Type: "http", Method: "GET", Timeout: 12, Order: 15, CreatedAt: createdOn, } if _, err := database.Create(s15); err != nil { return types.ErrWrap(err, types.ErrorCreateService) } 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, } database.Create(failure) } } } // insertHitRecords will create successful Hit records for 15 services func insertHitRecords(since time.Time, amount int) error { for i := int64(1); i <= 15; i++ { service := SelectService(i) log.Infoln(fmt.Sprintf("Adding %v hit records to service %v", amount, service.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, } if _, err := database.Create(hit); err != nil { return types.ErrWrap(err, types.ErrorCreateHit, service.Id) } } } return nil } // TmpRecords is used for testing Statping. It will create a SQLite database file // with sample data and store it in the /tmp folder to be used by the tests. func TmpRecords(dbFile string) error { var sqlFile = utils.Directory + "/" + dbFile if err := utils.CreateDirectory(utils.Directory + "/tmp"); err != nil { log.Error(err) } var tmpSqlFile = utils.Directory + "/tmp/" + types.SqliteFilename SampleHits = 480 var err error CoreApp = NewCore() CoreApp.Name = "Tester" CoreApp.Setup = true configs := &DbConfig{&types.DbConfig{ DbConn: "sqlite", Project: "Tester", Location: utils.Directory, SqlFile: sqlFile, }} log.Infoln("saving config.yml in: " + utils.Directory) if err := configs.Save(); err != nil { log.Error(err) } log.Infoln("loading config.yml from: " + utils.Directory) if configs, err = LoadConfigFile(utils.Directory); err != nil { log.Error(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.Error(err) } if err := utils.CopyFile(tmpSqlFile, sqlFile); err != nil { log.Error(err) } log.Infoln("loading config.yml from: " + utils.Directory) if err := CoreApp.Connect(false, utils.Directory); err != nil { log.Error(err) } log.Infoln("selecting the Core variable") if _, err := SelectCore(); err != nil { log.Error(err) } log.Infoln("inserting notifiers into database") if err := InsertNotifierDB(); err != nil { log.Error(err) } log.Infoln("inserting integrations into database") if err := InsertIntegratorDB(); err != nil { log.Error(err) } log.Infoln("loading all services") if _, err := SelectAllServices(false); err != nil { return err } if err := AttachNotifiers(); err != nil { log.Error(err) } if err := AddIntegrations(); err != nil { log.Error(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 := 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 }