Skip to content

Commit

Permalink
Added a status API to check database
Browse files Browse the repository at this point in the history
The API makes a direct request to the database
for its schema version to ensure the connection
is still active. This can be used with monitoring
software to ensure the database connection doesn't
break again.

This endpoint can be expanded to include memory
information and other status stuff.

Users must have the ViewDebugInfo permission which
is given to all full admins and any one in the
APIStatusUsers group.
  • Loading branch information
lfkeitel committed Feb 6, 2018
1 parent 525b6fd commit c09027b
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 14 deletions.
5 changes: 3 additions & 2 deletions src/common/config.go
Expand Up @@ -101,6 +101,7 @@ type Config struct {
ReadOnlyUsers []string
APIReadOnlyUsers []string
APIReadWriteUsers []string
APIStatusUsers []string

LDAP struct {
UseAD bool
Expand Down Expand Up @@ -240,8 +241,8 @@ func setSensibleDefaults(c *Config) (*Config, error) {
if len(c.Auth.HelpDeskUsers) == 0 {
c.Auth.HelpDeskUsers = []string{"helpdesk"}
}
if len(c.Auth.HelpDeskUsers) == 0 {
c.Auth.HelpDeskUsers = []string{"readonly"}
if len(c.Auth.ReadOnlyUsers) == 0 {
c.Auth.ReadOnlyUsers = []string{"readonly"}
}

// DHCP
Expand Down
10 changes: 10 additions & 0 deletions src/common/environment.go
Expand Up @@ -104,3 +104,13 @@ type DatabaseAccessor struct {
*sql.DB
Driver string
}

func (d *DatabaseAccessor) SchemaVersion() int {
var currDBVer int
verRow := d.DB.QueryRow(`SELECT "value" FROM "settings" WHERE "id" = 'db_version'`)
if verRow == nil {
return 0
}
verRow.Scan(&currDBVer)
return currDBVer
}
41 changes: 41 additions & 0 deletions src/controllers/api/status.go
@@ -0,0 +1,41 @@
package api

import (
"net/http"

"github.com/julienschmidt/httprouter"
"github.com/packet-guardian/packet-guardian/src/common"
"github.com/packet-guardian/packet-guardian/src/db"
"github.com/packet-guardian/packet-guardian/src/models"
)

type Status struct {
e *common.Environment
}

func NewStatusController(e *common.Environment) *Status {
return &Status{e: e}
}

func (s *Status) GetStatus(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
sessionUser := models.GetUserFromContext(r)

if !sessionUser.Can(models.ViewDebugInfo) {
w.WriteHeader(http.StatusUnauthorized)
return
}

dbVer := s.e.DB.SchemaVersion()
dbStatus := "ok"
if dbVer != db.DBVersion {
dbStatus = "warning"
}

data := map[string]interface{}{
"database_version": dbVer,
"database_status": dbStatus,
"database_type": s.e.DB.Driver,
}

common.NewAPIResponse("", data).WriteResponse(w, http.StatusOK)
}
2 changes: 1 addition & 1 deletion src/db/databaseCommon.go
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/packet-guardian/packet-guardian/src/common"
)

const dbVersion = 2
const DBVersion = 2

type dbInit interface {
init(*common.DatabaseAccessor, *common.Config) error
Expand Down
12 changes: 6 additions & 6 deletions src/db/databaseMySQL.go
Expand Up @@ -124,19 +124,19 @@ func (m *mySQLDB) migrateTables(d *common.DatabaseAccessor) error {

common.SystemLogger.WithFields(verbose.Fields{
"current-version": currDBVer,
"active-version": dbVersion,
"active-version": DBVersion,
}).Debug("Database Versions")

// No migration needed
if currDBVer == dbVersion {
if currDBVer == DBVersion {
return nil
}

if currDBVer > dbVersion {
if currDBVer > DBVersion {
return errors.New("Database is too new, can't rollback")
}

neededMigrations := m.migrateFuncs[currDBVer:dbVersion]
neededMigrations := m.migrateFuncs[currDBVer:DBVersion]
for _, migrate := range neededMigrations {
if migrate == nil {
continue
Expand All @@ -146,7 +146,7 @@ func (m *mySQLDB) migrateTables(d *common.DatabaseAccessor) error {
}
}

_, err := d.DB.Exec(`UPDATE "settings" SET "value" = ? WHERE "id" = 'db_version'`, dbVersion)
_, err := d.DB.Exec(`UPDATE "settings" SET "value" = ? WHERE "id" = 'db_version'`, DBVersion)
return err
}

Expand Down Expand Up @@ -234,7 +234,7 @@ func (m *mySQLDB) createSettingTable(d *common.DatabaseAccessor) error {
return err
}

_, err := d.DB.Exec(`INSERT INTO "settings" ("id", "value") VALUES ('db_version', ?)`, dbVersion)
_, err := d.DB.Exec(`INSERT INTO "settings" ("id", "value") VALUES ('db_version', ?)`, DBVersion)
return err
}

Expand Down
10 changes: 5 additions & 5 deletions src/db/databaseSQLite.go
Expand Up @@ -104,15 +104,15 @@ func (s *sqliteDB) migrateTables(d *common.DatabaseAccessor) error {

common.SystemLogger.WithFields(verbose.Fields{
"current-version": currDBVer,
"active-version": dbVersion,
"active-version": DBVersion,
}).Debug("Database Versions")

// No migration needed
if currDBVer == dbVersion {
if currDBVer == DBVersion {
return nil
}

neededMigrations := s.migrateFuncs[currDBVer:dbVersion]
neededMigrations := s.migrateFuncs[currDBVer:DBVersion]
for _, migrate := range neededMigrations {
if migrate == nil {
continue
Expand All @@ -122,7 +122,7 @@ func (s *sqliteDB) migrateTables(d *common.DatabaseAccessor) error {
}
}

_, err := d.DB.Exec(`UPDATE "settings" SET "value" = ? WHERE "id" = 'db_version'`, dbVersion)
_, err := d.DB.Exec(`UPDATE "settings" SET "value" = ? WHERE "id" = 'db_version'`, DBVersion)
return err
}

Expand Down Expand Up @@ -210,7 +210,7 @@ func (s *sqliteDB) createSettingTable(d *common.DatabaseAccessor) error {
return err
}

_, err := d.DB.Exec(`INSERT INTO "settings" ("id", "value") VALUES ('db_version', ?)`, dbVersion)
_, err := d.DB.Exec(`INSERT INTO "settings" ("id", "value") VALUES ('db_version', ?)`, DBVersion)
return err
}

Expand Down
3 changes: 3 additions & 0 deletions src/models/user.go
Expand Up @@ -84,6 +84,9 @@ func (u *User) LoadRights() {
u.Rights = u.Rights.With(APIRead)
u.Rights = u.Rights.With(APIWrite)
}
if common.StringInSlice(u.Username, u.e.Config.Auth.APIStatusUsers) {
u.Rights = u.Rights.With(ViewDebugInfo)
}

if u.IsBlacklisted() {
u.Rights = u.Rights.Without(ManageOwnRights)
Expand Down
3 changes: 3 additions & 0 deletions src/server/routes.go
Expand Up @@ -173,6 +173,9 @@ func apiRouter(e *common.Environment) http.Handler {
r.POST("/api/user", userAPIController.UserHandler)
r.DELETE("/api/user", userAPIController.UserHandler)

statusAPIController := api.NewStatusController(e)
r.GET("/api/status", statusAPIController.GetStatus)

return mid.CheckAuthAPI(r)
}

Expand Down

0 comments on commit c09027b

Please sign in to comment.