diff --git a/command/plugins.go b/command/plugins.go index fad4bae98..2c5b5cba7 100644 --- a/command/plugins.go +++ b/command/plugins.go @@ -84,8 +84,7 @@ func (r *multiVersionProviderResolver) ResolveProviders( continue } - client := tfplugin.Client(newest) - factories[name] = providerFactory(client) + factories[name] = providerFactory(newest) } else { msg := fmt.Sprintf("provider.%s: no suitable version installed", name) @@ -332,7 +331,7 @@ func (m *Meta) provisionerFactories() map[string]terraform.ProvisionerFactory { log.Printf("[WARN] failed to build command line for internal plugin %q: %s", name, err) continue } - factories[name] = provisionerFactory(client) + factories[name] = internalProvisionerFactory(client) } byName := plugins.ByName() @@ -341,8 +340,8 @@ func (m *Meta) provisionerFactories() map[string]terraform.ProvisionerFactory { // by name, we're guaranteed that the metas in our set all have // valid versions and that there's at least one meta. newest := metas.Newest() - client := tfplugin.Client(newest) - factories[name] = provisionerFactory(client) + + factories[name] = provisionerFactory(newest) } return factories @@ -369,8 +368,9 @@ func internalPluginClient(kind, name string) (*plugin.Client, error) { return plugin.NewClient(cfg), nil } -func providerFactory(client *plugin.Client) providers.Factory { +func providerFactory(meta discovery.PluginMeta) providers.Factory { return func() (providers.Interface, error) { + client := tfplugin.Client(meta) // Request the RPC client so we can get the provider // so we can build the actual RPC-implemented provider. rpcClient, err := client.Client() @@ -383,24 +383,41 @@ func providerFactory(client *plugin.Client) providers.Factory { return nil, err } - return raw.(providers.Interface), nil + // store the client so that the plugin can kill the child process + p := raw.(*tfplugin.GRPCProvider) + p.PluginClient = client + return p, nil } } -func provisionerFactory(client *plugin.Client) terraform.ProvisionerFactory { +func provisionerFactory(meta discovery.PluginMeta) terraform.ProvisionerFactory { return func() (provisioners.Interface, error) { - // Request the RPC client so we can get the provisioner - // so we can build the actual RPC-implemented provisioner. - rpcClient, err := client.Client() - if err != nil { - return nil, err - } - - raw, err := rpcClient.Dispense(tfplugin.ProvisionerPluginName) - if err != nil { - return nil, err - } - - return raw.(provisioners.Interface), nil + client := tfplugin.Client(meta) + return newProvisionerClient(client) } } + +func internalProvisionerFactory(client *plugin.Client) terraform.ProvisionerFactory { + return func() (provisioners.Interface, error) { + return newProvisionerClient(client) + } +} + +func newProvisionerClient(client *plugin.Client) (provisioners.Interface, error) { + // Request the RPC client so we can get the provisioner + // so we can build the actual RPC-implemented provisioner. + rpcClient, err := client.Client() + if err != nil { + return nil, err + } + + raw, err := rpcClient.Dispense(tfplugin.ProvisionerPluginName) + if err != nil { + return nil, err + } + + // store the client so that the plugin can kill the child process + p := raw.(*tfplugin.GRPCProvisioner) + p.PluginClient = client + return p, nil +} diff --git a/plugin/grpc_provider.go b/plugin/grpc_provider.go index b6375dbe3..4aea93901 100644 --- a/plugin/grpc_provider.go +++ b/plugin/grpc_provider.go @@ -25,7 +25,6 @@ type GRPCProviderPlugin struct { 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 @@ -41,7 +40,11 @@ func (p *GRPCProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Serve // terraform provioders types and the grpc proto types, directly converting // between the two. type GRPCProvider struct { - conn *grpc.ClientConn + // PluginClient provides a reference to the plugin.Client which controls the plugin process. + // This allows the GRPCProvider a way to shutdown the plugin process. + PluginClient *plugin.Client + + // Proto client use to make the grpc service calls. client proto.ProviderClient // this context is created by the plugin package, and is canceled when the @@ -495,8 +498,13 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p } // closing the grpc connection is final, and terraform will call it at the end of every phase. -// FIXME: do we need this, and if so, how do we fix it? func (p *GRPCProvider) Close() error { - log.Printf("[TRACE] GRPCProvider: Close") + // check this since it's not automatically inserted during plugin creation + if p.PluginClient == nil { + log.Println("[DEBUG] provider has no plugin.Client") + return nil + } + + p.PluginClient.Kill() return nil } diff --git a/plugin/grpc_provisioner.go b/plugin/grpc_provisioner.go index 45a5db886..712079286 100644 --- a/plugin/grpc_provisioner.go +++ b/plugin/grpc_provisioner.go @@ -4,6 +4,7 @@ import ( "context" "errors" "io" + "log" "sync" plugin "github.com/hashicorp/go-plugin" @@ -24,7 +25,6 @@ type GRPCProvisionerPlugin struct { 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 @@ -37,7 +37,10 @@ func (p *GRPCProvisionerPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Se // provisioners.Interface grpc implementation type GRPCProvisioner struct { - conn *grpc.ClientConn + // PluginClient provides a reference to the plugin.Client which controls the plugin process. + // This allows the GRPCProvider a way to shutdown the plugin process. + PluginClient *plugin.Client + client proto.ProvisionerClient ctx context.Context @@ -163,5 +166,12 @@ func (p *GRPCProvisioner) Stop() error { } func (p *GRPCProvisioner) Close() error { + // check this since it's not automatically inserted during plugin creation + if p.PluginClient == nil { + log.Println("[DEBUG] provider has no plugin.Client") + return nil + } + + p.PluginClient.Kill() return nil } diff --git a/terraform/context_components.go b/terraform/context_components.go index 252da1b1c..26ec99595 100644 --- a/terraform/context_components.go +++ b/terraform/context_components.go @@ -33,7 +33,7 @@ type basicComponentFactory struct { func (c *basicComponentFactory) ResourceProviders() []string { result := make([]string, len(c.providers)) - for k, _ := range c.providers { + for k := range c.providers { result = append(result, k) } @@ -42,7 +42,7 @@ func (c *basicComponentFactory) ResourceProviders() []string { func (c *basicComponentFactory) ResourceProvisioners() []string { result := make([]string, len(c.provisioners)) - for k, _ := range c.provisioners { + for k := range c.provisioners { result = append(result, k) }