diff --git a/config.go b/config.go index e5b3e73ade..fc55e9f6bd 100644 --- a/config.go +++ b/config.go @@ -63,26 +63,55 @@ func ConfigDir() (string, error) { return configDir() } -// LoadConfig loads the CLI configuration from ".terraformrc" files. -func LoadConfig(path string) (*Config, error) { +// LoadConfig reads the CLI configuration from the various filesystem locations +// and from the environment, returning a merged configuration along with any +// diagnostics (errors and warnings) encountered along the way. +func LoadConfig() (*Config, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + configVal := BuiltinConfig // copy + config := &configVal + + if mainFilename, err := cliConfigFile(); err == nil { + if _, err := os.Stat(mainFilename); err == nil { + mainConfig, mainDiags := loadConfigFile(mainFilename) + diags = diags.Append(mainDiags) + config = config.Merge(mainConfig) + } + } + + if envConfig := EnvConfig(); envConfig != nil { + // envConfig takes precedence + config = envConfig.Merge(config) + } + + diags = diags.Append(config.Validate()) + + return config, diags +} + +// loadConfigFile loads the CLI configuration from ".terraformrc" files. +func loadConfigFile(path string) (*Config, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + result := &Config{} + // Read the HCL file and prepare for parsing d, err := ioutil.ReadFile(path) if err != nil { - return nil, fmt.Errorf( - "Error reading %s: %s", path, err) + diags = diags.Append(fmt.Errorf("Error reading %s: %s", path, err)) + return result, diags } // Parse it obj, err := hcl.Parse(string(d)) if err != nil { - return nil, fmt.Errorf( - "Error parsing %s: %s", path, err) + diags = diags.Append(fmt.Errorf("Error parsing %s: %s", path, err)) + return result, diags } // Build up the result - var result Config if err := hcl.DecodeObject(&result, obj); err != nil { - return nil, err + diags = diags.Append(fmt.Errorf("Error parsing %s: %s", path, err)) + return result, diags } // Replace all env vars @@ -97,7 +126,7 @@ func LoadConfig(path string) (*Config, error) { result.PluginCacheDir = os.ExpandEnv(result.PluginCacheDir) } - return &result, nil + return result, diags } // EnvConfig returns a Config populated from environment variables. diff --git a/config_test.go b/config_test.go index 28fcec92a8..479013a504 100644 --- a/config_test.go +++ b/config_test.go @@ -13,7 +13,7 @@ import ( const fixtureDir = "./test-fixtures" func TestLoadConfig(t *testing.T) { - c, err := LoadConfig(filepath.Join(fixtureDir, "config")) + c, err := loadConfigFile(filepath.Join(fixtureDir, "config")) if err != nil { t.Fatalf("err: %s", err) } @@ -34,7 +34,7 @@ func TestLoadConfig_env(t *testing.T) { defer os.Unsetenv("TFTEST") os.Setenv("TFTEST", "hello") - c, err := LoadConfig(filepath.Join(fixtureDir, "config-env")) + c, err := loadConfigFile(filepath.Join(fixtureDir, "config-env")) if err != nil { t.Fatalf("err: %s", err) } @@ -55,7 +55,7 @@ func TestLoadConfig_env(t *testing.T) { } func TestLoadConfig_credentials(t *testing.T) { - got, err := LoadConfig(filepath.Join(fixtureDir, "credentials")) + got, err := loadConfigFile(filepath.Join(fixtureDir, "credentials")) if err != nil { t.Fatal(err) } diff --git a/main.go b/main.go index 85a51c54a6..77f441d20a 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,6 @@ import ( "github.com/hashicorp/terraform/command/format" "github.com/hashicorp/terraform/helper/logging" "github.com/hashicorp/terraform/terraform" - "github.com/hashicorp/terraform/tfdiags" "github.com/mattn/go-colorable" "github.com/mattn/go-shellwords" "github.com/mitchellh/cli" @@ -111,6 +110,8 @@ func init() { } func wrappedMain() int { + var err error + // We always need to close the DebugInfo before we exit. defer terraform.CloseDebugInfo() @@ -121,60 +122,34 @@ func wrappedMain() int { log.Printf("[INFO] Go runtime version: %s", runtime.Version()) log.Printf("[INFO] CLI args: %#v", os.Args) - // Load the configuration - config := BuiltinConfig - - // Load the configuration file if we have one, that can be used to - // define extra providers and provisioners. - clicfgFile, err := cliConfigFile() - if err != nil { - Ui.Error(fmt.Sprintf("Error loading CLI configuration: \n\n%s", err)) - return 1 - } - - if clicfgFile != "" { - usrcfg, err := LoadConfig(clicfgFile) - if err != nil { - Ui.Error(fmt.Sprintf("Error loading CLI configuration: \n\n%s", err)) - return 1 - } - - config = *config.Merge(usrcfg) - } - - if envConfig := EnvConfig(); envConfig != nil { - // envConfig takes precedence - config = *envConfig.Merge(&config) - } - - log.Printf("[DEBUG] CLI Config is %#v", config) - - { - var diags tfdiags.Diagnostics - diags = diags.Append(config.Validate()) - if len(diags) > 0 { - Ui.Error("There are some problems with the CLI configuration:") - for _, diag := range diags { - earlyColor := &colorstring.Colorize{ - Colors: colorstring.DefaultColors, - Disable: true, // Disable color to be conservative until we know better - Reset: true, - } - Ui.Error(format.Diagnostic(diag, earlyColor, 78)) - } - if diags.HasErrors() { - Ui.Error("As a result of the above problems, Terraform may not behave as intended.\n\n") + config, diags := LoadConfig() + if len(diags) > 0 { + // Since we haven't instantiated a command.Meta yet, we need to do + // some things manually here and use some "safe" defaults for things + // that command.Meta could otherwise figure out in smarter ways. + Ui.Error("There are some problems with the CLI configuration:") + for _, diag := range diags { + earlyColor := &colorstring.Colorize{ + Colors: colorstring.DefaultColors, + Disable: true, // Disable color to be conservative until we know better + Reset: true, } + Ui.Error(format.Diagnostic(diag, earlyColor, 78)) + } + if diags.HasErrors() { + Ui.Error("As a result of the above problems, Terraform may not behave as intended.\n\n") + // We continue to run anyway, since Terraform has reasonable defaults. } } + log.Printf("[DEBUG] CLI config is %#v", config) // In tests, Commands may already be set to provide mock commands if Commands == nil { - initCommands(&config) + initCommands(config) } // Run checkpoint - go runCheckpoint(&config) + go runCheckpoint(config) // Make sure we clean up any managed plugins at the end of this defer plugin.CleanupClients()