github.com/hashicorp/vault/sdk@v0.13.0/plugin/plugin_v5.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package plugin 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 11 "github.com/hashicorp/go-plugin" 12 "github.com/hashicorp/vault/sdk/helper/consts" 13 "github.com/hashicorp/vault/sdk/helper/pluginutil" 14 "github.com/hashicorp/vault/sdk/logical" 15 "github.com/hashicorp/vault/sdk/plugin/pb" 16 ) 17 18 // BackendPluginClientV5 is a wrapper around backendPluginClient 19 // that also contains its plugin.Client instance. It's primarily 20 // used to cleanly kill the client on Cleanup() 21 type BackendPluginClientV5 struct { 22 client pluginutil.PluginClient 23 24 logical.Backend 25 } 26 27 type ContextKey string 28 29 func (c ContextKey) String() string { 30 return "plugin" + string(c) 31 } 32 33 const ContextKeyPluginReload = ContextKey("plugin-reload") 34 35 // Cleanup cleans up the go-plugin client and the plugin catalog 36 func (b *BackendPluginClientV5) Cleanup(ctx context.Context) { 37 _, ok := ctx.Value(ContextKeyPluginReload).(string) 38 if !ok { 39 b.Backend.Cleanup(ctx) 40 b.client.Close() 41 return 42 } 43 b.Backend.Cleanup(ctx) 44 b.client.Reload() 45 } 46 47 func (b *BackendPluginClientV5) IsExternal() bool { 48 return true 49 } 50 51 func (b *BackendPluginClientV5) PluginVersion() logical.PluginVersion { 52 if versioner, ok := b.Backend.(logical.PluginVersioner); ok { 53 return versioner.PluginVersion() 54 } 55 return logical.EmptyPluginVersion 56 } 57 58 var _ logical.PluginVersioner = (*BackendPluginClientV5)(nil) 59 60 // NewBackendV5 will return an instance of an RPC-based client implementation of 61 // the backend for external plugins, or a concrete implementation of the 62 // backend if it is a builtin backend. The backend is returned as a 63 // logical.Backend interface. 64 func NewBackendV5(ctx context.Context, pluginName string, pluginType consts.PluginType, pluginVersion string, sys pluginutil.LookRunnerUtil, conf *logical.BackendConfig) (logical.Backend, error) { 65 // Look for plugin in the plugin catalog 66 pluginRunner, err := sys.LookupPluginVersion(ctx, pluginName, pluginType, pluginVersion) 67 if err != nil { 68 return nil, err 69 } 70 71 var backend logical.Backend 72 if pluginRunner.Builtin { 73 // Plugin is builtin so we can retrieve an instance of the interface 74 // from the pluginRunner. Then cast it to logical.Factory. 75 rawFactory, err := pluginRunner.BuiltinFactory() 76 if err != nil { 77 return nil, fmt.Errorf("error getting plugin type: %q", err) 78 } 79 80 if factory, ok := rawFactory.(logical.Factory); !ok { 81 return nil, fmt.Errorf("unsupported backend type: %q", pluginName) 82 } else { 83 if backend, err = factory(ctx, conf); err != nil { 84 return nil, err 85 } 86 } 87 } else { 88 // create a backendPluginClient instance 89 config := pluginutil.PluginClientConfig{ 90 Name: pluginName, 91 PluginSets: PluginSet, 92 PluginType: pluginType, 93 Version: pluginVersion, 94 HandshakeConfig: HandshakeConfig, 95 Logger: conf.Logger.Named(pluginName), 96 AutoMTLS: true, 97 Wrapper: sys, 98 } 99 backend, err = NewPluginClientV5(ctx, sys, config) 100 if err != nil { 101 return nil, err 102 } 103 } 104 105 return backend, nil 106 } 107 108 // PluginSet is the map of plugins we can dispense. 109 var PluginSet = map[int]plugin.PluginSet{ 110 5: { 111 "backend": &GRPCBackendPlugin{}, 112 }, 113 } 114 115 func Dispense(rpcClient plugin.ClientProtocol, pluginClient pluginutil.PluginClient) (logical.Backend, error) { 116 // Request the plugin 117 raw, err := rpcClient.Dispense("backend") 118 if err != nil { 119 return nil, err 120 } 121 122 var backend logical.Backend 123 // We should have a logical backend type now. This feels like a normal interface 124 // implementation but is in fact over an RPC connection. 125 switch c := raw.(type) { 126 case *backendGRPCPluginClient: 127 // This is an abstraction leak from go-plugin but it is necessary in 128 // order to enable multiplexing on multiplexed plugins 129 c.client = pb.NewBackendClient(pluginClient.Conn()) 130 c.versionClient = logical.NewPluginVersionClient(pluginClient.Conn()) 131 132 backend = c 133 default: 134 return nil, errors.New("unsupported plugin client type") 135 } 136 137 return &BackendPluginClientV5{ 138 client: pluginClient, 139 Backend: backend, 140 }, nil 141 } 142 143 func NewPluginClientV5(ctx context.Context, sys pluginutil.RunnerUtil, config pluginutil.PluginClientConfig) (logical.Backend, error) { 144 pluginClient, err := sys.NewPluginClient(ctx, config) 145 if err != nil { 146 return nil, err 147 } 148 149 // Request the plugin 150 raw, err := pluginClient.Dispense("backend") 151 if err != nil { 152 return nil, err 153 } 154 155 var backend logical.Backend 156 var transport string 157 // We should have a logical backend type now. This feels like a normal interface 158 // implementation but is in fact over an RPC connection. 159 switch c := raw.(type) { 160 case *backendGRPCPluginClient: 161 // This is an abstraction leak from go-plugin but it is necessary in 162 // order to enable multiplexing on multiplexed plugins 163 c.client = pb.NewBackendClient(pluginClient.Conn()) 164 c.versionClient = logical.NewPluginVersionClient(pluginClient.Conn()) 165 166 backend = c 167 transport = "gRPC" 168 default: 169 return nil, errors.New("unsupported plugin client type") 170 } 171 172 // Wrap the backend in a tracing middleware 173 if config.Logger.IsTrace() { 174 backend = &BackendTracingMiddleware{ 175 logger: config.Logger.With("transport", transport), 176 next: backend, 177 } 178 } 179 180 return &BackendPluginClientV5{ 181 client: pluginClient, 182 Backend: backend, 183 }, nil 184 }