ironsmith/datastore/ds.go

173 lines
3.5 KiB
Go

// Copyright 2016 Tim Shannon. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.
// Package datastore manages the bolt data files and the reading and writing data to them
package datastore
import (
"encoding/json"
"errors"
"time"
"github.com/boltdb/bolt"
)
//TODO: Move this all over to GobStore if I ever get around to finishing it
// ErrNotFound is the error returned when a value cannot be found in the store for the given key
var ErrNotFound = errors.New("Value not found in datastore")
// Store is a datastore for getting and setting data for a given ironsmith project
// run on top of a Bolt DB file
type Store struct {
bolt *bolt.DB
}
// Open opens an existing datastore file, or creates a new one
// caller is responsible for closing the datastore
func Open(filename string) (*Store, error) {
db, err := bolt.Open(filename, 0666, &bolt.Options{Timeout: 1 * time.Minute})
if err != nil {
return nil, err
}
store := &Store{
bolt: db,
}
err = store.bolt.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(bucketLog))
if err != nil {
return err
}
_, err = tx.CreateBucketIfNotExists([]byte(bucketReleases))
if err != nil {
return err
}
_, err = tx.CreateBucketIfNotExists([]byte(bucketFiles))
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
return store, nil
}
// Close closes the bolt datastore
func (ds *Store) Close() error {
return ds.bolt.Close()
}
// TrimVersions Removes versions from the datastore file until it reaches the maxVersions count
func (ds *Store) TrimVersions(maxVersions int) error {
if maxVersions <= 0 {
// no max set
return nil
}
versions, err := ds.Versions()
if err != nil {
return err
}
if len(versions) <= maxVersions {
return nil
}
remove := versions[maxVersions:]
for i := range remove {
err = ds.deleteVersion(remove[i].Version)
if err != nil {
return err
}
}
return nil
}
// removes the earliest instance of a specific version
func (ds *Store) deleteVersion(version string) error {
return ds.bolt.Update(func(tx *bolt.Tx) error {
// remove all logs for this version
c := tx.Bucket([]byte(bucketLog)).Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
lg := &Log{}
err := json.Unmarshal(v, lg)
if err != nil {
return err
}
if lg.Version != version {
break
}
err = c.Delete()
if err != nil {
return err
}
}
// remove all releases for this version
release, err := ds.Release(version)
if err == ErrNotFound {
return nil
}
if err != nil {
return err
}
err = tx.Bucket([]byte(bucketReleases)).Delete(release.FileKey.Bytes())
if err != nil {
return err
}
// remove release file for this version
err = tx.Bucket([]byte(bucketFiles)).Delete(release.FileKey.Bytes())
if err != nil {
return err
}
return nil
})
}
func (ds *Store) get(bucket string, key []byte, result interface{}) error {
return ds.bolt.View(func(tx *bolt.Tx) error {
dsValue := tx.Bucket([]byte(bucket)).Get(key)
if dsValue == nil {
return ErrNotFound
}
return json.Unmarshal(dsValue, result)
})
}
func (ds *Store) put(bucket string, key []byte, value interface{}) error {
var err error
dsValue, ok := value.([]byte)
if !ok {
dsValue, err = json.Marshal(value)
if err != nil {
return err
}
}
return ds.bolt.Update(func(tx *bolt.Tx) error {
return tx.Bucket([]byte(bucket)).Put(key, dsValue)
})
}