parent
ced0109c41
commit
f9b5d1a563
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
plugins: Added environment variable flag to opt-out specific plugins from multiplexing
|
||||||
|
```
|
|
@ -1,6 +1,6 @@
|
||||||
module github.com/hashicorp/vault/sdk
|
module github.com/hashicorp/vault/sdk
|
||||||
|
|
||||||
go 1.17
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/armon/go-metrics v0.3.9
|
github.com/armon/go-metrics v0.3.9
|
||||||
|
|
|
@ -31,6 +31,10 @@ const (
|
||||||
// PluginCACertPEMEnv is an ENV name used for holding a CA PEM-encoded
|
// PluginCACertPEMEnv is an ENV name used for holding a CA PEM-encoded
|
||||||
// string. Used for testing.
|
// string. Used for testing.
|
||||||
PluginCACertPEMEnv = "VAULT_TESTING_PLUGIN_CA_PEM"
|
PluginCACertPEMEnv = "VAULT_TESTING_PLUGIN_CA_PEM"
|
||||||
|
|
||||||
|
// PluginMultiplexingOptOut is an ENV name used to define a comma separated list of plugin names
|
||||||
|
// opted-out of the multiplexing feature; for emergencies if multiplexing ever causes issues
|
||||||
|
PluginMultiplexingOptOut = "VAULT_PLUGIN_MULTIPLEXING_OPT_OUT"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OptionallyEnableMlock determines if mlock should be called, and if so enables
|
// OptionallyEnableMlock determines if mlock should be called, and if so enables
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
package pluginutil
|
package pluginutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
context "context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
grpc "google.golang.org/grpc"
|
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||||
codes "google.golang.org/grpc/codes"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
status "google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PluginMultiplexingServerImpl struct {
|
type PluginMultiplexingServerImpl struct {
|
||||||
|
@ -16,17 +19,22 @@ type PluginMultiplexingServerImpl struct {
|
||||||
Supported bool
|
Supported bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm PluginMultiplexingServerImpl) MultiplexingSupport(ctx context.Context, req *MultiplexingSupportRequest) (*MultiplexingSupportResponse, error) {
|
func (pm PluginMultiplexingServerImpl) MultiplexingSupport(_ context.Context, _ *MultiplexingSupportRequest) (*MultiplexingSupportResponse, error) {
|
||||||
return &MultiplexingSupportResponse{
|
return &MultiplexingSupportResponse{
|
||||||
Supported: pm.Supported,
|
Supported: pm.Supported,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MultiplexingSupported(ctx context.Context, cc grpc.ClientConnInterface) (bool, error) {
|
func MultiplexingSupported(ctx context.Context, cc grpc.ClientConnInterface, name string) (bool, error) {
|
||||||
if cc == nil {
|
if cc == nil {
|
||||||
return false, fmt.Errorf("client connection is nil")
|
return false, fmt.Errorf("client connection is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out := strings.Split(os.Getenv(PluginMultiplexingOptOut), ",")
|
||||||
|
if strutil.StrListContains(out, name) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
req := new(MultiplexingSupportRequest)
|
req := new(MultiplexingSupportRequest)
|
||||||
resp, err := NewPluginMultiplexingClient(cc).MultiplexingSupport(ctx, req)
|
resp, err := NewPluginMultiplexingClient(cc).MultiplexingSupport(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -6,9 +6,82 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestMultiplexingSupported(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
env string
|
||||||
|
want bool
|
||||||
|
wantErr bool
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []testCase{
|
||||||
|
{
|
||||||
|
name: "multiplexing is supported if plugin is not opted out",
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
cc: &MockClientConnInterfaceNoop{},
|
||||||
|
name: "plugin",
|
||||||
|
},
|
||||||
|
env: "",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiplexing is not supported if plugin is opted out",
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
cc: &MockClientConnInterfaceNoop{},
|
||||||
|
name: "optedOutPlugin",
|
||||||
|
},
|
||||||
|
env: "optedOutPlugin",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiplexing is not supported if plugin among one of the opted out",
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
cc: &MockClientConnInterfaceNoop{},
|
||||||
|
name: "optedOutPlugin",
|
||||||
|
},
|
||||||
|
env: "firstPlugin,optedOutPlugin,otherPlugin",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiplexing is supported if different plugin is opted out",
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
cc: &MockClientConnInterfaceNoop{},
|
||||||
|
name: "plugin",
|
||||||
|
},
|
||||||
|
env: "optedOutPlugin",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Setenv(PluginMultiplexingOptOut, tt.env)
|
||||||
|
got, err := MultiplexingSupported(tt.args.ctx, tt.args.cc, tt.args.name)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("MultiplexingSupported() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("MultiplexingSupported() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetMultiplexIDFromContext(t *testing.T) {
|
func TestGetMultiplexIDFromContext(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
@ -71,3 +144,14 @@ func idCtx(t *testing.T, ids ...string) context.Context {
|
||||||
}
|
}
|
||||||
return metadata.NewIncomingContext(ctx, md)
|
return metadata.NewIncomingContext(ctx, md)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MockClientConnInterfaceNoop struct{}
|
||||||
|
|
||||||
|
func (m *MockClientConnInterfaceNoop) Invoke(_ context.Context, _ string, _ interface{}, reply interface{}, _ ...grpc.CallOption) error {
|
||||||
|
reply.(*MultiplexingSupportResponse).Supported = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockClientConnInterfaceNoop) NewStream(_ context.Context, _ *grpc.StreamDesc, _ string, _ ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
@ -348,21 +348,15 @@ func (c *PluginCatalog) newPluginClient(ctx context.Context, pluginRunner *plugi
|
||||||
|
|
||||||
clientConn := rpcClient.(*plugin.GRPCClient).Conn
|
clientConn := rpcClient.(*plugin.GRPCClient).Conn
|
||||||
|
|
||||||
muxed, err := pluginutil.MultiplexingSupported(ctx, clientConn)
|
muxed, err := pluginutil.MultiplexingSupported(ctx, clientConn, config.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if muxed {
|
|
||||||
// Wrap rpcClient with our implementation so that we can inject the
|
|
||||||
// ID into the context
|
|
||||||
pc.clientConn = &pluginClientConn{
|
pc.clientConn = &pluginClientConn{
|
||||||
ClientConn: clientConn,
|
ClientConn: clientConn,
|
||||||
id: id,
|
id: id,
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
pc.clientConn = clientConn
|
|
||||||
}
|
|
||||||
|
|
||||||
pc.ClientProtocol = rpcClient
|
pc.ClientProtocol = rpcClient
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue