From 000e8607060ea12e48bff44693d539ce5d8a975b Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 15 Jun 2017 14:26:12 -0400 Subject: [PATCH] Add plugin dir scaffolding add pluginDir to command.Meta, the flag to initialize it, and the methods to save and restore it. --- command/apply.go | 6 ++++++ command/command.go | 4 ++++ command/init.go | 9 +++++++++ command/meta.go | 6 ++++++ command/plan.go | 6 ++++++ command/plugins.go | 41 +++++++++++++++++++++++++++++++++++++++++ command/refresh.go | 6 ++++++ 7 files changed, 78 insertions(+) diff --git a/command/apply.go b/command/apply.go index 9c8c4904a..a4bbba7a6 100644 --- a/command/apply.go +++ b/command/apply.go @@ -65,6 +65,12 @@ func (c *ApplyCommand) Run(args []string) int { return 1 } + // Check for user-supplied plugin path + if c.pluginPath, err = c.loadPluginPath(); err != nil { + c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err)) + return 1 + } + if !c.Destroy && maybeInit { // We need the pwd for the getter operation below pwd, err := os.Getwd() diff --git a/command/command.go b/command/command.go index 2205c53e3..bb8c7be5c 100644 --- a/command/command.go +++ b/command/command.go @@ -15,6 +15,10 @@ var test bool = false // DefaultDataDir is the default directory for storing local data. const DefaultDataDir = ".terraform" +// PluginPathFile is the name of the file in the data dir which stores the list +// of directories supplied by the user with the `-plugin-dir` flag during init. +const PluginPathFile = "plugin_path" + // DefaultPluginVendorDir is the location in the config directory to look for // user-added plugin binaries. Terraform only reads from this path if it // exists, it is never created by terraform. diff --git a/command/init.go b/command/init.go index 5d33063e9..c98eb1f90 100644 --- a/command/init.go +++ b/command/init.go @@ -32,6 +32,7 @@ type InitCommand struct { func (c *InitCommand) Run(args []string) int { var flagBackend, flagGet, flagGetPlugins, flagUpgrade bool var flagConfigExtra map[string]interface{} + var flagPluginPath FlagStringSlice args = c.Meta.process(args, false) cmdFlags := c.flagSet("init") @@ -44,12 +45,15 @@ func (c *InitCommand) Run(args []string) int { cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure") cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "") + cmdFlags.Var(&flagPluginPath, "plugin-dir", "plugin directory") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 } + c.pluginPath = flagPluginPath + // set getProvider if we don't have a test version already if c.providerInstaller == nil { c.providerInstaller = &discovery.ProviderInstaller{ @@ -67,6 +71,11 @@ func (c *InitCommand) Run(args []string) int { return 1 } + if err := c.storePluginPath(c.pluginPath); err != nil { + c.Ui.Error(fmt.Sprintf("Error saving -plugin-path values: %s", err)) + return 1 + } + // Get our pwd. We don't always need it but always getting it is easier // than the logic to determine if it is or isn't needed. pwd, err := os.Getwd() diff --git a/command/meta.go b/command/meta.go index ffd9ec62e..4463d2077 100644 --- a/command/meta.go +++ b/command/meta.go @@ -48,6 +48,12 @@ type Meta struct { // DataDir method. dataDir string + // pluginPath is a user defined set of directories to look for plugins. + // This is set during init with the `-plugin-dir` flag, saved to a file in + // the data directory. + // This overrides all other search paths when discoverying plugins. + pluginPath []string + // Override certain behavior for tests within this package testingOverrides *testingOverrides diff --git a/command/plan.go b/command/plan.go index a38a22872..1edc179e5 100644 --- a/command/plan.go +++ b/command/plan.go @@ -45,6 +45,12 @@ func (c *PlanCommand) Run(args []string) int { return 1 } + // Check for user-supplied plugin path + if c.pluginPath, err = c.loadPluginPath(); err != nil { + c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err)) + return 1 + } + // Check if the path is a plan plan, err := c.Plan(configPath) if err != nil { diff --git a/command/plugins.go b/command/plugins.go index 75106a9a8..07a155255 100644 --- a/command/plugins.go +++ b/command/plugins.go @@ -1,8 +1,11 @@ package command import ( + "encoding/json" "fmt" + "io/ioutil" "log" + "os" "os/exec" "path/filepath" "runtime" @@ -69,6 +72,40 @@ func (r *multiVersionProviderResolver) ResolveProviders( return factories, errs } +// store the user-supplied path for plugin discovery +func (m *Meta) storePluginPath(pluginPath []string) error { + if len(pluginPath) == 0 { + return nil + } + + js, err := json.MarshalIndent(pluginPath, "", " ") + if err != nil { + return err + } + + return ioutil.WriteFile(filepath.Join(m.DataDir(), PluginPathFile), js, 0644) +} + +// Load the user-defined plugin search path into Meta.pluginPath if the file +// exists. +func (m *Meta) loadPluginPath() ([]string, error) { + js, err := ioutil.ReadFile(filepath.Join(m.DataDir(), PluginPathFile)) + if os.IsNotExist(err) { + return nil, nil + } + + if err != nil { + return nil, err + } + + var pluginPath []string + if err := json.Unmarshal(js, &pluginPath); err != nil { + return nil, err + } + + return pluginPath, nil +} + // the default location for automatically installed plugins func (m *Meta) pluginDir() string { return filepath.Join(m.DataDir(), "plugins", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)) @@ -80,6 +117,10 @@ func (m *Meta) pluginDir() string { // of the same plugin version are found, but newer versions always override // older versions where both satisfy the provider version constraints. func (m *Meta) pluginDirs(includeAutoInstalled bool) []string { + // user defined paths take precedence + if len(m.pluginPath) > 0 { + return m.pluginPath + } // When searching the following directories, earlier entries get precedence // if the same plugin version is found twice, but newer versions will diff --git a/command/refresh.go b/command/refresh.go index 72623ed45..7ffa9d890 100644 --- a/command/refresh.go +++ b/command/refresh.go @@ -44,6 +44,12 @@ func (c *RefreshCommand) Run(args []string) int { return 1 } + // Check for user-supplied plugin path + if c.pluginPath, err = c.loadPluginPath(); err != nil { + c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err)) + return 1 + } + var conf *config.Config if mod != nil { conf = mod.Config()