Remove netRPC based plugins (#6173)

* Remove netRPC backend plugins

* Remove netRPC database plugins

* Fix tests and comments
This commit is contained in:
Brian Kassouf
2019-02-12 09:31:03 -08:00
committed by GitHub
parent 4db655a026
commit 79a07dd2c6
19 changed files with 58 additions and 1795 deletions

View File

@@ -35,11 +35,12 @@ func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunne
// pluginSets is the map of plugins we can dispense.
pluginSets := map[int]plugin.PluginSet{
// Version 3 supports both protocols
// 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: plugin.PluginSet{
"database": &DatabasePlugin{
GRPCDatabasePlugin: new(GRPCDatabasePlugin),
},
"database": new(GRPCDatabasePlugin),
},
// Version 4 only supports gRPC
4: plugin.PluginSet{
@@ -76,9 +77,6 @@ func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunne
switch raw.(type) {
case *gRPCClient:
db = raw.(*gRPCClient)
case *databasePluginRPCClient:
logger.Warn("plugin is using deprecated netRPC transport, recompile plugin to upgrade to gRPC", "plugin", pluginRunner.Name)
db = raw.(*databasePluginRPCClient)
default:
return nil, errors.New("unsupported client type")
}

View File

@@ -1,197 +0,0 @@
package dbplugin
import (
"context"
"encoding/json"
"fmt"
"net/rpc"
"strings"
"time"
)
// ---- RPC server domain ----
// databasePluginRPCServer implements an RPC version of Database and is run
// inside a plugin. It wraps an underlying implementation of Database.
type databasePluginRPCServer struct {
impl Database
}
func (ds *databasePluginRPCServer) Type(_ struct{}, resp *string) error {
var err error
*resp, err = ds.impl.Type()
return err
}
func (ds *databasePluginRPCServer) CreateUser(args *CreateUserRequestRPC, resp *CreateUserResponse) error {
var err error
resp.Username, resp.Password, err = ds.impl.CreateUser(context.Background(), args.Statements, args.UsernameConfig, args.Expiration)
return err
}
func (ds *databasePluginRPCServer) RenewUser(args *RenewUserRequestRPC, _ *struct{}) error {
err := ds.impl.RenewUser(context.Background(), args.Statements, args.Username, args.Expiration)
return err
}
func (ds *databasePluginRPCServer) RevokeUser(args *RevokeUserRequestRPC, _ *struct{}) error {
err := ds.impl.RevokeUser(context.Background(), args.Statements, args.Username)
return err
}
func (ds *databasePluginRPCServer) RotateRootCredentials(args *RotateRootCredentialsRequestRPC, resp *RotateRootCredentialsResponse) error {
config, err := ds.impl.RotateRootCredentials(context.Background(), args.Statements)
if err != nil {
return err
}
resp.Config, err = json.Marshal(config)
return err
}
func (ds *databasePluginRPCServer) Initialize(args *InitializeRequestRPC, _ *struct{}) error {
return ds.Init(&InitRequestRPC{
Config: args.Config,
VerifyConnection: args.VerifyConnection,
}, &InitResponse{})
}
func (ds *databasePluginRPCServer) Init(args *InitRequestRPC, resp *InitResponse) error {
config, err := ds.impl.Init(context.Background(), args.Config, args.VerifyConnection)
if err != nil {
return err
}
resp.Config, err = json.Marshal(config)
return err
}
func (ds *databasePluginRPCServer) Close(_ struct{}, _ *struct{}) error {
ds.impl.Close()
return nil
}
// ---- RPC client domain ----
// databasePluginRPCClient implements Database and is used on the client to
// make RPC calls to a plugin.
type databasePluginRPCClient struct {
client *rpc.Client
}
func (dr *databasePluginRPCClient) Type() (string, error) {
var dbType string
err := dr.client.Call("Plugin.Type", struct{}{}, &dbType)
return fmt.Sprintf("plugin-%s", dbType), err
}
func (dr *databasePluginRPCClient) CreateUser(_ context.Context, statements Statements, usernameConfig UsernameConfig, expiration time.Time) (username string, password string, err error) {
req := CreateUserRequestRPC{
Statements: statements,
UsernameConfig: usernameConfig,
Expiration: expiration,
}
var resp CreateUserResponse
err = dr.client.Call("Plugin.CreateUser", req, &resp)
return resp.Username, resp.Password, err
}
func (dr *databasePluginRPCClient) RenewUser(_ context.Context, statements Statements, username string, expiration time.Time) error {
req := RenewUserRequestRPC{
Statements: statements,
Username: username,
Expiration: expiration,
}
return dr.client.Call("Plugin.RenewUser", req, &struct{}{})
}
func (dr *databasePluginRPCClient) RevokeUser(_ context.Context, statements Statements, username string) error {
req := RevokeUserRequestRPC{
Statements: statements,
Username: username,
}
return dr.client.Call("Plugin.RevokeUser", req, &struct{}{})
}
func (dr *databasePluginRPCClient) RotateRootCredentials(_ context.Context, statements []string) (saveConf map[string]interface{}, err error) {
req := RotateRootCredentialsRequestRPC{
Statements: statements,
}
var resp RotateRootCredentialsResponse
err = dr.client.Call("Plugin.RotateRootCredentials", req, &resp)
err = json.Unmarshal(resp.Config, &saveConf)
return saveConf, err
}
func (dr *databasePluginRPCClient) Initialize(_ context.Context, conf map[string]interface{}, verifyConnection bool) error {
_, err := dr.Init(nil, conf, verifyConnection)
return err
}
func (dr *databasePluginRPCClient) Init(_ context.Context, conf map[string]interface{}, verifyConnection bool) (saveConf map[string]interface{}, err error) {
req := InitRequestRPC{
Config: conf,
VerifyConnection: verifyConnection,
}
var resp InitResponse
err = dr.client.Call("Plugin.Init", req, &resp)
if err != nil {
if strings.Contains(err.Error(), "can't find method Plugin.Init") {
req := InitializeRequestRPC{
Config: conf,
VerifyConnection: verifyConnection,
}
err = dr.client.Call("Plugin.Initialize", req, &struct{}{})
if err == nil {
return conf, nil
}
}
return nil, err
}
err = json.Unmarshal(resp.Config, &saveConf)
return saveConf, err
}
func (dr *databasePluginRPCClient) Close() error {
return dr.client.Call("Plugin.Close", struct{}{}, &struct{}{})
}
// ---- RPC Request Args Domain ----
type InitializeRequestRPC struct {
Config map[string]interface{}
VerifyConnection bool
}
type InitRequestRPC struct {
Config map[string]interface{}
VerifyConnection bool
}
type CreateUserRequestRPC struct {
Statements Statements
UsernameConfig UsernameConfig
Expiration time.Time
}
type RenewUserRequestRPC struct {
Statements Statements
Username string
Expiration time.Time
}
type RevokeUserRequestRPC struct {
Statements Statements
Username string
}
type RotateRootCredentialsRequestRPC struct {
Statements []string
}

View File

@@ -3,7 +3,6 @@ package dbplugin
import (
"context"
"fmt"
"net/rpc"
"time"
"google.golang.org/grpc"
@@ -72,8 +71,6 @@ func PluginFactory(ctx context.Context, pluginName string, sys pluginutil.LookRu
switch db.(*DatabasePluginClient).Database.(type) {
case *gRPCClient:
transport = "gRPC"
case *databasePluginRPCClient:
transport = "netRPC"
}
}
@@ -110,17 +107,9 @@ var handshakeConfig = plugin.HandshakeConfig{
MagicCookieValue: "926a0820-aea2-be28-51d6-83cdf00e8edb",
}
var _ plugin.Plugin = &DatabasePlugin{}
var _ plugin.GRPCPlugin = &DatabasePlugin{}
var _ plugin.Plugin = &GRPCDatabasePlugin{}
var _ plugin.GRPCPlugin = &GRPCDatabasePlugin{}
// DatabasePlugin implements go-plugin's Plugin interface. It has methods for
// retrieving a server and a client instance of the plugin.
type DatabasePlugin struct {
*GRPCDatabasePlugin
}
// GRPCDatabasePlugin is the plugin.Plugin implementation that only supports GRPC
// transport
type GRPCDatabasePlugin struct {
@@ -130,17 +119,6 @@ type GRPCDatabasePlugin struct {
plugin.NetRPCUnsupportedPlugin
}
func (d DatabasePlugin) Server(*plugin.MuxBroker) (interface{}, error) {
impl := &DatabaseErrorSanitizerMiddleware{
next: d.Impl,
}
return &databasePluginRPCServer{impl: impl}, nil
}
func (DatabasePlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
return &databasePluginRPCClient{client: c}, nil
}
func (d GRPCDatabasePlugin) GRPCServer(_ *plugin.GRPCBroker, s *grpc.Server) error {
impl := &DatabaseErrorSanitizerMiddleware{
next: d.Impl,

View File

@@ -8,7 +8,6 @@ import (
"time"
log "github.com/hashicorp/go-hclog"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/builtin/logical/database/dbplugin"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/namespace"
@@ -96,7 +95,6 @@ func getCluster(t *testing.T) (*vault.TestCluster, logical.SystemView) {
sys := vault.TestDynamicSystemView(cores[0].Core)
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin", consts.PluginTypeDatabase, "TestPlugin_GRPC_Main", []string{}, "")
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin-netRPC", consts.PluginTypeDatabase, "TestPlugin_NetRPC_Main", []string{}, "")
return cluster, sys
}
@@ -121,31 +119,6 @@ func TestPlugin_GRPC_Main(t *testing.T) {
plugins.Serve(plugin, apiClientMeta.GetTLSConfig())
}
// This is not an actual test case, it's a helper function that will be executed
// by the go-plugin client via an exec call.
func TestPlugin_NetRPC_Main(t *testing.T) {
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" {
return
}
os.Unsetenv(pluginutil.PluginVaultVersionEnv)
p := &mockPlugin{
users: make(map[string][]string),
}
args := []string{"--tls-skip-verify=true"}
apiClientMeta := &pluginutil.APIClientMeta{}
flags := apiClientMeta.FlagSet()
flags.Parse(args)
tlsProvider := pluginutil.VaultPluginTLSProvider(apiClientMeta.GetTLSConfig())
serveConf := dbplugin.ServeConfig(p, tlsProvider)
serveConf.GRPCServer = nil
plugin.Serve(serveConf)
}
func TestPlugin_Init(t *testing.T) {
cluster, sys := getCluster(t)
defer cluster.Cleanup()
@@ -284,143 +257,3 @@ func TestPlugin_RevokeUser(t *testing.T) {
t.Fatalf("err: %s", err)
}
}
// Test the code is still compatible with an old netRPC plugin
func TestPlugin_NetRPC_Init(t *testing.T) {
cluster, sys := getCluster(t)
defer cluster.Cleanup()
dbRaw, err := dbplugin.PluginFactory(namespace.RootContext(nil), "test-plugin-netRPC", sys, log.NewNullLogger())
if err != nil {
t.Fatalf("err: %s", err)
}
connectionDetails := map[string]interface{}{
"test": 1,
}
_, err = dbRaw.Init(context.Background(), connectionDetails, true)
if err != nil {
t.Fatalf("err: %s", err)
}
err = dbRaw.Close()
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestPlugin_NetRPC_CreateUser(t *testing.T) {
cluster, sys := getCluster(t)
defer cluster.Cleanup()
db, err := dbplugin.PluginFactory(namespace.RootContext(nil), "test-plugin-netRPC", sys, log.NewNullLogger())
if err != nil {
t.Fatalf("err: %s", err)
}
defer db.Close()
connectionDetails := map[string]interface{}{
"test": 1,
}
_, err = db.Init(context.Background(), connectionDetails, true)
if err != nil {
t.Fatalf("err: %s", err)
}
usernameConf := dbplugin.UsernameConfig{
DisplayName: "test",
RoleName: "test",
}
us, pw, err := db.CreateUser(context.Background(), dbplugin.Statements{}, usernameConf, time.Now().Add(time.Minute))
if err != nil {
t.Fatalf("err: %s", err)
}
if us != "test" || pw != "test" {
t.Fatal("expected username and password to be 'test'")
}
// try and save the same user again to verify it saved the first time, this
// should return an error
_, _, err = db.CreateUser(context.Background(), dbplugin.Statements{}, usernameConf, time.Now().Add(time.Minute))
if err == nil {
t.Fatal("expected an error, user wasn't created correctly")
}
}
func TestPlugin_NetRPC_RenewUser(t *testing.T) {
cluster, sys := getCluster(t)
defer cluster.Cleanup()
db, err := dbplugin.PluginFactory(namespace.RootContext(nil), "test-plugin-netRPC", sys, log.NewNullLogger())
if err != nil {
t.Fatalf("err: %s", err)
}
defer db.Close()
connectionDetails := map[string]interface{}{
"test": 1,
}
_, err = db.Init(context.Background(), connectionDetails, true)
if err != nil {
t.Fatalf("err: %s", err)
}
usernameConf := dbplugin.UsernameConfig{
DisplayName: "test",
RoleName: "test",
}
us, _, err := db.CreateUser(context.Background(), dbplugin.Statements{}, usernameConf, time.Now().Add(time.Minute))
if err != nil {
t.Fatalf("err: %s", err)
}
err = db.RenewUser(context.Background(), dbplugin.Statements{}, us, time.Now().Add(time.Minute))
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestPlugin_NetRPC_RevokeUser(t *testing.T) {
cluster, sys := getCluster(t)
defer cluster.Cleanup()
db, err := dbplugin.PluginFactory(namespace.RootContext(nil), "test-plugin-netRPC", sys, log.NewNullLogger())
if err != nil {
t.Fatalf("err: %s", err)
}
defer db.Close()
connectionDetails := map[string]interface{}{
"test": 1,
}
_, err = db.Init(context.Background(), connectionDetails, true)
if err != nil {
t.Fatalf("err: %s", err)
}
usernameConf := dbplugin.UsernameConfig{
DisplayName: "test",
RoleName: "test",
}
us, _, err := db.CreateUser(context.Background(), dbplugin.Statements{}, usernameConf, time.Now().Add(time.Minute))
if err != nil {
t.Fatalf("err: %s", err)
}
// Test default revoke statements
err = db.RevokeUser(context.Background(), dbplugin.Statements{}, us)
if err != nil {
t.Fatalf("err: %s", err)
}
// Try adding the same username back so we can verify it was removed
_, _, err = db.CreateUser(context.Background(), dbplugin.Statements{}, usernameConf, time.Now().Add(time.Minute))
if err != nil {
t.Fatalf("err: %s", err)
}
}

View File

@@ -4,7 +4,6 @@ import (
"crypto/tls"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/helper/pluginutil"
)
// Serve is called from within a plugin and wraps the provided
@@ -17,11 +16,13 @@ func Serve(db Database, tlsProvider func() (*tls.Config, error)) {
func ServeConfig(db Database, tlsProvider func() (*tls.Config, error)) *plugin.ServeConfig {
// pluginSets is the map of plugins we can dispense.
pluginSets := 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: plugin.PluginSet{
"database": &DatabasePlugin{
GRPCDatabasePlugin: &GRPCDatabasePlugin{
Impl: db,
},
"database": &GRPCDatabasePlugin{
Impl: db,
},
},
4: plugin.PluginSet{
@@ -38,12 +39,5 @@ func ServeConfig(db Database, tlsProvider func() (*tls.Config, error)) *plugin.S
GRPCServer: plugin.DefaultGRPCServer,
}
// If we do not have gRPC support fallback to version 3
// Remove this block in 0.13
if !pluginutil.GRPCSupport() {
conf.GRPCServer = nil
delete(conf.VersionedPlugins, 4)
}
return conf
}

View File

@@ -2,7 +2,6 @@ package plugin
import (
"context"
"net/rpc"
"sync/atomic"
"google.golang.org/grpc"
@@ -13,16 +12,9 @@ import (
"github.com/hashicorp/vault/logical/plugin/pb"
)
var _ plugin.Plugin = (*BackendPlugin)(nil)
var _ plugin.GRPCPlugin = (*BackendPlugin)(nil)
var _ plugin.Plugin = (*GRPCBackendPlugin)(nil)
var _ plugin.GRPCPlugin = (*GRPCBackendPlugin)(nil)
// BackendPlugin is the plugin.Plugin implementation
type BackendPlugin struct {
*GRPCBackendPlugin
}
// GRPCBackendPlugin is the plugin.Plugin implementation that only supports GRPC
// transport
type GRPCBackendPlugin struct {
@@ -34,26 +26,6 @@ type GRPCBackendPlugin struct {
plugin.NetRPCUnsupportedPlugin
}
// Server gets called when on plugin.Serve()
func (b *BackendPlugin) Server(broker *plugin.MuxBroker) (interface{}, error) {
return &backendPluginServer{
factory: b.Factory,
broker: broker,
// We pass the logger down into the backend so go-plugin will forward
// logs for us.
logger: b.Logger,
}, nil
}
// Client gets called on plugin.NewClient()
func (b BackendPlugin) Client(broker *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
return &backendPluginClient{
client: c,
broker: broker,
metadataMode: b.MetadataMode,
}, nil
}
func (b GRPCBackendPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
pb.RegisterBackendServer(s, &backendGRPCPluginServer{
broker: broker,

View File

@@ -1,248 +0,0 @@
package plugin
import (
"context"
"errors"
"net/rpc"
log "github.com/hashicorp/go-hclog"
plugin "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)
}

View File

@@ -1,147 +0,0 @@
package plugin
import (
"context"
"errors"
"net/rpc"
hclog "github.com/hashicorp/go-hclog"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/helper/pluginutil"
"github.com/hashicorp/vault/logical"
)
var (
ErrServerInMetadataMode = errors.New("plugin server can not perform action while in metadata mode")
)
// backendPluginServer is the RPC server that backendPluginClient talks to,
// it methods conforming to requirements by net/rpc
type backendPluginServer struct {
broker *plugin.MuxBroker
backend logical.Backend
factory logical.Factory
logger hclog.Logger
sysViewClient *rpc.Client
storageClient *rpc.Client
}
func (b *backendPluginServer) HandleRequest(args *HandleRequestArgs, reply *HandleRequestReply) error {
if pluginutil.InMetadataMode() {
return ErrServerInMetadataMode
}
storage := &StorageClient{client: b.storageClient}
args.Request.Storage = storage
resp, err := b.backend.HandleRequest(context.Background(), args.Request)
*reply = HandleRequestReply{
Response: resp,
Error: wrapError(err),
}
return nil
}
func (b *backendPluginServer) SpecialPaths(_ interface{}, reply *SpecialPathsReply) error {
*reply = SpecialPathsReply{
Paths: b.backend.SpecialPaths(),
}
return nil
}
func (b *backendPluginServer) HandleExistenceCheck(args *HandleExistenceCheckArgs, reply *HandleExistenceCheckReply) error {
if pluginutil.InMetadataMode() {
return ErrServerInMetadataMode
}
storage := &StorageClient{client: b.storageClient}
args.Request.Storage = storage
checkFound, exists, err := b.backend.HandleExistenceCheck(context.TODO(), args.Request)
*reply = HandleExistenceCheckReply{
CheckFound: checkFound,
Exists: exists,
Error: wrapError(err),
}
return nil
}
func (b *backendPluginServer) Cleanup(_ interface{}, _ *struct{}) error {
b.backend.Cleanup(context.Background())
// Close rpc clients
b.sysViewClient.Close()
b.storageClient.Close()
return nil
}
func (b *backendPluginServer) InvalidateKey(args string, _ *struct{}) error {
if pluginutil.InMetadataMode() {
return ErrServerInMetadataMode
}
b.backend.InvalidateKey(context.Background(), args)
return nil
}
// Setup dials into the plugin's broker to get a shimmed storage, logger, and
// system view of the backend. This method also instantiates the underlying
// backend through its factory func for the server side of the plugin.
func (b *backendPluginServer) Setup(args *SetupArgs, reply *SetupReply) error {
// Dial for storage
storageConn, err := b.broker.Dial(args.StorageID)
if err != nil {
*reply = SetupReply{
Error: wrapError(err),
}
return nil
}
rawStorageClient := rpc.NewClient(storageConn)
b.storageClient = rawStorageClient
storage := &StorageClient{client: rawStorageClient}
// Dial for sys view
sysViewConn, err := b.broker.Dial(args.SysViewID)
if err != nil {
*reply = SetupReply{
Error: wrapError(err),
}
return nil
}
rawSysViewClient := rpc.NewClient(sysViewConn)
b.sysViewClient = rawSysViewClient
sysView := &SystemViewClient{client: rawSysViewClient}
config := &logical.BackendConfig{
StorageView: storage,
Logger: b.logger,
System: sysView,
Config: args.Config,
BackendUUID: args.BackendUUID,
}
// Call the underlying backend factory after shims have been created
// to set b.backend
backend, err := b.factory(context.Background(), config)
if err != nil {
*reply = SetupReply{
Error: wrapError(err),
}
}
b.backend = backend
return nil
}
func (b *backendPluginServer) Type(_ interface{}, reply *TypeReply) error {
*reply = TypeReply{
Type: b.backend.Type(),
}
return nil
}

View File

@@ -1,173 +0,0 @@
package plugin
import (
"context"
"testing"
"time"
log "github.com/hashicorp/go-hclog"
gplugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/helper/logging"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/plugin/mock"
)
func TestBackendPlugin_impl(t *testing.T) {
var _ gplugin.Plugin = new(BackendPlugin)
var _ logical.Backend = new(backendPluginClient)
}
func TestBackendPlugin_HandleRequest(t *testing.T) {
b, cleanup := testBackend(t)
defer cleanup()
resp, err := b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.CreateOperation,
Path: "kv/foo",
Data: map[string]interface{}{
"value": "bar",
},
})
if err != nil {
t.Fatal(err)
}
if resp.Data["value"] != "bar" {
t.Fatalf("bad: %#v", resp)
}
}
func TestBackendPlugin_SpecialPaths(t *testing.T) {
b, cleanup := testBackend(t)
defer cleanup()
paths := b.SpecialPaths()
if paths == nil {
t.Fatal("SpecialPaths() returned nil")
}
}
func TestBackendPlugin_System(t *testing.T) {
b, cleanup := testBackend(t)
defer cleanup()
sys := b.System()
if sys == nil {
t.Fatal("System() returned nil")
}
actual := sys.DefaultLeaseTTL()
expected := 300 * time.Second
if actual != expected {
t.Fatalf("bad: %v, expected %v", actual, expected)
}
}
func TestBackendPlugin_Logger(t *testing.T) {
b, cleanup := testBackend(t)
defer cleanup()
logger := b.Logger()
if logger == nil {
t.Fatal("Logger() returned nil")
}
}
func TestBackendPlugin_HandleExistenceCheck(t *testing.T) {
b, cleanup := testBackend(t)
defer cleanup()
checkFound, exists, err := b.HandleExistenceCheck(context.Background(), &logical.Request{
Operation: logical.CreateOperation,
Path: "kv/foo",
Data: map[string]interface{}{"value": "bar"},
})
if err != nil {
t.Fatal(err)
}
if !checkFound {
t.Fatal("existence check not found for path 'kv/foo")
}
if exists {
t.Fatal("existence check should have returned 'false' for 'kv/foo'")
}
}
func TestBackendPlugin_Cleanup(t *testing.T) {
b, cleanup := testBackend(t)
defer cleanup()
b.Cleanup(context.Background())
}
func TestBackendPlugin_InvalidateKey(t *testing.T) {
b, cleanup := testBackend(t)
defer cleanup()
ctx := context.Background()
resp, err := b.HandleRequest(ctx, &logical.Request{
Operation: logical.ReadOperation,
Path: "internal",
})
if err != nil {
t.Fatal(err)
}
if resp.Data["value"] == "" {
t.Fatalf("bad: %#v, expected non-empty value", resp)
}
b.InvalidateKey(ctx, "internal")
resp, err = b.HandleRequest(ctx, &logical.Request{
Operation: logical.ReadOperation,
Path: "internal",
})
if err != nil {
t.Fatal(err)
}
if resp.Data["value"] != "" {
t.Fatalf("bad: expected empty response data, got %#v", resp)
}
}
func TestBackendPlugin_Setup(t *testing.T) {
_, cleanup := testBackend(t)
defer cleanup()
}
func testBackend(t *testing.T) (logical.Backend, func()) {
// Create a mock provider
pluginMap := map[string]gplugin.Plugin{
"backend": &BackendPlugin{
GRPCBackendPlugin: &GRPCBackendPlugin{
Factory: mock.Factory,
},
},
}
client, _ := gplugin.TestPluginRPCConn(t, pluginMap, nil)
cleanup := func() {
client.Close()
}
// Request the backend
raw, err := client.Dispense(BackendPluginName)
if err != nil {
t.Fatal(err)
}
b := raw.(logical.Backend)
err = b.Setup(context.Background(), &logical.BackendConfig{
Logger: logging.NewVaultLogger(log.Debug),
System: &logical.StaticSystemView{
DefaultLeaseTTLVal: 300 * time.Second,
MaxLeaseTTLVal: 1800 * time.Second,
},
StorageView: &logical.InmemStorage{},
})
if err != nil {
t.Fatal(err)
}
return b, cleanup
}

View File

@@ -16,6 +16,7 @@ import (
)
var ErrPluginShutdown = errors.New("plugin is shut down")
var ErrClientInMetadataMode = errors.New("plugin client can not perform action while in metadata mode")
// Validate backendGRPCPluginClient satisfies the logical.Backend interface
var _ logical.Backend = &backendGRPCPluginClient{}

View File

@@ -2,6 +2,7 @@ package plugin
import (
"context"
"errors"
log "github.com/hashicorp/go-hclog"
plugin "github.com/hashicorp/go-plugin"
@@ -11,6 +12,8 @@ import (
"google.golang.org/grpc"
)
var ErrServerInMetadataMode = errors.New("plugin server can not perform action while in metadata mode")
type backendGRPCPluginServer struct {
broker *plugin.GRPCBroker
backend logical.Backend

View File

@@ -14,8 +14,8 @@ import (
)
func TestGRPCBackendPlugin_impl(t *testing.T) {
var _ gplugin.Plugin = new(BackendPlugin)
var _ logical.Backend = new(backendPluginClient)
var _ gplugin.Plugin = new(GRPCBackendPlugin)
var _ logical.Backend = new(backendGRPCPluginClient)
}
func TestGRPCBackendPlugin_HandleRequest(t *testing.T) {
@@ -140,15 +140,13 @@ func TestGRPCBackendPlugin_Setup(t *testing.T) {
func testGRPCBackend(t *testing.T) (logical.Backend, func()) {
// Create a mock provider
pluginMap := map[string]gplugin.Plugin{
"backend": &BackendPlugin{
GRPCBackendPlugin: &GRPCBackendPlugin{
Factory: mock.Factory,
Logger: log.New(&log.LoggerOptions{
Level: log.Debug,
Output: os.Stderr,
JSONFormat: true,
}),
},
"backend": &GRPCBackendPlugin{
Factory: mock.Factory,
Logger: log.New(&log.LoggerOptions{
Level: log.Debug,
Output: os.Stderr,
JSONFormat: true,
}),
},
}
client, _ := gplugin.TestPluginGRPCConn(t, pluginMap)

View File

@@ -108,3 +108,23 @@ func (s *GRPCStorageServer) Delete(ctx context.Context, args *pb.StorageDeleteAr
Err: pb.ErrToString(err),
}, nil
}
// NOOPStorage is used to deny access to the storage interface while running a
// backend plugin in metadata mode.
type NOOPStorage struct{}
func (s *NOOPStorage) List(_ context.Context, prefix string) ([]string, error) {
return []string{}, nil
}
func (s *NOOPStorage) Get(_ context.Context, key string) (*logical.StorageEntry, error) {
return nil, nil
}
func (s *NOOPStorage) Put(_ context.Context, entry *logical.StorageEntry) error {
return nil
}
func (s *NOOPStorage) Delete(_ context.Context, key string) error {
return nil
}

View File

@@ -2,13 +2,9 @@ package plugin
import (
"context"
"crypto/ecdsa"
"crypto/rsa"
"encoding/gob"
"errors"
"fmt"
"sync"
"time"
"github.com/hashicorp/errwrap"
log "github.com/hashicorp/go-hclog"
@@ -18,28 +14,6 @@ import (
"github.com/hashicorp/vault/logical"
)
// init registers basic structs with gob which will be used to transport complex
// types through the plugin server and client.
func init() {
// Common basic structs
gob.Register([]interface{}{})
gob.Register(map[string]interface{}{})
gob.Register(map[string]string{})
gob.Register(map[string]int{})
// Register these types since we have to serialize and de-serialize
// tls.ConnectionState over the wire as part of logical.Request.Connection.
gob.Register(rsa.PublicKey{})
gob.Register(ecdsa.PublicKey{})
gob.Register(time.Duration(0))
// Custom common error types for requests. If you add something here, you must
// also add it to the switch statement in `wrapError`!
gob.Register(&plugin.BasicError{})
gob.Register(logical.CodedError(0, ""))
gob.Register(&logical.StatusBadRequest{})
}
// BackendPluginClient is a wrapper around backendPluginClient
// that also contains its plugin.Client instance. It's primarily
// used to cleanly kill the client on Cleanup()
@@ -98,11 +72,13 @@ func NewBackend(ctx context.Context, pluginName string, pluginType consts.Plugin
func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner, logger log.Logger, isMetadataMode bool) (logical.Backend, error) {
// pluginMap is the map of plugins we can dispense.
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: plugin.PluginSet{
"backend": &BackendPlugin{
GRPCBackendPlugin: &GRPCBackendPlugin{
MetadataMode: isMetadataMode,
},
"backend": &GRPCBackendPlugin{
MetadataMode: isMetadataMode,
},
},
4: plugin.PluginSet{
@@ -142,10 +118,6 @@ func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunne
// We should have a logical backend type now. This feels like a normal interface
// implementation but is in fact over an RPC connection.
switch raw.(type) {
case *backendPluginClient:
logger.Warn("plugin is using deprecated netRPC transport, recompile plugin to upgrade to gRPC", "plugin", pluginRunner.Name)
backend = raw.(*backendPluginClient)
transport = "netRPC"
case *backendGRPCPluginClient:
backend = raw.(*backendGRPCPluginClient)
transport = "gRPC"

View File

@@ -39,12 +39,14 @@ func Serve(opts *ServeOpts) error {
// pluginMap is the map of plugins we can dispense.
pluginSets := 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: plugin.PluginSet{
"backend": &BackendPlugin{
GRPCBackendPlugin: &GRPCBackendPlugin{
Factory: opts.BackendFactoryFunc,
Logger: logger,
},
"backend": &GRPCBackendPlugin{
Factory: opts.BackendFactoryFunc,
Logger: logger,
},
},
4: plugin.PluginSet{
@@ -74,13 +76,6 @@ func Serve(opts *ServeOpts) error {
},
}
// If we do not have gRPC support fallback to version 3
// Remove this block in 0.13
if !pluginutil.GRPCSupport() {
serveOpts.GRPCServer = nil
delete(pluginSets, 4)
}
plugin.Serve(serveOpts)
return nil

View File

@@ -1,139 +0,0 @@
package plugin
import (
"context"
"net/rpc"
"github.com/hashicorp/vault/logical"
)
// StorageClient is an implementation of logical.Storage that communicates
// over RPC.
type StorageClient struct {
client *rpc.Client
}
func (s *StorageClient) List(_ context.Context, prefix string) ([]string, error) {
var reply StorageListReply
err := s.client.Call("Plugin.List", prefix, &reply)
if err != nil {
return reply.Keys, err
}
if reply.Error != nil {
return reply.Keys, reply.Error
}
return reply.Keys, nil
}
func (s *StorageClient) Get(_ context.Context, key string) (*logical.StorageEntry, error) {
var reply StorageGetReply
err := s.client.Call("Plugin.Get", key, &reply)
if err != nil {
return nil, err
}
if reply.Error != nil {
return nil, reply.Error
}
return reply.StorageEntry, nil
}
func (s *StorageClient) Put(_ context.Context, entry *logical.StorageEntry) error {
var reply StoragePutReply
err := s.client.Call("Plugin.Put", entry, &reply)
if err != nil {
return err
}
if reply.Error != nil {
return reply.Error
}
return nil
}
func (s *StorageClient) Delete(_ context.Context, key string) error {
var reply StorageDeleteReply
err := s.client.Call("Plugin.Delete", key, &reply)
if err != nil {
return err
}
if reply.Error != nil {
return reply.Error
}
return nil
}
// StorageServer is a net/rpc compatible structure for serving
type StorageServer struct {
impl logical.Storage
}
func (s *StorageServer) List(prefix string, reply *StorageListReply) error {
keys, err := s.impl.List(context.Background(), prefix)
*reply = StorageListReply{
Keys: keys,
Error: wrapError(err),
}
return nil
}
func (s *StorageServer) Get(key string, reply *StorageGetReply) error {
storageEntry, err := s.impl.Get(context.Background(), key)
*reply = StorageGetReply{
StorageEntry: storageEntry,
Error: wrapError(err),
}
return nil
}
func (s *StorageServer) Put(entry *logical.StorageEntry, reply *StoragePutReply) error {
err := s.impl.Put(context.Background(), entry)
*reply = StoragePutReply{
Error: wrapError(err),
}
return nil
}
func (s *StorageServer) Delete(key string, reply *StorageDeleteReply) error {
err := s.impl.Delete(context.Background(), key)
*reply = StorageDeleteReply{
Error: wrapError(err),
}
return nil
}
type StorageListReply struct {
Keys []string
Error error
}
type StorageGetReply struct {
StorageEntry *logical.StorageEntry
Error error
}
type StoragePutReply struct {
Error error
}
type StorageDeleteReply struct {
Error error
}
// NOOPStorage is used to deny access to the storage interface while running a
// backend plugin in metadata mode.
type NOOPStorage struct{}
func (s *NOOPStorage) List(_ context.Context, prefix string) ([]string, error) {
return []string{}, nil
}
func (s *NOOPStorage) Get(_ context.Context, key string) (*logical.StorageEntry, error) {
return nil, nil
}
func (s *NOOPStorage) Put(_ context.Context, entry *logical.StorageEntry) error {
return nil
}
func (s *NOOPStorage) Delete(_ context.Context, key string) error {
return nil
}

View File

@@ -11,22 +11,7 @@ import (
)
func TestStorage_impl(t *testing.T) {
var _ logical.Storage = new(StorageClient)
}
func TestStorage_RPC(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
storage := &logical.InmemStorage{}
server.RegisterName("Plugin", &StorageServer{
impl: storage,
})
testStorage := &StorageClient{client: client}
logical.TestStorage(t, testStorage)
var _ logical.Storage = new(GRPCStorageClient)
}
func TestStorage_GRPC(t *testing.T) {

View File

@@ -1,351 +0,0 @@
package plugin
import (
"context"
"net/rpc"
"time"
"fmt"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/license"
"github.com/hashicorp/vault/helper/pluginutil"
"github.com/hashicorp/vault/helper/wrapping"
"github.com/hashicorp/vault/logical"
)
type SystemViewClient struct {
client *rpc.Client
}
func (s *SystemViewClient) DefaultLeaseTTL() time.Duration {
var reply DefaultLeaseTTLReply
err := s.client.Call("Plugin.DefaultLeaseTTL", new(interface{}), &reply)
if err != nil {
return 0
}
return reply.DefaultLeaseTTL
}
func (s *SystemViewClient) MaxLeaseTTL() time.Duration {
var reply MaxLeaseTTLReply
err := s.client.Call("Plugin.MaxLeaseTTL", new(interface{}), &reply)
if err != nil {
return 0
}
return reply.MaxLeaseTTL
}
func (s *SystemViewClient) SudoPrivilege(ctx context.Context, path string, token string) bool {
var reply SudoPrivilegeReply
args := &SudoPrivilegeArgs{
Path: path,
Token: token,
}
err := s.client.Call("Plugin.SudoPrivilege", args, &reply)
if err != nil {
return false
}
return reply.Sudo
}
func (s *SystemViewClient) Tainted() bool {
var reply TaintedReply
err := s.client.Call("Plugin.Tainted", new(interface{}), &reply)
if err != nil {
return false
}
return reply.Tainted
}
func (s *SystemViewClient) CachingDisabled() bool {
var reply CachingDisabledReply
err := s.client.Call("Plugin.CachingDisabled", new(interface{}), &reply)
if err != nil {
return false
}
return reply.CachingDisabled
}
func (s *SystemViewClient) ReplicationState() consts.ReplicationState {
var reply ReplicationStateReply
err := s.client.Call("Plugin.ReplicationState", new(interface{}), &reply)
if err != nil {
return consts.ReplicationUnknown
}
return reply.ReplicationState
}
func (s *SystemViewClient) ResponseWrapData(ctx context.Context, data map[string]interface{}, ttl time.Duration, jwt bool) (*wrapping.ResponseWrapInfo, error) {
var reply ResponseWrapDataReply
// Do not allow JWTs to be returned
args := &ResponseWrapDataArgs{
Data: data,
TTL: ttl,
JWT: false,
}
err := s.client.Call("Plugin.ResponseWrapData", args, &reply)
if err != nil {
return nil, err
}
if reply.Error != nil {
return nil, reply.Error
}
return reply.ResponseWrapInfo, nil
}
func (s *SystemViewClient) LookupPlugin(_ context.Context, _ string, _ consts.PluginType) (*pluginutil.PluginRunner, error) {
return nil, fmt.Errorf("cannot call LookupPlugin from a plugin backend")
}
func (s *SystemViewClient) HasFeature(feature license.Features) bool {
// Not implemented
return false
}
func (s *SystemViewClient) MlockEnabled() bool {
var reply MlockEnabledReply
err := s.client.Call("Plugin.MlockEnabled", new(interface{}), &reply)
if err != nil {
return false
}
return reply.MlockEnabled
}
func (s *SystemViewClient) LocalMount() bool {
var reply LocalMountReply
err := s.client.Call("Plugin.LocalMount", new(interface{}), &reply)
if err != nil {
return false
}
return reply.Local
}
func (s *SystemViewClient) EntityInfo(entityID string) (*logical.Entity, error) {
var reply EntityInfoReply
args := &EntityInfoArgs{
EntityID: entityID,
}
err := s.client.Call("Plugin.EntityInfo", args, &reply)
if err != nil {
return nil, err
}
if reply.Error != nil {
return nil, reply.Error
}
return reply.Entity, nil
}
func (s *SystemViewClient) PluginEnv(_ context.Context) (*logical.PluginEnvironment, error) {
var reply PluginEnvReply
err := s.client.Call("Plugin.PluginEnv", new(interface{}), &reply)
if err != nil {
return nil, err
}
if reply.Error != nil {
return nil, reply.Error
}
return reply.PluginEnvironment, nil
}
type SystemViewServer struct {
impl logical.SystemView
}
func (s *SystemViewServer) DefaultLeaseTTL(_ interface{}, reply *DefaultLeaseTTLReply) error {
ttl := s.impl.DefaultLeaseTTL()
*reply = DefaultLeaseTTLReply{
DefaultLeaseTTL: ttl,
}
return nil
}
func (s *SystemViewServer) MaxLeaseTTL(_ interface{}, reply *MaxLeaseTTLReply) error {
ttl := s.impl.MaxLeaseTTL()
*reply = MaxLeaseTTLReply{
MaxLeaseTTL: ttl,
}
return nil
}
func (s *SystemViewServer) SudoPrivilege(args *SudoPrivilegeArgs, reply *SudoPrivilegeReply) error {
sudo := s.impl.SudoPrivilege(context.Background(), args.Path, args.Token)
*reply = SudoPrivilegeReply{
Sudo: sudo,
}
return nil
}
func (s *SystemViewServer) Tainted(_ interface{}, reply *TaintedReply) error {
tainted := s.impl.Tainted()
*reply = TaintedReply{
Tainted: tainted,
}
return nil
}
func (s *SystemViewServer) CachingDisabled(_ interface{}, reply *CachingDisabledReply) error {
cachingDisabled := s.impl.CachingDisabled()
*reply = CachingDisabledReply{
CachingDisabled: cachingDisabled,
}
return nil
}
func (s *SystemViewServer) ReplicationState(_ interface{}, reply *ReplicationStateReply) error {
replicationState := s.impl.ReplicationState()
*reply = ReplicationStateReply{
ReplicationState: replicationState,
}
return nil
}
func (s *SystemViewServer) ResponseWrapData(args *ResponseWrapDataArgs, reply *ResponseWrapDataReply) error {
// Do not allow JWTs to be returned
info, err := s.impl.ResponseWrapData(context.Background(), args.Data, args.TTL, false)
if err != nil {
*reply = ResponseWrapDataReply{
Error: wrapError(err),
}
return nil
}
*reply = ResponseWrapDataReply{
ResponseWrapInfo: info,
}
return nil
}
func (s *SystemViewServer) MlockEnabled(_ interface{}, reply *MlockEnabledReply) error {
enabled := s.impl.MlockEnabled()
*reply = MlockEnabledReply{
MlockEnabled: enabled,
}
return nil
}
func (s *SystemViewServer) LocalMount(_ interface{}, reply *LocalMountReply) error {
local := s.impl.LocalMount()
*reply = LocalMountReply{
Local: local,
}
return nil
}
func (s *SystemViewServer) EntityInfo(args *EntityInfoArgs, reply *EntityInfoReply) error {
entity, err := s.impl.EntityInfo(args.EntityID)
if err != nil {
*reply = EntityInfoReply{
Error: wrapError(err),
}
return nil
}
*reply = EntityInfoReply{
Entity: entity,
}
return nil
}
func (s *SystemViewServer) PluginEnv(_ interface{}, reply *PluginEnvReply) error {
pluginEnv, err := s.impl.PluginEnv(context.Background())
if err != nil {
*reply = PluginEnvReply{
Error: wrapError(err),
}
return nil
}
*reply = PluginEnvReply{
PluginEnvironment: pluginEnv,
}
return nil
}
type DefaultLeaseTTLReply struct {
DefaultLeaseTTL time.Duration
}
type MaxLeaseTTLReply struct {
MaxLeaseTTL time.Duration
}
type SudoPrivilegeArgs struct {
Path string
Token string
}
type SudoPrivilegeReply struct {
Sudo bool
}
type TaintedReply struct {
Tainted bool
}
type CachingDisabledReply struct {
CachingDisabled bool
}
type ReplicationStateReply struct {
ReplicationState consts.ReplicationState
}
type ResponseWrapDataArgs struct {
Data map[string]interface{}
TTL time.Duration
JWT bool
}
type ResponseWrapDataReply struct {
ResponseWrapInfo *wrapping.ResponseWrapInfo
Error error
}
type MlockEnabledReply struct {
MlockEnabled bool
}
type LocalMountReply struct {
Local bool
}
type EntityInfoArgs struct {
EntityID string
}
type EntityInfoReply struct {
Entity *logical.Entity
Error error
}
type PluginEnvReply struct {
PluginEnvironment *logical.PluginEnvironment
Error error
}

View File

@@ -1,231 +0,0 @@
package plugin
import (
"context"
"testing"
"reflect"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/logical"
)
func Test_impl(t *testing.T) {
var _ logical.SystemView = new(SystemViewClient)
}
func TestSystem_defaultLeaseTTL(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
sys := logical.TestSystemView()
server.RegisterName("Plugin", &SystemViewServer{
impl: sys,
})
testSystemView := &SystemViewClient{client: client}
expected := sys.DefaultLeaseTTL()
actual := testSystemView.DefaultLeaseTTL()
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("expected: %v, got: %v", expected, actual)
}
}
func TestSystem_maxLeaseTTL(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
sys := logical.TestSystemView()
server.RegisterName("Plugin", &SystemViewServer{
impl: sys,
})
testSystemView := &SystemViewClient{client: client}
expected := sys.MaxLeaseTTL()
actual := testSystemView.MaxLeaseTTL()
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("expected: %v, got: %v", expected, actual)
}
}
func TestSystem_sudoPrivilege(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
sys := logical.TestSystemView()
sys.SudoPrivilegeVal = true
server.RegisterName("Plugin", &SystemViewServer{
impl: sys,
})
testSystemView := &SystemViewClient{client: client}
ctx := context.Background()
expected := sys.SudoPrivilege(ctx, "foo", "bar")
actual := testSystemView.SudoPrivilege(ctx, "foo", "bar")
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("expected: %v, got: %v", expected, actual)
}
}
func TestSystem_tainted(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
sys := logical.TestSystemView()
sys.TaintedVal = true
server.RegisterName("Plugin", &SystemViewServer{
impl: sys,
})
testSystemView := &SystemViewClient{client: client}
expected := sys.Tainted()
actual := testSystemView.Tainted()
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("expected: %v, got: %v", expected, actual)
}
}
func TestSystem_cachingDisabled(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
sys := logical.TestSystemView()
sys.CachingDisabledVal = true
server.RegisterName("Plugin", &SystemViewServer{
impl: sys,
})
testSystemView := &SystemViewClient{client: client}
expected := sys.CachingDisabled()
actual := testSystemView.CachingDisabled()
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("expected: %v, got: %v", expected, actual)
}
}
func TestSystem_replicationState(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
sys := logical.TestSystemView()
sys.ReplicationStateVal = consts.ReplicationPerformancePrimary
server.RegisterName("Plugin", &SystemViewServer{
impl: sys,
})
testSystemView := &SystemViewClient{client: client}
expected := sys.ReplicationState()
actual := testSystemView.ReplicationState()
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("expected: %v, got: %v", expected, actual)
}
}
func TestSystem_responseWrapData(t *testing.T) {
t.SkipNow()
}
func TestSystem_lookupPlugin(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
sys := logical.TestSystemView()
server.RegisterName("Plugin", &SystemViewServer{
impl: sys,
})
testSystemView := &SystemViewClient{client: client}
if _, err := testSystemView.LookupPlugin(context.Background(), "foo", consts.PluginTypeDatabase); err == nil {
t.Fatal("LookPlugin(): expected error on due to unsupported call from plugin")
}
}
func TestSystem_mlockEnabled(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
sys := logical.TestSystemView()
sys.EnableMlock = true
server.RegisterName("Plugin", &SystemViewServer{
impl: sys,
})
testSystemView := &SystemViewClient{client: client}
expected := sys.MlockEnabled()
actual := testSystemView.MlockEnabled()
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("expected: %v, got: %v", expected, actual)
}
}
func TestSystem_entityInfo(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
sys := logical.TestSystemView()
sys.EntityVal = &logical.Entity{
ID: "test",
Name: "name",
}
server.RegisterName("Plugin", &SystemViewServer{
impl: sys,
})
testSystemView := &SystemViewClient{client: client}
actual, err := testSystemView.EntityInfo("")
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(sys.EntityVal, actual) {
t.Fatalf("expected: %v, got: %v", sys.EntityVal, actual)
}
}
func TestSystem_pluginEnv(t *testing.T) {
client, server := plugin.TestRPCConn(t)
defer client.Close()
sys := logical.TestSystemView()
sys.PluginEnvironment = &logical.PluginEnvironment{
VaultVersion: "0.10.42",
}
server.RegisterName("Plugin", &SystemViewServer{
impl: sys,
})
testSystemView := &SystemViewClient{client: client}
expected, err := sys.PluginEnv(context.Background())
if err != nil {
t.Fatal(err)
}
actual, err := testSystemView.PluginEnv(context.Background())
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("expected: %v, got: %v", expected, actual)
}
}