Merge pull request #24254 from hashicorp/jbardin/state-mv

state mv should always use target address to determine each mode.
This commit is contained in:
James Bardin 2020-03-03 10:21:03 -05:00 committed by GitHub
commit a3be47584a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 9 deletions

View File

@ -280,31 +280,30 @@ func (c *StateMvCommand) Run(args []string) int {
fromResourceAddr := addrFrom.ContainingResource() fromResourceAddr := addrFrom.ContainingResource()
fromResource := ssFrom.Resource(fromResourceAddr) fromResource := ssFrom.Resource(fromResourceAddr)
fromProviderAddr := fromResource.ProviderConfig fromProviderAddr := fromResource.ProviderConfig
fromEachMode := fromResource.EachMode
ssFrom.ForgetResourceInstanceAll(addrFrom) ssFrom.ForgetResourceInstanceAll(addrFrom)
ssFrom.RemoveResourceIfEmpty(fromResourceAddr) ssFrom.RemoveResourceIfEmpty(fromResourceAddr)
// since this is moving an instance, we can infer the target
// mode from the address.
toEachMode := eachModeForInstanceKey(addrTo.Resource.Key)
rs := stateTo.Resource(addrTo.ContainingResource()) rs := stateTo.Resource(addrTo.ContainingResource())
if rs == nil { if rs == nil {
// If we're moving to an address without an index then that // If we're moving to an address without an index then that
// suggests the user's intent is to establish both the // suggests the user's intent is to establish both the
// resource and the instance at the same time (since the // resource and the instance at the same time (since the
// address covers both). If there's an index in the // address covers both). If there's an index in the
// target then allow creating the new instance here, // target then allow creating the new instance here.
// inferring the mode from how the new address was parsed.
if addrTo.Resource.Key != addrs.NoKey {
fromEachMode = eachModeForInstanceKey(addrTo.Resource.Key)
}
resourceAddr := addrTo.ContainingResource() resourceAddr := addrTo.ContainingResource()
stateTo.SyncWrapper().SetResourceMeta( stateTo.SyncWrapper().SetResourceMeta(
resourceAddr, resourceAddr,
fromEachMode, toEachMode,
fromProviderAddr, // in this case, we bring the provider along as if we were moving the whole resource fromProviderAddr, // in this case, we bring the provider along as if we were moving the whole resource
) )
rs = stateTo.Resource(resourceAddr) rs = stateTo.Resource(resourceAddr)
} }
rs.EachMode = toEachMode
rs.Instances[addrTo.Resource.Key] = is rs.Instances[addrTo.Resource.Key] = is
} }
default: default:

View File

@ -68,7 +68,7 @@ func TestStateMv(t *testing.T) {
"test_instance.bar", "test_instance.bar",
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
} }
// Test it is correct // Test it is correct
@ -80,6 +80,70 @@ func TestStateMv(t *testing.T) {
t.Fatalf("bad: %#v", backups) t.Fatalf("bad: %#v", backups)
} }
testStateOutput(t, backups[0], testStateMvOutputOriginal) testStateOutput(t, backups[0], testStateMvOutputOriginal)
// Change the single instance to a counted instance
args = []string{
"-state", statePath,
"test_instance.bar",
"test_instance.bar[0]",
}
if code := c.Run(args); code != 0 {
t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
}
// extract the resource and verify the mode
s := testStateRead(t, statePath)
addr, diags := addrs.ParseAbsResourceStr("test_instance.bar")
if diags.HasErrors() {
t.Fatal(diags.Err())
}
i := s.Resource(addr)
if i.EachMode != states.EachList {
t.Fatalf("expected each mode List, got %s", i.EachMode)
}
// change from list to map
args = []string{
"-state", statePath,
"test_instance.bar[0]",
"test_instance.bar[\"baz\"]",
}
if code := c.Run(args); code != 0 {
t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
}
// extract the resource and verify the mode
s = testStateRead(t, statePath)
addr, diags = addrs.ParseAbsResourceStr("test_instance.bar")
if diags.HasErrors() {
t.Fatal(diags.Err())
}
i = s.Resource(addr)
if i.EachMode != states.EachMap {
t.Fatalf("expected each mode Map, got %s", i.EachMode)
}
// change from from map back to single
args = []string{
"-state", statePath,
"test_instance.bar[\"baz\"]",
"test_instance.bar",
}
if code := c.Run(args); code != 0 {
t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
}
// extract the resource and verify the mode
s = testStateRead(t, statePath)
addr, diags = addrs.ParseAbsResourceStr("test_instance.bar")
if diags.HasErrors() {
t.Fatal(diags.Err())
}
i = s.Resource(addr)
if i.EachMode != states.NoEach {
t.Fatalf("expected each mode NoEach, got %s", i.EachMode)
}
} }
func TestStateMv_resourceToInstance(t *testing.T) { func TestStateMv_resourceToInstance(t *testing.T) {