run built-in provisioners in-process
Use the new provisioner interfaces, and run the built-in provisioners in-process.
This commit is contained in:
parent
256a7ec95a
commit
5e089c2c09
|
@ -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"
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue