mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
physical: Adding interface, in-mem implementation, and skeleton for Consul/File
This commit is contained in:
17
physical/consul.go
Normal file
17
physical/consul.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package physical
|
||||||
|
|
||||||
|
import "github.com/hashicorp/consul/api"
|
||||||
|
|
||||||
|
// ConsulBackend is a physical backend that stores data at specific
|
||||||
|
// prefix within Consul. It is used for most production situations as
|
||||||
|
// it allows Vault to run on multiple machines in a highly-available manner.
|
||||||
|
type ConsulBackend struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConsulBackend constructs a Consul backend using the given API client
|
||||||
|
// and the prefix in the KV store.
|
||||||
|
func NewConsulBackend(client *api.Client, prefix string) (*ConsulBackend, error) {
|
||||||
|
// TODO
|
||||||
|
c := &ConsulBackend{}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
14
physical/file.go
Normal file
14
physical/file.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package physical
|
||||||
|
|
||||||
|
// FileBackend is a physical backend that stores data on disk
|
||||||
|
// at a given file path. It can be used for durable single server
|
||||||
|
// situations, or to develop locally where durability is not critical.
|
||||||
|
type FileBackend struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileBackend constructs a Filebackend using the given directory
|
||||||
|
func NewFileBackend(dir string) (*FileBackend, error) {
|
||||||
|
// TODO:
|
||||||
|
f := &FileBackend{}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
77
physical/inmem.go
Normal file
77
physical/inmem.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package physical
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/armon/go-radix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
root *radix.Tree
|
||||||
|
l sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInmem constructs a new in-memory backend
|
||||||
|
func NewInmem() *InmemBackend {
|
||||||
|
in := &InmemBackend{
|
||||||
|
root: radix.New(),
|
||||||
|
}
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put is used to insert or update an entry
|
||||||
|
func (i *InmemBackend) Put(entry *Entry) error {
|
||||||
|
i.l.Lock()
|
||||||
|
defer i.l.Unlock()
|
||||||
|
i.root.Insert(entry.Key, entry)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get is used to fetch an entry
|
||||||
|
func (i *InmemBackend) Get(key string) (*Entry, error) {
|
||||||
|
i.l.RLock()
|
||||||
|
defer i.l.RUnlock()
|
||||||
|
if raw, ok := i.root.Get(key); ok {
|
||||||
|
return raw.(*Entry), nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete is used to permanently delete an entry
|
||||||
|
func (i *InmemBackend) Delete(key string) error {
|
||||||
|
i.l.Lock()
|
||||||
|
defer i.l.Unlock()
|
||||||
|
i.root.Delete(key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List is used ot list all the keys under a given
|
||||||
|
// prefix, up to the next prefix.
|
||||||
|
func (i *InmemBackend) List(prefix string) ([]string, error) {
|
||||||
|
i.l.RLock()
|
||||||
|
defer i.l.RUnlock()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
9
physical/inmem_test.go
Normal file
9
physical/inmem_test.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package physical
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestInmem(t *testing.T) {
|
||||||
|
inm := NewInmem()
|
||||||
|
testBackend(t, inm)
|
||||||
|
testBackend_ListPrefix(t, inm)
|
||||||
|
}
|
||||||
28
physical/physical.go
Normal file
28
physical/physical.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package physical
|
||||||
|
|
||||||
|
// Backend is the interface required for a physical
|
||||||
|
// backend. A physical backend is used to durably store
|
||||||
|
// datd outside of Vault. As such, it is completely untrusted,
|
||||||
|
// and is only accessed via a security barrier. The backends
|
||||||
|
// must represent keys in a hierarchical manner. All methods
|
||||||
|
// are expected to be thread safe.
|
||||||
|
type Backend interface {
|
||||||
|
// Put is used to insert or update an entry
|
||||||
|
Put(entry *Entry) error
|
||||||
|
|
||||||
|
// Get is used to fetch an entry
|
||||||
|
Get(key string) (*Entry, error)
|
||||||
|
|
||||||
|
// Delete is used to permanently delete an entry
|
||||||
|
Delete(key string) error
|
||||||
|
|
||||||
|
// List is used ot list all the keys under a given
|
||||||
|
// prefix, up to the next prefix.
|
||||||
|
List(prefix string) ([]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry is used to represent data stored by the physical backend
|
||||||
|
type Entry struct {
|
||||||
|
Key string
|
||||||
|
Value []byte
|
||||||
|
}
|
||||||
145
physical/physical_test.go
Normal file
145
physical/physical_test.go
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package physical
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testBackend(t *testing.T, b Backend) {
|
||||||
|
// Should be empty
|
||||||
|
keys, err := b.List("")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if len(keys) != 0 {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete should work if it does not exist
|
||||||
|
err = b.Delete("foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get should fail
|
||||||
|
out, err := b.Get("foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if out != nil {
|
||||||
|
t.Fatalf("bad: %v", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make an entry
|
||||||
|
e := &Entry{Key: "foo", Value: []byte("test")}
|
||||||
|
err = b.Put(e)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get should work
|
||||||
|
out, err = b.Get("foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(out, e) {
|
||||||
|
t.Fatalf("bad: %v expected: %v", out, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List should not be empty
|
||||||
|
keys, err = b.List("")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if len(keys) != 1 {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
if keys[0] != "foo" {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete should work
|
||||||
|
err = b.Delete("foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be empty
|
||||||
|
keys, err = b.List("")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if len(keys) != 0 {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get should fail
|
||||||
|
out, err = b.Get("foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if out != nil {
|
||||||
|
t.Fatalf("bad: %v", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBackend_ListPrefix(t *testing.T, b Backend) {
|
||||||
|
e1 := &Entry{Key: "foo", Value: []byte("test")}
|
||||||
|
e2 := &Entry{Key: "foo/bar", Value: []byte("test")}
|
||||||
|
e3 := &Entry{Key: "foo/bar/baz", Value: []byte("test")}
|
||||||
|
|
||||||
|
err := b.Put(e1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
err = b.Put(e2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
err = b.Put(e3)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the root
|
||||||
|
keys, err := b.List("")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if len(keys) != 2 {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
if keys[0] != "foo" {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
if keys[1] != "foo/" {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan foo/
|
||||||
|
keys, err = b.List("foo/")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if len(keys) != 2 {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
if keys[0] != "bar" {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
if keys[1] != "bar/" {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan foo/bar/
|
||||||
|
keys, err = b.List("foo/bar/")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if len(keys) != 1 {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
if keys[0] != "baz" {
|
||||||
|
t.Fatalf("bad: %v", keys)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user