From 2d47325e595799e95ad1efcf3f9512ff6abea5ae Mon Sep 17 00:00:00 2001 From: The Terraform Team <52939924+teamterraform@users.noreply.github.com> Date: Wed, 9 Aug 2023 02:34:27 -0700 Subject: [PATCH] Backport of Make config errors more important during init operations into v1.5 (#33650) * backport of commit 4bfad538b016fe7953ffba95b9cd6ba00d6a57d3 * backport of commit 537c315ce39209508d57802f340703f6b94d56ad --------- Co-authored-by: Liam Cervante --- internal/command/init.go | 40 +++++++++-------- internal/command/init_test.go | 44 ++++++++++++++++--- internal/command/meta_backend.go | 13 +++++- .../main.tf | 10 +++++ 4 files changed, 80 insertions(+), 27 deletions(-) create mode 100644 internal/command/testdata/init-syntax-invalid-backend-attribute-invalid/main.tf diff --git a/internal/command/init.go b/internal/command/init.go index f8eaab66cdb1..91772604092f 100644 --- a/internal/command/init.go +++ b/internal/command/init.go @@ -247,24 +247,25 @@ func (c *InitCommand) Run(args []string) int { return 1 } - // If we pass the core version check, we want to show any errors from initializing the backend next, - // which will include syntax errors from loading the configuration. However, there's a special case - // where we are unable to load the backend from configuration or state _and_ the configuration has - // errors. In that case, we want to show a slightly friendlier error message for newcomers. - showBackendDiags := back != nil || rootModEarly.Backend != nil || rootModEarly.CloudConfig != nil - if showBackendDiags { - diags = diags.Append(backDiags) - if backDiags.HasErrors() { - c.showDiagnostics(diags) - return 1 - } - } else { - diags = diags.Append(earlyConfDiags) - if earlyConfDiags.HasErrors() { - c.Ui.Error(strings.TrimSpace(errInitConfigError)) - c.showDiagnostics(diags) - return 1 - } + // We've passed the core version check, now we can show errors from the + // configuration and backend initialisation. + + // Now, we can check the diagnostics from the early configuration and the + // backend. + diags = diags.Append(earlyConfDiags) + diags = diags.Append(backDiags) + if earlyConfDiags.HasErrors() { + c.Ui.Error(strings.TrimSpace(errInitConfigError)) + c.showDiagnostics(diags) + return 1 + } + + // Now, we can show any errors from initializing the backend, but we won't + // show the errInitConfigError preamble as we didn't detect problems with + // the early configuration. + if backDiags.HasErrors() { + c.showDiagnostics(diags) + return 1 } // If everything is ok with the core version check and backend initialization, @@ -1149,7 +1150,8 @@ func (c *InitCommand) Synopsis() string { } const errInitConfigError = ` -[reset]There are some problems with the configuration, described below. +[reset]Terraform encountered problems during initialisation, including problems +with the configuration, described below. The Terraform configuration must be valid before initialization so that Terraform can determine which modules and providers need to be installed. diff --git a/internal/command/init_test.go b/internal/command/init_test.go index e81db3e205b8..5162bc6788d3 100644 --- a/internal/command/init_test.go +++ b/internal/command/init_test.go @@ -2642,7 +2642,7 @@ func TestInit_invalidSyntaxNoBackend(t *testing.T) { } errStr := ui.ErrorWriter.String() - if subStr := "There are some problems with the configuration, described below"; !strings.Contains(errStr, subStr) { + if subStr := "Terraform encountered problems during initialisation, including problems\nwith the configuration, described below."; !strings.Contains(errStr, subStr) { t.Errorf("Error output should include preamble\nwant substr: %s\ngot:\n%s", subStr, errStr) } if subStr := "Error: Unsupported block type"; !strings.Contains(errStr, subStr) { @@ -2671,7 +2671,7 @@ func TestInit_invalidSyntaxWithBackend(t *testing.T) { } errStr := ui.ErrorWriter.String() - if subStr := "There are some problems with the configuration, described below"; !strings.Contains(errStr, subStr) { + if subStr := "Terraform encountered problems during initialisation, including problems\nwith the configuration, described below."; !strings.Contains(errStr, subStr) { t.Errorf("Error output should include preamble\nwant substr: %s\ngot:\n%s", subStr, errStr) } if subStr := "Error: Unsupported block type"; !strings.Contains(errStr, subStr) { @@ -2700,17 +2700,49 @@ func TestInit_invalidSyntaxInvalidBackend(t *testing.T) { } errStr := ui.ErrorWriter.String() - if subStr := "There are some problems with the configuration, described below"; strings.Contains(errStr, subStr) { - t.Errorf("Error output should not include preamble\nwant substr: %s\ngot:\n%s", subStr, errStr) + if subStr := "Terraform encountered problems during initialisation, including problems\nwith the configuration, described below."; !strings.Contains(errStr, subStr) { + t.Errorf("Error output should include preamble\nwant substr: %s\ngot:\n%s", subStr, errStr) } - if subStr := "Error: Unsupported block type"; strings.Contains(errStr, subStr) { - t.Errorf("Error output should not mention syntax errors\nwant substr: %s\ngot:\n%s", subStr, errStr) + if subStr := "Error: Unsupported block type"; !strings.Contains(errStr, subStr) { + t.Errorf("Error output should mention syntax errors\nwant substr: %s\ngot:\n%s", subStr, errStr) } if subStr := "Error: Unsupported backend type"; !strings.Contains(errStr, subStr) { t.Errorf("Error output should mention the invalid backend\nwant substr: %s\ngot:\n%s", subStr, errStr) } } +func TestInit_invalidSyntaxBackendAttribute(t *testing.T) { + td := t.TempDir() + testCopyDir(t, testFixturePath("init-syntax-invalid-backend-attribute-invalid"), td) + defer testChdir(t, td)() + + ui := cli.NewMockUi() + view, _ := testView(t) + m := Meta{ + Ui: ui, + View: view, + } + + c := &InitCommand{ + Meta: m, + } + + if code := c.Run(nil); code == 0 { + t.Fatalf("succeeded, but was expecting error\nstdout:\n%s\nstderr:\n%s", ui.OutputWriter, ui.ErrorWriter) + } + + errStr := ui.ErrorWriter.String() + if subStr := "Terraform encountered problems during initialisation, including problems\nwith the configuration, described below."; !strings.Contains(errStr, subStr) { + t.Errorf("Error output should include preamble\nwant substr: %s\ngot:\n%s", subStr, errStr) + } + if subStr := "Error: Invalid character"; !strings.Contains(errStr, subStr) { + t.Errorf("Error output should mention the invalid character\nwant substr: %s\ngot:\n%s", subStr, errStr) + } + if subStr := "Error: Invalid expression"; !strings.Contains(errStr, subStr) { + t.Errorf("Error output should mention the invalid expression\nwant substr: %s\ngot:\n%s", subStr, errStr) + } +} + // newMockProviderSource is a helper to succinctly construct a mock provider // source that contains a set of packages matching the given provider versions // that are available for installation (from temporary local files). diff --git a/internal/command/meta_backend.go b/internal/command/meta_backend.go index 908e87b08cdc..d56dfce051d3 100644 --- a/internal/command/meta_backend.go +++ b/internal/command/meta_backend.go @@ -19,6 +19,9 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" + ctyjson "github.com/zclconf/go-cty/cty/json" + "github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/cloud" "github.com/hashicorp/terraform/internal/command/arguments" @@ -29,8 +32,6 @@ import ( "github.com/hashicorp/terraform/internal/states/statemgr" "github.com/hashicorp/terraform/internal/terraform" "github.com/hashicorp/terraform/internal/tfdiags" - "github.com/zclconf/go-cty/cty" - ctyjson "github.com/zclconf/go-cty/cty/json" backendInit "github.com/hashicorp/terraform/internal/backend/init" backendLocal "github.com/hashicorp/terraform/internal/backend/local" @@ -1372,6 +1373,14 @@ func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.V return nil, cty.NilVal, diags } + if !configVal.IsWhollyKnown() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Unknown values within backend definition", + "The `terraform` configuration block should contain only concrete and static values. Another diagnostic should contain more information about which part of the configuration is problematic.")) + return nil, cty.NilVal, diags + } + // TODO: test if m.Input() { var err error diff --git a/internal/command/testdata/init-syntax-invalid-backend-attribute-invalid/main.tf b/internal/command/testdata/init-syntax-invalid-backend-attribute-invalid/main.tf new file mode 100644 index 000000000000..8e50cf0b0ed3 --- /dev/null +++ b/internal/command/testdata/init-syntax-invalid-backend-attribute-invalid/main.tf @@ -0,0 +1,10 @@ + +terraform { + backend "local" { + path = $invalid + } +} + +variable "input" { + type = string +}