From 5e089c2c093d8522a27004bd1ea988f48439e5d0 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 25 Nov 2020 17:22:40 -0500 Subject: [PATCH] run built-in provisioners in-process Use the new provisioner interfaces, and run the built-in provisioners in-process. --- command/internal_plugin.go | 97 --------------------------------- command/internal_plugin_list.go | 20 ------- command/internal_plugin_test.go | 55 ------------------- command/plugins.go | 43 ++++----------- commands.go | 6 -- 5 files changed, 10 insertions(+), 211 deletions(-) delete mode 100644 command/internal_plugin.go delete mode 100644 command/internal_plugin_list.go delete mode 100644 command/internal_plugin_test.go diff --git a/command/internal_plugin.go b/command/internal_plugin.go deleted file mode 100644 index 33de8569a..000000000 --- a/command/internal_plugin.go +++ /dev/null @@ -1,97 +0,0 @@ -package command - -import ( - "fmt" - "log" - "strings" - - "github.com/hashicorp/terraform/plugin" - "github.com/kardianos/osext" -) - -// InternalPluginCommand is a Command implementation that allows plugins to be -// compiled into the main Terraform binary and executed via a subcommand. -type InternalPluginCommand struct { - Meta -} - -const TFSPACE = "-TFSPACE-" - -// BuildPluginCommandString builds a special string for executing internal -// plugins. It has the following format: -// -// /path/to/terraform-TFSPACE-internal-plugin-TFSPACE-terraform-provider-aws -// -// We split the string on -TFSPACE- to build the command executor. The reason we -// use -TFSPACE- is so we can support spaces in the /path/to/terraform part. -func BuildPluginCommandString(pluginType, pluginName string) (string, error) { - terraformPath, err := osext.Executable() - if err != nil { - return "", err - } - parts := []string{terraformPath, "internal-plugin", pluginType, pluginName} - return strings.Join(parts, TFSPACE), nil -} - -// Internal plugins do not support any CLI args, but we do receive flags that -// main.go:mergeEnvArgs has merged in from EnvCLI. Instead of making main.go -// aware of this exception, we strip all flags from our args. Flags are easily -// identified by the '-' prefix, ensured by the cli package used. -func StripArgFlags(args []string) []string { - argsNoFlags := []string{} - for i := range args { - if !strings.HasPrefix(args[i], "-") { - argsNoFlags = append(argsNoFlags, args[i]) - } - } - return argsNoFlags -} - -func (c *InternalPluginCommand) Run(args []string) int { - // strip flags from args, only use subcommands. - args = StripArgFlags(args) - - if len(args) != 2 { - log.Printf("Wrong number of args; expected: terraform internal-plugin pluginType pluginName") - return 1 - } - - pluginType := args[0] - pluginName := args[1] - - log.SetPrefix(fmt.Sprintf("%s-%s (internal) ", pluginName, pluginType)) - - switch pluginType { - case "provisioner": - pluginFunc, found := InternalProvisioners[pluginName] - if !found { - log.Printf("[ERROR] Could not load provisioner: %s", pluginName) - return 1 - } - log.Printf("[INFO] Starting provisioner plugin %s", pluginName) - plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: pluginFunc, - }) - default: - log.Printf("[ERROR] Invalid plugin type %s", pluginType) - return 1 - } - - return 0 -} - -func (c *InternalPluginCommand) Help() string { - helpText := ` -Usage: terraform internal-plugin pluginType pluginName - - Runs an internally-compiled version of a plugin from the terraform binary. - - NOTE: this is an internal command and you should not call it yourself. -` - - return strings.TrimSpace(helpText) -} - -func (c *InternalPluginCommand) Synopsis() string { - return "internal plugin command" -} diff --git a/command/internal_plugin_list.go b/command/internal_plugin_list.go deleted file mode 100644 index 06230bdfb..000000000 --- a/command/internal_plugin_list.go +++ /dev/null @@ -1,20 +0,0 @@ -// -// This file is automatically generated by scripts/generate-plugins.go -- Do not edit! -// -package command - -import ( - fileprovisioner "github.com/hashicorp/terraform/builtin/provisioners/file" - localexecprovisioner "github.com/hashicorp/terraform/builtin/provisioners/local-exec" - remoteexecprovisioner "github.com/hashicorp/terraform/builtin/provisioners/remote-exec" - - "github.com/hashicorp/terraform/plugin" -) - -var InternalProviders = map[string]plugin.ProviderFunc{} - -var InternalProvisioners = map[string]plugin.ProvisionerFunc{ - "file": fileprovisioner.Provisioner, - "local-exec": localexecprovisioner.Provisioner, - "remote-exec": remoteexecprovisioner.Provisioner, -} diff --git a/command/internal_plugin_test.go b/command/internal_plugin_test.go deleted file mode 100644 index 4254090e5..000000000 --- a/command/internal_plugin_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package command - -import ( - "testing" -) - -func TestInternalPlugin_InternalProviders(t *testing.T) { - m := new(Meta) - providers := m.internalProviders() - // terraform is the only provider moved back to internal - for _, name := range []string{"terraform"} { - pf, ok := providers[name] - if !ok { - t.Errorf("Expected to find %s in InternalProviders", name) - } - - provider, err := pf() - if err != nil { - t.Fatal(err) - } - - if provider == nil { - t.Fatal("provider factory returned a nil provider") - } - } -} - -func TestInternalPlugin_InternalProvisioners(t *testing.T) { - for _, name := range []string{"file", "local-exec", "remote-exec"} { - if _, ok := InternalProvisioners[name]; !ok { - t.Errorf("Expected to find %s in InternalProvisioners", name) - } - } -} - -func TestInternalPlugin_BuildPluginCommandString(t *testing.T) { - actual, err := BuildPluginCommandString("provisioner", "remote-exec") - if err != nil { - t.Fatalf(err.Error()) - } - - expected := "-TFSPACE-internal-plugin-TFSPACE-provisioner-TFSPACE-remote-exec" - if actual[len(actual)-len(expected):] != expected { - t.Errorf("Expected command to end with %s; got:\n%s\n", expected, actual) - } -} - -func TestInternalPlugin_StripArgFlags(t *testing.T) { - actual := StripArgFlags([]string{"provisioner", "remote-exec", "-var-file=my_vars.tfvars", "-flag"}) - expected := []string{"provisioner", "remote-exec"} - // Must be same length and order. - if len(actual) != len(expected) || expected[0] != actual[0] || actual[1] != actual[1] { - t.Fatalf("Expected args to be exactly '%s', got '%s'", expected, actual) - } -} diff --git a/command/plugins.go b/command/plugins.go index 7de75841a..f57de348a 100644 --- a/command/plugins.go +++ b/command/plugins.go @@ -9,11 +9,13 @@ import ( "os/exec" "path/filepath" "runtime" - "strings" plugin "github.com/hashicorp/go-plugin" "github.com/kardianos/osext" + fileprovisioner "github.com/hashicorp/terraform/builtin/provisioners/file" + localexec "github.com/hashicorp/terraform/builtin/provisioners/local-exec" + remoteexec "github.com/hashicorp/terraform/builtin/provisioners/remote-exec" "github.com/hashicorp/terraform/internal/logging" tfplugin "github.com/hashicorp/terraform/plugin" "github.com/hashicorp/terraform/plugin/discovery" @@ -134,8 +136,8 @@ func (m *Meta) provisionerFactories() map[string]provisioners.Factory { // Wire up the internal provisioners first. These might be overridden // by discovered provisioners below. - for name := range InternalProvisioners { - factories[name] = internalProvisionerFactory(discovery.PluginMeta{Name: name}) + for name, factory := range internalProvisionerFactories() { + factories[name] = factory } byName := plugins.ByName() @@ -151,29 +153,6 @@ func (m *Meta) provisionerFactories() map[string]provisioners.Factory { return factories } -func internalPluginClient(kind, name string) (*plugin.Client, error) { - cmdLine, err := BuildPluginCommandString(kind, name) - if err != nil { - return nil, err - } - - // See the docstring for BuildPluginCommandString for why we need to do - // this split here. - cmdArgv := strings.Split(cmdLine, TFSPACE) - - cfg := &plugin.ClientConfig{ - Cmd: exec.Command(cmdArgv[0], cmdArgv[1:]...), - HandshakeConfig: tfplugin.Handshake, - Managed: true, - VersionedPlugins: tfplugin.VersionedPlugins, - AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, - AutoMTLS: enableProviderAutoMTLS, - Logger: logging.NewLogger(kind), - } - - return plugin.NewClient(cfg), nil -} - func provisionerFactory(meta discovery.PluginMeta) provisioners.Factory { return func() (provisioners.Interface, error) { cfg := &plugin.ClientConfig{ @@ -190,13 +169,11 @@ func provisionerFactory(meta discovery.PluginMeta) provisioners.Factory { } } -func internalProvisionerFactory(meta discovery.PluginMeta) provisioners.Factory { - return func() (provisioners.Interface, error) { - client, err := internalPluginClient("provisioner", meta.Name) - if err != nil { - return nil, fmt.Errorf("[WARN] failed to build command line for internal plugin %q: %s", meta.Name, err) - } - return newProvisionerClient(client) +func internalProvisionerFactories() map[string]provisioners.Factory { + return map[string]provisioners.Factory{ + "file": provisioners.FactoryFixed(fileprovisioner.New()), + "local-exec": provisioners.FactoryFixed(localexec.New()), + "remote-exec": provisioners.FactoryFixed(remoteexec.New()), } } diff --git a/commands.go b/commands.go index 1db6dfb63..c889d37f1 100644 --- a/commands.go +++ b/commands.go @@ -194,12 +194,6 @@ func initCommands( }, nil }, - "internal-plugin": func() (cli.Command, error) { - return &command.InternalPluginCommand{ - Meta: meta, - }, nil - }, - "login": func() (cli.Command, error) { return &command.LoginCommand{ Meta: meta,