db fixed - plugins

pull/10/head v0.28.9
Hunter Long 2018-07-03 14:39:56 -07:00
parent 87f493bf4e
commit 91daa01825
12 changed files with 142 additions and 72 deletions

View File

@ -18,7 +18,7 @@ services:
env:
global:
- VERSION=0.28.8
- VERSION=0.28.9
- DB_HOST=localhost
- DB_USER=travis
- DB_PASS=

View File

@ -1,6 +1,6 @@
FROM alpine:latest
ENV VERSION=v0.28.8
ENV VERSION=v0.28.9
RUN apk --no-cache add libstdc++ ca-certificates
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \

View File

@ -3,7 +3,7 @@
</p>
<p align="center">
<b>Statup - Web and App Status Monitoring for Any Type of Project</b><br>
<a href="https://github.com/hunterlong/statup/wiki">View Wiki</a> | <a href="https://demo.statup.io">Demo</a> <br> <a href="https://github.com/hunterlong/statup/wiki/Docker">Docker</a> | <a href="https://github.com/hunterlong/statup/wiki/AWS-EC2">EC2</a> | <a href="https://github.com/hunterlong/statup/wiki/Heroku">Heroku</a> | <a href="https://github.com/hunterlong/statup/wiki/Mac">Mac</a> | <a href="https://github.com/hunterlong/statup/wiki/Linux">Linux</a> | <a href="https://github.com/hunterlong/statup/wiki/Windows">Windows</a><br>
<a href="https://github.com/hunterlong/statup/wiki">View Wiki</a> | <a href="https://demo.statup.io">Demo</a> <br> <a href="https://github.com/hunterlong/statup/wiki/Docker">Docker</a> | <a href="https://github.com/hunterlong/statup/wiki/AWS-EC2">EC2</a> | <a href="https://github.com/hunterlong/statup/wiki/Heroku">Heroku</a> | <a href="https://github.com/hunterlong/statup/wiki/Mac">Mac</a> | <a href="https://github.com/hunterlong/statup/wiki/Linux">Linux</a> | <a href="https://github.com/hunterlong/statup/wiki/Windows">Windows</a> | <a href="https://github.com/hunterlong/statup/wiki/Statup-Plugins">Plugins</a>
</p>
# Statup - Status Page & Monitoring Server

View File

@ -10,19 +10,19 @@ type PluginJSON types.PluginJSON
type PluginRepos types.PluginRepos
type Core struct {
Name string `db:"name" json:"name"`
Description string `db:"description" json:"name"`
Config string `db:"config" json:"-"`
ApiKey string `db:"api_key" json:"-"`
ApiSecret string `db:"api_secret" json:"-"`
Style string `db:"style" json:"-"`
Footer string `db:"footer" json:"-"`
Domain string `db:"domain" json:"domain,omitempty"`
Version string `db:"version" json:"version,omitempty"`
Services []*Service `json:"services,omitempty"`
Plugins []plugin.Info
Repos []PluginJSON
//PluginFields []PluginSelect
Name string `db:"name" json:"name"`
Description string `db:"description" json:"name"`
Config string `db:"config" json:"-"`
ApiKey string `db:"api_key" json:"-"`
ApiSecret string `db:"api_secret" json:"-"`
Style string `db:"style" json:"-"`
Footer string `db:"footer" json:"-"`
Domain string `db:"domain" json:"domain,omitempty"`
Version string `db:"version" json:"version,omitempty"`
Services []*Service `json:"services,omitempty"`
Plugins []plugin.Info
Repos []PluginJSON
AllPlugins []plugin.PluginActions
Communications []*types.Communication
OfflineAssets bool
}
@ -37,7 +37,6 @@ var (
TmplBox *rice.Box
EmailBox *rice.Box
SetupMode bool
AllPlugins []plugin.PluginActions
UsingAssets bool
)

View File

