From d63c2fdd8d8c97f5dece0b4bd22ae75b844356bb Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 9 Oct 2018 17:47:53 -0700 Subject: [PATCH] command: Show snippet of invalid resource addresses in import If we fail to parse the resource address given to "terraform import" then it's helpful to produce a "source code" snippet of what the user provided so they might see more precisely which part of the address was invalid. --- command/import.go | 5 ++++- command/import_test.go | 6 +++--- command/meta_config.go | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/command/import.go b/command/import.go index 93ab80495c..6d72adae42 100644 --- a/command/import.go +++ b/command/import.go @@ -62,9 +62,11 @@ func (c *ImportCommand) Run(args []string) int { var diags tfdiags.Diagnostics // Parse the provided resource address. - traversal, travDiags := hclsyntax.ParseTraversalAbs([]byte(args[0]), "", hcl.Pos{Line: 1, Column: 1}) + traversalSrc := []byte(args[0]) + traversal, travDiags := hclsyntax.ParseTraversalAbs(traversalSrc, "", hcl.Pos{Line: 1, Column: 1}) diags = diags.Append(travDiags) if travDiags.HasErrors() { + c.registerSynthConfigSource("", traversalSrc) // so we can include a source snippet c.showDiagnostics(diags) c.Ui.Info(importCommandInvalidAddressReference) return 1 @@ -72,6 +74,7 @@ func (c *ImportCommand) Run(args []string) int { addr, addrDiags := addrs.ParseAbsResourceInstance(traversal) diags = diags.Append(addrDiags) if addrDiags.HasErrors() { + c.registerSynthConfigSource("", traversalSrc) // so we can include a source snippet c.showDiagnostics(diags) c.Ui.Info(importCommandInvalidAddressReference) return 1 diff --git a/command/import_test.go b/command/import_test.go index 0f2c381d75..148182147a 100644 --- a/command/import_test.go +++ b/command/import_test.go @@ -594,7 +594,7 @@ func TestImport_dataResource(t *testing.T) { } msg := ui.ErrorWriter.String() - if want := `resource address must refer to a managed resource`; !strings.Contains(msg, want) { + if want := `A managed resource address is required`; !strings.Contains(msg, want) { t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) } } @@ -624,7 +624,7 @@ func TestImport_invalidResourceAddr(t *testing.T) { } msg := ui.ErrorWriter.String() - if want := `invalid resource address "bananas"`; !strings.Contains(msg, want) { + if want := `Error: Invalid address`; !strings.Contains(msg, want) { t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) } } @@ -654,7 +654,7 @@ func TestImport_targetIsModule(t *testing.T) { } msg := ui.ErrorWriter.String() - if want := `resource address must include a full resource spec`; !strings.Contains(msg, want) { + if want := `Error: Invalid address`; !strings.Contains(msg, want) { t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) } } diff --git a/command/meta_config.go b/command/meta_config.go index dd2315c506..5a7385a311 100644 --- a/command/meta_config.go +++ b/command/meta_config.go @@ -309,6 +309,25 @@ func (m *Meta) modulesDir() string { return filepath.Join(m.DataDir(), "modules") } +// registerSynthConfigSource allows commands to add synthetic additional source +// buffers to the config loader's cache of sources (as returned by +// configSources), which is useful when a command is directly parsing something +// from the command line that may produce diagnostics, so that diagnostic +// snippets can still be produced. +// +// If this is called before a configLoader has been initialized then it will +// try to initialize the loader but ignore any initialization failure, turning +// the call into a no-op. (We presume that a caller will later call a different +// function that also initializes the config loader as a side effect, at which +// point those errors can be returned.) +func (m *Meta) registerSynthConfigSource(filename string, src []byte) { + loader, err := m.initConfigLoader() + if err != nil || loader == nil { + return // treated as no-op, since this is best-effort + } + loader.Parser().ForceFileSource(filename, src) +} + // initConfigLoader initializes the shared configuration loader if it isn't // already initialized. //