diff --git a/config.go b/config.go index 04334a58e0..e5b3e73ade 100644 --- a/config.go +++ b/config.go @@ -8,7 +8,10 @@ import ( "os" "github.com/hashicorp/hcl" + "github.com/hashicorp/terraform/command" + "github.com/hashicorp/terraform/svchost" + "github.com/hashicorp/terraform/tfdiags" ) const pluginCacheDirEnvVar = "TF_PLUGIN_CACHE_DIR" @@ -115,6 +118,43 @@ func EnvConfig() *Config { return config } +// Validate checks for errors in the configuration that cannot be detected +// just by HCL decoding, returning any problems as diagnostics. +// +// On success, the returned diagnostics will return false from the HasErrors +// method. A non-nil diagnostics is not necessarily an error, since it may +// contain just warnings. +func (c *Config) Validate() tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + if c == nil { + return diags + } + + // FIXME: Right now our config parsing doesn't retain enough information + // to give proper source references to any errors. We should improve + // on this when we change the CLI config parser to use HCL2. + + // Check that all "credentials" blocks have valid hostnames. + for givenHost := range c.Credentials { + _, err := svchost.ForComparison(givenHost) + if err != nil { + diags = diags.Append( + fmt.Errorf("The credentials %q block has an invalid hostname: %s", givenHost, err), + ) + } + } + + // Should have zero or one "credentials_helper" blocks + if len(c.CredentialsHelpers) > 1 { + diags = diags.Append( + fmt.Errorf("No more than one credentials_helper block may be specified"), + ) + } + + return diags +} + // Merge merges two configurations and returns a third entirely // new configuration with the two merged. func (c1 *Config) Merge(c2 *Config) *Config { diff --git a/config_test.go b/config_test.go index 3884e9abdc..28fcec92a8 100644 --- a/config_test.go +++ b/config_test.go @@ -82,6 +82,71 @@ func TestLoadConfig_credentials(t *testing.T) { } } +func TestConfigValidate(t *testing.T) { + tests := map[string]struct { + Config *Config + DiagCount int + }{ + "nil": { + nil, + 0, + }, + "empty": { + &Config{}, + 0, + }, + "credentials good": { + &Config{ + Credentials: map[string]map[string]interface{}{ + "example.com": map[string]interface{}{ + "token": "foo", + }, + }, + }, + 0, + }, + "credentials with bad hostname": { + &Config{ + Credentials: map[string]map[string]interface{}{ + "example..com": map[string]interface{}{ + "token": "foo", + }, + }, + }, + 1, // credentials block has invalid hostname + }, + "credentials helper good": { + &Config{ + CredentialsHelpers: map[string]*ConfigCredentialsHelper{ + "foo": {}, + }, + }, + 0, + }, + "credentials helper too many": { + &Config{ + CredentialsHelpers: map[string]*ConfigCredentialsHelper{ + "foo": {}, + "bar": {}, + }, + }, + 1, // no more than one credentials_helper block allowed + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + diags := test.Config.Validate() + if len(diags) != test.DiagCount { + t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount) + for _, diag := range diags { + t.Logf("- %#v", diag.Description()) + } + } + }) + } +} + func TestConfig_Merge(t *testing.T) { c1 := &Config{ Providers: map[string]string{ diff --git a/main.go b/main.go index 67c4bfcb6c..85a51c54a6 100644 --- a/main.go +++ b/main.go @@ -11,9 +11,13 @@ import ( "strings" "sync" + "github.com/mitchellh/colorstring" + "github.com/hashicorp/go-plugin" + "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" @@ -145,6 +149,25 @@ func wrappedMain() int { 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") + } + } + } + // In tests, Commands may already be set to provide mock commands if Commands == nil { initCommands(&config)