mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-03 03:58:01 +00:00
Remove netRPC based plugins (#6173)
* Remove netRPC backend plugins * Remove netRPC database plugins * Fix tests and comments
This commit is contained in:
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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{}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user