diff --git a/internal/command/init.go b/internal/command/init.go index ba0d6db934..2e6d13d9da 100644 --- a/internal/command/init.go +++ b/internal/command/init.go @@ -244,14 +244,28 @@ func (c *InitCommand) Run(args []string) int { return 1 } - // If the core version is OK, show any underlying config errors uncovered when initializing - // the backend. - diags = diags.Append(backDiags) - if backDiags.HasErrors() { - c.showDiagnostics(diags) - 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 + } } + // If everything is ok with the core version check and backend initialization, + // show other errors from loading the full configuration tree. diags = diags.Append(confDiags) if confDiags.HasErrors() { c.Ui.Error(strings.TrimSpace(errInitConfigError)) diff --git a/internal/command/init_test.go b/internal/command/init_test.go index 2609677bdf..e81db3e205 100644 --- a/internal/command/init_test.go +++ b/internal/command/init_test.go @@ -18,6 +18,7 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configschema" @@ -2620,6 +2621,96 @@ func TestInit_invalidBuiltInProviders(t *testing.T) { } } +func TestInit_invalidSyntaxNoBackend(t *testing.T) { + td := t.TempDir() + testCopyDir(t, testFixturePath("init-syntax-invalid-no-backend"), 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 := "There are some problems with 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 mention the syntax problem\nwant substr: %s\ngot:\n%s", subStr, errStr) + } +} + +func TestInit_invalidSyntaxWithBackend(t *testing.T) { + td := t.TempDir() + testCopyDir(t, testFixturePath("init-syntax-invalid-with-backend"), 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 := "There are some problems with 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 mention the syntax problem\nwant substr: %s\ngot:\n%s", subStr, errStr) + } +} + +func TestInit_invalidSyntaxInvalidBackend(t *testing.T) { + td := t.TempDir() + testCopyDir(t, testFixturePath("init-syntax-invalid-backend-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 := "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 := "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 backend type"; !strings.Contains(errStr, subStr) { + t.Errorf("Error output should mention the invalid backend\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/testdata/init-syntax-invalid-backend-invalid/main.tf b/internal/command/testdata/init-syntax-invalid-backend-invalid/main.tf new file mode 100644 index 0000000000..4fb3f33692 --- /dev/null +++ b/internal/command/testdata/init-syntax-invalid-backend-invalid/main.tf @@ -0,0 +1,7 @@ +terraform { + backend "nonexistent" {} +} + +bad_block { +} + diff --git a/internal/command/testdata/init-syntax-invalid-no-backend/main.tf b/internal/command/testdata/init-syntax-invalid-no-backend/main.tf new file mode 100644 index 0000000000..5f1451d26c --- /dev/null +++ b/internal/command/testdata/init-syntax-invalid-no-backend/main.tf @@ -0,0 +1,3 @@ +bad_block { +} + diff --git a/internal/command/testdata/init-syntax-invalid-with-backend/main.tf b/internal/command/testdata/init-syntax-invalid-with-backend/main.tf new file mode 100644 index 0000000000..2ea4406cc1 --- /dev/null +++ b/internal/command/testdata/init-syntax-invalid-with-backend/main.tf @@ -0,0 +1,7 @@ +terraform { + backend "local" {} +} + +bad_block { +} +