Multiplexing opt out flag (#16972)

* added mplexing opt-out flag
This commit is contained in:
Max Coulombe 2022-09-08 11:32:46 -04:00 committed by GitHub
parent ced0109c41
commit f9b5d1a563
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 113 additions and 20 deletions

3
changelog/16972.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
plugins: Added environment variable flag to opt-out specific plugins from multiplexing
```

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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
}

View File

@ -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