diff --git a/command/command_test.go b/command/command_test.go index 4f5acfe38..6a80036e9 100644 --- a/command/command_test.go +++ b/command/command_test.go @@ -879,3 +879,11 @@ func normalizeJSON(t *testing.T, src []byte) string { } return buf.String() } + +func mustResourceAddr(s string) addrs.AbsResource { + addr, diags := addrs.ParseAbsResourceStr(s) + if diags.HasErrors() { + panic(diags.Err()) + } + return addr +} diff --git a/command/state_mv.go b/command/state_mv.go index 829ad923b..33014b3dc 100644 --- a/command/state_mv.go +++ b/command/state_mv.go @@ -314,6 +314,28 @@ func (c *StateMvCommand) Run(args []string) int { fmt.Sprintf("Cannot move %s: Terraform doesn't know how to move this object.", rawAddrFrom), )) } + + // Look for any dependencies that may be effected and + // remove them to ensure they are recreated in full. + for _, mod := range stateTo.Modules { + for _, res := range mod.Resources { + for _, ins := range res.Instances { + if ins.Current == nil { + continue + } + + for _, dep := range ins.Current.Dependencies { + // check both directions here, since we may be moving + // an instance which is in a resource, or a module + // which can contain a resource. + if dep.TargetContains(rawAddrFrom) || rawAddrFrom.TargetContains(dep) { + ins.Current.Dependencies = nil + break + } + } + } + } + } } if dryRun { diff --git a/command/state_mv_test.go b/command/state_mv_test.go index 25addaefd..c8b70f41a 100644 --- a/command/state_mv_test.go +++ b/command/state_mv_test.go @@ -36,8 +36,9 @@ func TestStateMv(t *testing.T) { Name: "baz", }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), &states.ResourceInstanceObjectSrc{ - AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), - Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + Dependencies: []addrs.AbsResource{mustResourceAddr("test_instance.foo")}, }, addrs.ProviderConfig{Type: addrs.NewLegacyProvider("test")}.Absolute(addrs.RootModuleInstance), ) @@ -96,8 +97,9 @@ func TestStateMv_resourceToInstance(t *testing.T) { Name: "baz", }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), &states.ResourceInstanceObjectSrc{ - AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), - Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + Dependencies: []addrs.AbsResource{mustResourceAddr("test_instance.foo")}, }, addrs.ProviderConfig{Type: addrs.NewLegacyProvider("test")}.Absolute(addrs.RootModuleInstance), ) @@ -445,8 +447,9 @@ func TestStateMv_backupExplicit(t *testing.T) { Name: "baz", }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), &states.ResourceInstanceObjectSrc{ - AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), - Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + Dependencies: []addrs.AbsResource{mustResourceAddr("test_instance.foo")}, }, addrs.ProviderConfig{Type: addrs.NewLegacyProvider("test")}.Absolute(addrs.RootModuleInstance), ) @@ -897,42 +900,40 @@ func TestStateMv_toNewModule(t *testing.T) { } testStateOutput(t, stateOutPath2, testStateMvModuleNewModule_stateOut) } + func TestStateMv_withinBackend(t *testing.T) { td := tempDir(t) copy.CopyDir(testFixturePath("backend-unchanged"), td) defer os.RemoveAll(td) defer testChdir(t, td)() - state := &terraform.State{ - Modules: []*terraform.ModuleState{ - &terraform.ModuleState{ - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test_instance.foo": &terraform.ResourceState{ - Type: "test_instance", - Primary: &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "value", - "bar": "value", - }, - }, - }, - - "test_instance.baz": &terraform.ResourceState{ - Type: "test_instance", - Primary: &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "foo": "value", - "bar": "value", - }, - }, - }, - }, + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), + Status: states.ObjectReady, }, - }, - } + addrs.ProviderConfig{Type: addrs.NewLegacyProvider("test")}.Absolute(addrs.RootModuleInstance), + ) + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "baz", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + Dependencies: []addrs.AbsResource{mustResourceAddr("test_instance.foo")}, + }, + addrs.ProviderConfig{Type: addrs.NewLegacyProvider("test")}.Absolute(addrs.RootModuleInstance), + ) + }) // the local backend state file is "foo" statePath := "local-state.tfstate" @@ -944,7 +945,7 @@ func TestStateMv_withinBackend(t *testing.T) { } defer f.Close() - if err := terraform.WriteState(state, f); err != nil { + if err := writeStateForTesting(state, f); err != nil { t.Fatal(err) } @@ -1057,6 +1058,9 @@ test_instance.baz: provider = provider.test bar = value foo = value + + Dependencies: + test_instance.foo test_instance.foo: ID = bar provider = provider.test