1package physical
2
3import (
4	"context"
5	"strings"
6
7	log "github.com/hashicorp/go-hclog"
8)
9
10const DefaultParallelOperations = 128
11
12// The operation type
13type Operation string
14
15const (
16	DeleteOperation Operation = "delete"
17	GetOperation              = "get"
18	ListOperation             = "list"
19	PutOperation              = "put"
20)
21
22const (
23	ErrValueTooLarge = "put failed due to value being too large"
24)
25
26// Backend is the interface required for a physical
27// backend. A physical backend is used to durably store
28// data outside of Vault. As such, it is completely untrusted,
29// and is only accessed via a security barrier. The backends
30// must represent keys in a hierarchical manner. All methods
31// are expected to be thread safe.
32type Backend interface {
33	// Put is used to insert or update an entry
34	Put(ctx context.Context, entry *Entry) error
35
36	// Get is used to fetch an entry
37	Get(ctx context.Context, key string) (*Entry, error)
38
39	// Delete is used to permanently delete an entry
40	Delete(ctx context.Context, key string) error
41
42	// List is used to list all the keys under a given
43	// prefix, up to the next prefix.
44	List(ctx context.Context, prefix string) ([]string, error)
45}
46
47// HABackend is an extensions to the standard physical
48// backend to support high-availability. Vault only expects to
49// use mutual exclusion to allow multiple instances to act as a
50// hot standby for a leader that services all requests.
51type HABackend interface {
52	// LockWith is used for mutual exclusion based on the given key.
53	LockWith(key, value string) (Lock, error)
54
55	// Whether or not HA functionality is enabled
56	HAEnabled() bool
57}
58
59// ToggleablePurgemonster is an interface for backends that can toggle on or
60// off special functionality and/or support purging. This is only used for the
61// cache, don't use it for other things.
62type ToggleablePurgemonster interface {
63	Purge(ctx context.Context)
64	SetEnabled(bool)
65}
66
67// RedirectDetect is an optional interface that an HABackend
68// can implement. If they do, a redirect address can be automatically
69// detected.
70type RedirectDetect interface {
71	// DetectHostAddr is used to detect the host address
72	DetectHostAddr() (string, error)
73}
74
75type Lock interface {
76	// Lock is used to acquire the given lock
77	// The stopCh is optional and if closed should interrupt the lock
78	// acquisition attempt. The return struct should be closed when
79	// leadership is lost.
80	Lock(stopCh <-chan struct{}) (<-chan struct{}, error)
81
82	// Unlock is used to release the lock
83	Unlock() error
84
85	// Returns the value of the lock and if it is held
86	Value() (bool, string, error)
87}
88
89// Factory is the factory function to create a physical backend.
90type Factory func(config map[string]string, logger log.Logger) (Backend, error)
91
92// PermitPool is used to limit maximum outstanding requests
93type PermitPool struct {
94	sem chan int
95}
96
97// NewPermitPool returns a new permit pool with the provided
98// number of permits
99func NewPermitPool(permits int) *PermitPool {
100	if permits < 1 {
101		permits = DefaultParallelOperations
102	}
103	return &PermitPool{
104		sem: make(chan int, permits),
105	}
106}
107
108// Acquire returns when a permit has been acquired
109func (c *PermitPool) Acquire() {
110	c.sem <- 1
111}
112
113// Release returns a permit to the pool
114func (c *PermitPool) Release() {
115	<-c.sem
116}
117
118// Get number of requests in the permit pool
119func (c *PermitPool) CurrentPermits() int {
120	return len(c.sem)
121}
122
123// Prefixes is a shared helper function returns all parent 'folders' for a
124// given vault key.
125// e.g. for 'foo/bar/baz', it returns ['foo', 'foo/bar']
126func Prefixes(s string) []string {
127	components := strings.Split(s, "/")
128	result := []string{}
129	for i := 1; i < len(components); i++ {
130		result = append(result, strings.Join(components[:i], "/"))
131	}
132	return result
133}
134