mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-12 17:34:28 -05:00
4abb483902
Improved unit & integration tests for store, by making them independent and test a single thing.
198 lines
4.2 KiB
Go
198 lines
4.2 KiB
Go
package store
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// StdStore is the standard implementation of the Store interface.
|
|
type StdStore struct {
|
|
data map[string]string
|
|
mut, txmut sync.Mutex
|
|
}
|
|
|
|
// NewStdStore creates and initializes a new StdStore object.
|
|
func NewStdStore() *StdStore {
|
|
s := &StdStore{
|
|
data: make(map[string]string),
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// Get retrieves a value from StdStore by Type and Name.
|
|
func (s *StdStore) Get(request string) ([]byte, error) {
|
|
s.mut.Lock()
|
|
value, ok := s.data[request]
|
|
s.mut.Unlock()
|
|
|
|
if ok {
|
|
return []byte(value), nil
|
|
}
|
|
return nil, &ValueUnsetError{requestedValue: request}
|
|
}
|
|
|
|
// Put saves a value in StdStore by Type and Name.
|
|
func (s *StdStore) Put(request string, requestData []byte) error {
|
|
tx, err := s.BeginTransaction()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback()
|
|
if err := tx.Put(request, requestData); err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
|
|
func (s *StdStore) Delete(key string) error {
|
|
tx, err := s.BeginTransaction()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback()
|
|
if err := tx.Delete(key); err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
|
|
// Iterator returns an iterator for keys saved in StdStore with a given prefix.
|
|
// For an empty prefix this is an iterator for all keys in StdStore.
|
|
func (s *StdStore) Iterator(prefix string) (Iterator, error) {
|
|
keys := make([]string, 0)
|
|
s.mut.Lock()
|
|
for k := range s.data {
|
|
if strings.HasPrefix(k, prefix) {
|
|
keys = append(keys, k)
|
|
}
|
|
}
|
|
s.mut.Unlock()
|
|
|
|
return &StdIterator{0, keys}, nil
|
|
}
|
|
|
|
// BeginTransaction starts a new transaction.
|
|
func (s *StdStore) BeginTransaction() (Transaction, error) {
|
|
tx := stdTransaction{
|
|
store: s,
|
|
data: map[string]string{},
|
|
ongoingTransaction: true,
|
|
}
|
|
s.txmut.Lock()
|
|
|
|
s.mut.Lock()
|
|
for k, v := range s.data {
|
|
tx.data[k] = v
|
|
}
|
|
s.mut.Unlock()
|
|
|
|
return &tx, nil
|
|
}
|
|
|
|
func (s *StdStore) commit(data map[string]string) error {
|
|
s.mut.Lock()
|
|
s.data = data
|
|
s.mut.Unlock()
|
|
|
|
s.txmut.Unlock()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *StdStore) Transfer(newstore Store) error {
|
|
s.mut.Lock()
|
|
// copy key:value pairs from the old storage into etcd
|
|
for key, value := range s.data {
|
|
if err := newstore.Put(key, []byte(value)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
s.mut.Unlock()
|
|
return nil
|
|
}
|
|
|
|
type stdTransaction struct {
|
|
store *StdStore
|
|
data map[string]string
|
|
ongoingTransaction bool
|
|
}
|
|
|
|
// Get retrieves a value.
|
|
func (t *stdTransaction) Get(request string) ([]byte, error) {
|
|
if !t.ongoingTransaction {
|
|
return nil, &TransactionAlreadyCommittedError{op: "Get"}
|
|
}
|
|
if value, ok := t.data[request]; ok {
|
|
return []byte(value), nil
|
|
}
|
|
return nil, &ValueUnsetError{requestedValue: request}
|
|
}
|
|
|
|
// Put saves a value.
|
|
func (t *stdTransaction) Put(request string, requestData []byte) error {
|
|
if !t.ongoingTransaction {
|
|
return &TransactionAlreadyCommittedError{op: "Put"}
|
|
}
|
|
t.data[request] = string(requestData)
|
|
return nil
|
|
}
|
|
|
|
func (t *stdTransaction) Delete(key string) error {
|
|
if !t.ongoingTransaction {
|
|
return &TransactionAlreadyCommittedError{op: "Delete"}
|
|
}
|
|
delete(t.data, key)
|
|
return nil
|
|
}
|
|
|
|
// Iterator returns an iterator for all keys in the transaction with a given prefix.
|
|
func (t *stdTransaction) Iterator(prefix string) (Iterator, error) {
|
|
keys := make([]string, 0)
|
|
for k := range t.data {
|
|
if strings.HasPrefix(k, prefix) {
|
|
keys = append(keys, k)
|
|
}
|
|
}
|
|
|
|
return &StdIterator{0, keys}, nil
|
|
}
|
|
|
|
// Commit ends a transaction and persists the changes.
|
|
func (t *stdTransaction) Commit() error {
|
|
if err := t.store.commit(t.data); err != nil {
|
|
return err
|
|
}
|
|
t.store = nil
|
|
t.ongoingTransaction = false
|
|
return nil
|
|
}
|
|
|
|
// Rollback aborts a transaction.
|
|
func (t *stdTransaction) Rollback() {
|
|
if t.store != nil {
|
|
t.store.txmut.Unlock()
|
|
}
|
|
}
|
|
|
|
// StdIterator is the standard Iterator implementation.
|
|
type StdIterator struct {
|
|
idx int
|
|
keys []string
|
|
}
|
|
|
|
// GetNext returns the next element of the iterator.
|
|
func (i *StdIterator) GetNext() (string, error) {
|
|
if i.idx >= len(i.keys) {
|
|
return "", &NoElementsLeftError{idx: i.idx}
|
|
}
|
|
key := i.keys[i.idx]
|
|
i.idx++
|
|
return key, nil
|
|
}
|
|
|
|
// HasNext returns true if there are elements left to get with GetNext().
|
|
func (i *StdIterator) HasNext() bool {
|
|
return i.idx < len(i.keys)
|
|
}
|