Merge pull request #25523 from hashicorp/alisdair/fix-state-mv-panic

command: Fix state mv for only resource in module
This commit is contained in:
Alisdair McDiarmid 2020-08-11 12:18:32 -04:00 committed by GitHub
commit c5377ca50f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 78 additions and 5 deletions

View File

@ -190,10 +190,7 @@ func (c *StateMvCommand) Run(args []string) int {
return 1
}
diags = diags.Append(c.validateResourceMove(addrFrom, addrTo))
if stateTo.Module(addrTo.Module) == nil {
// moving something to a mew module, so we need to ensure it exists
stateTo.EnsureModule(addrTo.Module)
}
if stateTo.Resource(addrTo) != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
@ -223,7 +220,7 @@ func (c *StateMvCommand) Run(args []string) int {
// Update the address before adding it to the state.
rs.Addr = addrTo
stateTo.Module(addrTo.Module).Resources[addrTo.Resource.String()] = rs
stateTo.EnsureModule(addrTo.Module).Resources[addrTo.Resource.String()] = rs
}
case addrs.AbsResourceInstance:

View File

@ -1196,6 +1196,62 @@ func TestStateMv_fromBackendToLocal(t *testing.T) {
testStateOutput(t, statePath, testStateMvOriginal_backend)
}
// This test covers moving the only resource in a module to a new address in
// that module, which triggers the maybePruneModule functionality. This caused
// a panic report: https://github.com/hashicorp/terraform/issues/25520
func TestStateMv_onlyResourceInModule(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.Child("foo", addrs.NoKey)),
&states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`),
Status: states.ObjectReady,
},
addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("test"),
Module: addrs.RootModule,
},
)
})
statePath := testStateFile(t, state)
testStateOutput(t, statePath, testStateMvOnlyResourceInModule_original)
p := testProvider()
ui := new(cli.MockUi)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
},
}
args := []string{
"-state", statePath,
"module.foo.test_instance.foo",
"module.foo.test_instance.bar",
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
// Test it is correct
testStateOutput(t, statePath, testStateMvOnlyResourceInModule_output)
// Test we have backups
backups := testStateBackups(t, filepath.Dir(statePath))
if len(backups) != 1 {
t.Fatalf("bad: %#v", backups)
}
testStateOutput(t, backups[0], testStateMvOnlyResourceInModule_original)
}
const testStateMvOutputOriginal = `
test_instance.baz:
ID = foo
@ -1513,3 +1569,23 @@ test_instance.baz:
bar = value
foo = value
`
const testStateMvOnlyResourceInModule_original = `
<no state>
module.foo:
test_instance.foo.0:
ID = bar
provider = provider["registry.terraform.io/-/test"]
bar = value
foo = value
`
const testStateMvOnlyResourceInModule_output = `
<no state>
module.foo:
test_instance.bar.0:
ID = bar
provider = provider["registry.terraform.io/-/test"]
bar = value
foo = value
`