add -plugin-dir option
The -plugin-dir option lets the user specify custom search paths for plugins. This overrides all other plugin search paths, and prevents the auto-installation of plugins. We also make sure that the availability of plugins is always checked during init, even if -get-plugins=false or -plugin-dir is set.
This commit is contained in:
parent
000e860706
commit
be2069ac81
|
@ -22,6 +22,9 @@ import (
|
|||
type InitCommand struct {
|
||||
Meta
|
||||
|
||||
// getPlugins is for the -get-plugins flag
|
||||
getPlugins bool
|
||||
|
||||
// providerInstaller is used to download and install providers that
|
||||
// aren't found locally. This uses a discovery.ProviderInstaller instance
|
||||
// by default, but it can be overridden here as a way to mock fetching
|
||||
|
@ -30,7 +33,7 @@ type InitCommand struct {
|
|||
}
|
||||
|
||||
func (c *InitCommand) Run(args []string) int {
|
||||
var flagBackend, flagGet, flagGetPlugins, flagUpgrade bool
|
||||
var flagBackend, flagGet, flagUpgrade bool
|
||||
var flagConfigExtra map[string]interface{}
|
||||
var flagPluginPath FlagStringSlice
|
||||
|
||||
|
@ -39,7 +42,7 @@ func (c *InitCommand) Run(args []string) int {
|
|||
cmdFlags.BoolVar(&flagBackend, "backend", true, "")
|
||||
cmdFlags.Var((*variables.FlagAny)(&flagConfigExtra), "backend-config", "")
|
||||
cmdFlags.BoolVar(&flagGet, "get", true, "")
|
||||
cmdFlags.BoolVar(&flagGetPlugins, "get-plugins", true, "")
|
||||
cmdFlags.BoolVar(&c.getPlugins, "get-plugins", true, "")
|
||||
cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data")
|
||||
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
|
||||
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
|
||||
|
@ -52,7 +55,10 @@ func (c *InitCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
c.pluginPath = flagPluginPath
|
||||
if len(flagPluginPath) > 0 {
|
||||
c.pluginPath = flagPluginPath
|
||||
c.getPlugins = false
|
||||
}
|
||||
|
||||
// set getProvider if we don't have a test version already
|
||||
if c.providerInstaller == nil {
|
||||
|
@ -141,7 +147,7 @@ func (c *InitCommand) Run(args []string) int {
|
|||
|
||||
// If we're requesting backend configuration or looking for required
|
||||
// plugins, load the backend
|
||||
if flagBackend || flagGetPlugins {
|
||||
if flagBackend {
|
||||
header = true
|
||||
|
||||
// Only output that we're initializing a backend if we have
|
||||
|
@ -165,31 +171,29 @@ func (c *InitCommand) Run(args []string) int {
|
|||
}
|
||||
}
|
||||
|
||||
// Now that we have loaded all modules, check the module tree for missing providers
|
||||
if flagGetPlugins {
|
||||
sMgr, err := back.State(c.Workspace())
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error loading state: %s", err))
|
||||
return 1
|
||||
}
|
||||
// Now that we have loaded all modules, check the module tree for missing providers.
|
||||
sMgr, err := back.State(c.Workspace())
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error loading state: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := sMgr.RefreshState(); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error refreshing state: %s", err))
|
||||
return 1
|
||||
}
|
||||
if err := sMgr.RefreshState(); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error refreshing state: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(c.Colorize().Color(
|
||||
"[reset][bold]Initializing provider plugins...",
|
||||
))
|
||||
c.Ui.Output(c.Colorize().Color(
|
||||
"[reset][bold]Initializing provider plugins...",
|
||||
))
|
||||
|
||||
err = c.getProviders(path, sMgr.State(), flagUpgrade)
|
||||
if err != nil {
|
||||
// this function provides its own output
|
||||
log.Printf("[ERROR] %s", err)
|
||||
return 1
|
||||
}
|
||||
err = c.getProviders(path, sMgr.State(), flagUpgrade)
|
||||
if err != nil {
|
||||
// this function provides its own output
|
||||
log.Printf("[ERROR] %s", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
// If we outputted information, then we need to output a newline
|
||||
|
@ -229,17 +233,26 @@ func (c *InitCommand) getProviders(path string, state *terraform.State, upgrade
|
|||
missing := c.missingPlugins(available, requirements)
|
||||
|
||||
var errs error
|
||||
for provider, reqd := range missing {
|
||||
c.Ui.Output(fmt.Sprintf("- downloading plugin for provider %q...", provider))
|
||||
_, err := c.providerInstaller.Get(provider, reqd.Versions)
|
||||
if c.getPlugins {
|
||||
for provider, reqd := range missing {
|
||||
c.Ui.Output(fmt.Sprintf("- downloading plugin for provider %q...", provider))
|
||||
_, err := c.providerInstaller.Get(provider, reqd.Versions)
|
||||
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(errProviderNotFound, err, provider, reqd.Versions))
|
||||
errs = multierror.Append(errs, err)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(errProviderNotFound, err, provider, reqd.Versions))
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil {
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
} else if len(missing) > 0 {
|
||||
// we have missing providers, but aren't going to try and download them
|
||||
for provider, reqd := range missing {
|
||||
c.Ui.Error(fmt.Sprintf(errProviderNotFound, err, provider, reqd.Versions))
|
||||
errs = multierror.Append(errs, fmt.Errorf("missing provider %q", provider))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
|
@ -360,6 +373,11 @@ Options:
|
|||
|
||||
-no-color If specified, output won't contain any color.
|
||||
|
||||
-plugin-dir Directory containing plugin binaries. This overrides all
|
||||
default search paths for plugins, and prevents the
|
||||
automatic installation of plugins. This flag can be used
|
||||
multiple times.
|
||||
|
||||
-reconfigure Reconfigure the backend, ignoring any saved configuration.
|
||||
|
||||
-upgrade=false If installing modules (-get) or plugins (-get-plugins),
|
||||
|
|
|
@ -538,7 +538,7 @@ func TestInit_findVendoredProviders(t *testing.T) {
|
|||
if err := ioutil.WriteFile(greaterThanPath, []byte("test bin"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// TODO: change this to the -plugin-dir path
|
||||
// Check the current directory too
|
||||
betweenPath := filepath.Join(".", "terraform-provider-between_v2.3.4_x4")
|
||||
if err := ioutil.WriteFile(betweenPath, []byte("test bin"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -548,7 +548,6 @@ func TestInit_findVendoredProviders(t *testing.T) {
|
|||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestInit_getUpgradePlugins(t *testing.T) {
|
||||
|
@ -767,3 +766,101 @@ func TestInit_providerLockFile(t *testing.T) {
|
|||
t.Errorf("wrong provider lock file contents\ngot: %s\nwant: %s", buf, wantLockFile)
|
||||
}
|
||||
}
|
||||
|
||||
// Test user-supplied -plugin-dir
|
||||
func TestInit_pluginDirProviders(t *testing.T) {
|
||||
td := tempDir(t)
|
||||
copy.CopyDir(testFixturePath("init-get-providers"), td)
|
||||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
m := Meta{
|
||||
testingOverrides: metaOverridesForProvider(testProvider()),
|
||||
Ui: ui,
|
||||
}
|
||||
|
||||
c := &InitCommand{
|
||||
Meta: m,
|
||||
providerInstaller: &mockProviderInstaller{},
|
||||
}
|
||||
|
||||
// make our vendor paths
|
||||
pluginPath := []string{"a", "b", "c"}
|
||||
for _, p := range pluginPath {
|
||||
if err := os.MkdirAll(p, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// add some dummy providers in our plugin dirs
|
||||
for i, name := range []string{
|
||||
"terraform-provider-exact_v1.2.3_x4",
|
||||
"terraform-provider-greater_than_v2.3.4_x4",
|
||||
"terraform-provider-between_v2.3.4_x4",
|
||||
} {
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(pluginPath[i], name), []byte("test bin"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-plugin-dir", "a",
|
||||
"-plugin-dir", "b",
|
||||
"-plugin-dir", "c",
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: \n%s", ui.ErrorWriter)
|
||||
}
|
||||
}
|
||||
|
||||
// Test user-supplied -plugin-dir doesn't allow auto-install
|
||||
func TestInit_pluginDirProvidersDoesNotGet(t *testing.T) {
|
||||
td := tempDir(t)
|
||||
copy.CopyDir(testFixturePath("init-get-providers"), td)
|
||||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
m := Meta{
|
||||
testingOverrides: metaOverridesForProvider(testProvider()),
|
||||
Ui: ui,
|
||||
}
|
||||
|
||||
c := &InitCommand{
|
||||
Meta: m,
|
||||
providerInstaller: callbackPluginInstaller(func(provider string, req discovery.Constraints) (discovery.PluginMeta, error) {
|
||||
t.Fatalf("plugin installer should not have been called for %q", provider)
|
||||
return discovery.PluginMeta{}, nil
|
||||
}),
|
||||
}
|
||||
|
||||
// make our vendor paths
|
||||
pluginPath := []string{"a", "b"}
|
||||
for _, p := range pluginPath {
|
||||
if err := os.MkdirAll(p, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// add some dummy providers in our plugin dirs
|
||||
for i, name := range []string{
|
||||
"terraform-provider-exact_v1.2.3_x4",
|
||||
"terraform-provider-greater_than_v2.3.4_x4",
|
||||
} {
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(pluginPath[i], name), []byte("test bin"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-plugin-dir", "a",
|
||||
"-plugin-dir", "b",
|
||||
}
|
||||
if code := c.Run(args); code == 0 {
|
||||
// should have been an error
|
||||
t.Fatalf("bad: \n%s", ui.OutputWriter)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,6 +83,9 @@ func (m *Meta) storePluginPath(pluginPath []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// if this fails, so will WriteFile
|
||||
os.MkdirAll(m.DataDir(), 0755)
|
||||
|
||||
return ioutil.WriteFile(filepath.Join(m.DataDir(), PluginPathFile), js, 0644)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,37 @@ package command
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/plugin/discovery"
|
||||
)
|
||||
|
||||
func TestPluginPath(t *testing.T) {
|
||||
td, err := ioutil.TempDir("", "tf")
|
||||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
pluginPath := []string{"a", "b", "c"}
|
||||
|
||||
m := Meta{}
|
||||
if err := m.storePluginPath(pluginPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
restoredPath, err := m.loadPluginPath()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(pluginPath, restoredPath) {
|
||||
t.Fatalf("expected plugin path %#v, got %#v", pluginPath, restoredPath)
|
||||
}
|
||||
}
|
||||
|
||||
// mockProviderInstaller is a discovery.PluginInstaller implementation that
|
||||
// is a mock for discovery.ProviderInstaller.
|
||||
type mockProviderInstaller struct {
|
||||
|
|
Loading…
Reference in New Issue