/
plugin.go
185 lines (162 loc) · 5.35 KB
/
plugin.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
package plugin
import (
"context"
"errors"
"fmt"
"sync"
"github.com/hashicorp/errwrap"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/pluginutil"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/plugin/pb"
)
// BackendPluginClient is a wrapper around backendPluginClient
// that also contains its plugin.Client instance. It's primarily
// used to cleanly kill the client on Cleanup()
type BackendPluginClient struct {
client pluginutil.PluginClient
sync.Mutex
logical.Backend
}
// Cleanup calls the RPC client's Cleanup() func and also calls
// the go-plugin's client Kill() func
func (b *BackendPluginClient) Cleanup(ctx context.Context) {
b.Backend.Cleanup(ctx)
b.client.Close()
}
// NewBackend will return an instance of an RPC-based client implementation of the backend for
// external plugins, or a concrete implementation of the backend if it is a builtin backend.
// The backend is returned as a logical.Backend interface. The isMetadataMode param determines whether
// the plugin should run in metadata mode.
func NewBackend(ctx context.Context, pluginName string, pluginType consts.PluginType, sys pluginutil.LookRunnerUtil, conf *logical.BackendConfig, isMetadataMode bool) (logical.Backend, error) {
// Look for plugin in the plugin catalog
pluginRunner, err := sys.LookupPlugin(ctx, pluginName, pluginType)
if err != nil {
return nil, err
}
var backend logical.Backend
if pluginRunner.Builtin {
// Plugin is builtin so we can retrieve an instance of the interface
// from the pluginRunner. Then cast it to logical.Factory.
rawFactory, err := pluginRunner.BuiltinFactory()
if err != nil {
return nil, errwrap.Wrapf("error getting plugin type: {{err}}", err)
}
if factory, ok := rawFactory.(logical.Factory); !ok {
return nil, fmt.Errorf("unsupported backend type: %q", pluginName)
} else {
if backend, err = factory(ctx, conf); err != nil {
return nil, err
}
}
} else {
// create a backendPluginClient instance
config := pluginutil.PluginClientConfig{
Name: pluginName,
PluginSets: PluginSet,
PluginType: pluginType,
HandshakeConfig: HandshakeConfig,
Logger: conf.Logger.Named(pluginName),
IsMetadataMode: isMetadataMode,
// AutoMTLS: true,
Wrapper: sys,
}
backend, err = NewPluginClient(ctx, sys, config)
if err != nil {
return nil, err
}
}
return backend, nil
}
// PluginSet is the map of plugins we can dispense.
var PluginSet = map[int]plugin.PluginSet{
// Version 3 used to supports both protocols. We want to keep it around
// since it's possible old plugins built against this version will still
// work with gRPC. There is currently no difference between version 3
// and version 4.
3: {
"backend": &GRPCBackendPlugin{},
},
4: {
"backend": &GRPCBackendPlugin{},
},
5: {
"backend": &GRPCBackendPlugin{},
},
}
func Dispense(ctx context.Context, rpcClient plugin.ClientProtocol, pluginClient pluginutil.PluginClient) (logical.Backend, error) {
// Request the plugin
raw, err := rpcClient.Dispense("backend")
if err != nil {
return nil, err
}
var backend logical.Backend
// We should have a logical backend type now. This feels like a normal interface
// implementation but is in fact over an RPC connection.
switch c := raw.(type) {
case *backendGRPCPluginClient:
// This is an abstraction leak from go-plugin but it is necessary in
// order to enable multiplexing on multiplexed plugins
c.client = pb.NewBackendClient(pluginClient.Conn())
backend = c
default:
return nil, errors.New("unsupported plugin client type")
}
return &BackendPluginClient{
client: pluginClient,
Backend: backend,
}, nil
}
func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, config pluginutil.PluginClientConfig) (logical.Backend, error) {
pluginClient, err := sys.NewPluginClient(ctx, config)
if err != nil {
return nil, err
}
// Request the plugin
raw, err := pluginClient.Dispense("backend")
if err != nil {
return nil, err
}
var backend logical.Backend
var transport string
// We should have a logical backend type now. This feels like a normal interface
// implementation but is in fact over an RPC connection.
switch c := raw.(type) {
case *backendGRPCPluginClient:
// This is an abstraction leak from go-plugin but it is necessary in
// order to enable multiplexing on multiplexed plugins
c.client = pb.NewBackendClient(pluginClient.Conn())
backend = c
transport = "gRPC"
default:
return nil, errors.New("unsupported plugin client type")
}
// Wrap the backend in a tracing middleware
if config.Logger.IsTrace() {
backend = &backendTracingMiddleware{
logger: config.Logger.With("transport", transport),
next: backend,
}
}
return &BackendPluginClient{
client: pluginClient,
Backend: backend,
}, nil
}
// wrapError takes a generic error type and makes it usable with the plugin
// interface. Only errors which have exported fields and have been registered
// with gob can be unwrapped and transported. This checks error types and, if
// none match, wrap the error in a plugin.BasicError.
func wrapError(err error) error {
if err == nil {
return nil
}
switch err.(type) {
case *plugin.BasicError,
logical.HTTPCodedError,
*logical.StatusBadRequest:
return err
}
return plugin.NewBasicError(err)
}