update to start a new process for each plugin

Modify the plugin factories to create a new plugin process for each
individual plugin.
This commit is contained in:
James Bardin 2018-10-02 16:06:12 -04:00 committed by Martin Atkins
parent 35b375d3ee
commit 795161f615
4 changed files with 64 additions and 29 deletions

View File

@ -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,12 +383,27 @@ 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) {
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()
@ -401,6 +416,8 @@ func provisionerFactory(client *plugin.Client) terraform.ProvisionerFactory {
return nil, err
}
return raw.(provisioners.Interface), nil
}
// store the client so that the plugin can kill the child process
p := raw.(*tfplugin.GRPCProvisioner)
p.PluginClient = client
return p, nil
}

View File

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

View File

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

View File

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