@ -39,11 +39,13 @@ func DbConnection(dbType string) error {
if Configs.Port == "" {
Configs.Port = "3306"
}
mysqlSettings = mysql.ConnectionURL{
Database: Configs.Database,
Host: Configs.Host,
User: Configs.User,
Password: Configs.Password,
Options: map[string]string{"parseTime": "true", "charset": "utf8"},
}
DbSession, err = mysql.Open(mysqlSettings)
if err != nil {
@ -67,7 +69,6 @@ func DbConnection(dbType string) error {
}
//dbSession.SetLogging(true)
dbServer = dbType
OnLoad(DbSession)
return err
}

View File

@ -10,19 +10,19 @@ import (
)
func OnLoad(db sqlbuilder.Database) {
for _, p := range AllPlugins {
for _, p := range CoreApp.AllPlugins {
p.OnLoad(db)
}
}
func OnSuccess(s *Service) {
for _, p := range AllPlugins {
for _, p := range CoreApp.AllPlugins {
p.OnSuccess(structs.Map(s))
}
}
func OnFailure(s *Service, f FailureData) {
for _, p := range AllPlugins {
for _, p := range CoreApp.AllPlugins {
p.OnFailure(structs.Map(s))
}
@ -61,37 +61,37 @@ func onFailureEmail(s *Service, f FailureData) {
}
func OnSettingsSaved(c *Core) {
for _, p := range AllPlugins {
for _, p := range CoreApp.AllPlugins {
p.OnSettingsSaved(structs.Map(c))
}
}
func OnNewUser(u *User) {
for _, p := range AllPlugins {
for _, p := range CoreApp.AllPlugins {
p.OnNewUser(structs.Map(u))
}
}
func OnNewService(s *Service) {
for _, p := range AllPlugins {
for _, p := range CoreApp.AllPlugins {
p.OnNewService(structs.Map(s))
}
}
func OnDeletedService(s *Service) {
for _, p := range AllPlugins {
for _, p := range CoreApp.AllPlugins {
p.OnDeletedService(structs.Map(s))
}
}
func OnUpdateService(s *Service) {
for _, p := range AllPlugins {
for _, p := range CoreApp.AllPlugins {
p.OnUpdatedService(structs.Map(s))
}
}
func SelectPlugin(name string) plugin.PluginActions {
for _, p := range AllPlugins {
for _, p := range CoreApp.AllPlugins {
if p.GetInfo().Name == name {
return p
}

View File

@ -17,6 +17,9 @@ func (s *Service) CreateFailure(data FailureData) (int64, error) {
s.Failures = append(s.Failures, fail)
col := DbSession.Collection("failures")
uuid, err := col.Insert(fail)
if err != nil {
utils.Log(3, err)
}
if uuid == nil {
return 0, err
}
@ -26,7 +29,10 @@ func (s *Service) CreateFailure(data FailureData) (int64, error) {
func (s *Service) SelectAllFailures() []*Failure {
var fails []*Failure
col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id")
col.All(&fails)
err := col.All(&fails)
if err != nil {
utils.Log(3, fmt.Sprintf("Issue getting failures for service %v, %v", s.Name, err))
}
return fails
}

View File

@ -125,28 +125,33 @@ func (s *Service) SmallText() string {
}
func (s *Service) GraphData() string {
var d []DateScan
var d []*DateScan
increment := "minute"
since := time.Now().Add(time.Hour*-12 + time.Minute*0 + time.Second*0)
since := time.Now().Add(time.Hour*-24 + time.Minute*0 + time.Second*0)
// group by interval sql query for postgres, mysql and sqlite
sql := fmt.Sprintf("SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM hits WHERE service=%v AND created_at > '%v' GROUP BY 1 ORDER BY date_trunc ASC;", increment, s.Id, since.Format(time.RFC3339))
if dbServer == "mysql" {
sql = fmt.Sprintf("SELECT CONCAT(DATE(created_at), ' ', %v(created_at)) AS created_at, AVG(latency)*1000 AS value FROM hits WHERE service=%v AND DATE_FORMAT(created_at, 'Y-m-d H:i:s') BETWEEN DATE_FORMAT(NOW() - INTERVAL 12 HOUR, 'Y-m-d H:i:s') AND DATE_FORMAT(NOW(), 'Y-m-d H:i:s') GROUP BY created_at", increment, s.Id)
sql = fmt.Sprintf("SELECT CONCAT(date_format(created_at, '%%Y-%%m-%%dT%%TZ')) AS created_at, AVG(latency)*1000 AS value FROM hits WHERE service=%v AND DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%TZ') BETWEEN DATE_FORMAT(NOW() - INTERVAL 12 HOUR, '%%Y-%%m-%%dT%%TZ') AND DATE_FORMAT(NOW(), '%%Y-%%m-%%dT%%TZ') GROUP BY created_at", s.Id)
} else if dbServer == "sqlite" {
sql = fmt.Sprintf("SELECT created_at, AVG(latency)*1000 as value FROM hits WHERE service=%v AND created_at >= '%v' GROUP BY strftime('%%m', created_at)", s.Id, since.Format(time.RFC3339))
fmt.Println(sql)
sql = fmt.Sprintf("SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', created_at), AVG(latency)*1000 as value FROM hits WHERE service=%v AND created_at >= '%v' GROUP BY strftime('%%m', created_at)", s.Id, since.Format(time.RFC3339))
}
dated, err := DbSession.Query(db.Raw(sql))
if err != nil {
utils.Log(2, err)
return ""
}
for dated.Next() {
var gd DateScan
gd := new(DateScan)
var tt string
var ff float64
dated.Scan(&gd.CreatedAt, &ff)
err := dated.Scan(&tt, &ff)
if err != nil {
utils.Log(2, fmt.Sprintf("Issue loading chart data for service %v, %v", s.Name, err))
}
gd.CreatedAt, err = time.Parse(time.RFC3339, tt)
if err != nil {
utils.Log(2, fmt.Sprintf("Issue parsing time %v", err))
}
gd.Value = int64(ff)
d = append(d, gd)
}

23
main.go
View File

@ -82,11 +82,12 @@ func ForEachPlugin() {
}
func LoadPlugins() {
utils.Log(1, fmt.Sprintf("Loading any available Plugins from /plugins directory"))
if _, err := os.Stat("./plugins"); os.IsNotExist(err) {
os.Mkdir("./plugins", os.ModePerm)
}
ForEachPlugin()
//ForEachPlugin()
files, err := ioutil.ReadDir("./plugins")
if err != nil {
@ -94,33 +95,39 @@ func LoadPlugins() {
return
}
for _, f := range files {
utils.Log(1, fmt.Sprintf("Attempting to load plugin '%v'", f.Name()))
ext := strings.Split(f.Name(), ".")
if len(ext) != 2 {
utils.Log(3, fmt.Sprintf("Plugin '%v' must end in .so extension", f.Name()))
continue
}
if ext[1] != "so" {
utils.Log(3, fmt.Sprintf("Plugin '%v' must end in .so extension", f.Name()))
continue
}
plug, err := plg.Open("plugins/" + f.Name())
if err != nil {
utils.Log(2, fmt.Sprintf("Plugin '%v' could not load correctly.\n", f.Name()))
utils.Log(3, fmt.Sprintf("Plugin '%v' could not load correctly. %v", f.Name(), err))
continue
}
symPlugin, err := plug.Lookup("Plugin")
if err != nil {
utils.Log(3, fmt.Sprintf("Plugin '%v' could not load correctly. %v", f.Name(), err))
continue
}
var plugActions plugin.PluginActions
plugActions, ok := symPlugin.(plugin.PluginActions)
if !ok {
utils.Log(2, fmt.Sprintf("Plugin '%v' could not load correctly, error: %v\n", f.Name(), "unexpected type from module symbol"))
utils.Log(3, fmt.Sprintf("Plugin '%v' could not load correctly, error: %v", f.Name(), err))
continue
}
//allPlugins = append(allPlugins, plugActions)
plugActions.OnLoad(core.DbSession)
core.CoreApp.Plugins = append(core.CoreApp.Plugins, plugActions.GetInfo())
core.CoreApp.AllPlugins = append(core.CoreApp.AllPlugins, plugActions)
}
core.OnLoad(core.DbSession)
//utils.Log(1, fmt.Sprintf("Loaded %v Plugins\n", len(allPlugins)))
ForEachPlugin()
utils.Log(1, fmt.Sprintf("Loaded %v Plugins\n", len(core.CoreApp.Plugins)))
}

View File

@ -13,6 +13,7 @@ import (
"os"
"strings"
"testing"
"time"
)
var (
@ -24,19 +25,13 @@ func RunInit(t *testing.T) {
RenderBoxes()
os.Remove("./statup.db")
os.Remove("./config.yml")
os.Remove("./index.html")
route = handlers.Router()
LoadDotEnvs()
}
var forceSequential chan bool = make(chan bool, 1)
type databaseTest struct {
in string
out string
}
var dbTests []databaseTest
func TestRunAll(t *testing.T) {
databases := []string{"mysql", "sqlite", "postgres"}
@ -63,9 +58,12 @@ func TestRunAll(t *testing.T) {
t.Run(dbt+" Select Comms", func(t *testing.T) {
RunSelectAllMysqlCommunications(t)
})
t.Run(dbt+" Create User", func(t *testing.T) {
t.Run(dbt+" Create Users", func(t *testing.T) {
RunUser_Create(t)
})
t.Run(dbt+" Select Users", func(t *testing.T) {
RunUser_SelectAll(t)
})
t.Run(dbt+" Select Services", func(t *testing.T) {
RunSelectAllServices(t)
})
@ -87,7 +85,7 @@ func TestRunAll(t *testing.T) {
t.Run(dbt+" Chart Data", func(t *testing.T) {
RunService_GraphData(t)
})
t.Run(dbt+" Create Service", func(t *testing.T) {
t.Run(dbt+" Create Failing Service", func(t *testing.T) {
RunBadService_Create(t)
})
t.Run(dbt+" Check Service", func(t *testing.T) {
@ -96,9 +94,18 @@ func TestRunAll(t *testing.T) {
t.Run(dbt+" Select Hits", func(t *testing.T) {
RunService_Hits(t)
})
t.Run(dbt+" Select Failures", func(t *testing.T) {
RunService_Failures(t)
})
t.Run(dbt+" Select Limited Hits", func(t *testing.T) {
RunService_LimitedHits(t)
})
t.Run(dbt+" Delete Service", func(t *testing.T) {
RunDeleteService(t)
})
t.Run(dbt+" Delete User", func(t *testing.T) {
RunUser_Delete(t)
})
t.Run(dbt+" HTTP /", func(t *testing.T) {
RunIndexHandler(t)
})
@ -211,6 +218,8 @@ func RunSelectCoreMYQL(t *testing.T, db string) {
core.CoreApp, err = core.SelectCore()
assert.Nil(t, err)
assert.Equal(t, "Testing "+db, core.CoreApp.Name)
assert.NotEmpty(t, core.CoreApp.ApiKey)
assert.NotEmpty(t, core.CoreApp.ApiSecret)
assert.Equal(t, VERSION, core.CoreApp.Version)
}
@ -228,6 +237,12 @@ func RunSelectAllMysqlCommunications(t *testing.T) {
assert.Equal(t, 2, len(comms))
}
func RunUser_SelectAll(t *testing.T) {
users, err := core.SelectAllUsers()
assert.Nil(t, err)
assert.Equal(t, 2, len(users))
}
func RunUser_Create(t *testing.T) {
user := &core.User{
Username: "admin",
@ -236,7 +251,24 @@ func RunUser_Create(t *testing.T) {
}
id, err := user.Create()
assert.Nil(t, err)
assert.NotZero(t, id)
assert.Equal(t, int64(1), id)
user2 := &core.User{
Username: "superadmin",
Password: "admin",
Email: "info@adminer.com",
Admin: true,
}
id, err = user2.Create()
assert.Nil(t, err)
assert.Equal(t, int64(2), id)
}
func RunUser_Delete(t *testing.T) {
user, err := core.SelectUser(2)
assert.Nil(t, err)
assert.NotNil(t, user)
err = user.Delete()
assert.Nil(t, err)
}
func RunSelectAllServices(t *testing.T) {
@ -266,6 +298,7 @@ func RunService_Create(t *testing.T) {
id, err := service.Create()
assert.Nil(t, err)
assert.Equal(t, int64(5), id)
t.Log(service)
}
func RunService_AvgTime(t *testing.T) {
@ -286,6 +319,9 @@ func RunService_GraphData(t *testing.T) {
service := core.SelectService(1)
assert.NotNil(t, service)
data := service.GraphData()
t.Log(data)
assert.NotEqual(t, "null", data)
assert.False(t, strings.Contains(data, "0001-01-01T00:00:00Z"))
assert.NotEmpty(t, data)
}
@ -310,15 +346,24 @@ func RunBadService_Check(t *testing.T) {
assert.Equal(t, "JSON API Tester", service.Name)
}
func RunDeleteService(t *testing.T) {
service := core.SelectService(4)
assert.NotNil(t, service)
assert.Equal(t, "JSON API Tester", service.Name)
err := service.Delete()
assert.Nil(t, err)
}
func RunCreateService_Hits(t *testing.T) {
services, err := core.SelectAllServices()
assert.Nil(t, err)
assert.NotNil(t, services)
for i := 0; i <= 2; i++ {
for i := 0; i <= 10; i++ {
for _, s := range services {
service := s.Check()
assert.NotNil(t, service)
}
time.Sleep(1 * time.Second)
}
}
@ -330,6 +375,13 @@ func RunService_Hits(t *testing.T) {
assert.NotZero(t, len(hits))
}
func RunService_Failures(t *testing.T) {
t.SkipNow()
service := core.SelectService(6)
assert.NotNil(t, service)
assert.NotEmpty(t, service.Failures)
}
func RunService_LimitedHits(t *testing.T) {
service := core.SelectService(1)
assert.NotNil(t, service)
@ -363,7 +415,7 @@ func RunPrometheusHandler(t *testing.T) {
rr := httptest.NewRecorder()
route.ServeHTTP(rr, req)
t.Log(rr.Body.String())
assert.True(t, strings.Contains(rr.Body.String(), "statup_total_services 6"))
assert.True(t, strings.Contains(rr.Body.String(), "statup_total_services 5"))
}
func RunFailingPrometheusHandler(t *testing.T) {

View File

@ -17,7 +17,7 @@ CREATE TABLE users (
api_key VARCHAR(50),
api_secret VARCHAR(50),
administrator BOOL NOT NULL DEFAULT '0',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX (id)
);
CREATE TABLE services (
@ -32,34 +32,34 @@ CREATE TABLE services (
check_interval int(11),
post_data text,
order_id integer,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX (id)
);
CREATE TABLE hits (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
service INTEGER NOT NULL,
latency float,
created_at TIMESTAMP,
created_at DATETIME,
INDEX (id, service),
FOREIGN KEY (service) REFERENCES services(id)
FOREIGN KEY (service) REFERENCES services(id) ON DELETE CASCADE
);
CREATE TABLE failures (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
issue text,
method text,
service INTEGER NOT NULL,
created_at TIMESTAMP,
created_at DATETIME,
INDEX (id, service),
FOREIGN KEY (service) REFERENCES services(id)
FOREIGN KEY (service) REFERENCES services(id) ON DELETE CASCADE
);
CREATE TABLE checkins (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
service INTEGER NOT NULL,
check_interval integer,
api text,
created_at TIMESTAMP,
created_at DATETIME,
INDEX (id, service),
FOREIGN KEY (service) REFERENCES services(id)
FOREIGN KEY (service) REFERENCES services(id) ON DELETE CASCADE
);
CREATE TABLE communication (
id SERIAL PRIMARY KEY,
@ -75,5 +75,5 @@ CREATE TABLE communication (
enabled BOOL NOT NULL DEFAULT '0',
removable BOOL NOT NULL DEFAULT '0',
limits integer,
created_at TIMESTAMP
created_at DATETIME
);

View File

@ -18,7 +18,7 @@ CREATE TABLE users (
api_key text,
api_secret text,
administrator bool,
created_at TIMESTAMP
created_at DATETIME
);
CREATE TABLE services (
@ -33,14 +33,14 @@ CREATE TABLE services (
check_interval integer,
post_data text,
order_id integer,
created_at TIMESTAMP
created_at DATETIME
);
CREATE TABLE hits (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
latency float,
created_at TIMESTAMP
created_at DATETIME
);
CREATE TABLE failures (
@ -48,7 +48,7 @@ CREATE TABLE failures (
issue text,
method text,
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
created_at TIMESTAMP
created_at DATETIME
);
CREATE TABLE checkins (
@ -56,7 +56,7 @@ CREATE TABLE checkins (
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
check_interval integer,
api text,
created_at TIMESTAMP
created_at DATETIME
);
CREATE TABLE communication (
@ -73,7 +73,7 @@ CREATE TABLE communication (
enabled boolean,
removable boolean,
limits integer,
created_at TIMESTAMP
created_at DATETIME
);