diff --git a/command/state_mv.go b/command/state_mv.go index 912f128c8..20282382a 100644 --- a/command/state_mv.go +++ b/command/state_mv.go @@ -139,7 +139,7 @@ func (c *StateMvCommand) Run(args []string) int { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, msgInvalidTarget, - fmt.Sprintf("Cannot move %s to %s: the target must also be a module.", addrFrom, addrTo), + fmt.Sprintf("Cannot move %s to %s: the target must also be a module.", addrFrom, destAddr), )) c.showDiagnostics(diags) return 1 @@ -184,7 +184,7 @@ func (c *StateMvCommand) Run(args []string) int { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, msgInvalidTarget, - fmt.Sprintf("Cannot move %s to %s: the target must also be a whole resource.", addrFrom, addrTo), + fmt.Sprintf("Cannot move %s to %s: the source is a whole resource (not a resource instance) so the target must also be a whole resource.", addrFrom, destAddr), )) c.showDiagnostics(diags) return 1 @@ -231,7 +231,7 @@ func (c *StateMvCommand) Run(args []string) int { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, msgInvalidTarget, - fmt.Sprintf("Cannot move %s to %s: the target must also be a resource instance.", addrFrom, addrTo), + fmt.Sprintf("Cannot move %s to %s: the target must also be a resource instance.", addrFrom, destAddr), )) c.showDiagnostics(diags) return 1 diff --git a/command/state_mv_test.go b/command/state_mv_test.go index 7a631c44f..b9c21e5b7 100644 --- a/command/state_mv_test.go +++ b/command/state_mv_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp" "github.com/mitchellh/cli" "github.com/hashicorp/terraform/addrs" @@ -148,6 +149,7 @@ func TestStateMv(t *testing.T) { } func TestStateMv_resourceToInstance(t *testing.T) { + // A single resource (no count defined) state := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( addrs.Resource{ @@ -236,6 +238,72 @@ test_instance.baz: testStateOutput(t, backups[0], testStateMvOutputOriginal) } +func TestStateMv_resourceToInstanceErr(t *testing.T) { + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + s.SetResourceProvider( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "bar", + }.Absolute(addrs.RootModuleInstance), + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + statePath := testStateFile(t, state) + + p := testProvider() + ui := new(cli.MockUi) + c := &StateMvCommand{ + StateMeta{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + }, + }, + } + + args := []string{ + "-state", statePath, + "test_instance.foo", + "test_instance.bar[0]", + } + + if code := c.Run(args); code == 0 { + t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String()) + } + + expectedErr := ` +Error: Invalid target address + +Cannot move test_instance.foo to test_instance.bar[0]: the source is a whole +resource (not a resource instance) so the target must also be a whole +resource. + +` + errOutput := ui.ErrorWriter.String() + if errOutput != expectedErr { + t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", errOutput, expectedErr) + t.Errorf("%s", cmp.Diff(errOutput, expectedErr)) + } +} func TestStateMv_instanceToResource(t *testing.T) { state := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent(