1
1
mirror of https://github.com/mcuadros/ascode synced 2024-11-22 17:02:03 +01:00

terraform: plugin manager with provisioner support and tests

Signed-off-by: Máximo Cuadros <mcuadros@gmail.com>
This commit is contained in:
Máximo Cuadros 2019-07-24 23:30:39 +02:00
parent c8eb85ef8f
commit 04ffa10c88
4 changed files with 121 additions and 14 deletions

@ -28,7 +28,7 @@ func TestDocumentation(t *testing.T) {
func TestDo(t *testing.T) {
pm := &terraform.PluginManager{".providers"}
cli, meta := pm.Get("ignition", "1.1.0")
cli, meta := pm.Provider("ignition", "1.1.0")
fmt.Println(meta)
rpc, err := cli.Client()

@ -49,7 +49,11 @@ type Provider struct {
}
func MakeProvider(pm *terraform.PluginManager, name, version string) (*Provider, error) {
cli, meta := pm.Get(name, version)
cli, meta, err := pm.Provider(name, version, false)
if err != nil {
return nil, err
}
rpc, err := cli.Client()
if err != nil {
return nil, err

@ -3,25 +3,62 @@ package terraform
import (
"os"
"os/exec"
"strings"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/terraform/command"
tfplugin "github.com/hashicorp/terraform/plugin"
"github.com/hashicorp/terraform/plugin/discovery"
"github.com/mitchellh/cli"
)
// PluginManager is a wrapper arround the terraform tools to download and execute
// terraform plugins, like providers and provisioners.
type PluginManager struct {
Path string
}
func (m *PluginManager) Get(provider, version string) (*plugin.Client, discovery.PluginMeta) {
meta, ok := m.getLocal(provider, version)
if !ok {
meta, ok = m.getRemote(provider, version)
// Provider returns a client and the metadata for a given provider and version,
// first try to locate the provider in the local path, if not found, it
// downloads it from terraform registry. If forceLocal just tries to find
// the binary in the local filesystem.
func (m *PluginManager) Provider(provider, version string, forceLocal bool) (*plugin.Client, discovery.PluginMeta, error) {
meta, ok := m.getLocal("provider", provider, version)
if !ok && !forceLocal {
var err error
meta, ok, err = m.getProviderRemote(provider, version)
if err != nil {
return nil, discovery.PluginMeta{}, err
}
}
return client(meta), meta
return client(meta), meta, nil
}
// Provisioner returns a client and the metadata for a given provisioner, it
// try to locate it at the local Path, if not try to execute it from the
// built-in plugins in the terraform binary.
func (m *PluginManager) Provisioner(provisioner string) (*plugin.Client, discovery.PluginMeta, error) {
meta, ok := m.getLocal("provisioner", provisioner, "")
if ok {
return client(meta), meta, nil
}
// fallback to terraform internal provisioner.
cmdLine, _ := command.BuildPluginCommandString("provisioner", provisioner)
cmdArgv := strings.Split(cmdLine, command.TFSPACE)
// override the internal to the terraform binary.
cmdArgv[0] = "terraform"
meta = discovery.PluginMeta{
Name: provisioner,
Path: strings.Join(cmdArgv, command.TFSPACE),
}
return client(meta), meta, nil
}
func client(m discovery.PluginMeta) *plugin.Client {
@ -31,8 +68,10 @@ func client(m discovery.PluginMeta) *plugin.Client {
Output: os.Stderr,
})
cmdArgv := strings.Split(m.Path, command.TFSPACE)
return plugin.NewClient(&plugin.ClientConfig{
Cmd: exec.Command(m.Path),
Cmd: exec.Command(cmdArgv[0], cmdArgv[1:]...),
HandshakeConfig: tfplugin.Handshake,
VersionedPlugins: tfplugin.VersionedPlugins,
Managed: true,
@ -44,7 +83,7 @@ func client(m discovery.PluginMeta) *plugin.Client {
const defaultVersionContraint = "> 0"
func (m *PluginManager) getRemote(provider, v string) (discovery.PluginMeta, bool) {
func (m *PluginManager) getProviderRemote(provider, v string) (discovery.PluginMeta, bool, error) {
if v == "" {
v = defaultVersionContraint
}
@ -55,16 +94,16 @@ func (m *PluginManager) getRemote(provider, v string) (discovery.PluginMeta, boo
Ui: cli.NewMockUi(),
}
pm, _, err := installer.Get(provider, discovery.ConstraintStr(v).MustParse())
meta, _, err := installer.Get(provider, discovery.ConstraintStr(v).MustParse())
if err != nil {
panic(err)
return discovery.PluginMeta{}, false, err
}
return pm, true
return meta, true, nil
}
func (m *PluginManager) getLocal(provider, version string) (discovery.PluginMeta, bool) {
set := discovery.FindPlugins("provider", []string{m.Path})
func (m *PluginManager) getLocal(kind, provider, version string) (discovery.PluginMeta, bool) {
set := discovery.FindPlugins(kind, []string{m.Path})
set = set.WithName(provider)
if len(set) == 0 {
return discovery.PluginMeta{}, false

64
terraform/plugins_test.go Normal file

@ -0,0 +1,64 @@
package terraform
import (
"fmt"
"io/ioutil"
"strings"
"testing"
"github.com/hashicorp/terraform/plugin/discovery"
"github.com/stretchr/testify/assert"
)
func TestPluginManager_Provider(t *testing.T) {
path, err := ioutil.TempDir("", "provider")
assert.NoError(t, err)
pm := &PluginManager{Path: path}
cli, meta, err := pm.Provider("github", "2.1.0", false)
assert.NoError(t, err)
assert.NotNil(t, cli)
assert.Equal(t, meta.Version, discovery.VersionStr("2.1.0"))
cli, meta, err = pm.Provider("github", "2.1.0", true)
assert.NoError(t, err)
assert.NotNil(t, cli)
assert.Equal(t, meta.Version, discovery.VersionStr("2.1.0"))
cli, meta, err = pm.Provider("github", "2.1.0", false)
assert.NoError(t, err)
assert.NotNil(t, cli)
assert.Equal(t, meta.Version, discovery.VersionStr("2.1.0"))
}
func TestPluginManager_ProviderDefault(t *testing.T) {
path, err := ioutil.TempDir("", "provider")
assert.NoError(t, err)
pm := &PluginManager{Path: path}
cli, meta, err := pm.Provider("github", "", false)
assert.NoError(t, err)
assert.NotNil(t, cli)
assert.NotEqual(t, meta.Version, discovery.VersionStr("2.1.0"))
cli, meta, err = pm.Provider("github", "", true)
assert.NoError(t, err)
assert.NotNil(t, cli)
assert.NotEqual(t, meta.Version, discovery.VersionStr("2.1.0"))
fmt.Println(meta.Path)
assert.Equal(t, strings.Index(meta.Path, path), 0)
}
func TestPluginManager_ProvisionerDefault(t *testing.T) {
path, err := ioutil.TempDir("", "provisioner")
assert.NoError(t, err)
pm := &PluginManager{Path: path}
cli, meta, err := pm.Provisioner("file")
assert.NoError(t, err)
assert.NotNil(t, cli)
assert.Equal(t, strings.Index(meta.Path, "terraform-TFSPACE-internal-plugin-"), 0)
}