Skip to content
Permalink
Browse files

Preparing storage in boltdb

  • Loading branch information...
Depado committed Feb 21, 2016
1 parent 5c9b56f commit 5926aba63d2d85d5f43c2fba184b91f3385ffdbb
Showing with 373 additions and 24 deletions.
  1. +0 −1 configuration/configuration.go
  2. +129 −0 database/database.go
  3. +194 −0 database/database_test.go
  4. +0 −6 models/service.go
  5. +50 −17 templates/index.tmpl
@@ -97,7 +97,6 @@ func (c Configuration) Parse() (models.Services, error) {
BuildAPI: buildAPI,
BuildURL: buildURL,
RepoURL: repoURL,
Up: s.URL == "",
Icon: "/static/custom/" + s.Icon,
}
}
@@ -0,0 +1,129 @@
package database

import (
"fmt"
"time"

"github.com/boltdb/bolt"
)

// Storage is a type that contains a bolt.DB and a boolean that indicates if the connection is already open or not.
type Storage struct {
DB *bolt.DB
Path string
Opened bool
}

// Open opens the database connection and create the file if necessary
func (s *Storage) Open() error {
var err error
dbfile := s.Path
config := &bolt.Options{Timeout: 1 * time.Second}
s.DB, err = bolt.Open(dbfile, 0600, config)
if err == nil {
s.Opened = true
}
return err
}

// Close closes the connection (or at least attempts to)
func (s *Storage) Close() error {
s.Opened = false
err := s.DB.Close()
return err
}

// Storable is the type of data that can be stored/retrieved from the database.
type Storable interface {
Encode() ([]byte, error)
Decode([]byte) error
}

// Save saves some data inside the bucket at the specified key.
func (s Storage) Save(bucket, key string, data Storable) error {
if !s.Opened {
return fmt.Errorf("db must be opened before saving")
}
err := s.DB.Update(func(tx *bolt.Tx) error {
mBucket, err := tx.CreateBucketIfNotExists([]byte(bucket))
if err != nil {
return fmt.Errorf("Error creating bucket : %s", err)
}
enc, err := data.Encode()
if err != nil {
return fmt.Errorf("Could not encode : %s", err)
}
err = mBucket.Put([]byte(key), enc)
return err
})
return err
}

// Delete deletes data inside the bucket at the specified key.
func (s Storage) Delete(bucket, key string) error {
if !s.Opened {
return fmt.Errorf("db must be opened before using it")
}
err := s.DB.Update(func(tx *bolt.Tx) error {
mBucket := tx.Bucket([]byte(bucket))

if mBucket != nil {
err := mBucket.Delete([]byte(key))
return err
}
return nil
})
return err
}

// Get retrieves the specific Storable object from bucket and key
func (s Storage) Get(bucket, key string, to Storable) error {
if !s.Opened {
return fmt.Errorf("Database must be opened first.")
}
err := s.DB.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucket))
k := []byte(key)
err := to.Decode(b.Get(k))
return err
})
return err
}

// List keys
func (s Storage) List(bucket string, to *[]string) error {
if !s.Opened {
return fmt.Errorf("Database must be opened first.")
}
err := s.DB.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucket))
if b != nil {
err := b.ForEach(func(k, _ []byte) error {
*to = append(*to, fmt.Sprintf("%s", k))
return nil
})
return err
}
return nil
})
return err
}

// CreateBucket creates a bucket if it doesn't exist.
func (s Storage) CreateBucket(bucket string) error {
if !s.Opened {
return fmt.Errorf("db must be opened before creating bucket")
}
err := s.DB.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(bucket))
if err != nil {
return fmt.Errorf("Error creating bucket : %s", err)
}
return nil
})
return err
}

// MonitStorage is the general storage associated to the bot.
// It should be available to any plugin, middleware or any other part of the program.
var MonitStorage = Storage{}
@@ -0,0 +1,194 @@
package database

import (
"encoding/json"
"reflect"
"testing"
)

const bucketName = "testing"

var testStorage = Storage{Path: "data.db"}

// TestStorage represents a single command.
type TestStorage struct {
Name string
Value string
}

// Encode encodes a chain to json.
func (s *TestStorage) Encode() ([]byte, error) {
enc, err := json.Marshal(s)
if err != nil {
return nil, err
}
return enc, nil
}

// Decode decodes json to TestStorage
func (s *TestStorage) Decode(data []byte) error {
if err := json.Unmarshal(data, s); err != nil {
return err
}
return nil
}

// TestOpenCloseDB tests the database Open and Close functions
func TestOpenCloseDB(t *testing.T) {
if testStorage.Opened {
if err := testStorage.Close(); err != nil {
t.Error("Error while closing the database or `Opened` attribute set to true while it should not be")
}
}
if testStorage.Opened {
t.Error("The attribute `Opened` should be set to false and is not")
}
if err := testStorage.Open(); err != nil {
t.Error("Error while opening the database")
}
if !testStorage.Opened {
t.Error("The attribute `Opened` should be set to true and is not")
}
/*
// Induces boltdb crash : panic: runtime error: invalid memory address or nil pointer dereference
if err := testStorage.Open(); err.Error() != "timeout" {
t.Error("Error `timeout` should have been reported while opening already opened db instead of : %v", err)
}
*/
if err := testStorage.Close(); err != nil {
t.Error("Error while closing the database")
}
if testStorage.Opened {
t.Error("The attribute `Opened` should be set to false and is not")
}
}

// TestCreateBucket tests the CreateBucket function
func TestCreateBucket(t *testing.T) {
if testStorage.Opened {
if err := testStorage.Close(); err != nil {
t.Error("Could not close the db :", err)
}
}

if err := testStorage.CreateBucket(bucketName); err.Error() != "db must be opened before creating bucket" {
t.Errorf("Error not reported correctly, db is not opened : %v", err.Error())
}
if err := testStorage.Open(); err != nil {
t.Errorf("Cant open db for testing : %v", err)
}

if err := testStorage.CreateBucket(bucketName); err != nil {
t.Error("Could not create bucket")
}
}

// TestSave tests the Save function
func TestSave(t *testing.T) {
s := TestStorage{Name: "test", Value: "value"}

if err := testStorage.Save(bucketName, s.Name, &s); err != nil {
t.Errorf("Error while saving data to bucket : %v", err)
}
if err := testStorage.Close(); err != nil {
t.Skip("Unable to close the db properly in save test")
}

ts := TestStorage{Name: "test1", Value: "value1"}
if err := testStorage.Save(bucketName, ts.Name, &ts); err.Error() != "db must be opened before saving" {
t.Errorf("Error not reported correctly (db must be opened before operation) : %v", err)
}
}

// TestGet tests the Get function
func TestGet(t *testing.T) {
s := TestStorage{Name: "test"}
if testStorage.Opened {
if err := testStorage.Close(); err != nil {
t.Skip("Unable to close the db properly in Get test")
}
}
if err := testStorage.Get(bucketName, s.Name, &s); err.Error() != "Database must be opened first." {
t.Errorf("Error not reported correctly (db must be opened before operation) : %v", err)
}

if err := testStorage.Open(); err != nil {
t.Skip("Unable to open the db properly in Get test")
}

if err := testStorage.Get(bucketName, s.Name, &s); err != nil {
t.Errorf("Decode error while testing the Get function")
}

if s.Value != "value" {
t.Errorf("Wrong value got with Get()")
}
}

// TestDelete tests the deletion of a command
func TestDelete(t *testing.T) {
if testStorage.Opened {
if err := testStorage.Close(); err != nil {
t.Skip("Unable to close the db properly while testing the Delete function")
}
}
s := TestStorage{Name: "test"}
if err := testStorage.Delete(bucketName, s.Name); err.Error() != "db must be opened before using it" {
t.Errorf("Error should be reported : db not opened")
}
if err := testStorage.Open(); err != nil {
t.Skip("Unable to open db in deletion test")
}
if err := testStorage.Delete(bucketName, s.Name); err != nil {
t.Errorf("Error while deleting key : %v", err)
}

if err := testStorage.Delete("veryImprobableBucketName", s.Name); err != nil {
t.Errorf("Error reported on non existing bucket : %v", err)
}
}

// TestList tests the List function
func TestList(t *testing.T) {
var list []string

if testStorage.Opened {
if err := testStorage.Close(); err != nil {
t.Skip("Unable to close the db properly while testing the List function")
}
}

if err := testStorage.List(bucketName, &list); err.Error() != "Database must be opened first." {
t.Errorf("Error should be reported (db not opened) instead of : %v", err)
}

if err := testStorage.Open(); err != nil {
t.Skip("Unable to open the db properly while testing the List function")
}

keys := []string{"t1", "t2", "t3", "t4", "t5"}
BatchSave(keys)

if err := testStorage.List(bucketName, &list); err != nil {
t.Errorf("Error while listing bucket keys")
}

if !reflect.DeepEqual(list, keys) {
t.Errorf("Incorrect list returned")
}

if err := testStorage.List("reallyImprobableBucketName", &list); err != nil {
t.Errorf("Error reported instead of `nil` with non existing bucket")
}
}

func BatchSave(keys []string) error {
var t TestStorage
for _, k := range keys {
t = TestStorage{Name: k}
if err := testStorage.Save(bucketName, k, &t); err != nil {
return err
}
}
return nil
}
@@ -23,10 +23,8 @@ type Service struct {
Last string
RespTime time.Duration
Status int
Up bool
Icon string
LastBuilds Builds
LastBuild string
LastCommits Commits
}

@@ -40,19 +38,16 @@ func (s *Service) CheckStatus(client *http.Client) {
}()
req, err := http.NewRequest("GET", s.URL, nil)
if err != nil {
s.Up = false
log.Printf("[%s][ERROR] While building request : %v\n", s.Name, err)
return
}
resp, err := client.Do(req)
if err != nil {
s.Up = false
log.Printf("[%s][ERROR] While requesting : %v\n", s.Name, err)
return
}
defer resp.Body.Close()
s.Status = resp.StatusCode
s.Up = s.Status == 200
}

// CheckBuild checks the last build
@@ -78,7 +73,6 @@ func (s *Service) CheckBuild(client *http.Client) {
pall[i] = b.Parse()
}
s.LastBuilds = pall
s.LastBuild = pall[0].Status
}

// Check updates the status of the Host

0 comments on commit 5926aba

Please sign in to comment.
You can’t perform that action at this time.