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