From b4030238419aee096dc876f42a8116ee5261597a Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 14 Aug 2018 16:55:20 -0400 Subject: [PATCH] serve the new version 5 grpc plugins Use the new go-plugin version negotiation to server th appropriate plugin type when the client requests protocol version 5. --- plugin/grpc_provider.go | 20 ++++++++++ plugin/grpc_provisioner.go | 20 ++++++++++ plugin/resource_provider.go | 7 +++- plugin/resource_provisioner.go | 7 +++- plugin/serve.go | 69 +++++++++++++++++++++++++++++++--- 5 files changed, 113 insertions(+), 10 deletions(-) diff --git a/plugin/grpc_provider.go b/plugin/grpc_provider.go index 0edc7f762a..9da5331b96 100644 --- a/plugin/grpc_provider.go +++ b/plugin/grpc_provider.go @@ -5,6 +5,7 @@ import ( "errors" "sync" + plugin "github.com/hashicorp/go-plugin" "github.com/hashicorp/terraform/plugin/proto" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/version" @@ -12,6 +13,25 @@ import ( "google.golang.org/grpc" ) +// GRPCProviderPlugin implements plugin.GRPCPlugin for the go-plugin package. +type GRPCProviderPlugin struct { + plugin.Plugin + GRPCProvider func() proto.ProviderServer +} + +func (p *GRPCProviderPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return &GRPCProvider{ + conn: c, + client: proto.NewProviderClient(c), + ctx: ctx, + }, nil +} + +func (p *GRPCProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { + proto.RegisterProviderServer(s, p.GRPCProvider()) + return nil +} + // GRPCProvider handles the client, or core side of the plugin rpc connection. // The GRPCProvider methods are mostly a translation layer between the // terraform provioders types and the grpc proto types, directly converting diff --git a/plugin/grpc_provisioner.go b/plugin/grpc_provisioner.go index b2ad0f20b4..0f015c72f3 100644 --- a/plugin/grpc_provisioner.go +++ b/plugin/grpc_provisioner.go @@ -6,6 +6,7 @@ import ( "io" "sync" + plugin "github.com/hashicorp/go-plugin" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/plugin/proto" "github.com/hashicorp/terraform/provisioners" @@ -14,6 +15,25 @@ import ( "google.golang.org/grpc" ) +// GRPCProvisionerPlugin is the plugin.GRPCPlugin implementation. +type GRPCProvisionerPlugin struct { + plugin.Plugin + GRPCProvisioner func() proto.ProvisionerServer +} + +func (p *GRPCProvisionerPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return &GRPCProvisioner{ + conn: c, + client: proto.NewProvisionerClient(c), + ctx: ctx, + }, nil +} + +func (p *GRPCProvisionerPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { + proto.RegisterProvisionerServer(s, p.GRPCProvisioner()) + return nil +} + // provisioners.Interface grpc implementation type GRPCProvisioner struct { conn *grpc.ClientConn diff --git a/plugin/resource_provider.go b/plugin/resource_provider.go index d6a433c4e5..459661a550 100644 --- a/plugin/resource_provider.go +++ b/plugin/resource_provider.go @@ -9,11 +9,14 @@ import ( // ResourceProviderPlugin is the plugin.Plugin implementation. type ResourceProviderPlugin struct { - F func() terraform.ResourceProvider + ResourceProvider func() terraform.ResourceProvider } func (p *ResourceProviderPlugin) Server(b *plugin.MuxBroker) (interface{}, error) { - return &ResourceProviderServer{Broker: b, Provider: p.F()}, nil + return &ResourceProviderServer{ + Broker: b, + Provider: p.ResourceProvider(), + }, nil } func (p *ResourceProviderPlugin) Client( diff --git a/plugin/resource_provisioner.go b/plugin/resource_provisioner.go index 7ef435454b..f0cc341f13 100644 --- a/plugin/resource_provisioner.go +++ b/plugin/resource_provisioner.go @@ -10,11 +10,14 @@ import ( // ResourceProvisionerPlugin is the plugin.Plugin implementation. type ResourceProvisionerPlugin struct { - F func() terraform.ResourceProvisioner + ResourceProvisioner func() terraform.ResourceProvisioner } func (p *ResourceProvisionerPlugin) Server(b *plugin.MuxBroker) (interface{}, error) { - return &ResourceProvisionerServer{Broker: b, Provisioner: p.F()}, nil + return &ResourceProvisionerServer{ + Broker: b, + Provisioner: p.ResourceProvisioner(), + }, nil } func (p *ResourceProvisionerPlugin) Client( diff --git a/plugin/serve.go b/plugin/serve.go index 2028a613ff..9361430f95 100644 --- a/plugin/serve.go +++ b/plugin/serve.go @@ -2,6 +2,8 @@ package plugin import ( "github.com/hashicorp/go-plugin" + grpcplugin "github.com/hashicorp/terraform/helper/plugin" + "github.com/hashicorp/terraform/plugin/proto" "github.com/hashicorp/terraform/terraform" ) @@ -28,27 +30,82 @@ var Handshake = plugin.HandshakeConfig{ type ProviderFunc func() terraform.ResourceProvider type ProvisionerFunc func() terraform.ResourceProvisioner +type GRPCProviderFunc func() proto.ProviderServer +type GRPCProvisionerFunc func() proto.ProvisionerServer // ServeOpts are the configurations to serve a plugin. type ServeOpts struct { ProviderFunc ProviderFunc ProvisionerFunc ProvisionerFunc + + // Wrapped versions of the above plugins will automatically shimmed and + // added to the GRPC functions when possible. + GRPCProviderFunc GRPCProviderFunc + GRPCProvisionerFunc GRPCProvisionerFunc } // Serve serves a plugin. This function never returns and should be the final // function called in the main function of the plugin. func Serve(opts *ServeOpts) { + // since the plugins may not yet be aware of the new protocol, we + // automatically wrap the plugins in the grpc shims. + if opts.GRPCProviderFunc == nil && opts.ProviderFunc != nil { + provider := grpcplugin.NewGRPCProviderServerShim(opts.ProviderFunc()) + // this is almost always going to be a *schema.Provider, but check that + // we got back a valid provider just in case. + if provider != nil { + opts.GRPCProviderFunc = func() proto.ProviderServer { + return provider + } + } + } + if opts.GRPCProvisionerFunc == nil && opts.ProvisionerFunc != nil { + provider := grpcplugin.NewGRPCProvisionerServerShim(opts.ProvisionerFunc()) + if provider != nil { + opts.GRPCProvisionerFunc = func() proto.ProvisionerServer { + return provider + } + } + } + plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: Handshake, - Plugins: pluginMap(opts), + HandshakeConfig: Handshake, + VersionedPlugins: pluginSet(opts), + GRPCServer: plugin.DefaultGRPCServer, }) } -// pluginMap returns the map[string]plugin.Plugin to use for configuring a plugin -// server or client. +// pluginMap returns the legacy map[string]plugin.Plugin to use for configuring +// a plugin server or client. func pluginMap(opts *ServeOpts) map[string]plugin.Plugin { return map[string]plugin.Plugin{ - "provider": &ResourceProviderPlugin{F: opts.ProviderFunc}, - "provisioner": &ResourceProvisionerPlugin{F: opts.ProvisionerFunc}, + "provider": &ResourceProviderPlugin{ + ResourceProvider: opts.ProviderFunc, + }, + "provisioner": &ResourceProvisionerPlugin{ + ResourceProvisioner: opts.ProvisionerFunc, + }, + } +} + +func pluginSet(opts *ServeOpts) map[int]plugin.PluginSet { + // Set the legacy netrpc plugins at version 4. + // The oldest version is returned in when executed by a legacy go-plugin + // client. + plugins := map[int]plugin.PluginSet{ + 4: pluginMap(opts), + } + + // add the new protocol versions if they're configured + if opts.GRPCProviderFunc != nil || opts.GRPCProvisionerFunc != nil { + plugins[5] = plugin.PluginSet{ + "provider": &GRPCProviderPlugin{ + GRPCProvider: opts.GRPCProviderFunc, + }, + "provisioner": &GRPCProvisionerPlugin{ + GRPCProvisioner: opts.GRPCProvisionerFunc, + }, + } } + return plugins }