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.
This commit is contained in:
Martin Atkins 2018-10-09 17:47:53 -07:00
parent 27d086d8bd
commit d63c2fdd8d
3 changed files with 26 additions and 4 deletions

View File

@ -62,9 +62,11 @@ func (c *ImportCommand) Run(args []string) int {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
// Parse the provided resource address. // Parse the provided resource address.
traversal, travDiags := hclsyntax.ParseTraversalAbs([]byte(args[0]), "<import-address>", hcl.Pos{Line: 1, Column: 1}) traversalSrc := []byte(args[0])
traversal, travDiags := hclsyntax.ParseTraversalAbs(traversalSrc, "<import-address>", hcl.Pos{Line: 1, Column: 1})
diags = diags.Append(travDiags) diags = diags.Append(travDiags)
if travDiags.HasErrors() { if travDiags.HasErrors() {
c.registerSynthConfigSource("<import-address>", traversalSrc) // so we can include a source snippet
c.showDiagnostics(diags) c.showDiagnostics(diags)
c.Ui.Info(importCommandInvalidAddressReference) c.Ui.Info(importCommandInvalidAddressReference)
return 1 return 1
@ -72,6 +74,7 @@ func (c *ImportCommand) Run(args []string) int {
addr, addrDiags := addrs.ParseAbsResourceInstance(traversal) addr, addrDiags := addrs.ParseAbsResourceInstance(traversal)
diags = diags.Append(addrDiags) diags = diags.Append(addrDiags)
if addrDiags.HasErrors() { if addrDiags.HasErrors() {
c.registerSynthConfigSource("<import-address>", traversalSrc) // so we can include a source snippet
c.showDiagnostics(diags) c.showDiagnostics(diags)
c.Ui.Info(importCommandInvalidAddressReference) c.Ui.Info(importCommandInvalidAddressReference)
return 1 return 1

View File

@ -594,7 +594,7 @@ func TestImport_dataResource(t *testing.T) {
} }
msg := ui.ErrorWriter.String() 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) 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() 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) 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() 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) t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg)
} }
} }

View File

@ -309,6 +309,25 @@ func (m *Meta) modulesDir() string {
return filepath.Join(m.DataDir(), "modules") 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 // initConfigLoader initializes the shared configuration loader if it isn't
// already initialized. // already initialized.
// //