From f8b1fe47dd45fedc19b1ad99ca453130f5c63c2c Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Wed, 9 Aug 2023 11:19:00 +0200 Subject: [PATCH] Make config errors more important during init operations (#33628) * make config errors more important during init operations * address comments --- 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 9b337b4c24..b652613fd3 100644 --- a/internal/command/init.go +++ b/internal/command/init.go @@ -262,24 +262,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, @@ -1187,7 +1188,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 15ef100ccd..c78264aa39 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) + } +} + func TestInit_tests(t *testing.T) { // Create a temporary working directory that is empty td := t.TempDir() diff --git a/internal/command/meta_backend.go b/internal/command/meta_backend.go index aaf20e9a7f..ea7c61c4eb 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" @@ -1398,6 +1399,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 0000000000..8e50cf0b0e --- /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 +}