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  }