open-vault/physical/inmem/inmem.go

264 lines
5.8 KiB
Go
Raw Normal View History

package inmem
import (
"context"
"errors"
"os"
"strings"
"sync"
"sync/atomic"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/physical"
2016-08-19 20:45:17 +00:00
"github.com/armon/go-radix"
)
// Verify interfaces are satisfied
var _ physical.Backend = (*InmemBackend)(nil)
var _ physical.HABackend = (*InmemHABackend)(nil)
var _ physical.HABackend = (*TransactionalInmemHABackend)(nil)
var _ physical.Lock = (*InmemLock)(nil)
var _ physical.Transactional = (*TransactionalInmemBackend)(nil)
var _ physical.Transactional = (*TransactionalInmemHABackend)(nil)
var (
PutDisabledError = errors.New("put operations disabled in inmem backend")
GetDisabledError = errors.New("get operations disabled in inmem backend")
DeleteDisabledError = errors.New("delete operations disabled in inmem backend")
ListDisabledError = errors.New("list operations disabled in inmem backend")
)
// InmemBackend is an in-memory only physical backend. It is useful
// for testing and development situations where the data is not
// expected to be durable.
type InmemBackend struct {
2017-02-17 14:15:35 +00:00
sync.RWMutex
root *radix.Tree
permitPool *physical.PermitPool
2016-08-19 20:45:17 +00:00
logger log.Logger
failGet *uint32
failPut *uint32
failDelete *uint32
failList *uint32
logOps bool
}
2017-02-17 14:15:35 +00:00
type TransactionalInmemBackend struct {
InmemBackend
}
// NewInmem constructs a new in-memory backend
func NewInmem(_ map[string]string, logger log.Logger) (physical.Backend, error) {
in := &InmemBackend{
root: radix.New(),
permitPool: physical.NewPermitPool(physical.DefaultParallelOperations),
logger: logger,
failGet: new(uint32),
failPut: new(uint32),
failDelete: new(uint32),
failList: new(uint32),
logOps: os.Getenv("VAULT_INMEM_LOG_ALL_OPS") != "",
}
return in, nil
}
2017-02-17 14:15:35 +00:00
// Basically for now just creates a permit pool of size 1 so only one operation
// can run at a time
func NewTransactionalInmem(_ map[string]string, logger log.Logger) (physical.Backend, error) {
2017-02-17 14:15:35 +00:00
in := &TransactionalInmemBackend{
InmemBackend: InmemBackend{
root: radix.New(),
permitPool: physical.NewPermitPool(1),
2017-02-20 16:08:03 +00:00
logger: logger,
2018-06-09 22:22:45 +00:00
failGet: new(uint32),
failPut: new(uint32),
failDelete: new(uint32),
failList: new(uint32),
logOps: os.Getenv("VAULT_INMEM_LOG_ALL_OPS") != "",
2017-02-17 14:15:35 +00:00
},
}
return in, nil
2017-02-17 14:15:35 +00:00
}
// Put is used to insert or update an entry
func (i *InmemBackend) Put(ctx context.Context, entry *physical.Entry) error {
i.permitPool.Acquire()
defer i.permitPool.Release()
2017-02-17 14:15:35 +00:00
i.Lock()
defer i.Unlock()
return i.PutInternal(ctx, entry)
2017-02-17 14:15:35 +00:00
}
2015-11-03 19:48:05 +00:00
func (i *InmemBackend) PutInternal(ctx context.Context, entry *physical.Entry) error {
if i.logOps {
i.logger.Trace("put", "key", entry.Key)
}
if atomic.LoadUint32(i.failPut) != 0 {
return PutDisabledError
}
select {
case <-ctx.Done():
return ctx.Err()
default:
}
2017-11-30 14:43:07 +00:00
i.root.Insert(entry.Key, entry.Value)
return nil
}
2018-02-12 16:25:48 +00:00
func (i *InmemBackend) FailPut(fail bool) {
var val uint32
if fail {
val = 1
}
atomic.StoreUint32(i.failPut, val)
2018-02-12 16:25:48 +00:00
}
// Get is used to fetch an entry
func (i *InmemBackend) Get(ctx context.Context, key string) (*physical.Entry, error) {
i.permitPool.Acquire()
defer i.permitPool.Release()
2017-02-17 14:15:35 +00:00
i.RLock()
defer i.RUnlock()
return i.GetInternal(ctx, key)
2017-02-17 14:15:35 +00:00
}
2015-11-03 19:48:05 +00:00
func (i *InmemBackend) GetInternal(ctx context.Context, key string) (*physical.Entry, error) {
if i.logOps {
i.logger.Trace("get", "key", key)
}
if atomic.LoadUint32(i.failGet) != 0 {
return nil, GetDisabledError
}
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
if raw, ok := i.root.Get(key); ok {
2017-11-30 14:43:07 +00:00
return &physical.Entry{
Key: key,
Value: raw.([]byte),
}, nil
}
return nil, nil
}
2018-02-12 16:25:48 +00:00
func (i *InmemBackend) FailGet(fail bool) {
var val uint32
if fail {
val = 1
}
atomic.StoreUint32(i.failGet, val)
2018-02-12 16:25:48 +00:00
}
// Delete is used to permanently delete an entry
func (i *InmemBackend) Delete(ctx context.Context, key string) error {
i.permitPool.Acquire()
defer i.permitPool.Release()
2017-02-17 14:15:35 +00:00
i.Lock()
defer i.Unlock()
return i.DeleteInternal(ctx, key)
2017-02-17 14:15:35 +00:00
}
2015-11-03 19:48:05 +00:00
func (i *InmemBackend) DeleteInternal(ctx context.Context, key string) error {
if i.logOps {
i.logger.Trace("delete", "key", key)
}
if atomic.LoadUint32(i.failDelete) != 0 {
return DeleteDisabledError
}
select {
case <-ctx.Done():
return ctx.Err()
default:
}
i.root.Delete(key)
return nil
}
2018-02-12 16:25:48 +00:00
func (i *InmemBackend) FailDelete(fail bool) {
var val uint32
if fail {
val = 1
}
atomic.StoreUint32(i.failDelete, val)
2018-02-12 16:25:48 +00:00
}
// List is used ot list all the keys under a given
// prefix, up to the next prefix.
func (i *InmemBackend) List(ctx context.Context, prefix string) ([]string, error) {
i.permitPool.Acquire()
defer i.permitPool.Release()
2017-02-17 14:15:35 +00:00
i.RLock()
defer i.RUnlock()
return i.ListInternal(ctx, prefix)
2017-02-17 14:15:35 +00:00
}
2015-11-03 19:48:05 +00:00
func (i *InmemBackend) ListInternal(ctx context.Context, prefix string) ([]string, error) {
if i.logOps {
i.logger.Trace("list", "prefix", prefix)
}
if atomic.LoadUint32(i.failList) != 0 {
return nil, ListDisabledError
}
var out []string
seen := make(map[string]interface{})
walkFn := func(s string, v interface{}) bool {
trimmed := strings.TrimPrefix(s, prefix)
sep := strings.Index(trimmed, "/")
if sep == -1 {
out = append(out, trimmed)
} else {
trimmed = trimmed[:sep+1]
if _, ok := seen[trimmed]; !ok {
out = append(out, trimmed)
seen[trimmed] = struct{}{}
}
}
return false
}
i.root.WalkPrefix(prefix, walkFn)
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
return out, nil
}
2017-02-17 14:15:35 +00:00
2018-02-12 16:25:48 +00:00
func (i *InmemBackend) FailList(fail bool) {
var val uint32
if fail {
val = 1
}
atomic.StoreUint32(i.failList, val)
2018-02-12 16:25:48 +00:00
}
2017-02-17 14:15:35 +00:00
// Implements the transaction interface
func (t *TransactionalInmemBackend) Transaction(ctx context.Context, txns []*physical.TxnEntry) error {
2017-02-17 14:15:35 +00:00
t.permitPool.Acquire()
defer t.permitPool.Release()
t.Lock()
defer t.Unlock()
return physical.GenericTransactionHandler(ctx, t, txns)
2017-02-17 14:15:35 +00:00
}