From cb17a9a607446da9bd62b951dc11095a4b110eca Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 1 Sep 2017 16:20:25 -0700 Subject: [PATCH] main: allow enabling plugin caching via config file or environment Either the environment variable TF_PLUGIN_CACHE_DIR or a setting in the CLI config file (~/.terraformrc on Unix) allow opting in to the plugin caching behavior. This is opt-in because for new users we don't want to pollute their system with extra directories they don't know about. By opting in to caching, the user is assuming the responsibility to occasionally prune the cache over time as older plugins become stale and unused. --- commands.go | 1 + config.go | 33 +++++++++++++++++++++++++++++++++ main.go | 7 +++++++ 3 files changed, 41 insertions(+) diff --git a/commands.go b/commands.go index eec2f520e..045b783d2 100644 --- a/commands.go +++ b/commands.go @@ -38,6 +38,7 @@ func initCommands(config *Config) { Ui: Ui, RunningInAutomation: inAutomation, + PluginCacheDir: config.PluginCacheDir, } // The command list is included in the terraform -help diff --git a/config.go b/config.go index d9391c3ce..518832e46 100644 --- a/config.go +++ b/config.go @@ -11,6 +11,8 @@ import ( "github.com/hashicorp/terraform/command" ) +const pluginCacheDirEnvVar = "TF_PLUGIN_CACHE_DIR" + // Config is the structure of the configuration for the Terraform CLI. // // This is not the configuration for Terraform itself. That is in the @@ -21,6 +23,10 @@ type Config struct { DisableCheckpoint bool `hcl:"disable_checkpoint"` DisableCheckpointSignature bool `hcl:"disable_checkpoint_signature"` + + // If set, enables local caching of plugins in this directory to + // avoid repeatedly re-downloading over the Internet. + PluginCacheDir string `hcl:"plugin_cache_dir"` } // BuiltinConfig is the built-in defaults for the configuration. These @@ -75,9 +81,31 @@ func LoadConfig(path string) (*Config, error) { result.Provisioners[k] = os.ExpandEnv(v) } + if result.PluginCacheDir != "" { + result.PluginCacheDir = os.ExpandEnv(result.PluginCacheDir) + } + return &result, nil } +// EnvConfig returns a Config populated from environment variables. +// +// Any values specified in this config should override those set in the +// configuration file. +func EnvConfig() *Config { + config := &Config{} + + if envPluginCacheDir := os.Getenv(pluginCacheDirEnvVar); envPluginCacheDir != "" { + // No Expandenv here, because expanding environment variables inside + // an environment variable would be strange and seems unnecessary. + // (User can expand variables into the value while setting it using + // standard shell features.) + config.PluginCacheDir = envPluginCacheDir + } + + return config +} + // Merge merges two configurations and returns a third entirely // new configuration with the two merged. func (c1 *Config) Merge(c2 *Config) *Config { @@ -105,5 +133,10 @@ func (c1 *Config) Merge(c2 *Config) *Config { result.DisableCheckpoint = c1.DisableCheckpoint || c2.DisableCheckpoint result.DisableCheckpointSignature = c1.DisableCheckpointSignature || c2.DisableCheckpointSignature + result.PluginCacheDir = c1.PluginCacheDir + if result.PluginCacheDir == "" { + result.PluginCacheDir = c2.PluginCacheDir + } + return &result } diff --git a/main.go b/main.go index 62e4da603..67c4bfcb6 100644 --- a/main.go +++ b/main.go @@ -138,6 +138,13 @@ func wrappedMain() int { config = *config.Merge(usrcfg) } + if envConfig := EnvConfig(); envConfig != nil { + // envConfig takes precedence + config = *envConfig.Merge(&config) + } + + log.Printf("[DEBUG] CLI Config is %#v", config) + // In tests, Commands may already be set to provide mock commands if Commands == nil { initCommands(&config)