diff --git a/Makefile b/Makefile index f6f07c5001..32ee689396 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,8 @@ proto: protoc -I physical physical/types.proto --go_out=plugins=grpc:physical protoc -I helper/identity -I ../../.. helper/identity/types.proto --go_out=plugins=grpc:helper/identity protoc builtin/logical/database/dbplugin/*.proto --go_out=plugins=grpc:. - sed -i -e 's/Idp/IDP/' -e 's/Url/URL/' -e 's/Id/ID/' -e 's/EntityId/EntityID/' -e 's/Api/API/' -e 's/Qr/QR/' -e 's/protobuf:"/sentinel:"" protobuf:"/' helper/identity/types.pb.go helper/storagepacker/types.pb.go + protoc logical/plugin/pb/*.proto --go_out=plugins=grpc:. + sed -i -e 's/Idp/IDP/' -e 's/Url/URL/' -e 's/Id/ID/' -e 's/EntityId/EntityID/' -e 's/Api/API/' -e 's/Qr/QR/' -e 's/protobuf:"/sentinel:"" protobuf:"/' helper/identity/types.pb.go helper/storagepacker/types.pb.go logical/plugin/pb/backend.pb.go sed -i -e 's/Iv/IV/' -e 's/Hmac/HMAC/' physical/types.pb.go fmtcheck: diff --git a/builtin/logical/database/dbplugin/grpc_transport.go b/builtin/logical/database/dbplugin/grpc_transport.go index 0b277968ce..735d9e5b88 100644 --- a/builtin/logical/database/dbplugin/grpc_transport.go +++ b/builtin/logical/database/dbplugin/grpc_transport.go @@ -7,9 +7,9 @@ import ( "time" "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" "github.com/golang/protobuf/ptypes" + "github.com/hashicorp/vault/helper/pluginutil" ) var ( @@ -83,20 +83,12 @@ func (s *gRPCServer) Close(_ context.Context, _ *Empty) (*Empty, error) { type gRPCClient struct { client DatabaseClient clientConn *grpc.ClientConn + + doneCtx context.Context } func (c gRPCClient) Type() (string, error) { - // If the plugin has already shutdown, this will hang forever so we give it - // a one second timeout. - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - switch c.clientConn.GetState() { - case connectivity.Ready, connectivity.Idle: - default: - return "", ErrPluginShutdown - } - resp, err := c.client.Type(ctx, &Empty{}) + resp, err := c.client.Type(c.doneCtx, &Empty{}) if err != nil { return "", err } @@ -110,11 +102,10 @@ func (c gRPCClient) CreateUser(ctx context.Context, statements Statements, usern return "", "", err } - switch c.clientConn.GetState() { - case connectivity.Ready, connectivity.Idle: - default: - return "", "", ErrPluginShutdown - } + ctx, cancel := context.WithCancel(ctx) + quitCh := pluginutil.CtxCancelIfCanceled(cancel, c.doneCtx) + defer close(quitCh) + defer cancel() resp, err := c.client.CreateUser(ctx, &CreateUserRequest{ Statements: &statements, @@ -122,6 +113,10 @@ func (c gRPCClient) CreateUser(ctx context.Context, statements Statements, usern Expiration: t, }) if err != nil { + if c.doneCtx.Err() != nil { + return "", "", ErrPluginShutdown + } + return "", "", err } @@ -134,33 +129,47 @@ func (c *gRPCClient) RenewUser(ctx context.Context, statements Statements, usern return err } - switch c.clientConn.GetState() { - case connectivity.Ready, connectivity.Idle: - default: - return ErrPluginShutdown - } + ctx, cancel := context.WithCancel(ctx) + quitCh := pluginutil.CtxCancelIfCanceled(cancel, c.doneCtx) + defer close(quitCh) + defer cancel() _, err = c.client.RenewUser(ctx, &RenewUserRequest{ Statements: &statements, Username: username, Expiration: t, }) + if err != nil { + if c.doneCtx.Err() != nil { + return ErrPluginShutdown + } - return err + return err + } + + return nil } func (c *gRPCClient) RevokeUser(ctx context.Context, statements Statements, username string) error { - switch c.clientConn.GetState() { - case connectivity.Ready, connectivity.Idle: - default: - return ErrPluginShutdown - } + ctx, cancel := context.WithCancel(ctx) + quitCh := pluginutil.CtxCancelIfCanceled(cancel, c.doneCtx) + defer close(quitCh) + defer cancel() + _, err := c.client.RevokeUser(ctx, &RevokeUserRequest{ Statements: &statements, Username: username, }) - return err + if err != nil { + if c.doneCtx.Err() != nil { + return ErrPluginShutdown + } + + return err + } + + return nil } func (c *gRPCClient) Initialize(ctx context.Context, config map[string]interface{}, verifyConnection bool) error { @@ -169,30 +178,27 @@ func (c *gRPCClient) Initialize(ctx context.Context, config map[string]interface return err } - switch c.clientConn.GetState() { - case connectivity.Ready, connectivity.Idle: - default: - return ErrPluginShutdown - } + ctx, cancel := context.WithCancel(ctx) + quitCh := pluginutil.CtxCancelIfCanceled(cancel, c.doneCtx) + defer close(quitCh) + defer cancel() _, err = c.client.Initialize(ctx, &InitializeRequest{ Config: configRaw, VerifyConnection: verifyConnection, }) + if err != nil { + if c.doneCtx.Err() != nil { + return ErrPluginShutdown + } - return err -} - -func (c *gRPCClient) Close() error { - // If the plugin has already shutdown, this will hang forever so we give it - // a one second timeout. - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - switch c.clientConn.GetState() { - case connectivity.Ready, connectivity.Idle: - _, err := c.client.Close(ctx, &Empty{}) return err } return nil } + +func (c *gRPCClient) Close() error { + _, err := c.client.Close(c.doneCtx, &Empty{}) + return err +} diff --git a/builtin/logical/database/dbplugin/plugin.go b/builtin/logical/database/dbplugin/plugin.go index 0f4bfee802..75e2d16d89 100644 --- a/builtin/logical/database/dbplugin/plugin.go +++ b/builtin/logical/database/dbplugin/plugin.go @@ -103,6 +103,9 @@ var handshakeConfig = plugin.HandshakeConfig{ MagicCookieValue: "926a0820-aea2-be28-51d6-83cdf00e8edb", } +var _ plugin.Plugin = &DatabasePlugin{} +var _ plugin.GRPCPlugin = &DatabasePlugin{} + // DatabasePlugin implements go-plugin's Plugin interface. It has methods for // retrieving a server and a client instance of the plugin. type DatabasePlugin struct { @@ -117,14 +120,15 @@ func (DatabasePlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, e return &databasePluginRPCClient{client: c}, nil } -func (d DatabasePlugin) GRPCServer(s *grpc.Server) error { +func (d DatabasePlugin) GRPCServer(_ *plugin.GRPCBroker, s *grpc.Server) error { RegisterDatabaseServer(s, &gRPCServer{impl: d.impl}) return nil } -func (DatabasePlugin) GRPCClient(c *grpc.ClientConn) (interface{}, error) { +func (DatabasePlugin) GRPCClient(doneCtx context.Context, _ *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { return &gRPCClient{ client: NewDatabaseClient(c), clientConn: c, + doneCtx: doneCtx, }, nil } diff --git a/builtin/plugin/backend.go b/builtin/plugin/backend.go index 68f4339e55..9c41417225 100644 --- a/builtin/plugin/backend.go +++ b/builtin/plugin/backend.go @@ -157,7 +157,8 @@ func (b *backend) HandleRequest(ctx context.Context, req *logical.Request) (*log b.RUnlock() // Need to compare string value for case were err comes from plugin RPC // and is returned as plugin.BasicError type. - if err != nil && err.Error() == rpc.ErrShutdown.Error() { + if err != nil && + (err.Error() == rpc.ErrShutdown.Error() || err == bplugin.ErrPluginShutdown) { // Reload plugin if it's an rpc.ErrShutdown b.Lock() if b.canary == canary { @@ -206,7 +207,8 @@ func (b *backend) HandleExistenceCheck(ctx context.Context, req *logical.Request checkFound, exists, err := b.Backend.HandleExistenceCheck(ctx, req) b.RUnlock() - if err != nil && err.Error() == rpc.ErrShutdown.Error() { + if err != nil && + (err.Error() == rpc.ErrShutdown.Error() || err == bplugin.ErrPluginShutdown) { // Reload plugin if it's an rpc.ErrShutdown b.Lock() if b.canary == canary { diff --git a/helper/pluginutil/runner.go b/helper/pluginutil/runner.go index bd9986e2db..f2052a6894 100644 --- a/helper/pluginutil/runner.go +++ b/helper/pluginutil/runner.go @@ -1,6 +1,7 @@ package pluginutil import ( + "context" "crypto/sha256" "crypto/tls" "flag" @@ -168,3 +169,18 @@ func (f *APIClientMeta) GetTLSConfig() *api.TLSConfig { return nil } + +// CancelIfCanceled takes a context cancel func and a context. If the context is +// shutdown the cancelfunc is called. This is useful for merging two cancel +// functions. +func CtxCancelIfCanceled(f context.CancelFunc, ctxCanceler context.Context) chan struct{} { + quitCh := make(chan struct{}) + go func() { + select { + case <-quitCh: + case <-ctxCanceler.Done(): + f() + } + }() + return quitCh +} diff --git a/helper/pluginutil/tls.go b/helper/pluginutil/tls.go index 99278dba67..67cb392faa 100644 --- a/helper/pluginutil/tls.go +++ b/helper/pluginutil/tls.go @@ -97,6 +97,8 @@ func createClientTLSConfig(certBytes []byte, key *ecdsa.PrivateKey) (*tls.Config tlsConfig := &tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: clientCertPool, + ClientCAs: clientCertPool, + ClientAuth: tls.RequireAndVerifyClientCert, ServerName: clientCert.Subject.CommonName, MinVersion: tls.VersionTLS12, } @@ -234,6 +236,7 @@ func VaultPluginTLSProvider(apiTLSConfig *api.TLSConfig) func() (*tls.Config, er // TLS 1.2 minimum MinVersion: tls.VersionTLS12, Certificates: []tls.Certificate{cert}, + ServerName: serverCert.Subject.CommonName, } tlsConfig.BuildNameToCertificate() diff --git a/logical/plugin/backend.go b/logical/plugin/backend.go index 081922c9bd..741461daef 100644 --- a/logical/plugin/backend.go +++ b/logical/plugin/backend.go @@ -1,16 +1,23 @@ package plugin import ( + "context" "net/rpc" + "google.golang.org/grpc" + + hclog "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vault/helper/logbridge" "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/logical/plugin/pb" ) // BackendPlugin is the plugin.Plugin implementation type BackendPlugin struct { Factory func(*logical.BackendConfig) (logical.Backend, error) metadataMode bool + Logger hclog.Logger } // Server gets called when on plugin.Serve() @@ -22,3 +29,23 @@ func (b *BackendPlugin) Server(broker *plugin.MuxBroker) (interface{}, error) { func (b BackendPlugin) Client(broker *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { return &backendPluginClient{client: c, broker: broker, metadataMode: b.metadataMode}, nil } + +func (b BackendPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { + pb.RegisterBackendServer(s, &backendGRPCPluginServer{ + broker: broker, + factory: b.Factory, + // We pass the logger down into the backend so go-plugin will forward + // logs for us. + logger: logbridge.NewLogger(b.Logger).LogxiLogger(), + }) + return nil +} + +func (p *BackendPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return &backendGRPCPluginClient{ + client: pb.NewBackendClient(c), + clientConn: c, + broker: broker, + doneCtx: ctx, + }, nil +} diff --git a/logical/plugin/grpc_backend_client.go b/logical/plugin/grpc_backend_client.go new file mode 100644 index 0000000000..e2f1810891 --- /dev/null +++ b/logical/plugin/grpc_backend_client.go @@ -0,0 +1,212 @@ +package plugin + +import ( + "context" + "errors" + + "google.golang.org/grpc" + + "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vault/helper/pluginutil" + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/logical/plugin/pb" + log "github.com/mgutz/logxi/v1" +) + +var ErrPluginShutdown = errors.New("plugin is shut down") + +// backendPluginClient implements logical.Backend and is the +// go-plugin client. +type backendGRPCPluginClient struct { + broker *plugin.GRPCBroker + client pb.BackendClient + metadataMode bool + + system logical.SystemView + logger log.Logger + + // server is the grpc server used for serving storage and sysview requests. + server *grpc.Server + // clientConn is the underlying grpc connection to the server, we store it + // so it can be cleaned up. + clientConn *grpc.ClientConn + doneCtx context.Context +} + +func (b *backendGRPCPluginClient) HandleRequest(ctx context.Context, req *logical.Request) (*logical.Response, error) { + if b.metadataMode { + return nil, ErrClientInMetadataMode + } + + ctx, cancel := context.WithCancel(ctx) + quitCh := pluginutil.CtxCancelIfCanceled(cancel, b.doneCtx) + defer close(quitCh) + defer cancel() + + protoReq, err := pb.LogicalRequestToProtoRequest(req) + if err != nil { + return nil, err + } + + reply, err := b.client.HandleRequest(ctx, &pb.HandleRequestArgs{ + Request: protoReq, + }) + if err != nil { + if b.doneCtx.Err() != nil { + return nil, ErrPluginShutdown + } + + return nil, err + } + resp, err := pb.ProtoResponseToLogicalResponse(reply.Response) + if err != nil { + return nil, err + } + if reply.Err != nil { + return resp, pb.ProtoErrToErr(reply.Err) + } + + return resp, nil +} + +func (b *backendGRPCPluginClient) SpecialPaths() *logical.Paths { + // Timeout the connection + reply, err := b.client.SpecialPaths(b.doneCtx, &pb.Empty{}) + if err != nil { + return nil + } + + return &logical.Paths{ + Root: reply.Paths.Root, + Unauthenticated: reply.Paths.Unauthenticated, + LocalStorage: reply.Paths.LocalStorage, + SealWrapStorage: reply.Paths.SealWrapStorage, + } +} + +// 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 *backendGRPCPluginClient) 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 *backendGRPCPluginClient) Logger() log.Logger { + return b.logger +} + +func (b *backendGRPCPluginClient) HandleExistenceCheck(ctx context.Context, req *logical.Request) (bool, bool, error) { + if b.metadataMode { + return false, false, ErrClientInMetadataMode + } + + protoReq, err := pb.LogicalRequestToProtoRequest(req) + if err != nil { + return false, false, err + } + + ctx, cancel := context.WithCancel(ctx) + quitCh := pluginutil.CtxCancelIfCanceled(cancel, b.doneCtx) + defer close(quitCh) + defer cancel() + reply, err := b.client.HandleExistenceCheck(ctx, &pb.HandleExistenceCheckArgs{ + Request: protoReq, + }) + if err != nil { + if b.doneCtx.Err() != nil { + return false, false, ErrPluginShutdown + } + return false, false, err + } + if reply.Err != nil { + return false, false, pb.ProtoErrToErr(reply.Err) + } + + return reply.CheckFound, reply.Exists, nil +} + +func (b *backendGRPCPluginClient) Cleanup() { + b.client.Cleanup(b.doneCtx, &pb.Empty{}) + if b.server != nil { + b.server.GracefulStop() + } + b.clientConn.Close() +} + +func (b *backendGRPCPluginClient) Initialize() error { + if b.metadataMode { + return ErrClientInMetadataMode + } + + _, err := b.client.Initialize(b.doneCtx, &pb.Empty{}) + return err +} + +func (b *backendGRPCPluginClient) InvalidateKey(key string) { + if b.metadataMode { + return + } + b.client.InvalidateKey(b.doneCtx, &pb.InvalidateKeyArgs{ + Key: key, + }) +} + +func (b *backendGRPCPluginClient) Setup(config *logical.BackendConfig) error { + // Shim logical.Storage + storageImpl := config.StorageView + if b.metadataMode { + storageImpl = &NOOPStorage{} + } + storage := &GRPCStorageServer{ + impl: storageImpl, + } + + // Shim logical.SystemView + sysViewImpl := config.System + if b.metadataMode { + sysViewImpl = &logical.StaticSystemView{} + } + sysView := &gRPCSystemViewServer{ + impl: sysViewImpl, + } + + // Register the server in this closure. + serverFunc := func(opts []grpc.ServerOption) *grpc.Server { + s := grpc.NewServer(opts...) + pb.RegisterSystemViewServer(s, sysView) + pb.RegisterStorageServer(s, storage) + b.server = s + return s + } + brokerID := b.broker.NextId() + go b.broker.AcceptAndServe(brokerID, serverFunc) + + args := &pb.SetupArgs{ + BrokerID: brokerID, + Config: config.Config, + } + + reply, err := b.client.Setup(b.doneCtx, args) + if err != nil { + return err + } + if reply.Err != "" { + return errors.New(reply.Err) + } + + // Set system and logger for getter methods + b.system = config.System + b.logger = config.Logger + + return nil +} + +func (b *backendGRPCPluginClient) Type() logical.BackendType { + reply, err := b.client.Type(b.doneCtx, &pb.Empty{}) + if err != nil { + return logical.TypeUnknown + } + + return logical.BackendType(reply.Type) +} diff --git a/logical/plugin/grpc_backend_server.go b/logical/plugin/grpc_backend_server.go new file mode 100644 index 0000000000..980d2e0862 --- /dev/null +++ b/logical/plugin/grpc_backend_server.go @@ -0,0 +1,144 @@ +package plugin + +import ( + "context" + + plugin "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/logical/plugin/pb" + log "github.com/mgutz/logxi/v1" + "google.golang.org/grpc" +) + +type backendGRPCPluginServer struct { + broker *plugin.GRPCBroker + backend logical.Backend + + factory func(*logical.BackendConfig) (logical.Backend, error) + + brokeredClient *grpc.ClientConn + + logger log.Logger +} + +// 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 *backendGRPCPluginServer) Setup(ctx context.Context, args *pb.SetupArgs) (*pb.SetupReply, error) { + // Dial for storage + brokeredClient, err := b.broker.Dial(args.BrokerID) + if err != nil { + return &pb.SetupReply{}, err + } + b.brokeredClient = brokeredClient + storage := newGRPCStorageClient(brokeredClient) + sysView := newGRPCSystemView(brokeredClient) + + config := &logical.BackendConfig{ + StorageView: storage, + Logger: b.logger, + System: sysView, + Config: args.Config, + } + + // Call the underlying backend factory after shims have been created + // to set b.backend + backend, err := b.factory(config) + if err != nil { + return &pb.SetupReply{ + Err: pb.ErrToString(err), + }, nil + } + b.backend = backend + + return &pb.SetupReply{}, nil +} + +func (b *backendGRPCPluginServer) HandleRequest(ctx context.Context, args *pb.HandleRequestArgs) (*pb.HandleRequestReply, error) { + if inMetadataMode() { + return &pb.HandleRequestReply{}, ErrServerInMetadataMode + } + + logicalReq, err := pb.ProtoRequestToLogicalRequest(args.Request) + if err != nil { + return &pb.HandleRequestReply{}, err + } + + logicalReq.Storage = newGRPCStorageClient(b.brokeredClient) + + resp, respErr := b.backend.HandleRequest(ctx, logicalReq) + + pbResp, err := pb.LogicalResponseToProtoResponse(resp) + if err != nil { + return &pb.HandleRequestReply{}, err + } + + return &pb.HandleRequestReply{ + Response: pbResp, + Err: pb.ErrToProtoErr(respErr), + }, nil +} + +func (b *backendGRPCPluginServer) SpecialPaths(ctx context.Context, args *pb.Empty) (*pb.SpecialPathsReply, error) { + paths := b.backend.SpecialPaths() + + return &pb.SpecialPathsReply{ + Paths: &pb.Paths{ + Root: paths.Root, + Unauthenticated: paths.Unauthenticated, + LocalStorage: paths.LocalStorage, + SealWrapStorage: paths.SealWrapStorage, + }, + }, nil +} + +func (b *backendGRPCPluginServer) HandleExistenceCheck(ctx context.Context, args *pb.HandleExistenceCheckArgs) (*pb.HandleExistenceCheckReply, error) { + if inMetadataMode() { + return &pb.HandleExistenceCheckReply{}, ErrServerInMetadataMode + } + + logicalReq, err := pb.ProtoRequestToLogicalRequest(args.Request) + if err != nil { + return &pb.HandleExistenceCheckReply{}, err + } + logicalReq.Storage = newGRPCStorageClient(b.brokeredClient) + + checkFound, exists, err := b.backend.HandleExistenceCheck(ctx, logicalReq) + return &pb.HandleExistenceCheckReply{ + CheckFound: checkFound, + Exists: exists, + Err: pb.ErrToProtoErr(err), + }, nil +} + +func (b *backendGRPCPluginServer) Cleanup(ctx context.Context, _ *pb.Empty) (*pb.Empty, error) { + b.backend.Cleanup() + + // Close rpc clients + b.brokeredClient.Close() + return &pb.Empty{}, nil +} + +func (b *backendGRPCPluginServer) Initialize(ctx context.Context, _ *pb.Empty) (*pb.Empty, error) { + if inMetadataMode() { + return &pb.Empty{}, ErrServerInMetadataMode + } + + err := b.backend.Initialize() + return &pb.Empty{}, err +} + +func (b *backendGRPCPluginServer) InvalidateKey(ctx context.Context, args *pb.InvalidateKeyArgs) (*pb.Empty, error) { + if inMetadataMode() { + return &pb.Empty{}, ErrServerInMetadataMode + } + + b.backend.InvalidateKey(args.Key) + return &pb.Empty{}, nil +} + +func (b *backendGRPCPluginServer) Type(ctx context.Context, _ *pb.Empty) (*pb.TypeReply, error) { + return &pb.TypeReply{ + Type: uint32(b.backend.Type()), + }, nil +} diff --git a/logical/plugin/grpc_backend_test.go b/logical/plugin/grpc_backend_test.go new file mode 100644 index 0000000000..6c874b8856 --- /dev/null +++ b/logical/plugin/grpc_backend_test.go @@ -0,0 +1,186 @@ +package plugin + +import ( + "context" + "os" + "testing" + "time" + + hclog "github.com/hashicorp/go-hclog" + gplugin "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vault/helper/logformat" + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/logical/plugin/mock" + log "github.com/mgutz/logxi/v1" +) + +func TestGRPCBackendPlugin_impl(t *testing.T) { + var _ gplugin.Plugin = new(BackendPlugin) + var _ logical.Backend = new(backendPluginClient) +} + +func TestGRPCBackendPlugin_HandleRequest(t *testing.T) { + b, cleanup := testGRPCBackend(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 TestGRPCBackendPlugin_SpecialPaths(t *testing.T) { + b, cleanup := testGRPCBackend(t) + defer cleanup() + + paths := b.SpecialPaths() + if paths == nil { + t.Fatal("SpecialPaths() returned nil") + } +} + +func TestGRPCBackendPlugin_System(t *testing.T) { + b, cleanup := testGRPCBackend(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 TestGRPCBackendPlugin_Logger(t *testing.T) { + b, cleanup := testGRPCBackend(t) + defer cleanup() + + logger := b.Logger() + if logger == nil { + t.Fatal("Logger() returned nil") + } +} + +func TestGRPCBackendPlugin_HandleExistenceCheck(t *testing.T) { + b, cleanup := testGRPCBackend(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 TestGRPCBackendPlugin_Cleanup(t *testing.T) { + b, cleanup := testGRPCBackend(t) + defer cleanup() + + b.Cleanup() +} + +func TestGRPCBackendPlugin_Initialize(t *testing.T) { + b, cleanup := testGRPCBackend(t) + defer cleanup() + + err := b.Initialize() + if err != nil { + t.Fatal(err) + } +} + +func TestGRPCBackendPlugin_InvalidateKey(t *testing.T) { + b, cleanup := testGRPCBackend(t) + defer cleanup() + + resp, err := b.HandleRequest(context.Background(), &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("internal") + + resp, err = b.HandleRequest(context.Background(), &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 TestGRPCBackendPlugin_Setup(t *testing.T) { + _, cleanup := testGRPCBackend(t) + defer cleanup() +} + +func testGRPCBackend(t *testing.T) (logical.Backend, func()) { + // Create a mock provider + pluginMap := map[string]gplugin.Plugin{ + "backend": &BackendPlugin{ + Factory: mock.Factory, + Logger: hclog.New(&hclog.LoggerOptions{ + Level: hclog.Trace, + Output: os.Stderr, + JSONFormat: true, + }), + }, + } + client, _ := gplugin.TestPluginGRPCConn(t, pluginMap) + 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(&logical.BackendConfig{ + Logger: logformat.NewVaultLogger(log.LevelTrace), + System: &logical.StaticSystemView{ + DefaultLeaseTTLVal: 300 * time.Second, + MaxLeaseTTLVal: 1800 * time.Second, + }, + StorageView: &logical.InmemStorage{}, + }) + if err != nil { + t.Fatal(err) + } + + return b, cleanup +} diff --git a/logical/plugin/grpc_storage.go b/logical/plugin/grpc_storage.go new file mode 100644 index 0000000000..2a3df4a40b --- /dev/null +++ b/logical/plugin/grpc_storage.go @@ -0,0 +1,110 @@ +package plugin + +import ( + "context" + "errors" + + "google.golang.org/grpc" + + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/logical/plugin/pb" +) + +func newGRPCStorageClient(conn *grpc.ClientConn) *GRPCStorageClient { + return &GRPCStorageClient{ + client: pb.NewStorageClient(conn), + } +} + +// GRPCStorageClient is an implementation of logical.Storage that communicates +// over RPC. +type GRPCStorageClient struct { + client pb.StorageClient +} + +func (s *GRPCStorageClient) List(prefix string) ([]string, error) { + reply, err := s.client.List(context.Background(), &pb.StorageListArgs{ + Prefix: prefix, + }) + if err != nil { + return reply.Keys, err + } + if reply.Err != "" { + return reply.Keys, errors.New(reply.Err) + } + return reply.Keys, nil +} + +func (s *GRPCStorageClient) Get(key string) (*logical.StorageEntry, error) { + reply, err := s.client.Get(context.Background(), &pb.StorageGetArgs{ + Key: key, + }) + if err != nil { + return nil, err + } + if reply.Err != "" { + return nil, errors.New(reply.Err) + } + return pb.ProtoStorageEntryToLogicalStorageEntry(reply.Entry), nil +} + +func (s *GRPCStorageClient) Put(entry *logical.StorageEntry) error { + reply, err := s.client.Put(context.Background(), &pb.StoragePutArgs{ + Entry: pb.LogicalStorageEntryToProtoStorageEntry(entry), + }) + if err != nil { + return err + } + if reply.Err != "" { + return errors.New(reply.Err) + } + return nil +} + +func (s *GRPCStorageClient) Delete(key string) error { + reply, err := s.client.Delete(context.Background(), &pb.StorageDeleteArgs{ + Key: key, + }) + if err != nil { + return err + } + if reply.Err != "" { + return errors.New(reply.Err) + } + return nil +} + +// StorageServer is a net/rpc compatible structure for serving +type GRPCStorageServer struct { + impl logical.Storage +} + +func (s *GRPCStorageServer) List(ctx context.Context, args *pb.StorageListArgs) (*pb.StorageListReply, error) { + keys, err := s.impl.List(args.Prefix) + return &pb.StorageListReply{ + Keys: keys, + Err: pb.ErrToString(err), + }, nil +} + +func (s *GRPCStorageServer) Get(ctx context.Context, args *pb.StorageGetArgs) (*pb.StorageGetReply, error) { + storageEntry, err := s.impl.Get(args.Key) + return &pb.StorageGetReply{ + Entry: pb.LogicalStorageEntryToProtoStorageEntry(storageEntry), + Err: pb.ErrToString(err), + }, nil +} + +func (s *GRPCStorageServer) Put(ctx context.Context, args *pb.StoragePutArgs) (*pb.StoragePutReply, error) { + err := s.impl.Put(pb.ProtoStorageEntryToLogicalStorageEntry(args.Entry)) + return &pb.StoragePutReply{ + Err: pb.ErrToString(err), + }, nil +} + +func (s *GRPCStorageServer) Delete(ctx context.Context, args *pb.StorageDeleteArgs) (*pb.StorageDeleteReply, error) { + err := s.impl.Delete(args.Key) + return &pb.StorageDeleteReply{ + Err: pb.ErrToString(err), + }, nil +} diff --git a/logical/plugin/grpc_system.go b/logical/plugin/grpc_system.go new file mode 100644 index 0000000000..12a4cab6db --- /dev/null +++ b/logical/plugin/grpc_system.go @@ -0,0 +1,202 @@ +package plugin + +import ( + "context" + "encoding/json" + "errors" + "time" + + "google.golang.org/grpc" + + "fmt" + + "github.com/hashicorp/vault/helper/consts" + "github.com/hashicorp/vault/helper/pluginutil" + "github.com/hashicorp/vault/helper/wrapping" + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/logical/plugin/pb" +) + +func newGRPCSystemView(conn *grpc.ClientConn) *gRPCSystemViewClient { + return &gRPCSystemViewClient{ + client: pb.NewSystemViewClient(conn), + } +} + +type gRPCSystemViewClient struct { + client pb.SystemViewClient +} + +func (s *gRPCSystemViewClient) DefaultLeaseTTL() time.Duration { + reply, err := s.client.DefaultLeaseTTL(context.Background(), &pb.Empty{}) + if err != nil { + return 0 + } + + return time.Duration(reply.TTL) +} + +func (s *gRPCSystemViewClient) MaxLeaseTTL() time.Duration { + reply, err := s.client.MaxLeaseTTL(context.Background(), &pb.Empty{}) + if err != nil { + return 0 + } + + return time.Duration(reply.TTL) +} + +func (s *gRPCSystemViewClient) SudoPrivilege(path string, token string) bool { + reply, err := s.client.SudoPrivilege(context.Background(), &pb.SudoPrivilegeArgs{ + Path: path, + Token: token, + }) + if err != nil { + return false + } + + return reply.Sudo +} + +func (s *gRPCSystemViewClient) Tainted() bool { + reply, err := s.client.Tainted(context.Background(), &pb.Empty{}) + if err != nil { + return false + } + + return reply.Tainted +} + +func (s *gRPCSystemViewClient) CachingDisabled() bool { + reply, err := s.client.CachingDisabled(context.Background(), &pb.Empty{}) + if err != nil { + return false + } + + return reply.Disabled +} + +func (s *gRPCSystemViewClient) ReplicationState() consts.ReplicationState { + reply, err := s.client.ReplicationState(context.Background(), &pb.Empty{}) + if err != nil { + return consts.ReplicationDisabled + } + + return consts.ReplicationState(reply.State) +} + +func (s *gRPCSystemViewClient) ResponseWrapData(data map[string]interface{}, ttl time.Duration, jwt bool) (*wrapping.ResponseWrapInfo, error) { + buf, err := json.Marshal(data) + if err != nil { + return nil, err + } + + reply, err := s.client.ResponseWrapData(context.Background(), &pb.ResponseWrapDataArgs{ + Data: buf, + TTL: int64(ttl), + JWT: false, + }) + if err != nil { + return nil, err + } + if reply.Err != "" { + return nil, errors.New(reply.Err) + } + + info, err := pb.ProtoResponseWrapInfoToLogicalResponseWrapInfo(reply.WrapInfo) + if err != nil { + return nil, err + } + + return info, nil +} + +func (s *gRPCSystemViewClient) LookupPlugin(name string) (*pluginutil.PluginRunner, error) { + return nil, fmt.Errorf("cannot call LookupPlugin from a plugin backend") +} + +func (s *gRPCSystemViewClient) MlockEnabled() bool { + reply, err := s.client.MlockEnabled(context.Background(), &pb.Empty{}) + if err != nil { + return false + } + + return reply.Enabled +} + +type gRPCSystemViewServer struct { + impl logical.SystemView +} + +func (s *gRPCSystemViewServer) DefaultLeaseTTL(ctx context.Context, _ *pb.Empty) (*pb.TTLReply, error) { + ttl := s.impl.DefaultLeaseTTL() + return &pb.TTLReply{ + TTL: int64(ttl), + }, nil +} + +func (s *gRPCSystemViewServer) MaxLeaseTTL(ctx context.Context, _ *pb.Empty) (*pb.TTLReply, error) { + ttl := s.impl.MaxLeaseTTL() + return &pb.TTLReply{ + TTL: int64(ttl), + }, nil +} + +func (s *gRPCSystemViewServer) SudoPrivilege(ctx context.Context, args *pb.SudoPrivilegeArgs) (*pb.SudoPrivilegeReply, error) { + sudo := s.impl.SudoPrivilege(args.Path, args.Token) + return &pb.SudoPrivilegeReply{ + Sudo: sudo, + }, nil +} + +func (s *gRPCSystemViewServer) Tainted(ctx context.Context, _ *pb.Empty) (*pb.TaintedReply, error) { + tainted := s.impl.Tainted() + return &pb.TaintedReply{ + Tainted: tainted, + }, nil +} + +func (s *gRPCSystemViewServer) CachingDisabled(ctx context.Context, _ *pb.Empty) (*pb.CachingDisabledReply, error) { + cachingDisabled := s.impl.CachingDisabled() + return &pb.CachingDisabledReply{ + Disabled: cachingDisabled, + }, nil +} + +func (s *gRPCSystemViewServer) ReplicationState(ctx context.Context, _ *pb.Empty) (*pb.ReplicationStateReply, error) { + replicationState := s.impl.ReplicationState() + return &pb.ReplicationStateReply{ + State: int32(replicationState), + }, nil +} + +func (s *gRPCSystemViewServer) ResponseWrapData(ctx context.Context, args *pb.ResponseWrapDataArgs) (*pb.ResponseWrapDataReply, error) { + data := map[string]interface{}{} + err := json.Unmarshal(args.Data, &data) + if err != nil { + return &pb.ResponseWrapDataReply{}, err + } + + // Do not allow JWTs to be returned + info, err := s.impl.ResponseWrapData(data, time.Duration(args.TTL), false) + if err != nil { + return &pb.ResponseWrapDataReply{ + Err: pb.ErrToString(err), + }, nil + } + + pbInfo, err := pb.LogicalResponseWrapInfoToProtoResponseWrapInfo(info) + if err != nil { + return &pb.ResponseWrapDataReply{}, err + } + + return &pb.ResponseWrapDataReply{ + WrapInfo: pbInfo, + }, nil +} + +func (s *gRPCSystemViewServer) MlockEnabled(ctx context.Context, _ *pb.Empty) (*pb.MlockEnabledReply, error) { + enabled := s.impl.MlockEnabled() + return &pb.MlockEnabledReply{ + Enabled: enabled, + }, nil +} diff --git a/logical/plugin/grpc_system_test.go b/logical/plugin/grpc_system_test.go new file mode 100644 index 0000000000..7942a56bac --- /dev/null +++ b/logical/plugin/grpc_system_test.go @@ -0,0 +1,163 @@ +package plugin + +import ( + "testing" + + "google.golang.org/grpc" + + "reflect" + + plugin "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vault/helper/consts" + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/logical/plugin/pb" +) + +func TestSystem_GRPC_GRPC_impl(t *testing.T) { + var _ logical.SystemView = new(gRPCSystemViewClient) +} + +func TestSystem_GRPC_defaultLeaseTTL(t *testing.T) { + sys := logical.TestSystemView() + client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { + pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{ + impl: sys, + }) + }) + defer client.Close() + testSystemView := newGRPCSystemView(client) + + expected := sys.DefaultLeaseTTL() + actual := testSystemView.DefaultLeaseTTL() + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected: %v, got: %v", expected, actual) + } +} + +func TestSystem_GRPC_maxLeaseTTL(t *testing.T) { + sys := logical.TestSystemView() + client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { + pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{ + impl: sys, + }) + }) + defer client.Close() + testSystemView := newGRPCSystemView(client) + + expected := sys.MaxLeaseTTL() + actual := testSystemView.MaxLeaseTTL() + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected: %v, got: %v", expected, actual) + } +} + +func TestSystem_GRPC_sudoPrivilege(t *testing.T) { + sys := logical.TestSystemView() + sys.SudoPrivilegeVal = true + client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { + pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{ + impl: sys, + }) + }) + defer client.Close() + testSystemView := newGRPCSystemView(client) + + expected := sys.SudoPrivilege("foo", "bar") + actual := testSystemView.SudoPrivilege("foo", "bar") + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected: %v, got: %v", expected, actual) + } +} + +func TestSystem_GRPC_tainted(t *testing.T) { + sys := logical.TestSystemView() + sys.TaintedVal = true + client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { + pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{ + impl: sys, + }) + }) + defer client.Close() + testSystemView := newGRPCSystemView(client) + + expected := sys.Tainted() + actual := testSystemView.Tainted() + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected: %v, got: %v", expected, actual) + } +} + +func TestSystem_GRPC_cachingDisabled(t *testing.T) { + sys := logical.TestSystemView() + sys.CachingDisabledVal = true + client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { + pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{ + impl: sys, + }) + }) + defer client.Close() + testSystemView := newGRPCSystemView(client) + + expected := sys.CachingDisabled() + actual := testSystemView.CachingDisabled() + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected: %v, got: %v", expected, actual) + } +} + +func TestSystem_GRPC_replicationState(t *testing.T) { + sys := logical.TestSystemView() + sys.ReplicationStateVal = consts.ReplicationPerformancePrimary + client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { + pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{ + impl: sys, + }) + }) + defer client.Close() + testSystemView := newGRPCSystemView(client) + + expected := sys.ReplicationState() + actual := testSystemView.ReplicationState() + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected: %v, got: %v", expected, actual) + } +} + +func TestSystem_GRPC_responseWrapData(t *testing.T) { + t.SkipNow() +} + +func TestSystem_GRPC_lookupPlugin(t *testing.T) { + sys := logical.TestSystemView() + client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { + pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{ + impl: sys, + }) + }) + defer client.Close() + + testSystemView := newGRPCSystemView(client) + + if _, err := testSystemView.LookupPlugin("foo"); err == nil { + t.Fatal("LookPlugin(): expected error on due to unsupported call from plugin") + } +} + +func TestSystem_GRPC_mlockEnabled(t *testing.T) { + sys := logical.TestSystemView() + sys.EnableMlock = true + client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { + pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{ + impl: sys, + }) + }) + defer client.Close() + + testSystemView := newGRPCSystemView(client) + + expected := sys.MlockEnabled() + actual := testSystemView.MlockEnabled() + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected: %v, got: %v", expected, actual) + } +} diff --git a/logical/plugin/middleware.go b/logical/plugin/middleware.go new file mode 100644 index 0000000000..e646c00828 --- /dev/null +++ b/logical/plugin/middleware.go @@ -0,0 +1,99 @@ +package plugin + +import ( + "context" + "time" + + "github.com/hashicorp/vault/logical" + log "github.com/mgutz/logxi/v1" +) + +// backendPluginClient implements logical.Backend and is the +// go-plugin client. +type backendTracingMiddleware struct { + logger log.Logger + transport string + typeStr string + + next logical.Backend +} + +func (b *backendTracingMiddleware) HandleRequest(ctx context.Context, req *logical.Request) (resp *logical.Response, err error) { + defer func(then time.Time) { + b.logger.Trace("plugin.HandleRequest", "path", req.Path, "status", "finished", "type", b.typeStr, "transport", b.transport, "err", err, "took", time.Since(then)) + }(time.Now()) + + b.logger.Trace("plugin.HandleRequest", "path", req.Path, "status", "started", "type", b.typeStr, "transport", b.transport) + return b.next.HandleRequest(ctx, req) +} + +func (b *backendTracingMiddleware) SpecialPaths() *logical.Paths { + defer func(then time.Time) { + b.logger.Trace("plugin.SpecialPaths", "status", "finished", "type", b.typeStr, "transport", b.transport, "took", time.Since(then)) + }(time.Now()) + + b.logger.Trace("plugin.SpecialPaths", "status", "started", "type", b.typeStr, "transport", b.transport) + return b.next.SpecialPaths() +} + +func (b *backendTracingMiddleware) System() logical.SystemView { + return b.next.System() +} + +func (b *backendTracingMiddleware) Logger() log.Logger { + return b.next.Logger() +} + +func (b *backendTracingMiddleware) HandleExistenceCheck(ctx context.Context, req *logical.Request) (found bool, exists bool, err error) { + defer func(then time.Time) { + b.logger.Trace("plugin.HandleExistenceCheck", "path", req.Path, "status", "finished", "type", b.typeStr, "transport", b.transport, "err", err, "took", time.Since(then)) + }(time.Now()) + + b.logger.Trace("plugin.HandleExistenceCheck", "path", req.Path, "status", "started", "type", b.typeStr, "transport", b.transport) + return b.next.HandleExistenceCheck(ctx, req) +} + +func (b *backendTracingMiddleware) Cleanup() { + defer func(then time.Time) { + b.logger.Trace("plugin.Cleanup", "status", "finished", "type", b.typeStr, "transport", b.transport, "took", time.Since(then)) + }(time.Now()) + + b.logger.Trace("plugin.Cleanup", "status", "started", "type", b.typeStr, "transport", b.transport) + b.next.Cleanup() +} + +func (b *backendTracingMiddleware) Initialize() (err error) { + defer func(then time.Time) { + b.logger.Trace("plugin.Initialize", "status", "finished", "type", b.typeStr, "transport", b.transport, "err", err, "took", time.Since(then)) + }(time.Now()) + + b.logger.Trace("plugin.Initialize", "status", "started", "type", b.typeStr, "transport", b.transport) + return b.next.Initialize() +} + +func (b *backendTracingMiddleware) InvalidateKey(key string) { + defer func(then time.Time) { + b.logger.Trace("plugin.InvalidateKey", "key", key, "status", "finished", "type", b.typeStr, "transport", b.transport, "took", time.Since(then)) + }(time.Now()) + + b.logger.Trace("plugin.InvalidateKey", "key", key, "status", "started", "type", b.typeStr, "transport", b.transport) + b.next.InvalidateKey(key) +} + +func (b *backendTracingMiddleware) Setup(config *logical.BackendConfig) (err error) { + defer func(then time.Time) { + b.logger.Trace("plugin.Setup", "status", "finished", "type", b.typeStr, "transport", b.transport, "err", err, "took", time.Since(then)) + }(time.Now()) + + b.logger.Trace("plugin.Setup", "status", "started", "type", b.typeStr, "transport", b.transport) + return b.next.Setup(config) +} + +func (b *backendTracingMiddleware) Type() logical.BackendType { + defer func(then time.Time) { + b.logger.Trace("plugin.Type", "status", "finished", "type", b.typeStr, "transport", b.transport, "took", time.Since(then)) + }(time.Now()) + + b.logger.Trace("plugin.Type", "status", "started", "type", b.typeStr, "transport", b.transport) + return b.next.Type() +} diff --git a/logical/plugin/mock/path_errors.go b/logical/plugin/mock/path_errors.go index a0d124b08c..e43be6fcd8 100644 --- a/logical/plugin/mock/path_errors.go +++ b/logical/plugin/mock/path_errors.go @@ -2,10 +2,13 @@ package mock import ( "context" + "errors" "net/rpc" + "github.com/hashicorp/vault/helper/errutil" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" + "github.com/hashicorp/vault/logical/plugin/pb" ) // pathInternal is used to test viewing internal backend values. In this case, @@ -24,9 +27,49 @@ func errorPaths(b *backend) []*framework.Path { logical.ReadOperation: b.pathErrorRPCRead, }, }, + &framework.Path{ + Pattern: "errors/type", + Fields: map[string]*framework.FieldSchema{ + "err_type": &framework.FieldSchema{Type: framework.TypeInt}, + }, + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.CreateOperation: b.pathErrorRPCRead, + logical.UpdateOperation: b.pathErrorRPCRead, + }, + }, } } func (b *backend) pathErrorRPCRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { - return nil, rpc.ErrShutdown + errTypeRaw, ok := data.GetOk("err_type") + if !ok { + return nil, rpc.ErrShutdown + } + + var err error + switch uint32(errTypeRaw.(int)) { + case pb.ErrTypeUnknown: + err = errors.New("test") + case pb.ErrTypeUserError: + err = errutil.UserError{Err: "test"} + case pb.ErrTypeInternalError: + err = errutil.InternalError{Err: "test"} + case pb.ErrTypeCodedError: + err = logical.CodedError(403, "test") + case pb.ErrTypeStatusBadRequest: + err = &logical.StatusBadRequest{Err: "test"} + case pb.ErrTypeUnsupportedOperation: + err = logical.ErrUnsupportedOperation + case pb.ErrTypeUnsupportedPath: + err = logical.ErrUnsupportedPath + case pb.ErrTypeInvalidRequest: + err = logical.ErrInvalidRequest + case pb.ErrTypePermissionDenied: + err = logical.ErrPermissionDenied + case pb.ErrTypeMultiAuthzPending: + err = logical.ErrMultiAuthzPending + } + + return nil, err + } diff --git a/logical/plugin/mock/path_kv.go b/logical/plugin/mock/path_kv.go index db55e49425..defd5b576a 100644 --- a/logical/plugin/mock/path_kv.go +++ b/logical/plugin/mock/path_kv.go @@ -56,6 +56,7 @@ func (b *backend) pathKVRead(ctx context.Context, req *logical.Request, data *fr value := string(entry.Value) + b.Logger().Info("reading value", "key", req.Path, "value", value) // Return the secret return &logical.Response{ Data: map[string]interface{}{ @@ -67,6 +68,7 @@ func (b *backend) pathKVRead(ctx context.Context, req *logical.Request, data *fr func (b *backend) pathKVCreateUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { value := data.Get("value").(string) + b.Logger().Info("storing value", "key", req.Path, "value", value) entry := &logical.StorageEntry{ Key: req.Path, Value: []byte(value), diff --git a/logical/plugin/pb/backend.pb.go b/logical/plugin/pb/backend.pb.go new file mode 100644 index 0000000000..c15b952fe3 --- /dev/null +++ b/logical/plugin/pb/backend.pb.go @@ -0,0 +1,2321 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: logical/plugin/pb/backend.proto + +/* +Package pb is a generated protocol buffer package. + +It is generated from these files: + logical/plugin/pb/backend.proto + +It has these top-level messages: + Empty + Header + ProtoError + Paths + Request + Alias + Auth + LeaseOptions + Secret + Response + ResponseWrapInfo + RequestWrapInfo + HandleRequestArgs + HandleRequestReply + SpecialPathsReply + HandleExistenceCheckArgs + HandleExistenceCheckReply + SetupArgs + SetupReply + TypeReply + InvalidateKeyArgs + StorageEntry + StorageListArgs + StorageListReply + StorageGetArgs + StorageGetReply + StoragePutArgs + StoragePutReply + StorageDeleteArgs + StorageDeleteReply + TTLReply + SudoPrivilegeArgs + SudoPrivilegeReply + TaintedReply + CachingDisabledReply + ReplicationStateReply + ResponseWrapDataArgs + ResponseWrapDataReply + MlockEnabledReply +*/ +package pb + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/golang/protobuf/ptypes/timestamp" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Empty struct { +} + +func (m *Empty) Reset() { *m = Empty{} } +func (m *Empty) String() string { return proto.CompactTextString(m) } +func (*Empty) ProtoMessage() {} +func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type Header struct { + Header []string `sentinel:"" protobuf:"bytes,1,rep,name=header" json:"header,omitempty"` +} + +func (m *Header) Reset() { *m = Header{} } +func (m *Header) String() string { return proto.CompactTextString(m) } +func (*Header) ProtoMessage() {} +func (*Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *Header) GetHeader() []string { + if m != nil { + return m.Header + } + return nil +} + +type ProtoError struct { + ErrType uint32 `sentinel:"" protobuf:"varint,1,opt,name=err_type,json=errType" json:"err_type,omitempty"` + ErrMsg string `sentinel:"" protobuf:"bytes,2,opt,name=err_msg,json=errMsg" json:"err_msg,omitempty"` + ErrCode int64 `sentinel:"" protobuf:"varint,3,opt,name=err_code,json=errCode" json:"err_code,omitempty"` +} + +func (m *ProtoError) Reset() { *m = ProtoError{} } +func (m *ProtoError) String() string { return proto.CompactTextString(m) } +func (*ProtoError) ProtoMessage() {} +func (*ProtoError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *ProtoError) GetErrType() uint32 { + if m != nil { + return m.ErrType + } + return 0 +} + +func (m *ProtoError) GetErrMsg() string { + if m != nil { + return m.ErrMsg + } + return "" +} + +func (m *ProtoError) GetErrCode() int64 { + if m != nil { + return m.ErrCode + } + return 0 +} + +// Paths is the structure of special paths that is used for SpecialPaths. +type Paths struct { + // Root are the paths that require a root token to access + Root []string `sentinel:"" protobuf:"bytes,1,rep,name=root" json:"root,omitempty"` + // Unauthenticated are the paths that can be accessed without any auth. + Unauthenticated []string `sentinel:"" protobuf:"bytes,2,rep,name=unauthenticated" json:"unauthenticated,omitempty"` + // LocalStorage are paths (prefixes) that are local to this instance; this + // indicates that these paths should not be replicated + LocalStorage []string `sentinel:"" protobuf:"bytes,3,rep,name=local_storage,json=localStorage" json:"local_storage,omitempty"` + // SealWrapStorage are storage paths that, when using a capable seal, + // should be seal wrapped with extra encryption. It is exact matching + // unless it ends with '/' in which case it will be treated as a prefix. + SealWrapStorage []string `sentinel:"" protobuf:"bytes,4,rep,name=seal_wrap_storage,json=sealWrapStorage" json:"seal_wrap_storage,omitempty"` +} + +func (m *Paths) Reset() { *m = Paths{} } +func (m *Paths) String() string { return proto.CompactTextString(m) } +func (*Paths) ProtoMessage() {} +func (*Paths) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *Paths) GetRoot() []string { + if m != nil { + return m.Root + } + return nil +} + +func (m *Paths) GetUnauthenticated() []string { + if m != nil { + return m.Unauthenticated + } + return nil +} + +func (m *Paths) GetLocalStorage() []string { + if m != nil { + return m.LocalStorage + } + return nil +} + +func (m *Paths) GetSealWrapStorage() []string { + if m != nil { + return m.SealWrapStorage + } + return nil +} + +type Request struct { + // ID is the uuid associated with each request + ID string `sentinel:"" protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + // If set, the name given to the replication secondary where this request + // originated + ReplicationCluster string `sentinel:"" protobuf:"bytes,2,opt,name=ReplicationCluster" json:"ReplicationCluster,omitempty"` + // Operation is the requested operation type + Operation string `sentinel:"" protobuf:"bytes,3,opt,name=operation" json:"operation,omitempty"` + // Path is the part of the request path not consumed by the + // routing. As an example, if the original request path is "prod/aws/foo" + // and the AWS logical backend is mounted at "prod/aws/", then the + // final path is "foo" since the mount prefix is trimmed. + Path string `sentinel:"" protobuf:"bytes,4,opt,name=path" json:"path,omitempty"` + // Request data is an opaque map that must have string keys. + Data []byte `sentinel:"" protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` + // Secret will be non-nil only for Revoke and Renew operations + // to represent the secret that was returned prior. + Secret *Secret `sentinel:"" protobuf:"bytes,6,opt,name=secret" json:"secret,omitempty"` + // Auth will be non-nil only for Renew operations + // to represent the auth that was returned prior. + Auth *Auth `sentinel:"" protobuf:"bytes,7,opt,name=auth" json:"auth,omitempty"` + // Headers will contain the http headers from the request. This value will + // be used in the audit broker to ensure we are auditing only the allowed + // headers. + Headers map[string]*Header `sentinel:"" protobuf:"bytes,8,rep,name=headers" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // ClientToken is provided to the core so that the identity + // can be verified and ACLs applied. This value is passed + // through to the logical backends but after being salted and + // hashed. + ClientToken string `sentinel:"" protobuf:"bytes,9,opt,name=client_token,json=clientToken" json:"client_token,omitempty"` + // ClientTokenAccessor is provided to the core so that the it can get + // logged as part of request audit logging. + ClientTokenAccessor string `sentinel:"" protobuf:"bytes,10,opt,name=client_token_accessor,json=clientTokenAccessor" json:"client_token_accessor,omitempty"` + // DisplayName is provided to the logical backend to help associate + // dynamic secrets with the source entity. This is not a sensitive + // name, but is useful for operators. + DisplayName string `sentinel:"" protobuf:"bytes,11,opt,name=display_name,json=displayName" json:"display_name,omitempty"` + // MountPoint is provided so that a logical backend can generate + // paths relative to itself. The `Path` is effectively the client + // request path with the MountPoint trimmed off. + MountPoint string `sentinel:"" protobuf:"bytes,12,opt,name=mount_point,json=mountPoint" json:"mount_point,omitempty"` + // MountType is provided so that a logical backend can make decisions + // based on the specific mount type (e.g., if a mount type has different + // aliases, generating different defaults depending on the alias) + MountType string `sentinel:"" protobuf:"bytes,13,opt,name=mount_type,json=mountType" json:"mount_type,omitempty"` + // MountAccessor is provided so that identities returned by the authentication + // backends can be tied to the mount it belongs to. + MountAccessor string `sentinel:"" protobuf:"bytes,14,opt,name=mount_accessor,json=mountAccessor" json:"mount_accessor,omitempty"` + // WrapInfo contains requested response wrapping parameters + WrapInfo *RequestWrapInfo `sentinel:"" protobuf:"bytes,15,opt,name=wrap_info,json=wrapInfo" json:"wrap_info,omitempty"` + // ClientTokenRemainingUses represents the allowed number of uses left on the + // token supplied + ClientTokenRemainingUses int64 `sentinel:"" protobuf:"varint,16,opt,name=client_token_remaining_uses,json=clientTokenRemainingUses" json:"client_token_remaining_uses,omitempty"` + // EntityID is the identity of the caller extracted out of the token used + // to make this request + EntityID string `sentinel:"" protobuf:"bytes,17,opt,name=entity_id,json=entityId" json:"entity_id,omitempty"` + // PolicyOverride indicates that the requestor wishes to override + // soft-mandatory Sentinel policies + PolicyOverride bool `sentinel:"" protobuf:"varint,18,opt,name=policy_override,json=policyOverride" json:"policy_override,omitempty"` + // Whether the request is unauthenticated, as in, had no client token + // attached. Useful in some situations where the client token is not made + // accessible. + Unauthenticated bool `sentinel:"" protobuf:"varint,19,opt,name=unauthenticated" json:"unauthenticated,omitempty"` +} + +func (m *Request) Reset() { *m = Request{} } +func (m *Request) String() string { return proto.CompactTextString(m) } +func (*Request) ProtoMessage() {} +func (*Request) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *Request) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *Request) GetReplicationCluster() string { + if m != nil { + return m.ReplicationCluster + } + return "" +} + +func (m *Request) GetOperation() string { + if m != nil { + return m.Operation + } + return "" +} + +func (m *Request) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *Request) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *Request) GetSecret() *Secret { + if m != nil { + return m.Secret + } + return nil +} + +func (m *Request) GetAuth() *Auth { + if m != nil { + return m.Auth + } + return nil +} + +func (m *Request) GetHeaders() map[string]*Header { + if m != nil { + return m.Headers + } + return nil +} + +func (m *Request) GetClientToken() string { + if m != nil { + return m.ClientToken + } + return "" +} + +func (m *Request) GetClientTokenAccessor() string { + if m != nil { + return m.ClientTokenAccessor + } + return "" +} + +func (m *Request) GetDisplayName() string { + if m != nil { + return m.DisplayName + } + return "" +} + +func (m *Request) GetMountPoint() string { + if m != nil { + return m.MountPoint + } + return "" +} + +func (m *Request) GetMountType() string { + if m != nil { + return m.MountType + } + return "" +} + +func (m *Request) GetMountAccessor() string { + if m != nil { + return m.MountAccessor + } + return "" +} + +func (m *Request) GetWrapInfo() *RequestWrapInfo { + if m != nil { + return m.WrapInfo + } + return nil +} + +func (m *Request) GetClientTokenRemainingUses() int64 { + if m != nil { + return m.ClientTokenRemainingUses + } + return 0 +} + +func (m *Request) GetEntityID() string { + if m != nil { + return m.EntityID + } + return "" +} + +func (m *Request) GetPolicyOverride() bool { + if m != nil { + return m.PolicyOverride + } + return false +} + +func (m *Request) GetUnauthenticated() bool { + if m != nil { + return m.Unauthenticated + } + return false +} + +type Alias struct { + // MountType is the backend mount's type to which this identity belongs + MountType string `sentinel:"" protobuf:"bytes,1,opt,name=mount_type,json=mountType" json:"mount_type,omitempty"` + // MountAccessor is the identifier of the mount entry to which this + // identity belongs + MountAccessor string `sentinel:"" protobuf:"bytes,2,opt,name=mount_accessor,json=mountAccessor" json:"mount_accessor,omitempty"` + // Name is the identifier of this identity in its authentication source + Name string `sentinel:"" protobuf:"bytes,3,opt,name=name" json:"name,omitempty"` +} + +func (m *Alias) Reset() { *m = Alias{} } +func (m *Alias) String() string { return proto.CompactTextString(m) } +func (*Alias) ProtoMessage() {} +func (*Alias) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *Alias) GetMountType() string { + if m != nil { + return m.MountType + } + return "" +} + +func (m *Alias) GetMountAccessor() string { + if m != nil { + return m.MountAccessor + } + return "" +} + +func (m *Alias) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type Auth struct { + LeaseOptions *LeaseOptions `sentinel:"" protobuf:"bytes,1,opt,name=lease_options,json=leaseOptions" json:"lease_options,omitempty"` + // InternalData is JSON-encodable data that is stored with the auth struct. + // This will be sent back during a Renew/Revoke for storing internal data + // used for those operations. + InternalData []byte `sentinel:"" protobuf:"bytes,2,opt,name=internal_data,json=internalData,proto3" json:"internal_data,omitempty"` + // DisplayName is a non-security sensitive identifier that is + // applicable to this Auth. It is used for logging and prefixing + // of dynamic secrets. For example, DisplayName may be "armon" for + // the github credential backend. If the client token is used to + // generate a SQL credential, the user may be "github-armon-uuid". + // This is to help identify the source without using audit tables. + DisplayName string `sentinel:"" protobuf:"bytes,3,opt,name=display_name,json=displayName" json:"display_name,omitempty"` + // Policies is the list of policies that the authenticated user + // is associated with. + Policies []string `sentinel:"" protobuf:"bytes,4,rep,name=policies" json:"policies,omitempty"` + // Metadata is used to attach arbitrary string-type metadata to + // an authenticated user. This metadata will be outputted into the + // audit log. + Metadata map[string]string `sentinel:"" protobuf:"bytes,5,rep,name=metadata" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // ClientToken is the token that is generated for the authentication. + // This will be filled in by Vault core when an auth structure is + // returned. Setting this manually will have no effect. + ClientToken string `sentinel:"" protobuf:"bytes,6,opt,name=client_token,json=clientToken" json:"client_token,omitempty"` + // Accessor is the identifier for the ClientToken. This can be used + // to perform management functionalities (especially revocation) when + // ClientToken in the audit logs are obfuscated. Accessor can be used + // to revoke a ClientToken and to lookup the capabilities of the ClientToken, + // both without actually knowing the ClientToken. + Accessor string `sentinel:"" protobuf:"bytes,7,opt,name=accessor" json:"accessor,omitempty"` + // Period indicates that the token generated using this Auth object + // should never expire. The token should be renewed within the duration + // specified by this period. + Period int64 `sentinel:"" protobuf:"varint,8,opt,name=period" json:"period,omitempty"` + // Number of allowed uses of the issued token + NumUses int64 `sentinel:"" protobuf:"varint,9,opt,name=num_uses,json=numUses" json:"num_uses,omitempty"` + // EntityID is the identifier of the entity in identity store to which the + // identity of the authenticating client belongs to. + EntityID string `sentinel:"" protobuf:"bytes,10,opt,name=entity_id,json=entityId" json:"entity_id,omitempty"` + // Alias is the information about the authenticated client returned by + // the auth backend + Alias *Alias `sentinel:"" protobuf:"bytes,11,opt,name=alias" json:"alias,omitempty"` + // GroupAliases are the informational mappings of external groups which an + // authenticated user belongs to. This is used to check if there are + // mappings groups for the group aliases in identity store. For all the + // matching groups, the entity ID of the user will be added. + GroupAliases []*Alias `sentinel:"" protobuf:"bytes,12,rep,name=group_aliases,json=groupAliases" json:"group_aliases,omitempty"` +} + +func (m *Auth) Reset() { *m = Auth{} } +func (m *Auth) String() string { return proto.CompactTextString(m) } +func (*Auth) ProtoMessage() {} +func (*Auth) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *Auth) GetLeaseOptions() *LeaseOptions { + if m != nil { + return m.LeaseOptions + } + return nil +} + +func (m *Auth) GetInternalData() []byte { + if m != nil { + return m.InternalData + } + return nil +} + +func (m *Auth) GetDisplayName() string { + if m != nil { + return m.DisplayName + } + return "" +} + +func (m *Auth) GetPolicies() []string { + if m != nil { + return m.Policies + } + return nil +} + +func (m *Auth) GetMetadata() map[string]string { + if m != nil { + return m.Metadata + } + return nil +} + +func (m *Auth) GetClientToken() string { + if m != nil { + return m.ClientToken + } + return "" +} + +func (m *Auth) GetAccessor() string { + if m != nil { + return m.Accessor + } + return "" +} + +func (m *Auth) GetPeriod() int64 { + if m != nil { + return m.Period + } + return 0 +} + +func (m *Auth) GetNumUses() int64 { + if m != nil { + return m.NumUses + } + return 0 +} + +func (m *Auth) GetEntityID() string { + if m != nil { + return m.EntityID + } + return "" +} + +func (m *Auth) GetAlias() *Alias { + if m != nil { + return m.Alias + } + return nil +} + +func (m *Auth) GetGroupAliases() []*Alias { + if m != nil { + return m.GroupAliases + } + return nil +} + +type LeaseOptions struct { + TTL int64 `sentinel:"" protobuf:"varint,1,opt,name=TTL" json:"TTL,omitempty"` + Renewable bool `sentinel:"" protobuf:"varint,2,opt,name=renewable" json:"renewable,omitempty"` + Increment int64 `sentinel:"" protobuf:"varint,3,opt,name=increment" json:"increment,omitempty"` + IssueTime *google_protobuf.Timestamp `sentinel:"" protobuf:"bytes,4,opt,name=issue_time,json=issueTime" json:"issue_time,omitempty"` +} + +func (m *LeaseOptions) Reset() { *m = LeaseOptions{} } +func (m *LeaseOptions) String() string { return proto.CompactTextString(m) } +func (*LeaseOptions) ProtoMessage() {} +func (*LeaseOptions) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *LeaseOptions) GetTTL() int64 { + if m != nil { + return m.TTL + } + return 0 +} + +func (m *LeaseOptions) GetRenewable() bool { + if m != nil { + return m.Renewable + } + return false +} + +func (m *LeaseOptions) GetIncrement() int64 { + if m != nil { + return m.Increment + } + return 0 +} + +func (m *LeaseOptions) GetIssueTime() *google_protobuf.Timestamp { + if m != nil { + return m.IssueTime + } + return nil +} + +type Secret struct { + LeaseOptions *LeaseOptions `sentinel:"" protobuf:"bytes,1,opt,name=lease_options,json=leaseOptions" json:"lease_options,omitempty"` + // InternalData is JSON-encodable data that is stored with the secret. + // This will be sent back during a Renew/Revoke for storing internal data + // used for those operations. + InternalData []byte `sentinel:"" protobuf:"bytes,2,opt,name=internal_data,json=internalData,proto3" json:"internal_data,omitempty"` + // LeaseID is the ID returned to the user to manage this secret. + // This is generated by Vault core. Any set value will be ignored. + // For requests, this will always be blank. + LeaseID string `sentinel:"" protobuf:"bytes,3,opt,name=lease_id,json=leaseId" json:"lease_id,omitempty"` +} + +func (m *Secret) Reset() { *m = Secret{} } +func (m *Secret) String() string { return proto.CompactTextString(m) } +func (*Secret) ProtoMessage() {} +func (*Secret) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *Secret) GetLeaseOptions() *LeaseOptions { + if m != nil { + return m.LeaseOptions + } + return nil +} + +func (m *Secret) GetInternalData() []byte { + if m != nil { + return m.InternalData + } + return nil +} + +func (m *Secret) GetLeaseID() string { + if m != nil { + return m.LeaseID + } + return "" +} + +type Response struct { + // Secret, if not nil, denotes that this response represents a secret. + Secret *Secret `sentinel:"" protobuf:"bytes,1,opt,name=secret" json:"secret,omitempty"` + // Auth, if not nil, contains the authentication information for + // this response. This is only checked and means something for + // credential backends. + Auth *Auth `sentinel:"" protobuf:"bytes,2,opt,name=auth" json:"auth,omitempty"` + // Response data is an opaque map that must have string keys. For + // secrets, this data is sent down to the user as-is. To store internal + // data that you don't want the user to see, store it in + // Secret.InternalData. + Data []byte `sentinel:"" protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + // Redirect is an HTTP URL to redirect to for further authentication. + // This is only valid for credential backends. This will be blanked + // for any logical backend and ignored. + Redirect string `sentinel:"" protobuf:"bytes,4,opt,name=redirect" json:"redirect,omitempty"` + // Warnings allow operations or backends to return warnings in response + // to user actions without failing the action outright. + Warnings []string `sentinel:"" protobuf:"bytes,5,rep,name=warnings" json:"warnings,omitempty"` + // Information for wrapping the response in a cubbyhole + WrapInfo *ResponseWrapInfo `sentinel:"" protobuf:"bytes,6,opt,name=wrap_info,json=wrapInfo" json:"wrap_info,omitempty"` +} + +func (m *Response) Reset() { *m = Response{} } +func (m *Response) String() string { return proto.CompactTextString(m) } +func (*Response) ProtoMessage() {} +func (*Response) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *Response) GetSecret() *Secret { + if m != nil { + return m.Secret + } + return nil +} + +func (m *Response) GetAuth() *Auth { + if m != nil { + return m.Auth + } + return nil +} + +func (m *Response) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *Response) GetRedirect() string { + if m != nil { + return m.Redirect + } + return "" +} + +func (m *Response) GetWarnings() []string { + if m != nil { + return m.Warnings + } + return nil +} + +func (m *Response) GetWrapInfo() *ResponseWrapInfo { + if m != nil { + return m.WrapInfo + } + return nil +} + +type ResponseWrapInfo struct { + // Setting to non-zero specifies that the response should be wrapped. + // Specifies the desired TTL of the wrapping token. + TTL int64 `sentinel:"" protobuf:"varint,1,opt,name=TTL" json:"TTL,omitempty"` + // The token containing the wrapped response + Token string `sentinel:"" protobuf:"bytes,2,opt,name=token" json:"token,omitempty"` + // The token accessor for the wrapped response token + Accessor string `sentinel:"" protobuf:"bytes,3,opt,name=accessor" json:"accessor,omitempty"` + // The creation time. This can be used with the TTL to figure out an + // expected expiration. + CreationTime *google_protobuf.Timestamp `sentinel:"" protobuf:"bytes,4,opt,name=creation_time,json=creationTime" json:"creation_time,omitempty"` + // If the contained response is the output of a token creation call, the + // created token's accessor will be accessible here + WrappedAccessor string `sentinel:"" protobuf:"bytes,5,opt,name=wrapped_accessor,json=wrappedAccessor" json:"wrapped_accessor,omitempty"` + // WrappedEntityID is the entity identifier of the caller who initiated the + // wrapping request + WrappedEntityID string `sentinel:"" protobuf:"bytes,6,opt,name=wrapped_entity_id,json=wrappedEntityID" json:"wrapped_entity_id,omitempty"` + // The format to use. This doesn't get returned, it's only internal. + Format string `sentinel:"" protobuf:"bytes,7,opt,name=format" json:"format,omitempty"` + // CreationPath is the original request path that was used to create + // the wrapped response. + CreationPath string `sentinel:"" protobuf:"bytes,8,opt,name=creation_path,json=creationPath" json:"creation_path,omitempty"` + // Controls seal wrapping behavior downstream for specific use cases + SealWrap bool `sentinel:"" protobuf:"varint,9,opt,name=seal_wrap,json=sealWrap" json:"seal_wrap,omitempty"` +} + +func (m *ResponseWrapInfo) Reset() { *m = ResponseWrapInfo{} } +func (m *ResponseWrapInfo) String() string { return proto.CompactTextString(m) } +func (*ResponseWrapInfo) ProtoMessage() {} +func (*ResponseWrapInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *ResponseWrapInfo) GetTTL() int64 { + if m != nil { + return m.TTL + } + return 0 +} + +func (m *ResponseWrapInfo) GetToken() string { + if m != nil { + return m.Token + } + return "" +} + +func (m *ResponseWrapInfo) GetAccessor() string { + if m != nil { + return m.Accessor + } + return "" +} + +func (m *ResponseWrapInfo) GetCreationTime() *google_protobuf.Timestamp { + if m != nil { + return m.CreationTime + } + return nil +} + +func (m *ResponseWrapInfo) GetWrappedAccessor() string { + if m != nil { + return m.WrappedAccessor + } + return "" +} + +func (m *ResponseWrapInfo) GetWrappedEntityID() string { + if m != nil { + return m.WrappedEntityID + } + return "" +} + +func (m *ResponseWrapInfo) GetFormat() string { + if m != nil { + return m.Format + } + return "" +} + +func (m *ResponseWrapInfo) GetCreationPath() string { + if m != nil { + return m.CreationPath + } + return "" +} + +func (m *ResponseWrapInfo) GetSealWrap() bool { + if m != nil { + return m.SealWrap + } + return false +} + +type RequestWrapInfo struct { + // Setting to non-zero specifies that the response should be wrapped. + // Specifies the desired TTL of the wrapping token. + TTL int64 `sentinel:"" protobuf:"varint,1,opt,name=TTL" json:"TTL,omitempty"` + // The format to use for the wrapped response; if not specified it's a bare + // token + Format string `sentinel:"" protobuf:"bytes,2,opt,name=format" json:"format,omitempty"` + // A flag to conforming backends that data for a given request should be + // seal wrapped + SealWrap bool `sentinel:"" protobuf:"varint,3,opt,name=seal_wrap,json=sealWrap" json:"seal_wrap,omitempty"` +} + +func (m *RequestWrapInfo) Reset() { *m = RequestWrapInfo{} } +func (m *RequestWrapInfo) String() string { return proto.CompactTextString(m) } +func (*RequestWrapInfo) ProtoMessage() {} +func (*RequestWrapInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *RequestWrapInfo) GetTTL() int64 { + if m != nil { + return m.TTL + } + return 0 +} + +func (m *RequestWrapInfo) GetFormat() string { + if m != nil { + return m.Format + } + return "" +} + +func (m *RequestWrapInfo) GetSealWrap() bool { + if m != nil { + return m.SealWrap + } + return false +} + +// HandleRequestArgs is the args for HandleRequest method. +type HandleRequestArgs struct { + StorageID uint32 `sentinel:"" protobuf:"varint,1,opt,name=storage_id,json=storageId" json:"storage_id,omitempty"` + Request *Request `sentinel:"" protobuf:"bytes,2,opt,name=request" json:"request,omitempty"` +} + +func (m *HandleRequestArgs) Reset() { *m = HandleRequestArgs{} } +func (m *HandleRequestArgs) String() string { return proto.CompactTextString(m) } +func (*HandleRequestArgs) ProtoMessage() {} +func (*HandleRequestArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *HandleRequestArgs) GetStorageID() uint32 { + if m != nil { + return m.StorageID + } + return 0 +} + +func (m *HandleRequestArgs) GetRequest() *Request { + if m != nil { + return m.Request + } + return nil +} + +// HandleRequestReply is the reply for HandleRequest method. +type HandleRequestReply struct { + Response *Response `sentinel:"" protobuf:"bytes,1,opt,name=response" json:"response,omitempty"` + Err *ProtoError `sentinel:"" protobuf:"bytes,2,opt,name=err" json:"err,omitempty"` +} + +func (m *HandleRequestReply) Reset() { *m = HandleRequestReply{} } +func (m *HandleRequestReply) String() string { return proto.CompactTextString(m) } +func (*HandleRequestReply) ProtoMessage() {} +func (*HandleRequestReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *HandleRequestReply) GetResponse() *Response { + if m != nil { + return m.Response + } + return nil +} + +func (m *HandleRequestReply) GetErr() *ProtoError { + if m != nil { + return m.Err + } + return nil +} + +// SpecialPathsReply is the reply for SpecialPaths method. +type SpecialPathsReply struct { + Paths *Paths `sentinel:"" protobuf:"bytes,1,opt,name=paths" json:"paths,omitempty"` +} + +func (m *SpecialPathsReply) Reset() { *m = SpecialPathsReply{} } +func (m *SpecialPathsReply) String() string { return proto.CompactTextString(m) } +func (*SpecialPathsReply) ProtoMessage() {} +func (*SpecialPathsReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +func (m *SpecialPathsReply) GetPaths() *Paths { + if m != nil { + return m.Paths + } + return nil +} + +// HandleExistenceCheckArgs is the args for HandleExistenceCheck method. +type HandleExistenceCheckArgs struct { + StorageID uint32 `sentinel:"" protobuf:"varint,1,opt,name=storage_id,json=storageId" json:"storage_id,omitempty"` + Request *Request `sentinel:"" protobuf:"bytes,2,opt,name=request" json:"request,omitempty"` +} + +func (m *HandleExistenceCheckArgs) Reset() { *m = HandleExistenceCheckArgs{} } +func (m *HandleExistenceCheckArgs) String() string { return proto.CompactTextString(m) } +func (*HandleExistenceCheckArgs) ProtoMessage() {} +func (*HandleExistenceCheckArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +func (m *HandleExistenceCheckArgs) GetStorageID() uint32 { + if m != nil { + return m.StorageID + } + return 0 +} + +func (m *HandleExistenceCheckArgs) GetRequest() *Request { + if m != nil { + return m.Request + } + return nil +} + +// HandleExistenceCheckReply is the reply for HandleExistenceCheck method. +type HandleExistenceCheckReply struct { + CheckFound bool `sentinel:"" protobuf:"varint,1,opt,name=check_found,json=checkFound" json:"check_found,omitempty"` + Exists bool `sentinel:"" protobuf:"varint,2,opt,name=exists" json:"exists,omitempty"` + Err *ProtoError `sentinel:"" protobuf:"bytes,3,opt,name=err" json:"err,omitempty"` +} + +func (m *HandleExistenceCheckReply) Reset() { *m = HandleExistenceCheckReply{} } +func (m *HandleExistenceCheckReply) String() string { return proto.CompactTextString(m) } +func (*HandleExistenceCheckReply) ProtoMessage() {} +func (*HandleExistenceCheckReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +func (m *HandleExistenceCheckReply) GetCheckFound() bool { + if m != nil { + return m.CheckFound + } + return false +} + +func (m *HandleExistenceCheckReply) GetExists() bool { + if m != nil { + return m.Exists + } + return false +} + +func (m *HandleExistenceCheckReply) GetErr() *ProtoError { + if m != nil { + return m.Err + } + return nil +} + +// SetupArgs is the args for Setup method. +type SetupArgs struct { + BrokerID uint32 `sentinel:"" protobuf:"varint,1,opt,name=broker_id,json=brokerId" json:"broker_id,omitempty"` + Config map[string]string `sentinel:"" protobuf:"bytes,2,rep,name=Config" json:"Config,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` +} + +func (m *SetupArgs) Reset() { *m = SetupArgs{} } +func (m *SetupArgs) String() string { return proto.CompactTextString(m) } +func (*SetupArgs) ProtoMessage() {} +func (*SetupArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *SetupArgs) GetBrokerID() uint32 { + if m != nil { + return m.BrokerID + } + return 0 +} + +func (m *SetupArgs) GetConfig() map[string]string { + if m != nil { + return m.Config + } + return nil +} + +// SetupReply is the reply for Setup method. +type SetupReply struct { + Err string `sentinel:"" protobuf:"bytes,1,opt,name=err" json:"err,omitempty"` +} + +func (m *SetupReply) Reset() { *m = SetupReply{} } +func (m *SetupReply) String() string { return proto.CompactTextString(m) } +func (*SetupReply) ProtoMessage() {} +func (*SetupReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + +func (m *SetupReply) GetErr() string { + if m != nil { + return m.Err + } + return "" +} + +// TypeReply is the reply for the Type method. +type TypeReply struct { + Type uint32 `sentinel:"" protobuf:"varint,1,opt,name=type" json:"type,omitempty"` +} + +func (m *TypeReply) Reset() { *m = TypeReply{} } +func (m *TypeReply) String() string { return proto.CompactTextString(m) } +func (*TypeReply) ProtoMessage() {} +func (*TypeReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } + +func (m *TypeReply) GetType() uint32 { + if m != nil { + return m.Type + } + return 0 +} + +type InvalidateKeyArgs struct { + Key string `sentinel:"" protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` +} + +func (m *InvalidateKeyArgs) Reset() { *m = InvalidateKeyArgs{} } +func (m *InvalidateKeyArgs) String() string { return proto.CompactTextString(m) } +func (*InvalidateKeyArgs) ProtoMessage() {} +func (*InvalidateKeyArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } + +func (m *InvalidateKeyArgs) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +type StorageEntry struct { + Key string `sentinel:"" protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` + Value []byte `sentinel:"" protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + SealWrap bool `sentinel:"" protobuf:"varint,3,opt,name=seal_wrap,json=sealWrap" json:"seal_wrap,omitempty"` +} + +func (m *StorageEntry) Reset() { *m = StorageEntry{} } +func (m *StorageEntry) String() string { return proto.CompactTextString(m) } +func (*StorageEntry) ProtoMessage() {} +func (*StorageEntry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } + +func (m *StorageEntry) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +func (m *StorageEntry) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *StorageEntry) GetSealWrap() bool { + if m != nil { + return m.SealWrap + } + return false +} + +type StorageListArgs struct { + Prefix string `sentinel:"" protobuf:"bytes,1,opt,name=prefix" json:"prefix,omitempty"` +} + +func (m *StorageListArgs) Reset() { *m = StorageListArgs{} } +func (m *StorageListArgs) String() string { return proto.CompactTextString(m) } +func (*StorageListArgs) ProtoMessage() {} +func (*StorageListArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } + +func (m *StorageListArgs) GetPrefix() string { + if m != nil { + return m.Prefix + } + return "" +} + +type StorageListReply struct { + Keys []string `sentinel:"" protobuf:"bytes,1,rep,name=keys" json:"keys,omitempty"` + Err string `sentinel:"" protobuf:"bytes,2,opt,name=err" json:"err,omitempty"` +} + +func (m *StorageListReply) Reset() { *m = StorageListReply{} } +func (m *StorageListReply) String() string { return proto.CompactTextString(m) } +func (*StorageListReply) ProtoMessage() {} +func (*StorageListReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } + +func (m *StorageListReply) GetKeys() []string { + if m != nil { + return m.Keys + } + return nil +} + +func (m *StorageListReply) GetErr() string { + if m != nil { + return m.Err + } + return "" +} + +type StorageGetArgs struct { + Key string `sentinel:"" protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` +} + +func (m *StorageGetArgs) Reset() { *m = StorageGetArgs{} } +func (m *StorageGetArgs) String() string { return proto.CompactTextString(m) } +func (*StorageGetArgs) ProtoMessage() {} +func (*StorageGetArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } + +func (m *StorageGetArgs) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +type StorageGetReply struct { + Entry *StorageEntry `sentinel:"" protobuf:"bytes,1,opt,name=entry" json:"entry,omitempty"` + Err string `sentinel:"" protobuf:"bytes,2,opt,name=err" json:"err,omitempty"` +} + +func (m *StorageGetReply) Reset() { *m = StorageGetReply{} } +func (m *StorageGetReply) String() string { return proto.CompactTextString(m) } +func (*StorageGetReply) ProtoMessage() {} +func (*StorageGetReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } + +func (m *StorageGetReply) GetEntry() *StorageEntry { + if m != nil { + return m.Entry + } + return nil +} + +func (m *StorageGetReply) GetErr() string { + if m != nil { + return m.Err + } + return "" +} + +type StoragePutArgs struct { + Entry *StorageEntry `sentinel:"" protobuf:"bytes,1,opt,name=entry" json:"entry,omitempty"` +} + +func (m *StoragePutArgs) Reset() { *m = StoragePutArgs{} } +func (m *StoragePutArgs) String() string { return proto.CompactTextString(m) } +func (*StoragePutArgs) ProtoMessage() {} +func (*StoragePutArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } + +func (m *StoragePutArgs) GetEntry() *StorageEntry { + if m != nil { + return m.Entry + } + return nil +} + +type StoragePutReply struct { + Err string `sentinel:"" protobuf:"bytes,1,opt,name=err" json:"err,omitempty"` +} + +func (m *StoragePutReply) Reset() { *m = StoragePutReply{} } +func (m *StoragePutReply) String() string { return proto.CompactTextString(m) } +func (*StoragePutReply) ProtoMessage() {} +func (*StoragePutReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } + +func (m *StoragePutReply) GetErr() string { + if m != nil { + return m.Err + } + return "" +} + +type StorageDeleteArgs struct { + Key string `sentinel:"" protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` +} + +func (m *StorageDeleteArgs) Reset() { *m = StorageDeleteArgs{} } +func (m *StorageDeleteArgs) String() string { return proto.CompactTextString(m) } +func (*StorageDeleteArgs) ProtoMessage() {} +func (*StorageDeleteArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } + +func (m *StorageDeleteArgs) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +type StorageDeleteReply struct { + Err string `sentinel:"" protobuf:"bytes,1,opt,name=err" json:"err,omitempty"` +} + +func (m *StorageDeleteReply) Reset() { *m = StorageDeleteReply{} } +func (m *StorageDeleteReply) String() string { return proto.CompactTextString(m) } +func (*StorageDeleteReply) ProtoMessage() {} +func (*StorageDeleteReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } + +func (m *StorageDeleteReply) GetErr() string { + if m != nil { + return m.Err + } + return "" +} + +type TTLReply struct { + TTL int64 `sentinel:"" protobuf:"varint,1,opt,name=TTL" json:"TTL,omitempty"` +} + +func (m *TTLReply) Reset() { *m = TTLReply{} } +func (m *TTLReply) String() string { return proto.CompactTextString(m) } +func (*TTLReply) ProtoMessage() {} +func (*TTLReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } + +func (m *TTLReply) GetTTL() int64 { + if m != nil { + return m.TTL + } + return 0 +} + +type SudoPrivilegeArgs struct { + Path string `sentinel:"" protobuf:"bytes,1,opt,name=path" json:"path,omitempty"` + Token string `sentinel:"" protobuf:"bytes,2,opt,name=token" json:"token,omitempty"` +} + +func (m *SudoPrivilegeArgs) Reset() { *m = SudoPrivilegeArgs{} } +func (m *SudoPrivilegeArgs) String() string { return proto.CompactTextString(m) } +func (*SudoPrivilegeArgs) ProtoMessage() {} +func (*SudoPrivilegeArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } + +func (m *SudoPrivilegeArgs) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *SudoPrivilegeArgs) GetToken() string { + if m != nil { + return m.Token + } + return "" +} + +type SudoPrivilegeReply struct { + Sudo bool `sentinel:"" protobuf:"varint,1,opt,name=sudo" json:"sudo,omitempty"` +} + +func (m *SudoPrivilegeReply) Reset() { *m = SudoPrivilegeReply{} } +func (m *SudoPrivilegeReply) String() string { return proto.CompactTextString(m) } +func (*SudoPrivilegeReply) ProtoMessage() {} +func (*SudoPrivilegeReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } + +func (m *SudoPrivilegeReply) GetSudo() bool { + if m != nil { + return m.Sudo + } + return false +} + +type TaintedReply struct { + Tainted bool `sentinel:"" protobuf:"varint,1,opt,name=tainted" json:"tainted,omitempty"` +} + +func (m *TaintedReply) Reset() { *m = TaintedReply{} } +func (m *TaintedReply) String() string { return proto.CompactTextString(m) } +func (*TaintedReply) ProtoMessage() {} +func (*TaintedReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} } + +func (m *TaintedReply) GetTainted() bool { + if m != nil { + return m.Tainted + } + return false +} + +type CachingDisabledReply struct { + Disabled bool `sentinel:"" protobuf:"varint,1,opt,name=disabled" json:"disabled,omitempty"` +} + +func (m *CachingDisabledReply) Reset() { *m = CachingDisabledReply{} } +func (m *CachingDisabledReply) String() string { return proto.CompactTextString(m) } +func (*CachingDisabledReply) ProtoMessage() {} +func (*CachingDisabledReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} } + +func (m *CachingDisabledReply) GetDisabled() bool { + if m != nil { + return m.Disabled + } + return false +} + +type ReplicationStateReply struct { + State int32 `sentinel:"" protobuf:"varint,1,opt,name=state" json:"state,omitempty"` +} + +func (m *ReplicationStateReply) Reset() { *m = ReplicationStateReply{} } +func (m *ReplicationStateReply) String() string { return proto.CompactTextString(m) } +func (*ReplicationStateReply) ProtoMessage() {} +func (*ReplicationStateReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{35} } + +func (m *ReplicationStateReply) GetState() int32 { + if m != nil { + return m.State + } + return 0 +} + +type ResponseWrapDataArgs struct { + Data []byte `sentinel:"" protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + TTL int64 `sentinel:"" protobuf:"varint,2,opt,name=TTL" json:"TTL,omitempty"` + JWT bool `sentinel:"" protobuf:"varint,3,opt,name=JWT" json:"JWT,omitempty"` +} + +func (m *ResponseWrapDataArgs) Reset() { *m = ResponseWrapDataArgs{} } +func (m *ResponseWrapDataArgs) String() string { return proto.CompactTextString(m) } +func (*ResponseWrapDataArgs) ProtoMessage() {} +func (*ResponseWrapDataArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{36} } + +func (m *ResponseWrapDataArgs) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *ResponseWrapDataArgs) GetTTL() int64 { + if m != nil { + return m.TTL + } + return 0 +} + +func (m *ResponseWrapDataArgs) GetJWT() bool { + if m != nil { + return m.JWT + } + return false +} + +type ResponseWrapDataReply struct { + WrapInfo *ResponseWrapInfo `sentinel:"" protobuf:"bytes,1,opt,name=wrap_info,json=wrapInfo" json:"wrap_info,omitempty"` + Err string `sentinel:"" protobuf:"bytes,2,opt,name=err" json:"err,omitempty"` +} + +func (m *ResponseWrapDataReply) Reset() { *m = ResponseWrapDataReply{} } +func (m *ResponseWrapDataReply) String() string { return proto.CompactTextString(m) } +func (*ResponseWrapDataReply) ProtoMessage() {} +func (*ResponseWrapDataReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} } + +func (m *ResponseWrapDataReply) GetWrapInfo() *ResponseWrapInfo { + if m != nil { + return m.WrapInfo + } + return nil +} + +func (m *ResponseWrapDataReply) GetErr() string { + if m != nil { + return m.Err + } + return "" +} + +type MlockEnabledReply struct { + Enabled bool `sentinel:"" protobuf:"varint,1,opt,name=enabled" json:"enabled,omitempty"` +} + +func (m *MlockEnabledReply) Reset() { *m = MlockEnabledReply{} } +func (m *MlockEnabledReply) String() string { return proto.CompactTextString(m) } +func (*MlockEnabledReply) ProtoMessage() {} +func (*MlockEnabledReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{38} } + +func (m *MlockEnabledReply) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func init() { + proto.RegisterType((*Empty)(nil), "pb.Empty") + proto.RegisterType((*Header)(nil), "pb.Header") + proto.RegisterType((*ProtoError)(nil), "pb.ProtoError") + proto.RegisterType((*Paths)(nil), "pb.Paths") + proto.RegisterType((*Request)(nil), "pb.Request") + proto.RegisterType((*Alias)(nil), "pb.Alias") + proto.RegisterType((*Auth)(nil), "pb.Auth") + proto.RegisterType((*LeaseOptions)(nil), "pb.LeaseOptions") + proto.RegisterType((*Secret)(nil), "pb.Secret") + proto.RegisterType((*Response)(nil), "pb.Response") + proto.RegisterType((*ResponseWrapInfo)(nil), "pb.ResponseWrapInfo") + proto.RegisterType((*RequestWrapInfo)(nil), "pb.RequestWrapInfo") + proto.RegisterType((*HandleRequestArgs)(nil), "pb.HandleRequestArgs") + proto.RegisterType((*HandleRequestReply)(nil), "pb.HandleRequestReply") + proto.RegisterType((*SpecialPathsReply)(nil), "pb.SpecialPathsReply") + proto.RegisterType((*HandleExistenceCheckArgs)(nil), "pb.HandleExistenceCheckArgs") + proto.RegisterType((*HandleExistenceCheckReply)(nil), "pb.HandleExistenceCheckReply") + proto.RegisterType((*SetupArgs)(nil), "pb.SetupArgs") + proto.RegisterType((*SetupReply)(nil), "pb.SetupReply") + proto.RegisterType((*TypeReply)(nil), "pb.TypeReply") + proto.RegisterType((*InvalidateKeyArgs)(nil), "pb.InvalidateKeyArgs") + proto.RegisterType((*StorageEntry)(nil), "pb.StorageEntry") + proto.RegisterType((*StorageListArgs)(nil), "pb.StorageListArgs") + proto.RegisterType((*StorageListReply)(nil), "pb.StorageListReply") + proto.RegisterType((*StorageGetArgs)(nil), "pb.StorageGetArgs") + proto.RegisterType((*StorageGetReply)(nil), "pb.StorageGetReply") + proto.RegisterType((*StoragePutArgs)(nil), "pb.StoragePutArgs") + proto.RegisterType((*StoragePutReply)(nil), "pb.StoragePutReply") + proto.RegisterType((*StorageDeleteArgs)(nil), "pb.StorageDeleteArgs") + proto.RegisterType((*StorageDeleteReply)(nil), "pb.StorageDeleteReply") + proto.RegisterType((*TTLReply)(nil), "pb.TTLReply") + proto.RegisterType((*SudoPrivilegeArgs)(nil), "pb.SudoPrivilegeArgs") + proto.RegisterType((*SudoPrivilegeReply)(nil), "pb.SudoPrivilegeReply") + proto.RegisterType((*TaintedReply)(nil), "pb.TaintedReply") + proto.RegisterType((*CachingDisabledReply)(nil), "pb.CachingDisabledReply") + proto.RegisterType((*ReplicationStateReply)(nil), "pb.ReplicationStateReply") + proto.RegisterType((*ResponseWrapDataArgs)(nil), "pb.ResponseWrapDataArgs") + proto.RegisterType((*ResponseWrapDataReply)(nil), "pb.ResponseWrapDataReply") + proto.RegisterType((*MlockEnabledReply)(nil), "pb.MlockEnabledReply") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for Backend service + +type BackendClient interface { + HandleRequest(ctx context.Context, in *HandleRequestArgs, opts ...grpc.CallOption) (*HandleRequestReply, error) + SpecialPaths(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*SpecialPathsReply, error) + HandleExistenceCheck(ctx context.Context, in *HandleExistenceCheckArgs, opts ...grpc.CallOption) (*HandleExistenceCheckReply, error) + Initialize(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + Cleanup(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + InvalidateKey(ctx context.Context, in *InvalidateKeyArgs, opts ...grpc.CallOption) (*Empty, error) + Setup(ctx context.Context, in *SetupArgs, opts ...grpc.CallOption) (*SetupReply, error) + Type(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TypeReply, error) +} + +type backendClient struct { + cc *grpc.ClientConn +} + +func NewBackendClient(cc *grpc.ClientConn) BackendClient { + return &backendClient{cc} +} + +func (c *backendClient) HandleRequest(ctx context.Context, in *HandleRequestArgs, opts ...grpc.CallOption) (*HandleRequestReply, error) { + out := new(HandleRequestReply) + err := grpc.Invoke(ctx, "/pb.Backend/HandleRequest", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backendClient) SpecialPaths(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*SpecialPathsReply, error) { + out := new(SpecialPathsReply) + err := grpc.Invoke(ctx, "/pb.Backend/SpecialPaths", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backendClient) HandleExistenceCheck(ctx context.Context, in *HandleExistenceCheckArgs, opts ...grpc.CallOption) (*HandleExistenceCheckReply, error) { + out := new(HandleExistenceCheckReply) + err := grpc.Invoke(ctx, "/pb.Backend/HandleExistenceCheck", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backendClient) Initialize(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := grpc.Invoke(ctx, "/pb.Backend/Initialize", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backendClient) Cleanup(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := grpc.Invoke(ctx, "/pb.Backend/Cleanup", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backendClient) InvalidateKey(ctx context.Context, in *InvalidateKeyArgs, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := grpc.Invoke(ctx, "/pb.Backend/InvalidateKey", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backendClient) Setup(ctx context.Context, in *SetupArgs, opts ...grpc.CallOption) (*SetupReply, error) { + out := new(SetupReply) + err := grpc.Invoke(ctx, "/pb.Backend/Setup", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backendClient) Type(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TypeReply, error) { + out := new(TypeReply) + err := grpc.Invoke(ctx, "/pb.Backend/Type", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Backend service + +type BackendServer interface { + HandleRequest(context.Context, *HandleRequestArgs) (*HandleRequestReply, error) + SpecialPaths(context.Context, *Empty) (*SpecialPathsReply, error) + HandleExistenceCheck(context.Context, *HandleExistenceCheckArgs) (*HandleExistenceCheckReply, error) + Initialize(context.Context, *Empty) (*Empty, error) + Cleanup(context.Context, *Empty) (*Empty, error) + InvalidateKey(context.Context, *InvalidateKeyArgs) (*Empty, error) + Setup(context.Context, *SetupArgs) (*SetupReply, error) + Type(context.Context, *Empty) (*TypeReply, error) +} + +func RegisterBackendServer(s *grpc.Server, srv BackendServer) { + s.RegisterService(&_Backend_serviceDesc, srv) +} + +func _Backend_HandleRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HandleRequestArgs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackendServer).HandleRequest(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Backend/HandleRequest", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackendServer).HandleRequest(ctx, req.(*HandleRequestArgs)) + } + return interceptor(ctx, in, info, handler) +} + +func _Backend_SpecialPaths_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackendServer).SpecialPaths(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Backend/SpecialPaths", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackendServer).SpecialPaths(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Backend_HandleExistenceCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HandleExistenceCheckArgs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackendServer).HandleExistenceCheck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Backend/HandleExistenceCheck", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackendServer).HandleExistenceCheck(ctx, req.(*HandleExistenceCheckArgs)) + } + return interceptor(ctx, in, info, handler) +} + +func _Backend_Initialize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackendServer).Initialize(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Backend/Initialize", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackendServer).Initialize(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Backend_Cleanup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackendServer).Cleanup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Backend/Cleanup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackendServer).Cleanup(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Backend_InvalidateKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InvalidateKeyArgs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackendServer).InvalidateKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Backend/InvalidateKey", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackendServer).InvalidateKey(ctx, req.(*InvalidateKeyArgs)) + } + return interceptor(ctx, in, info, handler) +} + +func _Backend_Setup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetupArgs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackendServer).Setup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Backend/Setup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackendServer).Setup(ctx, req.(*SetupArgs)) + } + return interceptor(ctx, in, info, handler) +} + +func _Backend_Type_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackendServer).Type(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Backend/Type", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackendServer).Type(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +var _Backend_serviceDesc = grpc.ServiceDesc{ + ServiceName: "pb.Backend", + HandlerType: (*BackendServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "HandleRequest", + Handler: _Backend_HandleRequest_Handler, + }, + { + MethodName: "SpecialPaths", + Handler: _Backend_SpecialPaths_Handler, + }, + { + MethodName: "HandleExistenceCheck", + Handler: _Backend_HandleExistenceCheck_Handler, + }, + { + MethodName: "Initialize", + Handler: _Backend_Initialize_Handler, + }, + { + MethodName: "Cleanup", + Handler: _Backend_Cleanup_Handler, + }, + { + MethodName: "InvalidateKey", + Handler: _Backend_InvalidateKey_Handler, + }, + { + MethodName: "Setup", + Handler: _Backend_Setup_Handler, + }, + { + MethodName: "Type", + Handler: _Backend_Type_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "logical/plugin/pb/backend.proto", +} + +// Client API for Storage service + +type StorageClient interface { + List(ctx context.Context, in *StorageListArgs, opts ...grpc.CallOption) (*StorageListReply, error) + Get(ctx context.Context, in *StorageGetArgs, opts ...grpc.CallOption) (*StorageGetReply, error) + Put(ctx context.Context, in *StoragePutArgs, opts ...grpc.CallOption) (*StoragePutReply, error) + Delete(ctx context.Context, in *StorageDeleteArgs, opts ...grpc.CallOption) (*StorageDeleteReply, error) +} + +type storageClient struct { + cc *grpc.ClientConn +} + +func NewStorageClient(cc *grpc.ClientConn) StorageClient { + return &storageClient{cc} +} + +func (c *storageClient) List(ctx context.Context, in *StorageListArgs, opts ...grpc.CallOption) (*StorageListReply, error) { + out := new(StorageListReply) + err := grpc.Invoke(ctx, "/pb.Storage/List", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *storageClient) Get(ctx context.Context, in *StorageGetArgs, opts ...grpc.CallOption) (*StorageGetReply, error) { + out := new(StorageGetReply) + err := grpc.Invoke(ctx, "/pb.Storage/Get", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *storageClient) Put(ctx context.Context, in *StoragePutArgs, opts ...grpc.CallOption) (*StoragePutReply, error) { + out := new(StoragePutReply) + err := grpc.Invoke(ctx, "/pb.Storage/Put", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *storageClient) Delete(ctx context.Context, in *StorageDeleteArgs, opts ...grpc.CallOption) (*StorageDeleteReply, error) { + out := new(StorageDeleteReply) + err := grpc.Invoke(ctx, "/pb.Storage/Delete", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Storage service + +type StorageServer interface { + List(context.Context, *StorageListArgs) (*StorageListReply, error) + Get(context.Context, *StorageGetArgs) (*StorageGetReply, error) + Put(context.Context, *StoragePutArgs) (*StoragePutReply, error) + Delete(context.Context, *StorageDeleteArgs) (*StorageDeleteReply, error) +} + +func RegisterStorageServer(s *grpc.Server, srv StorageServer) { + s.RegisterService(&_Storage_serviceDesc, srv) +} + +func _Storage_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StorageListArgs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StorageServer).List(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Storage/List", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StorageServer).List(ctx, req.(*StorageListArgs)) + } + return interceptor(ctx, in, info, handler) +} + +func _Storage_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StorageGetArgs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StorageServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Storage/Get", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StorageServer).Get(ctx, req.(*StorageGetArgs)) + } + return interceptor(ctx, in, info, handler) +} + +func _Storage_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StoragePutArgs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StorageServer).Put(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Storage/Put", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StorageServer).Put(ctx, req.(*StoragePutArgs)) + } + return interceptor(ctx, in, info, handler) +} + +func _Storage_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StorageDeleteArgs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StorageServer).Delete(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.Storage/Delete", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StorageServer).Delete(ctx, req.(*StorageDeleteArgs)) + } + return interceptor(ctx, in, info, handler) +} + +var _Storage_serviceDesc = grpc.ServiceDesc{ + ServiceName: "pb.Storage", + HandlerType: (*StorageServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "List", + Handler: _Storage_List_Handler, + }, + { + MethodName: "Get", + Handler: _Storage_Get_Handler, + }, + { + MethodName: "Put", + Handler: _Storage_Put_Handler, + }, + { + MethodName: "Delete", + Handler: _Storage_Delete_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "logical/plugin/pb/backend.proto", +} + +// Client API for SystemView service + +type SystemViewClient interface { + DefaultLeaseTTL(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TTLReply, error) + MaxLeaseTTL(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TTLReply, error) + SudoPrivilege(ctx context.Context, in *SudoPrivilegeArgs, opts ...grpc.CallOption) (*SudoPrivilegeReply, error) + Tainted(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TaintedReply, error) + CachingDisabled(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*CachingDisabledReply, error) + ReplicationState(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ReplicationStateReply, error) + ResponseWrapData(ctx context.Context, in *ResponseWrapDataArgs, opts ...grpc.CallOption) (*ResponseWrapDataReply, error) + MlockEnabled(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*MlockEnabledReply, error) +} + +type systemViewClient struct { + cc *grpc.ClientConn +} + +func NewSystemViewClient(cc *grpc.ClientConn) SystemViewClient { + return &systemViewClient{cc} +} + +func (c *systemViewClient) DefaultLeaseTTL(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TTLReply, error) { + out := new(TTLReply) + err := grpc.Invoke(ctx, "/pb.SystemView/DefaultLeaseTTL", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *systemViewClient) MaxLeaseTTL(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TTLReply, error) { + out := new(TTLReply) + err := grpc.Invoke(ctx, "/pb.SystemView/MaxLeaseTTL", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *systemViewClient) SudoPrivilege(ctx context.Context, in *SudoPrivilegeArgs, opts ...grpc.CallOption) (*SudoPrivilegeReply, error) { + out := new(SudoPrivilegeReply) + err := grpc.Invoke(ctx, "/pb.SystemView/SudoPrivilege", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *systemViewClient) Tainted(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TaintedReply, error) { + out := new(TaintedReply) + err := grpc.Invoke(ctx, "/pb.SystemView/Tainted", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *systemViewClient) CachingDisabled(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*CachingDisabledReply, error) { + out := new(CachingDisabledReply) + err := grpc.Invoke(ctx, "/pb.SystemView/CachingDisabled", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *systemViewClient) ReplicationState(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ReplicationStateReply, error) { + out := new(ReplicationStateReply) + err := grpc.Invoke(ctx, "/pb.SystemView/ReplicationState", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *systemViewClient) ResponseWrapData(ctx context.Context, in *ResponseWrapDataArgs, opts ...grpc.CallOption) (*ResponseWrapDataReply, error) { + out := new(ResponseWrapDataReply) + err := grpc.Invoke(ctx, "/pb.SystemView/ResponseWrapData", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *systemViewClient) MlockEnabled(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*MlockEnabledReply, error) { + out := new(MlockEnabledReply) + err := grpc.Invoke(ctx, "/pb.SystemView/MlockEnabled", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for SystemView service + +type SystemViewServer interface { + DefaultLeaseTTL(context.Context, *Empty) (*TTLReply, error) + MaxLeaseTTL(context.Context, *Empty) (*TTLReply, error) + SudoPrivilege(context.Context, *SudoPrivilegeArgs) (*SudoPrivilegeReply, error) + Tainted(context.Context, *Empty) (*TaintedReply, error) + CachingDisabled(context.Context, *Empty) (*CachingDisabledReply, error) + ReplicationState(context.Context, *Empty) (*ReplicationStateReply, error) + ResponseWrapData(context.Context, *ResponseWrapDataArgs) (*ResponseWrapDataReply, error) + MlockEnabled(context.Context, *Empty) (*MlockEnabledReply, error) +} + +func RegisterSystemViewServer(s *grpc.Server, srv SystemViewServer) { + s.RegisterService(&_SystemView_serviceDesc, srv) +} + +func _SystemView_DefaultLeaseTTL_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SystemViewServer).DefaultLeaseTTL(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.SystemView/DefaultLeaseTTL", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SystemViewServer).DefaultLeaseTTL(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _SystemView_MaxLeaseTTL_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SystemViewServer).MaxLeaseTTL(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.SystemView/MaxLeaseTTL", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SystemViewServer).MaxLeaseTTL(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _SystemView_SudoPrivilege_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SudoPrivilegeArgs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SystemViewServer).SudoPrivilege(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.SystemView/SudoPrivilege", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SystemViewServer).SudoPrivilege(ctx, req.(*SudoPrivilegeArgs)) + } + return interceptor(ctx, in, info, handler) +} + +func _SystemView_Tainted_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SystemViewServer).Tainted(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.SystemView/Tainted", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SystemViewServer).Tainted(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _SystemView_CachingDisabled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SystemViewServer).CachingDisabled(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.SystemView/CachingDisabled", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SystemViewServer).CachingDisabled(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _SystemView_ReplicationState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SystemViewServer).ReplicationState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.SystemView/ReplicationState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SystemViewServer).ReplicationState(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _SystemView_ResponseWrapData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResponseWrapDataArgs) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SystemViewServer).ResponseWrapData(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.SystemView/ResponseWrapData", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SystemViewServer).ResponseWrapData(ctx, req.(*ResponseWrapDataArgs)) + } + return interceptor(ctx, in, info, handler) +} + +func _SystemView_MlockEnabled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SystemViewServer).MlockEnabled(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.SystemView/MlockEnabled", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SystemViewServer).MlockEnabled(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +var _SystemView_serviceDesc = grpc.ServiceDesc{ + ServiceName: "pb.SystemView", + HandlerType: (*SystemViewServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "DefaultLeaseTTL", + Handler: _SystemView_DefaultLeaseTTL_Handler, + }, + { + MethodName: "MaxLeaseTTL", + Handler: _SystemView_MaxLeaseTTL_Handler, + }, + { + MethodName: "SudoPrivilege", + Handler: _SystemView_SudoPrivilege_Handler, + }, + { + MethodName: "Tainted", + Handler: _SystemView_Tainted_Handler, + }, + { + MethodName: "CachingDisabled", + Handler: _SystemView_CachingDisabled_Handler, + }, + { + MethodName: "ReplicationState", + Handler: _SystemView_ReplicationState_Handler, + }, + { + MethodName: "ResponseWrapData", + Handler: _SystemView_ResponseWrapData_Handler, + }, + { + MethodName: "MlockEnabled", + Handler: _SystemView_MlockEnabled_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "logical/plugin/pb/backend.proto", +} + +func init() { proto.RegisterFile("logical/plugin/pb/backend.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 2030 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xef, 0x6e, 0xdb, 0xc8, + 0x11, 0x87, 0x24, 0x4b, 0xa2, 0x46, 0x92, 0xff, 0x6c, 0x7c, 0x29, 0xa3, 0xe4, 0x6a, 0x95, 0x87, + 0xb8, 0xba, 0x00, 0x27, 0x27, 0xea, 0xbf, 0x5c, 0x8b, 0xbb, 0xc2, 0x75, 0x7c, 0x39, 0xf7, 0x92, + 0x3b, 0x83, 0x76, 0x7b, 0x2d, 0x5a, 0x40, 0xb7, 0x16, 0xc7, 0x32, 0x61, 0x8a, 0x64, 0x97, 0x4b, + 0x3b, 0xea, 0x97, 0x3e, 0x42, 0x81, 0x7e, 0xb8, 0x3e, 0x49, 0xdf, 0xa1, 0x40, 0x3f, 0xf7, 0x45, + 0xfa, 0x04, 0xc5, 0xce, 0x2e, 0x29, 0x52, 0x92, 0x9b, 0x14, 0x68, 0xbf, 0xed, 0xfc, 0xd9, 0x99, + 0xd9, 0xd9, 0x99, 0xdf, 0x2c, 0x09, 0x7b, 0x41, 0x34, 0xf5, 0x27, 0x3c, 0x38, 0x88, 0x83, 0x74, + 0xea, 0x87, 0x07, 0xf1, 0xc5, 0xc1, 0x05, 0x9f, 0x5c, 0x63, 0xe8, 0x0d, 0x63, 0x11, 0xc9, 0x88, + 0x55, 0xe3, 0x8b, 0xde, 0xde, 0x34, 0x8a, 0xa6, 0x01, 0x1e, 0x10, 0xe7, 0x22, 0xbd, 0x3c, 0x90, + 0xfe, 0x0c, 0x13, 0xc9, 0x67, 0xb1, 0x56, 0x72, 0x9a, 0x50, 0x3f, 0x9e, 0xc5, 0x72, 0xee, 0xf4, + 0xa1, 0xf1, 0x39, 0x72, 0x0f, 0x05, 0xbb, 0x0f, 0x8d, 0x2b, 0x5a, 0xd9, 0x95, 0x7e, 0x6d, 0xd0, + 0x72, 0x0d, 0xe5, 0xfc, 0x0e, 0xe0, 0x54, 0xed, 0x39, 0x16, 0x22, 0x12, 0xec, 0x01, 0x58, 0x28, + 0xc4, 0x58, 0xce, 0x63, 0xb4, 0x2b, 0xfd, 0xca, 0xa0, 0xeb, 0x36, 0x51, 0x88, 0xf3, 0x79, 0x8c, + 0xec, 0x3b, 0xa0, 0x96, 0xe3, 0x59, 0x32, 0xb5, 0xab, 0xfd, 0x8a, 0xb2, 0x80, 0x42, 0xbc, 0x4e, + 0xa6, 0xd9, 0x9e, 0x49, 0xe4, 0xa1, 0x5d, 0xeb, 0x57, 0x06, 0x35, 0xda, 0x73, 0x14, 0x79, 0xe8, + 0x7c, 0x5b, 0x81, 0xfa, 0x29, 0x97, 0x57, 0x09, 0x63, 0xb0, 0x21, 0xa2, 0x48, 0x1a, 0xe7, 0xb4, + 0x66, 0x03, 0xd8, 0x4a, 0x43, 0x9e, 0xca, 0x2b, 0x0c, 0xa5, 0x3f, 0xe1, 0x12, 0x3d, 0xbb, 0x4a, + 0xe2, 0x65, 0x36, 0xfb, 0x00, 0xba, 0x41, 0x34, 0xe1, 0xc1, 0x38, 0x91, 0x91, 0xe0, 0x53, 0xe5, + 0x47, 0xe9, 0x75, 0x88, 0x79, 0xa6, 0x79, 0xec, 0x09, 0xec, 0x24, 0xc8, 0x83, 0xf1, 0xad, 0xe0, + 0x71, 0xae, 0xb8, 0xa1, 0x0d, 0x2a, 0xc1, 0xd7, 0x82, 0xc7, 0x46, 0xd7, 0xf9, 0x73, 0x03, 0x9a, + 0x2e, 0xfe, 0x21, 0xc5, 0x44, 0xb2, 0x4d, 0xa8, 0xfa, 0x1e, 0x9d, 0xb6, 0xe5, 0x56, 0x7d, 0x8f, + 0x0d, 0x81, 0xb9, 0x18, 0x07, 0xca, 0xb5, 0x1f, 0x85, 0x47, 0x41, 0x9a, 0x48, 0x14, 0xe6, 0xcc, + 0x6b, 0x24, 0xec, 0x11, 0xb4, 0xa2, 0x18, 0x05, 0xf1, 0x28, 0x01, 0x2d, 0x77, 0xc1, 0x50, 0x07, + 0x8f, 0xb9, 0xbc, 0xb2, 0x37, 0x48, 0x40, 0x6b, 0xc5, 0xf3, 0xb8, 0xe4, 0x76, 0xbd, 0x5f, 0x19, + 0x74, 0x5c, 0x5a, 0x33, 0x07, 0x1a, 0x09, 0x4e, 0x04, 0x4a, 0xbb, 0xd1, 0xaf, 0x0c, 0xda, 0x23, + 0x18, 0xc6, 0x17, 0xc3, 0x33, 0xe2, 0xb8, 0x46, 0xc2, 0x1e, 0xc1, 0x86, 0xca, 0x8b, 0xdd, 0x24, + 0x0d, 0x4b, 0x69, 0x1c, 0xa6, 0xf2, 0xca, 0x25, 0x2e, 0x1b, 0x41, 0x53, 0xdf, 0x69, 0x62, 0x5b, + 0xfd, 0xda, 0xa0, 0x3d, 0xb2, 0x95, 0x82, 0x39, 0xe5, 0x50, 0x97, 0x41, 0x72, 0x1c, 0x4a, 0x31, + 0x77, 0x33, 0x45, 0xf6, 0x3d, 0xe8, 0x4c, 0x02, 0x1f, 0x43, 0x39, 0x96, 0xd1, 0x35, 0x86, 0x76, + 0x8b, 0xa2, 0x6c, 0x6b, 0xde, 0xb9, 0x62, 0xb1, 0x11, 0xbc, 0x57, 0x54, 0x19, 0xf3, 0xc9, 0x04, + 0x93, 0x24, 0x12, 0x36, 0x90, 0xee, 0xbd, 0x82, 0xee, 0xa1, 0x11, 0x29, 0xb3, 0x9e, 0x9f, 0xc4, + 0x01, 0x9f, 0x8f, 0x43, 0x3e, 0x43, 0xbb, 0xad, 0xcd, 0x1a, 0xde, 0x97, 0x7c, 0x86, 0x6c, 0x0f, + 0xda, 0xb3, 0x28, 0x0d, 0xe5, 0x38, 0x8e, 0xfc, 0x50, 0xda, 0x1d, 0xd2, 0x00, 0x62, 0x9d, 0x2a, + 0x0e, 0x7b, 0x1f, 0x34, 0xa5, 0x8b, 0xb1, 0xab, 0xf3, 0x4a, 0x1c, 0x2a, 0xc7, 0xc7, 0xb0, 0xa9, + 0xc5, 0x79, 0x3c, 0x9b, 0xa4, 0xd2, 0x25, 0x6e, 0x1e, 0xc9, 0x53, 0x68, 0x51, 0x3d, 0xf8, 0xe1, + 0x65, 0x64, 0x6f, 0x51, 0xde, 0xee, 0x15, 0xd2, 0xa2, 0x6a, 0xe2, 0x24, 0xbc, 0x8c, 0x5c, 0xeb, + 0xd6, 0xac, 0xd8, 0x27, 0xf0, 0xb0, 0x74, 0x5e, 0x81, 0x33, 0xee, 0x87, 0x7e, 0x38, 0x1d, 0xa7, + 0x09, 0x26, 0xf6, 0x36, 0x55, 0xb8, 0x5d, 0x38, 0xb5, 0x9b, 0x29, 0xfc, 0x2a, 0xc1, 0x84, 0x3d, + 0x84, 0x96, 0xaa, 0x5b, 0x39, 0x1f, 0xfb, 0x9e, 0xbd, 0x43, 0x21, 0x59, 0x9a, 0x71, 0xe2, 0xb1, + 0xef, 0xc3, 0x56, 0x1c, 0x05, 0xfe, 0x64, 0x3e, 0x8e, 0x6e, 0x50, 0x08, 0xdf, 0x43, 0x9b, 0xf5, + 0x2b, 0x03, 0xcb, 0xdd, 0xd4, 0xec, 0xaf, 0x0c, 0x77, 0x5d, 0x6b, 0xdc, 0x23, 0xc5, 0x65, 0x76, + 0xef, 0x33, 0xe8, 0x14, 0xaf, 0x96, 0x6d, 0x43, 0xed, 0x1a, 0xe7, 0xa6, 0x9c, 0xd5, 0x92, 0xf5, + 0xa1, 0x7e, 0xc3, 0x83, 0x14, 0xa9, 0x84, 0x4d, 0x61, 0xe9, 0x2d, 0xae, 0x16, 0xfc, 0xb4, 0xfa, + 0xbc, 0xe2, 0x70, 0xa8, 0x1f, 0x06, 0x3e, 0x4f, 0x96, 0xf2, 0x5e, 0x79, 0x7b, 0xde, 0xab, 0xeb, + 0xf2, 0xce, 0x60, 0x83, 0x6e, 0x5e, 0xf7, 0x03, 0xad, 0x9d, 0x7f, 0xd5, 0x60, 0x43, 0xd5, 0x2b, + 0xfb, 0x11, 0x74, 0x03, 0xe4, 0x09, 0x8e, 0xa3, 0x58, 0xf5, 0x48, 0x42, 0x5e, 0xda, 0xa3, 0x6d, + 0x15, 0xd9, 0x2b, 0x25, 0xf8, 0x4a, 0xf3, 0xdd, 0x4e, 0x50, 0xa0, 0x14, 0x0a, 0xf8, 0xa1, 0x44, + 0x11, 0xf2, 0x60, 0x4c, 0xfd, 0x53, 0xa5, 0xfe, 0xe9, 0x64, 0xcc, 0x17, 0xaa, 0x8f, 0x96, 0x4b, + 0xaf, 0xb6, 0x5a, 0x7a, 0x3d, 0xb0, 0x28, 0xdd, 0x3e, 0x26, 0x06, 0x1f, 0x72, 0x9a, 0x8d, 0xc0, + 0x9a, 0xa1, 0xe4, 0xa6, 0x3d, 0x55, 0x17, 0xdd, 0xcf, 0xda, 0x6c, 0xf8, 0xda, 0x08, 0x74, 0x0f, + 0xe5, 0x7a, 0x2b, 0x4d, 0xd4, 0x58, 0x6d, 0xa2, 0x1e, 0x58, 0x79, 0xbe, 0x9a, 0xba, 0x28, 0x32, + 0x5a, 0x21, 0x73, 0x8c, 0xc2, 0x8f, 0x3c, 0xdb, 0xa2, 0xda, 0x32, 0x94, 0xc2, 0xd5, 0x30, 0x9d, + 0xe9, 0xaa, 0x6b, 0x69, 0x5c, 0x0d, 0xd3, 0xd9, 0x6a, 0x91, 0xc1, 0x52, 0x91, 0xed, 0x41, 0x9d, + 0xab, 0x9b, 0xa4, 0xae, 0x6b, 0x8f, 0x5a, 0x14, 0xbf, 0x62, 0xb8, 0x9a, 0xcf, 0x86, 0xd0, 0x9d, + 0x8a, 0x28, 0x8d, 0xc7, 0x44, 0x62, 0x62, 0x77, 0xe8, 0xa0, 0x05, 0xc5, 0x0e, 0xc9, 0x0f, 0xb5, + 0xb8, 0xf7, 0x33, 0xe8, 0x96, 0x8e, 0xbe, 0xa6, 0xc6, 0x76, 0x8b, 0x35, 0xd6, 0x2a, 0xd6, 0xd5, + 0x5f, 0x2b, 0xd0, 0x29, 0xde, 0xa9, 0xda, 0x7c, 0x7e, 0xfe, 0x8a, 0x36, 0xd7, 0x5c, 0xb5, 0x54, + 0x00, 0x2a, 0x30, 0xc4, 0x5b, 0x7e, 0x11, 0x68, 0x03, 0x96, 0xbb, 0x60, 0x28, 0xa9, 0x1f, 0x4e, + 0x04, 0xce, 0x30, 0x94, 0x66, 0xbe, 0x2c, 0x18, 0xec, 0x63, 0x00, 0x3f, 0x49, 0x52, 0x1c, 0xab, + 0x11, 0x48, 0x20, 0xdb, 0x1e, 0xf5, 0x86, 0x7a, 0x3e, 0x0e, 0xb3, 0xf9, 0x38, 0x3c, 0xcf, 0xe6, + 0xa3, 0xdb, 0x22, 0x6d, 0x45, 0x3b, 0x7f, 0x82, 0x86, 0xc6, 0xd7, 0xff, 0x6b, 0x3d, 0x3e, 0x00, + 0x4b, 0xdb, 0xf6, 0x3d, 0x53, 0x8b, 0x4d, 0xa2, 0x4f, 0x3c, 0xe7, 0x1f, 0x15, 0xb0, 0x5c, 0x4c, + 0xe2, 0x28, 0x4c, 0xb0, 0x80, 0xff, 0x95, 0xb7, 0xe2, 0x7f, 0x75, 0x2d, 0xfe, 0x67, 0x53, 0xa5, + 0x56, 0x98, 0x2a, 0x3d, 0xb0, 0x04, 0x7a, 0xbe, 0xc0, 0x89, 0x34, 0x13, 0x28, 0xa7, 0x95, 0xec, + 0x96, 0x0b, 0x05, 0x5c, 0x09, 0x95, 0x7a, 0xcb, 0xcd, 0x69, 0xf6, 0xac, 0x08, 0x9b, 0x7a, 0x20, + 0xed, 0x6a, 0xd8, 0xd4, 0xe1, 0xae, 0xe2, 0xa6, 0xf3, 0xf7, 0x2a, 0x6c, 0x2f, 0x8b, 0xd7, 0x5c, + 0xf6, 0x2e, 0xd4, 0x75, 0x97, 0x98, 0x4a, 0x91, 0x2b, 0xfd, 0x51, 0x5b, 0xea, 0x8f, 0x9f, 0x43, + 0x77, 0x22, 0x90, 0xa6, 0xe9, 0xbb, 0xde, 0x72, 0x27, 0xdb, 0xa0, 0x58, 0xec, 0x43, 0xd8, 0x56, + 0x51, 0xc6, 0xe8, 0x2d, 0x40, 0xab, 0x4e, 0x4e, 0xb6, 0x0c, 0x3f, 0x87, 0xad, 0x27, 0xb0, 0x93, + 0xa9, 0x2e, 0x1a, 0xac, 0x51, 0xd2, 0x3d, 0xce, 0xfa, 0xec, 0x3e, 0x34, 0x2e, 0x23, 0x31, 0xe3, + 0xd2, 0x74, 0xb4, 0xa1, 0x54, 0x59, 0xe4, 0xf1, 0xd2, 0xe8, 0xb7, 0x48, 0x9c, 0xc7, 0xa4, 0x1e, + 0x44, 0xaa, 0x83, 0xf3, 0xc7, 0x0a, 0x75, 0xb7, 0xe5, 0x5a, 0xd9, 0x23, 0xc5, 0xf9, 0x0d, 0x6c, + 0x2d, 0xcd, 0xa7, 0x35, 0x89, 0x5c, 0xb8, 0xaf, 0x96, 0xdc, 0x97, 0x2c, 0xd7, 0x96, 0x2c, 0xff, + 0x16, 0x76, 0x3e, 0xe7, 0xa1, 0x17, 0xa0, 0xb1, 0x7f, 0x28, 0xa6, 0x84, 0xf8, 0xe6, 0xb9, 0x34, + 0x36, 0x0f, 0xa1, 0xae, 0xdb, 0x32, 0x9c, 0x13, 0x8f, 0x3d, 0x86, 0xa6, 0xd0, 0xda, 0xa6, 0xf0, + 0xda, 0x85, 0x01, 0xea, 0x66, 0x32, 0xe7, 0x1b, 0x60, 0x25, 0xd3, 0xea, 0xa5, 0x34, 0x67, 0x03, + 0x55, 0x80, 0xba, 0x28, 0x4c, 0x61, 0x77, 0x8a, 0x75, 0xe4, 0xe6, 0x52, 0xd6, 0x87, 0x1a, 0x0a, + 0x61, 0x5c, 0x6c, 0x2a, 0xa5, 0xc5, 0xbb, 0xd4, 0x55, 0x22, 0xe7, 0x87, 0xb0, 0x73, 0x16, 0xe3, + 0xc4, 0xe7, 0x01, 0xbd, 0x29, 0xb5, 0x83, 0x3d, 0xa8, 0xab, 0x24, 0x67, 0x3d, 0x4b, 0x20, 0xa6, + 0xc5, 0x9a, 0xef, 0x7c, 0x03, 0xb6, 0x8e, 0xeb, 0xf8, 0x8d, 0x9f, 0x48, 0x0c, 0x27, 0x78, 0x74, + 0x85, 0x93, 0xeb, 0xff, 0xe1, 0xc9, 0x6f, 0xe0, 0xc1, 0x3a, 0x0f, 0x59, 0x7c, 0xed, 0x89, 0xa2, + 0xc6, 0x97, 0x51, 0x1a, 0x6a, 0x1f, 0x96, 0x0b, 0xc4, 0xfa, 0x4c, 0x71, 0xd4, 0x3d, 0xa2, 0xda, + 0x97, 0x18, 0xe8, 0x33, 0x54, 0x96, 0x8f, 0xda, 0xdd, 0xf9, 0xf8, 0xb6, 0x02, 0xad, 0x33, 0x94, + 0x69, 0x4c, 0x67, 0x79, 0x08, 0xad, 0x0b, 0x11, 0x5d, 0xa3, 0x58, 0x1c, 0xc5, 0xd2, 0x8c, 0x13, + 0x8f, 0x3d, 0x83, 0xc6, 0x51, 0x14, 0x5e, 0xfa, 0x53, 0x7a, 0x61, 0xb7, 0x47, 0x0f, 0x34, 0xba, + 0x98, 0xbd, 0x43, 0x2d, 0xd3, 0x73, 0xcd, 0x28, 0xf6, 0x3e, 0x86, 0x76, 0x81, 0xfd, 0x5f, 0x61, + 0xfe, 0x77, 0x01, 0xc8, 0xb6, 0xce, 0xc0, 0xb6, 0x3e, 0x88, 0xd9, 0xa9, 0x02, 0xdf, 0x83, 0x96, + 0x7a, 0x4b, 0x68, 0x31, 0x83, 0x8d, 0xc2, 0xe7, 0x06, 0xad, 0x9d, 0xc7, 0xb0, 0x73, 0x12, 0xde, + 0xf0, 0xc0, 0xf7, 0xb8, 0xc4, 0x2f, 0x70, 0x4e, 0x07, 0x5c, 0x89, 0xc0, 0x39, 0x83, 0x8e, 0x79, + 0xd0, 0xbf, 0x53, 0x8c, 0x1d, 0x13, 0xe3, 0x7f, 0x6e, 0x91, 0x0f, 0x61, 0xcb, 0x18, 0x7d, 0xe5, + 0x9b, 0x06, 0x51, 0x13, 0x5a, 0xe0, 0xa5, 0xff, 0xc6, 0x98, 0x36, 0x94, 0xf3, 0x1c, 0xb6, 0x0b, + 0xaa, 0xf9, 0x71, 0xae, 0x71, 0x9e, 0x64, 0x1f, 0x3a, 0x6a, 0x9d, 0x65, 0xa0, 0xba, 0xc8, 0x80, + 0x03, 0x9b, 0x66, 0xe7, 0x4b, 0x94, 0x77, 0x9c, 0xee, 0x8b, 0x3c, 0x90, 0x97, 0x68, 0x8c, 0xef, + 0x43, 0x1d, 0xd5, 0x49, 0x8b, 0x03, 0xaa, 0x98, 0x01, 0x57, 0x8b, 0xd7, 0x38, 0x7c, 0x9e, 0x3b, + 0x3c, 0x4d, 0xb5, 0xc3, 0x77, 0xb4, 0xe5, 0x7c, 0x90, 0x87, 0x71, 0x9a, 0xca, 0xbb, 0x6e, 0xf4, + 0x31, 0xec, 0x18, 0xa5, 0x17, 0x18, 0xa0, 0xc4, 0x3b, 0x8e, 0xb4, 0x0f, 0xac, 0xa4, 0x76, 0x97, + 0xb9, 0x47, 0x60, 0x9d, 0x9f, 0xbf, 0xca, 0xa5, 0x65, 0xe4, 0x73, 0x3e, 0x81, 0x9d, 0xb3, 0xd4, + 0x8b, 0x4e, 0x85, 0x7f, 0xe3, 0x07, 0x38, 0xd5, 0xce, 0xb2, 0xef, 0xac, 0x4a, 0xe1, 0x3b, 0x6b, + 0xed, 0xac, 0x71, 0x06, 0xc0, 0x4a, 0xdb, 0xf3, 0x7b, 0x4b, 0x52, 0x2f, 0x32, 0x0d, 0x4a, 0x6b, + 0x67, 0x00, 0x9d, 0x73, 0xae, 0xa6, 0xb9, 0xa7, 0x75, 0x6c, 0x68, 0x4a, 0x4d, 0x1b, 0xb5, 0x8c, + 0x74, 0x46, 0xb0, 0x7b, 0xc4, 0x27, 0x57, 0x7e, 0x38, 0x7d, 0xe1, 0x27, 0xea, 0xd9, 0x62, 0x76, + 0xf4, 0xc0, 0xf2, 0x0c, 0xc3, 0x6c, 0xc9, 0x69, 0xe7, 0x23, 0x78, 0xaf, 0xf0, 0x35, 0x79, 0x26, + 0x79, 0x96, 0x8f, 0x5d, 0xa8, 0x27, 0x8a, 0xa2, 0x1d, 0x75, 0x57, 0x13, 0xce, 0x97, 0xb0, 0x5b, + 0x1c, 0xaf, 0xea, 0x71, 0x91, 0x1d, 0x9c, 0xc6, 0x7e, 0xa5, 0x30, 0xf6, 0x4d, 0xce, 0xaa, 0x8b, + 0x69, 0xb1, 0x0d, 0xb5, 0x5f, 0x7e, 0x7d, 0x6e, 0x8a, 0x5d, 0x2d, 0x9d, 0xdf, 0x2b, 0xf7, 0x65, + 0x7b, 0xda, 0x7d, 0x69, 0xf6, 0x57, 0xde, 0x65, 0xf6, 0xaf, 0xa9, 0xb7, 0x8f, 0x60, 0xe7, 0x75, + 0x10, 0x4d, 0xae, 0x8f, 0xc3, 0x42, 0x36, 0x6c, 0x68, 0x62, 0x58, 0x4c, 0x46, 0x46, 0x8e, 0xfe, + 0x52, 0x83, 0xe6, 0x2f, 0xf4, 0x7f, 0x0e, 0xf6, 0x29, 0x74, 0x4b, 0x83, 0x84, 0xbd, 0x47, 0x5f, + 0x2c, 0xcb, 0x63, 0xab, 0x77, 0x7f, 0x85, 0xad, 0xbd, 0x3c, 0x85, 0x4e, 0x71, 0x4c, 0x30, 0x1a, + 0x09, 0xf4, 0x3b, 0xa4, 0x47, 0x96, 0x56, 0x67, 0xc8, 0x19, 0xec, 0xae, 0x03, 0x70, 0xf6, 0x68, + 0xe1, 0x61, 0x75, 0x78, 0xf4, 0xde, 0xbf, 0x4b, 0xaa, 0x8d, 0x3a, 0x00, 0x27, 0xa1, 0x2f, 0x7d, + 0x1e, 0xf8, 0x7f, 0xc4, 0x62, 0x10, 0x8b, 0x25, 0xdb, 0x83, 0xe6, 0x51, 0x80, 0x3c, 0x4c, 0xe3, + 0x3b, 0x14, 0x9e, 0x41, 0xb7, 0x04, 0x84, 0x3a, 0x17, 0x2b, 0xd8, 0x58, 0xdc, 0xb2, 0x0f, 0x75, + 0x02, 0x5f, 0xd6, 0x2d, 0x61, 0x7c, 0x6f, 0x33, 0x27, 0x75, 0x7c, 0x7d, 0xd8, 0xa0, 0x0f, 0xba, + 0x82, 0x63, 0xda, 0x91, 0x23, 0xf3, 0xe8, 0x9f, 0x15, 0x68, 0x66, 0x3f, 0x57, 0x9e, 0xc1, 0x86, + 0xc2, 0x38, 0x76, 0xaf, 0x00, 0x13, 0x19, 0x3e, 0xf6, 0x76, 0x97, 0x98, 0xda, 0xc1, 0x10, 0x6a, + 0x2f, 0x51, 0x32, 0x56, 0x10, 0x1a, 0xb0, 0xeb, 0xdd, 0x2b, 0xf3, 0x72, 0xfd, 0xd3, 0xb4, 0xac, + 0x6f, 0xb0, 0xaa, 0xa4, 0x9f, 0xa3, 0xd0, 0x4f, 0xa0, 0xa1, 0x51, 0x44, 0x27, 0x65, 0x05, 0x7f, + 0x74, 0x81, 0xac, 0xe2, 0xcd, 0xe8, 0x6f, 0x35, 0x80, 0xb3, 0x79, 0x22, 0x71, 0xf6, 0x6b, 0x1f, + 0x6f, 0xd9, 0x13, 0xd8, 0x7a, 0x81, 0x97, 0x3c, 0x0d, 0x24, 0xbd, 0xf5, 0x55, 0xb7, 0x14, 0x72, + 0x42, 0xcf, 0x95, 0x1c, 0x8c, 0xf6, 0xa1, 0xfd, 0x9a, 0xbf, 0x79, 0xbb, 0xde, 0xa7, 0xd0, 0x2d, + 0x61, 0x8c, 0x09, 0x71, 0x19, 0xb5, 0x4c, 0x88, 0xab, 0x68, 0xb4, 0x0f, 0x4d, 0x83, 0x3c, 0x45, + 0x1f, 0x84, 0xd1, 0x25, 0x44, 0xfa, 0x31, 0x6c, 0x2d, 0xe1, 0x4e, 0x51, 0x9f, 0x7e, 0x00, 0xad, + 0xc5, 0xa5, 0xe7, 0xea, 0xad, 0x5e, 0xc6, 0x9e, 0xe2, 0xc6, 0x07, 0xba, 0xdf, 0xd7, 0x81, 0xd3, + 0xcb, 0xf2, 0x2b, 0x9f, 0xbe, 0x71, 0xec, 0x65, 0x78, 0xc8, 0xc0, 0x29, 0x33, 0xb4, 0x0e, 0x66, + 0x9e, 0x42, 0xa7, 0x88, 0x10, 0x2b, 0x6d, 0xba, 0x02, 0x1f, 0x17, 0x0d, 0x7a, 0xe9, 0xff, 0xe0, + 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x2b, 0x08, 0x2c, 0x55, 0x24, 0x15, 0x00, 0x00, +} diff --git a/logical/plugin/pb/backend.proto b/logical/plugin/pb/backend.proto new file mode 100644 index 0000000000..6ad577f306 --- /dev/null +++ b/logical/plugin/pb/backend.proto @@ -0,0 +1,450 @@ +syntax = "proto3"; +package pb; + +import "google/protobuf/timestamp.proto"; + +message Empty {} + +message Header { + repeated string header = 1; +} + +message ProtoError { + uint32 err_type = 1; + string err_msg = 2; + int64 err_code = 3; +} + +// Paths is the structure of special paths that is used for SpecialPaths. +message Paths { + // Root are the paths that require a root token to access + repeated string root = 1; + + // Unauthenticated are the paths that can be accessed without any auth. + repeated string unauthenticated = 2; + + // LocalStorage are paths (prefixes) that are local to this instance; this + // indicates that these paths should not be replicated + repeated string local_storage = 3; + + // SealWrapStorage are storage paths that, when using a capable seal, + // should be seal wrapped with extra encryption. It is exact matching + // unless it ends with '/' in which case it will be treated as a prefix. + repeated string seal_wrap_storage = 4; +} + +message Request { + // Id is the uuid associated with each request + string id = 1; + + // If set, the name given to the replication secondary where this request + // originated + string ReplicationCluster = 2; + + // Operation is the requested operation type + string operation = 3; + + // Path is the part of the request path not consumed by the + // routing. As an example, if the original request path is "prod/aws/foo" + // and the AWS logical backend is mounted at "prod/aws/", then the + // final path is "foo" since the mount prefix is trimmed. + string path = 4; + + // Request data is an opaque map that must have string keys. + bytes data = 5; + + // Secret will be non-nil only for Revoke and Renew operations + // to represent the secret that was returned prior. + Secret secret = 6; + + // Auth will be non-nil only for Renew operations + // to represent the auth that was returned prior. + Auth auth = 7; + + // Headers will contain the http headers from the request. This value will + // be used in the audit broker to ensure we are auditing only the allowed + // headers. + map headers = 8; + + // ClientToken is provided to the core so that the identity + // can be verified and ACLs applied. This value is passed + // through to the logical backends but after being salted and + // hashed. + string client_token = 9; + + // ClientTokenAccessor is provided to the core so that the it can get + // logged as part of request audit logging. + string client_token_accessor = 10; + + // DisplayName is provided to the logical backend to help associate + // dynamic secrets with the source entity. This is not a sensitive + // name, but is useful for operators. + string display_name = 11; + + // MountPoint is provided so that a logical backend can generate + // paths relative to itself. The `Path` is effectively the client + // request path with the MountPoint trimmed off. + string mount_point = 12; + + // MountType is provided so that a logical backend can make decisions + // based on the specific mount type (e.g., if a mount type has different + // aliases, generating different defaults depending on the alias) + string mount_type = 13; + + // MountAccessor is provided so that identities returned by the authentication + // backends can be tied to the mount it belongs to. + string mount_accessor = 14; + + // WrapInfo contains requested response wrapping parameters + RequestWrapInfo wrap_info = 15; + + // ClientTokenRemainingUses represents the allowed number of uses left on the + // token supplied + int64 client_token_remaining_uses = 16; + + // EntityID is the identity of the caller extracted out of the token used + // to make this request + string entity_id = 17; + + // PolicyOverride indicates that the requestor wishes to override + // soft-mandatory Sentinel policies + bool policy_override = 18; + + // Whether the request is unauthenticated, as in, had no client token + // attached. Useful in some situations where the client token is not made + // accessible. + bool unauthenticated = 19; +} + +message Alias { + // MountType is the backend mount's type to which this identity belongs + string mount_type = 1; + + // MountAccessor is the identifier of the mount entry to which this + // identity belongs + string mount_accessor = 2; + + // Name is the identifier of this identity in its authentication source + string name = 3; +} + +message Auth { + LeaseOptions lease_options = 1; + + // InternalData is JSON-encodable data that is stored with the auth struct. + // This will be sent back during a Renew/Revoke for storing internal data + // used for those operations. + bytes internal_data = 2; + + // DisplayName is a non-security sensitive identifier that is + // applicable to this Auth. It is used for logging and prefixing + // of dynamic secrets. For example, DisplayName may be "armon" for + // the github credential backend. If the client token is used to + // generate a SQL credential, the user may be "github-armon-uuid". + // This is to help identify the source without using audit tables. + string display_name = 3; + + // Policies is the list of policies that the authenticated user + // is associated with. + repeated string policies = 4; + + // Metadata is used to attach arbitrary string-type metadata to + // an authenticated user. This metadata will be outputted into the + // audit log. + map metadata = 5; + + // ClientToken is the token that is generated for the authentication. + // This will be filled in by Vault core when an auth structure is + // returned. Setting this manually will have no effect. + string client_token = 6; + + // Accessor is the identifier for the ClientToken. This can be used + // to perform management functionalities (especially revocation) when + // ClientToken in the audit logs are obfuscated. Accessor can be used + // to revoke a ClientToken and to lookup the capabilities of the ClientToken, + // both without actually knowing the ClientToken. + string accessor = 7; + + // Period indicates that the token generated using this Auth object + // should never expire. The token should be renewed within the duration + // specified by this period. + int64 period = 8; + + // Number of allowed uses of the issued token + int64 num_uses = 9; + + // EntityID is the identifier of the entity in identity store to which the + // identity of the authenticating client belongs to. + string entity_id = 10; + + // Alias is the information about the authenticated client returned by + // the auth backend + Alias alias = 11; + + // GroupAliases are the informational mappings of external groups which an + // authenticated user belongs to. This is used to check if there are + // mappings groups for the group aliases in identity store. For all the + // matching groups, the entity ID of the user will be added. + repeated Alias group_aliases = 12; +} + +message LeaseOptions { + int64 TTL = 1; + + bool renewable = 2; + + int64 increment = 3; + + google.protobuf.Timestamp issue_time = 4; +} + +message Secret { + LeaseOptions lease_options = 1; + + // InternalData is JSON-encodable data that is stored with the secret. + // This will be sent back during a Renew/Revoke for storing internal data + // used for those operations. + bytes internal_data = 2; + + // LeaseID is the ID returned to the user to manage this secret. + // This is generated by Vault core. Any set value will be ignored. + // For requests, this will always be blank. + string lease_id = 3; +} + +message Response { + // Secret, if not nil, denotes that this response represents a secret. + Secret secret = 1; + + // Auth, if not nil, contains the authentication information for + // this response. This is only checked and means something for + // credential backends. + Auth auth = 2; + + // Response data is an opaque map that must have string keys. For + // secrets, this data is sent down to the user as-is. To store internal + // data that you don't want the user to see, store it in + // Secret.InternalData. + bytes data = 3; + + // Redirect is an HTTP URL to redirect to for further authentication. + // This is only valid for credential backends. This will be blanked + // for any logical backend and ignored. + string redirect = 4; + + // Warnings allow operations or backends to return warnings in response + // to user actions without failing the action outright. + repeated string warnings = 5; + + // Information for wrapping the response in a cubbyhole + ResponseWrapInfo wrap_info = 6; +} + +message ResponseWrapInfo { + // Setting to non-zero specifies that the response should be wrapped. + // Specifies the desired TTL of the wrapping token. + int64 TTL = 1; + + // The token containing the wrapped response + string token = 2; + + // The token accessor for the wrapped response token + string accessor = 3; + + // The creation time. This can be used with the TTL to figure out an + // expected expiration. + google.protobuf.Timestamp creation_time = 4; + + // If the contained response is the output of a token creation call, the + // created token's accessor will be accessible here + string wrapped_accessor = 5; + + // WrappedEntityID is the entity identifier of the caller who initiated the + // wrapping request + string wrapped_entity_id = 6; + + // The format to use. This doesn't get returned, it's only internal. + string format = 7; + + // CreationPath is the original request path that was used to create + // the wrapped response. + string creation_path = 8; + + // Controls seal wrapping behavior downstream for specific use cases + bool seal_wrap = 9; +} + +message RequestWrapInfo { + // Setting to non-zero specifies that the response should be wrapped. + // Specifies the desired TTL of the wrapping token. + int64 TTL = 1; + + // The format to use for the wrapped response; if not specified it's a bare + // token + string format = 2; + + // A flag to conforming backends that data for a given request should be + // seal wrapped + bool seal_wrap = 3; +} + +// HandleRequestArgs is the args for HandleRequest method. +message HandleRequestArgs { + uint32 storage_id = 1; + Request request = 2; +} + +// HandleRequestReply is the reply for HandleRequest method. +message HandleRequestReply { + Response response = 1; + ProtoError err = 2; +} + +// SpecialPathsReply is the reply for SpecialPaths method. +message SpecialPathsReply { + Paths paths = 1; +} + +// HandleExistenceCheckArgs is the args for HandleExistenceCheck method. +message HandleExistenceCheckArgs { + uint32 storage_id = 1; + Request request = 2; +} + +// HandleExistenceCheckReply is the reply for HandleExistenceCheck method. +message HandleExistenceCheckReply { + bool check_found = 1; + bool exists = 2; + ProtoError err = 3; +} + +// SetupArgs is the args for Setup method. +message SetupArgs { + uint32 broker_id = 1; + map Config = 2; +} + +// SetupReply is the reply for Setup method. +message SetupReply { + string err = 1; +} + +// TypeReply is the reply for the Type method. +message TypeReply { + uint32 type = 1; +} + +message InvalidateKeyArgs { + string key = 1; +} + +service Backend { + rpc HandleRequest(HandleRequestArgs) returns (HandleRequestReply); + rpc SpecialPaths(Empty) returns (SpecialPathsReply); + rpc HandleExistenceCheck(HandleExistenceCheckArgs) returns (HandleExistenceCheckReply); + rpc Initialize(Empty) returns (Empty); + rpc Cleanup(Empty) returns (Empty); + rpc InvalidateKey(InvalidateKeyArgs) returns (Empty); + rpc Setup(SetupArgs) returns (SetupReply); + rpc Type(Empty) returns (TypeReply); +} + +message StorageEntry { + string key = 1; + bytes value = 2; + bool seal_wrap = 3; +} + +message StorageListArgs { + string prefix = 1; +} + +message StorageListReply { + repeated string keys = 1; + string err = 2; +} + +message StorageGetArgs { + string key = 1; +} + +message StorageGetReply { + StorageEntry entry = 1; + string err = 2; +} + +message StoragePutArgs { + StorageEntry entry = 1; +} + +message StoragePutReply { + string err = 1; +} + +message StorageDeleteArgs { + string key = 1; +} + +message StorageDeleteReply { + string err = 1; +} + +service Storage { + rpc List(StorageListArgs) returns (StorageListReply); + rpc Get(StorageGetArgs) returns (StorageGetReply); + rpc Put(StoragePutArgs) returns (StoragePutReply); + rpc Delete(StorageDeleteArgs) returns (StorageDeleteReply); +} + +message TTLReply { + int64 TTL = 1; +} + +message SudoPrivilegeArgs { + string path = 1; + string token = 2; +} + +message SudoPrivilegeReply { + bool sudo = 1; +} + +message TaintedReply { + bool tainted = 1; +} + +message CachingDisabledReply { + bool disabled = 1; +} + +message ReplicationStateReply { + int32 state = 1; +} + +message ResponseWrapDataArgs { + bytes data = 1; + int64 TTL = 2; + bool JWT = 3; +} + +message ResponseWrapDataReply { + ResponseWrapInfo wrap_info = 1; + string err = 2; +} + +message MlockEnabledReply { + bool enabled = 1; +} + +service SystemView { + rpc DefaultLeaseTTL(Empty) returns (TTLReply); + rpc MaxLeaseTTL(Empty) returns (TTLReply); + rpc SudoPrivilege(SudoPrivilegeArgs) returns (SudoPrivilegeReply); + rpc Tainted(Empty) returns (TaintedReply); + rpc CachingDisabled(Empty) returns (CachingDisabledReply); + rpc ReplicationState(Empty) returns (ReplicationStateReply); + rpc ResponseWrapData(ResponseWrapDataArgs) returns (ResponseWrapDataReply); + rpc MlockEnabled(Empty) returns (MlockEnabledReply); +} + + diff --git a/logical/plugin/pb/translation.go b/logical/plugin/pb/translation.go new file mode 100644 index 0000000000..2c3550e57c --- /dev/null +++ b/logical/plugin/pb/translation.go @@ -0,0 +1,539 @@ +package pb + +import ( + "encoding/json" + "errors" + "time" + + "github.com/golang/protobuf/ptypes" + "github.com/hashicorp/vault/helper/errutil" + "github.com/hashicorp/vault/helper/wrapping" + "github.com/hashicorp/vault/logical" +) + +const ( + ErrTypeUnknown uint32 = iota + ErrTypeUserError + ErrTypeInternalError + ErrTypeCodedError + ErrTypeStatusBadRequest + ErrTypeUnsupportedOperation + ErrTypeUnsupportedPath + ErrTypeInvalidRequest + ErrTypePermissionDenied + ErrTypeMultiAuthzPending +) + +func ProtoErrToErr(e *ProtoError) error { + if e == nil { + return nil + } + + var err error + switch e.ErrType { + case ErrTypeUnknown: + err = errors.New(e.ErrMsg) + case ErrTypeUserError: + err = errutil.UserError{Err: e.ErrMsg} + case ErrTypeInternalError: + err = errutil.InternalError{Err: e.ErrMsg} + case ErrTypeCodedError: + err = logical.CodedError(int(e.ErrCode), e.ErrMsg) + case ErrTypeStatusBadRequest: + err = &logical.StatusBadRequest{Err: e.ErrMsg} + case ErrTypeUnsupportedOperation: + err = logical.ErrUnsupportedOperation + case ErrTypeUnsupportedPath: + err = logical.ErrUnsupportedPath + case ErrTypeInvalidRequest: + err = logical.ErrInvalidRequest + case ErrTypePermissionDenied: + err = logical.ErrPermissionDenied + case ErrTypeMultiAuthzPending: + err = logical.ErrMultiAuthzPending + } + + return err +} + +func ErrToProtoErr(e error) *ProtoError { + if e == nil { + return nil + } + pbErr := &ProtoError{ + ErrMsg: e.Error(), + ErrType: ErrTypeUnknown, + } + + switch e.(type) { + case errutil.UserError: + pbErr.ErrType = ErrTypeUserError + case errutil.InternalError: + pbErr.ErrType = ErrTypeInternalError + case logical.HTTPCodedError: + pbErr.ErrType = ErrTypeCodedError + pbErr.ErrCode = int64(e.(logical.HTTPCodedError).Code()) + case *logical.StatusBadRequest: + pbErr.ErrType = ErrTypeStatusBadRequest + } + + switch { + case e == logical.ErrUnsupportedOperation: + pbErr.ErrType = ErrTypeUnsupportedOperation + case e == logical.ErrUnsupportedPath: + pbErr.ErrType = ErrTypeUnsupportedPath + case e == logical.ErrInvalidRequest: + pbErr.ErrType = ErrTypeInvalidRequest + case e == logical.ErrPermissionDenied: + pbErr.ErrType = ErrTypePermissionDenied + case e == logical.ErrMultiAuthzPending: + pbErr.ErrType = ErrTypeMultiAuthzPending + } + + return pbErr +} + +func ErrToString(e error) string { + if e == nil { + return "" + } + + return e.Error() +} + +func LogicalStorageEntryToProtoStorageEntry(e *logical.StorageEntry) *StorageEntry { + if e == nil { + return nil + } + + return &StorageEntry{ + Key: e.Key, + Value: e.Value, + SealWrap: e.SealWrap, + } +} + +func ProtoStorageEntryToLogicalStorageEntry(e *StorageEntry) *logical.StorageEntry { + if e == nil { + return nil + } + + return &logical.StorageEntry{ + Key: e.Key, + Value: e.Value, + SealWrap: e.SealWrap, + } +} + +func ProtoLeaseOptionsToLogicalLeaseOptions(l *LeaseOptions) (logical.LeaseOptions, error) { + if l == nil { + return logical.LeaseOptions{}, nil + } + + t, err := ptypes.Timestamp(l.IssueTime) + return logical.LeaseOptions{ + TTL: time.Duration(l.TTL), + Renewable: l.Renewable, + Increment: time.Duration(l.Increment), + IssueTime: t, + }, err +} + +func LogicalLeaseOptionsToProtoLeaseOptions(l logical.LeaseOptions) (*LeaseOptions, error) { + t, err := ptypes.TimestampProto(l.IssueTime) + if err != nil { + return nil, err + } + + return &LeaseOptions{ + TTL: int64(l.TTL), + Renewable: l.Renewable, + Increment: int64(l.Increment), + IssueTime: t, + }, err +} + +func ProtoSecretToLogicalSecret(s *Secret) (*logical.Secret, error) { + if s == nil { + return nil, nil + } + + data := map[string]interface{}{} + err := json.Unmarshal(s.InternalData, &data) + if err != nil { + return nil, err + } + + lease, err := ProtoLeaseOptionsToLogicalLeaseOptions(s.LeaseOptions) + if err != nil { + return nil, err + } + + return &logical.Secret{ + LeaseOptions: lease, + InternalData: data, + LeaseID: s.LeaseID, + }, nil +} + +func LogicalSecretToProtoSecret(s *logical.Secret) (*Secret, error) { + if s == nil { + return nil, nil + } + + buf, err := json.Marshal(s.InternalData) + if err != nil { + return nil, err + } + + lease, err := LogicalLeaseOptionsToProtoLeaseOptions(s.LeaseOptions) + if err != nil { + return nil, err + } + + return &Secret{ + LeaseOptions: lease, + InternalData: buf, + LeaseID: s.LeaseID, + }, err +} + +func LogicalRequestToProtoRequest(r *logical.Request) (*Request, error) { + if r == nil { + return nil, nil + } + + buf, err := json.Marshal(r.Data) + if err != nil { + return nil, err + } + + secret, err := LogicalSecretToProtoSecret(r.Secret) + if err != nil { + return nil, err + } + + auth, err := LogicalAuthToProtoAuth(r.Auth) + if err != nil { + return nil, err + } + + headers := map[string]*Header{} + for k, v := range r.Headers { + headers[k] = &Header{v} + } + + return &Request{ + ID: r.ID, + ReplicationCluster: r.ReplicationCluster, + Operation: string(r.Operation), + Path: r.Path, + Data: buf, + Secret: secret, + Auth: auth, + Headers: headers, + ClientToken: r.ClientToken, + ClientTokenAccessor: r.ClientTokenAccessor, + DisplayName: r.DisplayName, + MountPoint: r.MountPoint, + MountType: r.MountType, + MountAccessor: r.MountAccessor, + WrapInfo: LogicalRequestWrapInfoToProtoRequestWrapInfo(r.WrapInfo), + ClientTokenRemainingUses: int64(r.ClientTokenRemainingUses), + //MFACreds: MFACreds, + EntityID: r.EntityID, + PolicyOverride: r.PolicyOverride, + Unauthenticated: r.Unauthenticated, + }, nil +} + +func ProtoRequestToLogicalRequest(r *Request) (*logical.Request, error) { + if r == nil { + return nil, nil + } + + data := map[string]interface{}{} + err := json.Unmarshal(r.Data, &data) + if err != nil { + return nil, err + } + + secret, err := ProtoSecretToLogicalSecret(r.Secret) + if err != nil { + return nil, err + } + + auth, err := ProtoAuthToLogicalAuth(r.Auth) + if err != nil { + return nil, err + } + + var headers map[string][]string + if len(r.Headers) > 0 { + headers = make(map[string][]string, len(r.Headers)) + for k, v := range r.Headers { + headers[k] = v.Header + } + } + + return &logical.Request{ + ID: r.ID, + ReplicationCluster: r.ReplicationCluster, + Operation: logical.Operation(r.Operation), + Path: r.Path, + Data: data, + Secret: secret, + Auth: auth, + Headers: headers, + ClientToken: r.ClientToken, + ClientTokenAccessor: r.ClientTokenAccessor, + DisplayName: r.DisplayName, + MountPoint: r.MountPoint, + MountType: r.MountType, + MountAccessor: r.MountAccessor, + WrapInfo: ProtoRequestWrapInfoToLogicalRequestWrapInfo(r.WrapInfo), + ClientTokenRemainingUses: int(r.ClientTokenRemainingUses), + //MFACreds: MFACreds, + EntityID: r.EntityID, + PolicyOverride: r.PolicyOverride, + Unauthenticated: r.Unauthenticated, + }, nil +} + +func LogicalRequestWrapInfoToProtoRequestWrapInfo(i *logical.RequestWrapInfo) *RequestWrapInfo { + if i == nil { + return nil + } + + return &RequestWrapInfo{ + TTL: int64(i.TTL), + Format: i.Format, + SealWrap: i.SealWrap, + } +} + +func ProtoRequestWrapInfoToLogicalRequestWrapInfo(i *RequestWrapInfo) *logical.RequestWrapInfo { + if i == nil { + return nil + } + + return &logical.RequestWrapInfo{ + TTL: time.Duration(i.TTL), + Format: i.Format, + SealWrap: i.SealWrap, + } +} + +func ProtoResponseToLogicalResponse(r *Response) (*logical.Response, error) { + if r == nil { + return nil, nil + } + + secret, err := ProtoSecretToLogicalSecret(r.Secret) + if err != nil { + return nil, err + } + + auth, err := ProtoAuthToLogicalAuth(r.Auth) + if err != nil { + return nil, err + } + + data := map[string]interface{}{} + err = json.Unmarshal(r.Data, &data) + if err != nil { + return nil, err + } + + wrapInfo, err := ProtoResponseWrapInfoToLogicalResponseWrapInfo(r.WrapInfo) + if err != nil { + return nil, err + } + + return &logical.Response{ + Secret: secret, + Auth: auth, + Data: data, + Redirect: r.Redirect, + Warnings: r.Warnings, + WrapInfo: wrapInfo, + }, nil +} + +func ProtoResponseWrapInfoToLogicalResponseWrapInfo(i *ResponseWrapInfo) (*wrapping.ResponseWrapInfo, error) { + if i == nil { + return nil, nil + } + + t, err := ptypes.Timestamp(i.CreationTime) + if err != nil { + return nil, err + } + + return &wrapping.ResponseWrapInfo{ + TTL: time.Duration(i.TTL), + Token: i.Token, + Accessor: i.Accessor, + CreationTime: t, + WrappedAccessor: i.WrappedAccessor, + WrappedEntityID: i.WrappedEntityID, + Format: i.Format, + CreationPath: i.CreationPath, + SealWrap: i.SealWrap, + }, nil +} + +func LogicalResponseWrapInfoToProtoResponseWrapInfo(i *wrapping.ResponseWrapInfo) (*ResponseWrapInfo, error) { + if i == nil { + return nil, nil + } + + t, err := ptypes.TimestampProto(i.CreationTime) + if err != nil { + return nil, err + } + + return &ResponseWrapInfo{ + TTL: int64(i.TTL), + Token: i.Token, + Accessor: i.Accessor, + CreationTime: t, + WrappedAccessor: i.WrappedAccessor, + WrappedEntityID: i.WrappedEntityID, + Format: i.Format, + CreationPath: i.CreationPath, + SealWrap: i.SealWrap, + }, nil +} + +func LogicalResponseToProtoResponse(r *logical.Response) (*Response, error) { + if r == nil { + return nil, nil + } + + secret, err := LogicalSecretToProtoSecret(r.Secret) + if err != nil { + return nil, err + } + + auth, err := LogicalAuthToProtoAuth(r.Auth) + if err != nil { + return nil, err + } + + buf, err := json.Marshal(r.Data) + if err != nil { + return nil, err + } + + wrapInfo, err := LogicalResponseWrapInfoToProtoResponseWrapInfo(r.WrapInfo) + if err != nil { + return nil, err + } + + return &Response{ + Secret: secret, + Auth: auth, + Data: buf, + Redirect: r.Redirect, + Warnings: r.Warnings, + WrapInfo: wrapInfo, + }, nil +} + +func LogicalAliasToProtoAlias(a *logical.Alias) *Alias { + if a == nil { + return nil + } + + return &Alias{ + MountType: a.MountType, + MountAccessor: a.MountAccessor, + Name: a.Name, + } +} + +func ProtoAliasToLogicalAlias(a *Alias) *logical.Alias { + if a == nil { + return nil + } + + return &logical.Alias{ + MountType: a.MountType, + MountAccessor: a.MountAccessor, + Name: a.Name, + } +} + +func LogicalAuthToProtoAuth(a *logical.Auth) (*Auth, error) { + if a == nil { + return nil, nil + } + + buf, err := json.Marshal(a.InternalData) + if err != nil { + return nil, err + } + + groupAliases := make([]*Alias, len(a.GroupAliases)) + for i, al := range a.GroupAliases { + groupAliases[i] = LogicalAliasToProtoAlias(al) + } + + lo, err := LogicalLeaseOptionsToProtoLeaseOptions(a.LeaseOptions) + if err != nil { + return nil, err + } + + return &Auth{ + LeaseOptions: lo, + InternalData: buf, + DisplayName: a.DisplayName, + Policies: a.Policies, + Metadata: a.Metadata, + ClientToken: a.ClientToken, + Accessor: a.Accessor, + Period: int64(a.Period), + NumUses: int64(a.NumUses), + EntityID: a.EntityID, + Alias: LogicalAliasToProtoAlias(a.Alias), + GroupAliases: groupAliases, + }, nil +} + +func ProtoAuthToLogicalAuth(a *Auth) (*logical.Auth, error) { + if a == nil { + return nil, nil + } + + data := map[string]interface{}{} + err := json.Unmarshal(a.InternalData, &data) + if err != nil { + return nil, err + } + + groupAliases := make([]*logical.Alias, len(a.GroupAliases)) + for i, al := range a.GroupAliases { + groupAliases[i] = ProtoAliasToLogicalAlias(al) + } + + lo, err := ProtoLeaseOptionsToLogicalLeaseOptions(a.LeaseOptions) + if err != nil { + return nil, err + } + + return &logical.Auth{ + LeaseOptions: lo, + InternalData: data, + DisplayName: a.DisplayName, + Policies: a.Policies, + Metadata: a.Metadata, + ClientToken: a.ClientToken, + Accessor: a.Accessor, + Period: time.Duration(a.Period), + NumUses: int(a.NumUses), + EntityID: a.EntityID, + Alias: ProtoAliasToLogicalAlias(a.Alias), + GroupAliases: groupAliases, + }, nil +} diff --git a/logical/plugin/pb/translation_test.go b/logical/plugin/pb/translation_test.go new file mode 100644 index 0000000000..bee6553daf --- /dev/null +++ b/logical/plugin/pb/translation_test.go @@ -0,0 +1,262 @@ +package pb + +import ( + "errors" + "reflect" + "testing" + "time" + + "github.com/hashicorp/vault/helper/errutil" + "github.com/hashicorp/vault/helper/wrapping" + "github.com/hashicorp/vault/logical" +) + +func TestTranslation_Errors(t *testing.T) { + errs := []error{ + nil, + errors.New("test"), + errutil.UserError{Err: "test"}, + errutil.InternalError{Err: "test"}, + logical.CodedError(403, "test"), + &logical.StatusBadRequest{Err: "test"}, + logical.ErrUnsupportedOperation, + logical.ErrUnsupportedPath, + logical.ErrInvalidRequest, + logical.ErrPermissionDenied, + logical.ErrMultiAuthzPending, + } + + for _, err := range errs { + pe := ErrToProtoErr(err) + e := ProtoErrToErr(pe) + + if !reflect.DeepEqual(e, err) { + t.Fatal("Errs did not match: %#v, %#v", e, err) + } + } +} + +func TestTranslation_StorageEntry(t *testing.T) { + tCases := []*logical.StorageEntry{ + nil, + &logical.StorageEntry{Key: "key", Value: []byte("value")}, + &logical.StorageEntry{Key: "key1", Value: []byte("value1"), SealWrap: true}, + &logical.StorageEntry{Key: "key1", SealWrap: true}, + } + + for _, c := range tCases { + p := LogicalStorageEntryToProtoStorageEntry(c) + e := ProtoStorageEntryToLogicalStorageEntry(p) + + if !reflect.DeepEqual(c, e) { + t.Fatal("Entries did not match: %#v, %#v", e, c) + } + } +} + +func TestTranslation_Request(t *testing.T) { + tCases := []*logical.Request{ + nil, + &logical.Request{ + ID: "ID", + ReplicationCluster: "RID", + Operation: logical.CreateOperation, + Path: "test/foo", + ClientToken: "token", + ClientTokenAccessor: "accessor", + DisplayName: "display", + MountPoint: "test", + MountType: "secret", + MountAccessor: "test-231234", + ClientTokenRemainingUses: 1, + EntityID: "tester", + PolicyOverride: true, + Unauthenticated: true, + }, + &logical.Request{ + ID: "ID", + ReplicationCluster: "RID", + Operation: logical.CreateOperation, + Path: "test/foo", + Data: map[string]interface{}{ + "string": "string", + "bool": true, + "array": []interface{}{"1", "2"}, + "map": map[string]interface{}{ + "key": "value", + }, + }, + Secret: &logical.Secret{ + LeaseOptions: logical.LeaseOptions{ + TTL: time.Second, + Renewable: true, + Increment: time.Second, + IssueTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), + }, + InternalData: map[string]interface{}{ + "role": "test", + }, + LeaseID: "LeaseID", + }, + Auth: &logical.Auth{ + LeaseOptions: logical.LeaseOptions{ + TTL: time.Second, + Renewable: true, + Increment: time.Second, + IssueTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), + }, + InternalData: map[string]interface{}{ + "role": "test", + }, + DisplayName: "test", + Policies: []string{"test", "Test"}, + Metadata: map[string]string{ + "test": "test", + }, + ClientToken: "token", + Accessor: "accessor", + Period: 5 * time.Second, + NumUses: 1, + EntityID: "id", + Alias: &logical.Alias{ + MountType: "type", + MountAccessor: "accessor", + Name: "name", + }, + GroupAliases: []*logical.Alias{ + &logical.Alias{ + MountType: "type", + MountAccessor: "accessor", + Name: "name", + }, + }, + }, + Headers: map[string][]string{ + "X-Vault-Test": []string{"test"}, + }, + ClientToken: "token", + ClientTokenAccessor: "accessor", + DisplayName: "display", + MountPoint: "test", + MountType: "secret", + MountAccessor: "test-231234", + WrapInfo: &logical.RequestWrapInfo{ + TTL: time.Second, + Format: "token", + SealWrap: true, + }, + ClientTokenRemainingUses: 1, + EntityID: "tester", + PolicyOverride: true, + Unauthenticated: true, + }, + } + + for _, c := range tCases { + p, err := LogicalRequestToProtoRequest(c) + if err != nil { + t.Fatal(err) + } + r, err := ProtoRequestToLogicalRequest(p) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(c, r) { + t.Fatalf("Requests did not match: \n%#v, \n%#v", c, r) + } + } +} + +func TestTranslation_Response(t *testing.T) { + tCases := []*logical.Response{ + nil, + &logical.Response{ + Data: map[string]interface{}{ + "data": "blah", + }, + Warnings: []string{"warning"}, + }, + &logical.Response{ + Data: map[string]interface{}{ + "string": "string", + "bool": true, + "array": []interface{}{"1", "2"}, + "map": map[string]interface{}{ + "key": "value", + }, + }, + Secret: &logical.Secret{ + LeaseOptions: logical.LeaseOptions{ + TTL: time.Second, + Renewable: true, + Increment: time.Second, + IssueTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), + }, + InternalData: map[string]interface{}{ + "role": "test", + }, + LeaseID: "LeaseID", + }, + Auth: &logical.Auth{ + LeaseOptions: logical.LeaseOptions{ + TTL: time.Second, + Renewable: true, + Increment: time.Second, + IssueTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), + }, + InternalData: map[string]interface{}{ + "role": "test", + }, + DisplayName: "test", + Policies: []string{"test", "Test"}, + Metadata: map[string]string{ + "test": "test", + }, + ClientToken: "token", + Accessor: "accessor", + Period: 5 * time.Second, + NumUses: 1, + EntityID: "id", + Alias: &logical.Alias{ + MountType: "type", + MountAccessor: "accessor", + Name: "name", + }, + GroupAliases: []*logical.Alias{ + &logical.Alias{ + MountType: "type", + MountAccessor: "accessor", + Name: "name", + }, + }, + }, + WrapInfo: &wrapping.ResponseWrapInfo{ + TTL: time.Second, + Token: "token", + Accessor: "accessor", + CreationTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), + WrappedAccessor: "wrapped-accessor", + WrappedEntityID: "id", + Format: "token", + CreationPath: "test/foo", + SealWrap: true, + }, + }, + } + + for _, c := range tCases { + p, err := LogicalResponseToProtoResponse(c) + if err != nil { + t.Fatal(err) + } + r, err := ProtoResponseToLogicalResponse(p) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(c, r) { + t.Fatalf("Requests did not match: \n%#v, \n%#v", c, r) + } + } +} diff --git a/logical/plugin/plugin.go b/logical/plugin/plugin.go index ef8f6d9ded..2a51356165 100644 --- a/logical/plugin/plugin.go +++ b/logical/plugin/plugin.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "crypto/rsa" "encoding/gob" + "errors" "fmt" "time" @@ -44,13 +45,13 @@ type BackendPluginClient struct { client *plugin.Client sync.Mutex - *backendPluginClient + logical.Backend } // Cleanup calls the RPC client's Cleanup() func and also calls // the go-plugin's client Kill() func func (b *BackendPluginClient) Cleanup() { - b.backendPluginClient.Cleanup() + b.Backend.Cleanup() b.client.Kill() } @@ -122,13 +123,34 @@ func newPluginClient(sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginR return nil, err } + var backend logical.Backend + var transport string // We should have a logical backend type now. This feels like a normal interface // implementation but is in fact over an RPC connection. - backendRPC := raw.(*backendPluginClient) + switch raw.(type) { + case *backendPluginClient: + backend = raw.(*backendPluginClient) + transport = "netRPC" + case *backendGRPCPluginClient: + backend = raw.(*backendGRPCPluginClient) + transport = "gRPC" + default: + return nil, errors.New("Unsupported plugin client type") + } + + // Wrap the backend in a tracing middleware + if logger.IsTrace() { + backend = &backendTracingMiddleware{ + logger: logger, + transport: transport, + typeStr: pluginRunner.Name, + next: backend, + } + } return &BackendPluginClient{ - client: client, - backendPluginClient: backendRPC, + client: client, + Backend: backend, }, nil } diff --git a/logical/plugin/serve.go b/logical/plugin/serve.go index 279f6d8ac1..550e6c6051 100644 --- a/logical/plugin/serve.go +++ b/logical/plugin/serve.go @@ -2,7 +2,9 @@ package plugin import ( "crypto/tls" + "os" + hclog "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" "github.com/hashicorp/vault/helper/pluginutil" "github.com/hashicorp/vault/logical" @@ -18,15 +20,26 @@ type TLSProdiverFunc func() (*tls.Config, error) type ServeOpts struct { BackendFactoryFunc BackendFactoryFunc TLSProviderFunc TLSProdiverFunc + Logger hclog.Logger } // Serve is a helper function used to serve a backend plugin. This // should be ran on the plugin's main process. func Serve(opts *ServeOpts) error { + logger := opts.Logger + if logger == nil { + logger = hclog.New(&hclog.LoggerOptions{ + Level: hclog.Trace, + Output: os.Stderr, + JSONFormat: true, + }) + } + // pluginMap is the map of plugins we can dispense. var pluginMap = map[string]plugin.Plugin{ "backend": &BackendPlugin{ Factory: opts.BackendFactoryFunc, + Logger: logger, }, } @@ -40,6 +53,10 @@ func Serve(opts *ServeOpts) error { HandshakeConfig: handshakeConfig, Plugins: pluginMap, TLSProvider: opts.TLSProviderFunc, + Logger: logger, + + // A non-nil value here enables gRPC serving for this plugin... + GRPCServer: plugin.DefaultGRPCServer, }) return nil diff --git a/logical/plugin/storage_test.go b/logical/plugin/storage_test.go index 9899a82be2..87653463e5 100644 --- a/logical/plugin/storage_test.go +++ b/logical/plugin/storage_test.go @@ -3,15 +3,18 @@ package plugin import ( "testing" + "google.golang.org/grpc" + plugin "github.com/hashicorp/go-plugin" "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/logical/plugin/pb" ) func TestStorage_impl(t *testing.T) { var _ logical.Storage = new(StorageClient) } -func TestStorage_operations(t *testing.T) { +func TestStorage_RPC(t *testing.T) { client, server := plugin.TestRPCConn(t) defer client.Close() @@ -25,3 +28,18 @@ func TestStorage_operations(t *testing.T) { logical.TestStorage(t, testStorage) } + +func TestStorage_GRPC(t *testing.T) { + storage := &logical.InmemStorage{} + client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { + pb.RegisterStorageServer(s, &GRPCStorageServer{ + impl: storage, + }) + }) + defer client.Close() + + testStorage := &GRPCStorageClient{client: pb.NewStorageClient(client)} + + logical.TestStorage(t, testStorage) + +} diff --git a/vendor/github.com/hashicorp/go-plugin/client.go b/vendor/github.com/hashicorp/go-plugin/client.go index c3cbc45e70..9509d78b18 100644 --- a/vendor/github.com/hashicorp/go-plugin/client.go +++ b/vendor/github.com/hashicorp/go-plugin/client.go @@ -2,6 +2,7 @@ package plugin import ( "bufio" + "context" "crypto/subtle" "crypto/tls" "errors" @@ -79,6 +80,7 @@ type Client struct { client ClientProtocol protocol Protocol logger hclog.Logger + doneCtx context.Context } // ClientConfig is the configuration used to initialize a new @@ -310,7 +312,7 @@ func (c *Client) Client() (ClientProtocol, error) { c.client, err = newRPCClient(c) case ProtocolGRPC: - c.client, err = newGRPCClient(c) + c.client, err = newGRPCClient(c.doneCtx, c) default: return nil, fmt.Errorf("unknown server protocol: %s", c.protocol) @@ -423,6 +425,9 @@ func (c *Client) Start() (addr net.Addr, err error) { // Create the logging channel for when we kill c.doneLogging = make(chan struct{}) + // Create a context for when we kill + var ctxCancel context.CancelFunc + c.doneCtx, ctxCancel = context.WithCancel(context.Background()) if c.config.Reattach != nil { // Verify the process still exists. If not, then it is an error @@ -457,6 +462,8 @@ func (c *Client) Start() (addr net.Addr, err error) { // Close the logging channel since that doesn't work on reattach close(c.doneLogging) + // Cancel the context + ctxCancel() }(p.Pid) // Set the address and process @@ -534,6 +541,8 @@ func (c *Client) Start() (addr net.Addr, err error) { // Mark that we exited close(exitCh) + // Cancel the context, marking that we exited + ctxCancel() // Set that we exited, which takes a lock c.l.Lock() @@ -707,18 +716,29 @@ func (c *Client) Protocol() Protocol { return c.protocol } +func netAddrDialer(addr net.Addr) func(string, time.Duration) (net.Conn, error) { + return func(_ string, _ time.Duration) (net.Conn, error) { + // Connect to the client + conn, err := net.Dial(addr.Network(), addr.String()) + if err != nil { + return nil, err + } + if tcpConn, ok := conn.(*net.TCPConn); ok { + // Make sure to set keep alive so that the connection doesn't die + tcpConn.SetKeepAlive(true) + } + + return conn, nil + } +} + // dialer is compatible with grpc.WithDialer and creates the connection // to the plugin. func (c *Client) dialer(_ string, timeout time.Duration) (net.Conn, error) { - // Connect to the client - conn, err := net.Dial(c.address.Network(), c.address.String()) + conn, err := netAddrDialer(c.address)("", timeout) if err != nil { return nil, err } - if tcpConn, ok := conn.(*net.TCPConn); ok { - // Make sure to set keep alive so that the connection doesn't die - tcpConn.SetKeepAlive(true) - } // If we have a TLS config we wrap our connection. We only do this // for net/rpc since gRPC uses its own mechanism for TLS. diff --git a/vendor/github.com/hashicorp/go-plugin/grpc_broker.go b/vendor/github.com/hashicorp/go-plugin/grpc_broker.go new file mode 100644 index 0000000000..49fd21c618 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/grpc_broker.go @@ -0,0 +1,455 @@ +package plugin + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "log" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/oklog/run" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +// streamer interface is used in the broker to send/receive connection +// information. +type streamer interface { + Send(*ConnInfo) error + Recv() (*ConnInfo, error) + Close() +} + +// sendErr is used to pass errors back during a send. +type sendErr struct { + i *ConnInfo + ch chan error +} + +// gRPCBrokerServer is used by the plugin to start a stream and to send +// connection information to/from the plugin. Implements GRPCBrokerServer and +// streamer interfaces. +type gRPCBrokerServer struct { + // send is used to send connection info to the gRPC stream. + send chan *sendErr + + // recv is used to receive connection info from the gRPC stream. + recv chan *ConnInfo + + // quit closes down the stream. + quit chan struct{} + + // o is used to ensure we close the quit channel only once. + o sync.Once +} + +func newGRPCBrokerServer() *gRPCBrokerServer { + return &gRPCBrokerServer{ + send: make(chan *sendErr), + recv: make(chan *ConnInfo), + quit: make(chan struct{}), + } +} + +// StartStream implements the GRPCBrokerServer interface and will block until +// the quit channel is closed or the context reports Done. The stream will pass +// connection information to/from the client. +func (s *gRPCBrokerServer) StartStream(stream GRPCBroker_StartStreamServer) error { + doneCh := stream.Context().Done() + defer s.Close() + + // Proccess send stream + go func() { + for { + select { + case <-doneCh: + return + case <-s.quit: + return + case se := <-s.send: + err := stream.Send(se.i) + se.ch <- err + } + } + }() + + // Process receive stream + for { + i, err := stream.Recv() + if err != nil { + return err + } + select { + case <-doneCh: + return nil + case <-s.quit: + return nil + case s.recv <- i: + } + } + + return nil +} + +// Send is used by the GRPCBroker to pass connection information into the stream +// to the client. +func (s *gRPCBrokerServer) Send(i *ConnInfo) error { + ch := make(chan error) + defer close(ch) + + select { + case <-s.quit: + return errors.New("broker closed") + case s.send <- &sendErr{ + i: i, + ch: ch, + }: + } + + return <-ch +} + +// Recv is used by the GRPCBroker to pass connection information that has been +// sent from the client from the stream to the broker. +func (s *gRPCBrokerServer) Recv() (*ConnInfo, error) { + select { + case <-s.quit: + return nil, errors.New("broker closed") + case i := <-s.recv: + return i, nil + } +} + +// Close closes the quit channel, shutting down the stream. +func (s *gRPCBrokerServer) Close() { + s.o.Do(func() { + close(s.quit) + }) +} + +// gRPCBrokerClientImpl is used by the client to start a stream and to send +// connection information to/from the client. Implements GRPCBrokerClient and +// streamer interfaces. +type gRPCBrokerClientImpl struct { + // client is the underlying GRPC client used to make calls to the server. + client GRPCBrokerClient + + // send is used to send connection info to the gRPC stream. + send chan *sendErr + + // recv is used to receive connection info from the gRPC stream. + recv chan *ConnInfo + + // quit closes down the stream. + quit chan struct{} + + // o is used to ensure we close the quit channel only once. + o sync.Once +} + +func newGRPCBrokerClient(conn *grpc.ClientConn) *gRPCBrokerClientImpl { + return &gRPCBrokerClientImpl{ + client: NewGRPCBrokerClient(conn), + send: make(chan *sendErr), + recv: make(chan *ConnInfo), + quit: make(chan struct{}), + } +} + +// StartStream implements the GRPCBrokerClient interface and will block until +// the quit channel is closed or the context reports Done. The stream will pass +// connection information to/from the plugin. +func (s *gRPCBrokerClientImpl) StartStream() error { + ctx, cancelFunc := context.WithCancel(context.Background()) + defer cancelFunc() + defer s.Close() + + stream, err := s.client.StartStream(ctx) + if err != nil { + return err + } + doneCh := stream.Context().Done() + + go func() { + for { + select { + case <-doneCh: + return + case <-s.quit: + return + case se := <-s.send: + err := stream.Send(se.i) + se.ch <- err + } + } + }() + + for { + i, err := stream.Recv() + if err != nil { + return err + } + select { + case <-doneCh: + return nil + case <-s.quit: + return nil + case s.recv <- i: + } + } + + return nil +} + +// Send is used by the GRPCBroker to pass connection information into the stream +// to the plugin. +func (s *gRPCBrokerClientImpl) Send(i *ConnInfo) error { + ch := make(chan error) + defer close(ch) + + select { + case <-s.quit: + return errors.New("broker closed") + case s.send <- &sendErr{ + i: i, + ch: ch, + }: + } + + return <-ch +} + +// Recv is used by the GRPCBroker to pass connection information that has been +// sent from the plugin to the broker. +func (s *gRPCBrokerClientImpl) Recv() (*ConnInfo, error) { + select { + case <-s.quit: + return nil, errors.New("broker closed") + case i := <-s.recv: + return i, nil + } +} + +// Close closes the quit channel, shutting down the stream. +func (s *gRPCBrokerClientImpl) Close() { + s.o.Do(func() { + close(s.quit) + }) +} + +// GRPCBroker is responsible for brokering connections by unique ID. +// +// It is used by plugins to create multiple gRPC connections and data +// streams between the plugin process and the host process. +// +// This allows a plugin to request a channel with a specific ID to connect to +// or accept a connection from, and the broker handles the details of +// holding these channels open while they're being negotiated. +// +// The Plugin interface has access to these for both Server and Client. +// The broker can be used by either (optionally) to reserve and connect to +// new streams. This is useful for complex args and return values, +// or anything else you might need a data stream for. +type GRPCBroker struct { + nextId uint32 + streamer streamer + streams map[uint32]*gRPCBrokerPending + tls *tls.Config + doneCh chan struct{} + o sync.Once + + sync.Mutex +} + +type gRPCBrokerPending struct { + ch chan *ConnInfo + doneCh chan struct{} +} + +func newGRPCBroker(s streamer, tls *tls.Config) *GRPCBroker { + return &GRPCBroker{ + streamer: s, + streams: make(map[uint32]*gRPCBrokerPending), + tls: tls, + doneCh: make(chan struct{}), + } +} + +// Accept accepts a connection by ID. +// +// This should not be called multiple times with the same ID at one time. +func (b *GRPCBroker) Accept(id uint32) (net.Listener, error) { + listener, err := serverListener() + if err != nil { + return nil, err + } + + err = b.streamer.Send(&ConnInfo{ + ServiceId: id, + Network: listener.Addr().Network(), + Address: listener.Addr().String(), + }) + if err != nil { + return nil, err + } + + return listener, nil +} + +// AcceptAndServe is used to accept a specific stream ID and immediately +// serve a gRPC server on that stream ID. This is used to easily serve +// complex arguments. Each AcceptAndServe call opens a new listener socket and +// sends the connection info down the stream to the dialer. Since a new +// connection is opened every call, these calls should be used sparingly. +// Multiple gRPC server implementations can be registered to a single +// AcceptAndServe call. +func (b *GRPCBroker) AcceptAndServe(id uint32, s func([]grpc.ServerOption) *grpc.Server) { + listener, err := b.Accept(id) + if err != nil { + log.Printf("[ERR] plugin: plugin acceptAndServe error: %s", err) + return + } + defer listener.Close() + + var opts []grpc.ServerOption + if b.tls != nil { + opts = []grpc.ServerOption{grpc.Creds(credentials.NewTLS(b.tls))} + } + + server := s(opts) + + // Here we use a run group to close this goroutine if the server is shutdown + // or the broker is shutdown. + var g run.Group + { + // Serve on the listener, if shutting down call GracefulStop. + g.Add(func() error { + return server.Serve(listener) + }, func(err error) { + server.GracefulStop() + }) + } + { + // block on the closeCh or the doneCh. If we are shutting down close the + // closeCh. + closeCh := make(chan struct{}) + g.Add(func() error { + select { + case <-b.doneCh: + case <-closeCh: + } + return nil + }, func(err error) { + close(closeCh) + }) + } + + // Block until we are done + g.Run() +} + +// Close closes the stream and all servers. +func (b *GRPCBroker) Close() error { + b.streamer.Close() + b.o.Do(func() { + close(b.doneCh) + }) + return nil +} + +// Dial opens a connection by ID. +func (b *GRPCBroker) Dial(id uint32) (conn *grpc.ClientConn, err error) { + var c *ConnInfo + + // Open the stream + p := b.getStream(id) + select { + case c = <-p.ch: + close(p.doneCh) + case <-time.After(5 * time.Second): + return nil, fmt.Errorf("timeout waiting for connection info") + } + + var addr net.Addr + switch c.Network { + case "tcp": + addr, err = net.ResolveTCPAddr("tcp", c.Address) + case "unix": + addr, err = net.ResolveUnixAddr("unix", c.Address) + default: + err = fmt.Errorf("Unknown address type: %s", c.Address) + } + if err != nil { + return nil, err + } + + return dialGRPCConn(b.tls, netAddrDialer(addr)) +} + +// NextId returns a unique ID to use next. +// +// It is possible for very long-running plugin hosts to wrap this value, +// though it would require a very large amount of calls. In practice +// we've never seen it happen. +func (m *GRPCBroker) NextId() uint32 { + return atomic.AddUint32(&m.nextId, 1) +} + +// Run starts the brokering and should be executed in a goroutine, since it +// blocks forever, or until the session closes. +// +// Uses of GRPCBroker never need to call this. It is called internally by +// the plugin host/client. +func (m *GRPCBroker) Run() { + for { + stream, err := m.streamer.Recv() + if err != nil { + // Once we receive an error, just exit + break + } + + // Initialize the waiter + p := m.getStream(stream.ServiceId) + select { + case p.ch <- stream: + default: + } + + go m.timeoutWait(stream.ServiceId, p) + } +} + +func (m *GRPCBroker) getStream(id uint32) *gRPCBrokerPending { + m.Lock() + defer m.Unlock() + + p, ok := m.streams[id] + if ok { + return p + } + + m.streams[id] = &gRPCBrokerPending{ + ch: make(chan *ConnInfo, 1), + doneCh: make(chan struct{}), + } + return m.streams[id] +} + +func (m *GRPCBroker) timeoutWait(id uint32, p *gRPCBrokerPending) { + // Wait for the stream to either be picked up and connected, or + // for a timeout. + select { + case <-p.doneCh: + case <-time.After(5 * time.Second): + } + + m.Lock() + defer m.Unlock() + + // Delete the stream so no one else can grab it + delete(m.streams, id) +} diff --git a/vendor/github.com/hashicorp/go-plugin/grpc_broker.pb.go b/vendor/github.com/hashicorp/go-plugin/grpc_broker.pb.go new file mode 100644 index 0000000000..d490dafbaa --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/grpc_broker.pb.go @@ -0,0 +1,190 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: grpc_broker.proto + +/* +Package plugin is a generated protocol buffer package. + +It is generated from these files: + grpc_broker.proto + +It has these top-level messages: + ConnInfo +*/ +package plugin + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type ConnInfo struct { + ServiceId uint32 `protobuf:"varint,1,opt,name=service_id,json=serviceId" json:"service_id,omitempty"` + Network string `protobuf:"bytes,2,opt,name=network" json:"network,omitempty"` + Address string `protobuf:"bytes,3,opt,name=address" json:"address,omitempty"` +} + +func (m *ConnInfo) Reset() { *m = ConnInfo{} } +func (m *ConnInfo) String() string { return proto.CompactTextString(m) } +func (*ConnInfo) ProtoMessage() {} +func (*ConnInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *ConnInfo) GetServiceId() uint32 { + if m != nil { + return m.ServiceId + } + return 0 +} + +func (m *ConnInfo) GetNetwork() string { + if m != nil { + return m.Network + } + return "" +} + +func (m *ConnInfo) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func init() { + proto.RegisterType((*ConnInfo)(nil), "plugin.ConnInfo") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for GRPCBroker service + +type GRPCBrokerClient interface { + StartStream(ctx context.Context, opts ...grpc.CallOption) (GRPCBroker_StartStreamClient, error) +} + +type gRPCBrokerClient struct { + cc *grpc.ClientConn +} + +func NewGRPCBrokerClient(cc *grpc.ClientConn) GRPCBrokerClient { + return &gRPCBrokerClient{cc} +} + +func (c *gRPCBrokerClient) StartStream(ctx context.Context, opts ...grpc.CallOption) (GRPCBroker_StartStreamClient, error) { + stream, err := grpc.NewClientStream(ctx, &_GRPCBroker_serviceDesc.Streams[0], c.cc, "/plugin.GRPCBroker/StartStream", opts...) + if err != nil { + return nil, err + } + x := &gRPCBrokerStartStreamClient{stream} + return x, nil +} + +type GRPCBroker_StartStreamClient interface { + Send(*ConnInfo) error + Recv() (*ConnInfo, error) + grpc.ClientStream +} + +type gRPCBrokerStartStreamClient struct { + grpc.ClientStream +} + +func (x *gRPCBrokerStartStreamClient) Send(m *ConnInfo) error { + return x.ClientStream.SendMsg(m) +} + +func (x *gRPCBrokerStartStreamClient) Recv() (*ConnInfo, error) { + m := new(ConnInfo) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// Server API for GRPCBroker service + +type GRPCBrokerServer interface { + StartStream(GRPCBroker_StartStreamServer) error +} + +func RegisterGRPCBrokerServer(s *grpc.Server, srv GRPCBrokerServer) { + s.RegisterService(&_GRPCBroker_serviceDesc, srv) +} + +func _GRPCBroker_StartStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(GRPCBrokerServer).StartStream(&gRPCBrokerStartStreamServer{stream}) +} + +type GRPCBroker_StartStreamServer interface { + Send(*ConnInfo) error + Recv() (*ConnInfo, error) + grpc.ServerStream +} + +type gRPCBrokerStartStreamServer struct { + grpc.ServerStream +} + +func (x *gRPCBrokerStartStreamServer) Send(m *ConnInfo) error { + return x.ServerStream.SendMsg(m) +} + +func (x *gRPCBrokerStartStreamServer) Recv() (*ConnInfo, error) { + m := new(ConnInfo) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _GRPCBroker_serviceDesc = grpc.ServiceDesc{ + ServiceName: "plugin.GRPCBroker", + HandlerType: (*GRPCBrokerServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "StartStream", + Handler: _GRPCBroker_StartStream_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "grpc_broker.proto", +} + +func init() { proto.RegisterFile("grpc_broker.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 170 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4c, 0x2f, 0x2a, 0x48, + 0x8e, 0x4f, 0x2a, 0xca, 0xcf, 0x4e, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2b, + 0xc8, 0x29, 0x4d, 0xcf, 0xcc, 0x53, 0x8a, 0xe5, 0xe2, 0x70, 0xce, 0xcf, 0xcb, 0xf3, 0xcc, 0x4b, + 0xcb, 0x17, 0x92, 0xe5, 0xe2, 0x2a, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x8d, 0xcf, 0x4c, 0x91, + 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0d, 0xe2, 0x84, 0x8a, 0x78, 0xa6, 0x08, 0x49, 0x70, 0xb1, 0xe7, + 0xa5, 0x96, 0x94, 0xe7, 0x17, 0x65, 0x4b, 0x30, 0x29, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x20, + 0x99, 0xc4, 0x94, 0x94, 0xa2, 0xd4, 0xe2, 0x62, 0x09, 0x66, 0x88, 0x0c, 0x94, 0x6b, 0xe4, 0xcc, + 0xc5, 0xe5, 0x1e, 0x14, 0xe0, 0xec, 0x04, 0xb6, 0x5a, 0xc8, 0x94, 0x8b, 0x3b, 0xb8, 0x24, 0xb1, + 0xa8, 0x24, 0xb8, 0xa4, 0x28, 0x35, 0x31, 0x57, 0x48, 0x40, 0x0f, 0xe2, 0x08, 0x3d, 0x98, 0x0b, + 0xa4, 0x30, 0x44, 0x34, 0x18, 0x0d, 0x18, 0x93, 0xd8, 0xc0, 0x4e, 0x36, 0x06, 0x04, 0x00, 0x00, + 0xff, 0xff, 0x7b, 0x5d, 0xfb, 0xe1, 0xc7, 0x00, 0x00, 0x00, +} diff --git a/vendor/github.com/hashicorp/go-plugin/grpc_broker.proto b/vendor/github.com/hashicorp/go-plugin/grpc_broker.proto new file mode 100644 index 0000000000..f578348566 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/grpc_broker.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; +package plugin; + +message ConnInfo { + uint32 service_id = 1; + string network = 2; + string address = 3; +} + +service GRPCBroker { + rpc StartStream(stream ConnInfo) returns (stream ConnInfo); +} + + diff --git a/vendor/github.com/hashicorp/go-plugin/grpc_client.go b/vendor/github.com/hashicorp/go-plugin/grpc_client.go index 3bcf95efc5..44294d0d3a 100644 --- a/vendor/github.com/hashicorp/go-plugin/grpc_client.go +++ b/vendor/github.com/hashicorp/go-plugin/grpc_client.go @@ -1,7 +1,10 @@ package plugin import ( + "crypto/tls" "fmt" + "net" + "time" "golang.org/x/net/context" "google.golang.org/grpc" @@ -9,14 +12,12 @@ import ( "google.golang.org/grpc/health/grpc_health_v1" ) -// newGRPCClient creates a new GRPCClient. The Client argument is expected -// to be successfully started already with a lock held. -func newGRPCClient(c *Client) (*GRPCClient, error) { +func dialGRPCConn(tls *tls.Config, dialer func(string, time.Duration) (net.Conn, error)) (*grpc.ClientConn, error) { // Build dialing options. opts := make([]grpc.DialOption, 0, 5) // We use a custom dialer so that we can connect over unix domain sockets - opts = append(opts, grpc.WithDialer(c.dialer)) + opts = append(opts, grpc.WithDialer(dialer)) // go-plugin expects to block the connection opts = append(opts, grpc.WithBlock()) @@ -26,11 +27,11 @@ func newGRPCClient(c *Client) (*GRPCClient, error) { // If we have no TLS configuration set, we need to explicitly tell grpc // that we're connecting with an insecure connection. - if c.config.TLSConfig == nil { + if tls == nil { opts = append(opts, grpc.WithInsecure()) } else { opts = append(opts, grpc.WithTransportCredentials( - credentials.NewTLS(c.config.TLSConfig))) + credentials.NewTLS(tls))) } // Connect. Note the first parameter is unused because we use a custom @@ -40,9 +41,28 @@ func newGRPCClient(c *Client) (*GRPCClient, error) { return nil, err } + return conn, nil +} + +// newGRPCClient creates a new GRPCClient. The Client argument is expected +// to be successfully started already with a lock held. +func newGRPCClient(doneCtx context.Context, c *Client) (*GRPCClient, error) { + conn, err := dialGRPCConn(c.config.TLSConfig, c.dialer) + if err != nil { + return nil, err + } + + // Start the broker. + brokerGRPCClient := newGRPCBrokerClient(conn) + broker := newGRPCBroker(brokerGRPCClient, c.config.TLSConfig) + go broker.Run() + go brokerGRPCClient.StartStream() + return &GRPCClient{ Conn: conn, Plugins: c.config.Plugins, + doneCtx: doneCtx, + broker: broker, }, nil } @@ -50,10 +70,14 @@ func newGRPCClient(c *Client) (*GRPCClient, error) { type GRPCClient struct { Conn *grpc.ClientConn Plugins map[string]Plugin + + doneCtx context.Context + broker *GRPCBroker } // ClientProtocol impl. func (c *GRPCClient) Close() error { + c.broker.Close() return c.Conn.Close() } @@ -69,7 +93,7 @@ func (c *GRPCClient) Dispense(name string) (interface{}, error) { return nil, fmt.Errorf("plugin %q doesn't support gRPC", name) } - return p.GRPCClient(c.Conn) + return p.GRPCClient(c.doneCtx, c.broker, c.Conn) } // ClientProtocol impl. diff --git a/vendor/github.com/hashicorp/go-plugin/grpc_server.go b/vendor/github.com/hashicorp/go-plugin/grpc_server.go index 177a0cdd7d..9ec751e63d 100644 --- a/vendor/github.com/hashicorp/go-plugin/grpc_server.go +++ b/vendor/github.com/hashicorp/go-plugin/grpc_server.go @@ -51,6 +51,7 @@ type GRPCServer struct { config GRPCServerConfig server *grpc.Server + broker *GRPCBroker } // ServerProtocol impl. @@ -68,6 +69,12 @@ func (s *GRPCServer) Init() error { GRPCServiceName, grpc_health_v1.HealthCheckResponse_SERVING) grpc_health_v1.RegisterHealthServer(s.server, healthCheck) + // Register the broker service + brokerServer := newGRPCBrokerServer() + RegisterGRPCBrokerServer(s.server, brokerServer) + s.broker = newGRPCBroker(brokerServer, s.TLS) + go s.broker.Run() + // Register all our plugins onto the gRPC server. for k, raw := range s.Plugins { p, ok := raw.(GRPCPlugin) @@ -75,7 +82,7 @@ func (s *GRPCServer) Init() error { return fmt.Errorf("%q is not a GRPC-compatibile plugin", k) } - if err := p.GRPCServer(s.server); err != nil { + if err := p.GRPCServer(s.broker, s.server); err != nil { return fmt.Errorf("error registring %q: %s", k, err) } } diff --git a/vendor/github.com/hashicorp/go-plugin/plugin.go b/vendor/github.com/hashicorp/go-plugin/plugin.go index 6b7bdd1cfd..79d9674633 100644 --- a/vendor/github.com/hashicorp/go-plugin/plugin.go +++ b/vendor/github.com/hashicorp/go-plugin/plugin.go @@ -9,6 +9,7 @@ package plugin import ( + "context" "errors" "net/rpc" @@ -33,11 +34,12 @@ type GRPCPlugin interface { // GRPCServer should register this plugin for serving with the // given GRPCServer. Unlike Plugin.Server, this is only called once // since gRPC plugins serve singletons. - GRPCServer(*grpc.Server) error + GRPCServer(*GRPCBroker, *grpc.Server) error // GRPCClient should return the interface implementation for the plugin - // you're serving via gRPC. - GRPCClient(*grpc.ClientConn) (interface{}, error) + // you're serving via gRPC. The provided context will be canceled by + // go-plugin in the event of the plugin process exiting. + GRPCClient(context.Context, *GRPCBroker, *grpc.ClientConn) (interface{}, error) } // NetRPCUnsupportedPlugin implements Plugin but returns errors for the diff --git a/vendor/github.com/hashicorp/go-plugin/testing.go b/vendor/github.com/hashicorp/go-plugin/testing.go index c6bf7c4ed3..91a0ee33c0 100644 --- a/vendor/github.com/hashicorp/go-plugin/testing.go +++ b/vendor/github.com/hashicorp/go-plugin/testing.go @@ -2,6 +2,7 @@ package plugin import ( "bytes" + "context" "net" "net/rpc" @@ -110,10 +111,16 @@ func TestPluginGRPCConn(t testing.T, ps map[string]Plugin) (*GRPCClient, *GRPCSe // Connection successful, close the listener l.Close() + brokerGRPCClient := newGRPCBrokerClient(conn) + broker := newGRPCBroker(brokerGRPCClient, nil) + go broker.Run() + go brokerGRPCClient.StartStream() // Create the client client := &GRPCClient{ Conn: conn, Plugins: ps, + broker: broker, + doneCtx: context.Background(), } return client, server diff --git a/vendor/github.com/oklog/run/LICENSE b/vendor/github.com/oklog/run/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/github.com/oklog/run/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/oklog/run/README.md b/vendor/github.com/oklog/run/README.md new file mode 100644 index 0000000000..a7228cd9a3 --- /dev/null +++ b/vendor/github.com/oklog/run/README.md @@ -0,0 +1,73 @@ +# run + +[![GoDoc](https://godoc.org/github.com/oklog/run?status.svg)](https://godoc.org/github.com/oklog/run) +[![Build Status](https://travis-ci.org/oklog/run.svg?branch=master)](https://travis-ci.org/oklog/run) +[![Go Report Card](https://goreportcard.com/badge/github.com/oklog/run)](https://goreportcard.com/report/github.com/oklog/run) +[![Apache 2 licensed](https://img.shields.io/badge/license-Apache2-blue.svg)](https://raw.githubusercontent.com/oklog/run/master/LICENSE) + +run.Group is a universal mechanism to manage goroutine lifecycles. + +Create a zero-value run.Group, and then add actors to it. Actors are defined as +a pair of functions: an **execute** function, which should run synchronously; +and an **interrupt** function, which, when invoked, should cause the execute +function to return. Finally, invoke Run, which blocks until the first actor +returns. This general-purpose API allows callers to model pretty much any +runnable task, and achieve well-defined lifecycle semantics for the group. + +run.Group was written to manage component lifecycles in func main for +[OK Log](https://github.com/oklog/oklog). +But it's useful in any circumstance where you need to orchestrate multiple +goroutines as a unit whole. +[Click here](https://www.youtube.com/watch?v=LHe1Cb_Ud_M&t=15m45s) to see a +video of a talk where run.Group is described. + +## Examples + +### context.Context + +```go +ctx, cancel := context.WithCancel(context.Background()) +g.Add(func() error { + return myProcess(ctx, ...) +}, func(error) { + cancel() +}) +``` + +### net.Listener + +```go +ln, _ := net.Listen("tcp", ":8080") +g.Add(func() error { + return http.Serve(ln, nil) +}, func(error) { + ln.Close() +}) +``` + +### io.ReadCloser + +```go +var conn io.ReadCloser = ... +g.Add(func() error { + s := bufio.NewScanner(conn) + for s.Scan() { + println(s.Text()) + } + return s.Err() +}, func(error) { + conn.Close() +}) +``` + +## Comparisons + +Package run is somewhat similar to package +[errgroup](https://godoc.org/golang.org/x/sync/errgroup), +except it doesn't require actor goroutines to understand context semantics. + +It's somewhat similar to package +[tomb.v1](https://godoc.org/gopkg.in/tomb.v1) or +[tomb.v2](https://godoc.org/gopkg.in/tomb.v2), +except it has a much smaller API surface, delegating e.g. staged shutdown of +goroutines to the caller. diff --git a/vendor/github.com/oklog/run/group.go b/vendor/github.com/oklog/run/group.go new file mode 100644 index 0000000000..832d47dd16 --- /dev/null +++ b/vendor/github.com/oklog/run/group.go @@ -0,0 +1,62 @@ +// Package run implements an actor-runner with deterministic teardown. It is +// somewhat similar to package errgroup, except it does not require actor +// goroutines to understand context semantics. This makes it suitable for use in +// more circumstances; for example, goroutines which are handling connections +// from net.Listeners, or scanning input from a closable io.Reader. +package run + +// Group collects actors (functions) and runs them concurrently. +// When one actor (function) returns, all actors are interrupted. +// The zero value of a Group is useful. +type Group struct { + actors []actor +} + +// Add an actor (function) to the group. Each actor must be pre-emptable by an +// interrupt function. That is, if interrupt is invoked, execute should return. +// Also, it must be safe to call interrupt even after execute has returned. +// +// The first actor (function) to return interrupts all running actors. +// The error is passed to the interrupt functions, and is returned by Run. +func (g *Group) Add(execute func() error, interrupt func(error)) { + g.actors = append(g.actors, actor{execute, interrupt}) +} + +// Run all actors (functions) concurrently. +// When the first actor returns, all others are interrupted. +// Run only returns when all actors have exited. +// Run returns the error returned by the first exiting actor. +func (g *Group) Run() error { + if len(g.actors) == 0 { + return nil + } + + // Run each actor. + errors := make(chan error, len(g.actors)) + for _, a := range g.actors { + go func(a actor) { + errors <- a.execute() + }(a) + } + + // Wait for the first actor to stop. + err := <-errors + + // Signal all actors to stop. + for _, a := range g.actors { + a.interrupt(err) + } + + // Wait for all actors to stop. + for i := 1; i < cap(errors); i++ { + <-errors + } + + // Return the original error. + return err +} + +type actor struct { + execute func() error + interrupt func(error) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 2dfdfd37c6..78c21ceaa6 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1021,10 +1021,10 @@ "revisionTime": "2017-06-22T06:09:55Z" }, { - "checksumSHA1": "QSH/KjHRLljyZDXMmkd9fdqbr3I=", + "checksumSHA1": "fqYiU+mDu70k2aeHdYZrZbCnUjM=", "path": "github.com/hashicorp/go-plugin", - "revision": "e37881a3f1a07fce82b3d99ce0342a72e53386bc", - "revisionTime": "2018-01-11T18:21:30Z" + "revision": "4b3b29102a1e16e60f22e79904ef3490f210f58d", + "revisionTime": "2018-01-18T02:43:51Z" }, { "checksumSHA1": "yzoWV7yrS/TvOrKy5ZrdUjsYaOA=", @@ -1376,6 +1376,12 @@ "revision": "c95c6e5c2d1a3d37fc44c8c6dc9e231c7500667d", "revisionTime": "2017-10-19T11:44:56Z" }, + { + "checksumSHA1": "Sfxv8SV6j8m6YD+hwvlMJjq2zfg=", + "path": "github.com/oklog/run", + "revision": "4dadeb3030eda0273a12382bb2348ffc7c9d1a39", + "revisionTime": "2017-11-14T00:29:35Z" + }, { "checksumSHA1": "OFNit1Qx2DdWhotfREKodDNUwCM=", "path": "github.com/opencontainers/go-digest",