/
backend_client.go
248 lines (208 loc) · 5.9 KB
/
backend_client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
package plugin
import (
"context"
"errors"
"net/rpc"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/logical"
)
var (
ErrClientInMetadataMode = errors.New("plugin client can not perform action while in metadata mode")
)
// backendPluginClient implements logical.Backend and is the
// go-plugin client.
type backendPluginClient struct {
broker *plugin.MuxBroker
client *rpc.Client
metadataMode bool
system logical.SystemView
logger log.Logger
}
// HandleRequestArgs is the args for HandleRequest method.
type HandleRequestArgs struct {
StorageID uint32
Request *logical.Request
}
// HandleRequestReply is the reply for HandleRequest method.
type HandleRequestReply struct {
Response *logical.Response
Error error
}
// SpecialPathsReply is the reply for SpecialPaths method.
type SpecialPathsReply struct {
Paths *logical.Paths
}
// SystemReply is the reply for System method.
type SystemReply struct {
SystemView logical.SystemView
Error error
}
// HandleExistenceCheckArgs is the args for HandleExistenceCheck method.
type HandleExistenceCheckArgs struct {
StorageID uint32
Request *logical.Request
}
// HandleExistenceCheckReply is the reply for HandleExistenceCheck method.
type HandleExistenceCheckReply struct {
CheckFound bool
Exists bool
Error error
}
// SetupArgs is the args for Setup method.
type SetupArgs struct {
StorageID uint32
LoggerID uint32
SysViewID uint32
Config map[string]string
BackendUUID string
}
// SetupReply is the reply for Setup method.
type SetupReply struct {
Error error
}
// TypeReply is the reply for the Type method.
type TypeReply struct {
Type logical.BackendType
}
func (b *backendPluginClient) HandleRequest(ctx context.Context, req *logical.Request) (*logical.Response, error) {
if b.metadataMode {
return nil, ErrClientInMetadataMode
}
// Do not send the storage, since go-plugin cannot serialize
// interfaces. The server will pick up the storage from the shim.
req.Storage = nil
args := &HandleRequestArgs{
Request: req,
}
var reply HandleRequestReply
if req.Connection != nil {
oldConnState := req.Connection.ConnState
req.Connection.ConnState = nil
defer func() {
req.Connection.ConnState = oldConnState
}()
}
err := b.client.Call("Plugin.HandleRequest", args, &reply)
if err != nil {
return nil, err
}
if reply.Error != nil {
if reply.Error.Error() == logical.ErrUnsupportedOperation.Error() {
return nil, logical.ErrUnsupportedOperation
}
return reply.Response, reply.Error
}
return reply.Response, nil
}
func (b *backendPluginClient) SpecialPaths() *logical.Paths {
var reply SpecialPathsReply
err := b.client.Call("Plugin.SpecialPaths", new(interface{}), &reply)
if err != nil {
return nil
}
return reply.Paths
}
// System returns vault's system view. The backend client stores the view during
// Setup, so there is no need to shim the system just to get it back.
func (b *backendPluginClient) System() logical.SystemView {
return b.system
}
// Logger returns vault's logger. The backend client stores the logger during
// Setup, so there is no need to shim the logger just to get it back.
func (b *backendPluginClient) Logger() log.Logger {
return b.logger
}
func (b *backendPluginClient) HandleExistenceCheck(ctx context.Context, req *logical.Request) (bool, bool, error) {
if b.metadataMode {
return false, false, ErrClientInMetadataMode
}
// Do not send the storage, since go-plugin cannot serialize
// interfaces. The server will pick up the storage from the shim.
req.Storage = nil
args := &HandleExistenceCheckArgs{
Request: req,
}
var reply HandleExistenceCheckReply
if req.Connection != nil {
oldConnState := req.Connection.ConnState
req.Connection.ConnState = nil
defer func() {
req.Connection.ConnState = oldConnState
}()
}
err := b.client.Call("Plugin.HandleExistenceCheck", args, &reply)
if err != nil {
return false, false, err
}
if reply.Error != nil {
// THINKING: Should be be a switch on all error types?
if reply.Error.Error() == logical.ErrUnsupportedPath.Error() {
return false, false, logical.ErrUnsupportedPath
}
return false, false, reply.Error
}
return reply.CheckFound, reply.Exists, nil
}
func (b *backendPluginClient) Cleanup(ctx context.Context) {
b.client.Call("Plugin.Cleanup", new(interface{}), &struct{}{})
}
func (b *backendPluginClient) Initialize(ctx context.Context) error {
if b.metadataMode {
return ErrClientInMetadataMode
}
err := b.client.Call("Plugin.Initialize", new(interface{}), &struct{}{})
return err
}
func (b *backendPluginClient) InvalidateKey(ctx context.Context, key string) {
if b.metadataMode {
return
}
b.client.Call("Plugin.InvalidateKey", key, &struct{}{})
}
func (b *backendPluginClient) Setup(ctx context.Context, config *logical.BackendConfig) error {
// Shim logical.Storage
storageImpl := config.StorageView
if b.metadataMode {
storageImpl = &NOOPStorage{}
}
storageID := b.broker.NextId()
go b.broker.AcceptAndServe(storageID, &StorageServer{
impl: storageImpl,
})
// Shim logical.SystemView
sysViewImpl := config.System
if b.metadataMode {
sysViewImpl = &logical.StaticSystemView{}
}
sysViewID := b.broker.NextId()
go b.broker.AcceptAndServe(sysViewID, &SystemViewServer{
impl: sysViewImpl,
})
args := &SetupArgs{
StorageID: storageID,
SysViewID: sysViewID,
Config: config.Config,
BackendUUID: config.BackendUUID,
}
var reply SetupReply
err := b.client.Call("Plugin.Setup", args, &reply)
if err != nil {
return err
}
if reply.Error != nil {
return reply.Error
}
// Set system and logger for getter methods
b.system = config.System
b.logger = config.Logger
return nil
}
func (b *backendPluginClient) Type() logical.BackendType {
var reply TypeReply
err := b.client.Call("Plugin.Type", new(interface{}), &reply)
if err != nil {
return logical.TypeUnknown
}
return logical.BackendType(reply.Type)
}