Allow moving instances to new resources
If a state mv target happens to be a resource that doesn't exist, allow the creation of the new resource inferring the EachMode from the target address.
This commit is contained in:
parent
ba9cb786c3
commit
a5cb36b34c
|
@ -278,7 +278,9 @@ func (c *StateMvCommand) Run(args []string) int {
|
||||||
c.Ui.Output(fmt.Sprintf("%s %q to %q", prefix, addrFrom.String(), args[1]))
|
c.Ui.Output(fmt.Sprintf("%s %q to %q", prefix, addrFrom.String(), args[1]))
|
||||||
if !dryRun {
|
if !dryRun {
|
||||||
fromResourceAddr := addrFrom.ContainingResource()
|
fromResourceAddr := addrFrom.ContainingResource()
|
||||||
fromProviderAddr := ssFrom.Resource(fromResourceAddr).ProviderConfig
|
fromResource := ssFrom.Resource(fromResourceAddr)
|
||||||
|
fromProviderAddr := fromResource.ProviderConfig
|
||||||
|
fromEachMode := fromResource.EachMode
|
||||||
ssFrom.ForgetResourceInstanceAll(addrFrom)
|
ssFrom.ForgetResourceInstanceAll(addrFrom)
|
||||||
ssFrom.RemoveResourceIfEmpty(fromResourceAddr)
|
ssFrom.RemoveResourceIfEmpty(fromResourceAddr)
|
||||||
|
|
||||||
|
@ -287,22 +289,17 @@ func (c *StateMvCommand) Run(args []string) int {
|
||||||
// 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), but if there's an index in the
|
// address covers both). If there's an index in the
|
||||||
// target then the resource must already exist.
|
// target then allow creating the new instance here,
|
||||||
|
// inferring the mode from how the new address was parsed.
|
||||||
if addrTo.Resource.Key != addrs.NoKey {
|
if addrTo.Resource.Key != addrs.NoKey {
|
||||||
diags = diags.Append(tfdiags.Sourceless(
|
fromEachMode = eachModeForInstanceKey(addrTo.Resource.Key)
|
||||||
tfdiags.Error,
|
|
||||||
msgInvalidTarget,
|
|
||||||
fmt.Sprintf("Cannot move to %s: %s does not exist in the current state.", addrTo, addrTo.ContainingResource()),
|
|
||||||
))
|
|
||||||
c.showDiagnostics(diags)
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceAddr := addrTo.ContainingResource()
|
resourceAddr := addrTo.ContainingResource()
|
||||||
stateTo.SyncWrapper().SetResourceMeta(
|
stateTo.SyncWrapper().SetResourceMeta(
|
||||||
resourceAddr,
|
resourceAddr,
|
||||||
states.NoEach,
|
fromEachMode,
|
||||||
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)
|
||||||
|
@ -358,6 +355,20 @@ func (c *StateMvCommand) Run(args []string) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func eachModeForInstanceKey(key addrs.InstanceKey) states.EachMode {
|
||||||
|
switch key.(type) {
|
||||||
|
case addrs.IntKey:
|
||||||
|
return states.EachList
|
||||||
|
case addrs.StringKey:
|
||||||
|
return states.EachMap
|
||||||
|
default:
|
||||||
|
if key == addrs.NoKey {
|
||||||
|
return states.NoEach
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("don't know an each mode for instance key %#v", key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sourceObjectAddrs takes a single source object address and expands it to
|
// sourceObjectAddrs takes a single source object address and expands it to
|
||||||
// potentially multiple objects that need to be handled within it.
|
// potentially multiple objects that need to be handled within it.
|
||||||
//
|
//
|
||||||
|
|
|
@ -237,6 +237,74 @@ test_instance.foo.0:
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStateMv_instanceToNewResource(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.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
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[0]",
|
||||||
|
"test_instance.bar[\"new\"]",
|
||||||
|
}
|
||||||
|
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, `
|
||||||
|
test_instance.bar["new"]:
|
||||||
|
ID = bar
|
||||||
|
provider = provider.test
|
||||||
|
bar = value
|
||||||
|
foo = value
|
||||||
|
`)
|
||||||
|
|
||||||
|
// now move the instance to a new resource in a new module
|
||||||
|
args = []string{
|
||||||
|
"-state", statePath,
|
||||||
|
"test_instance.bar[\"new\"]",
|
||||||
|
"module.test.test_instance.baz[\"new\"]",
|
||||||
|
}
|
||||||
|
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, `
|
||||||
|
<no state>
|
||||||
|
module.test:
|
||||||
|
test_instance.baz["new"]:
|
||||||
|
ID = bar
|
||||||
|
provider = provider.test
|
||||||
|
bar = value
|
||||||
|
foo = value
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestStateMv_differentResourceTypes(t *testing.T) {
|
func TestStateMv_differentResourceTypes(t *testing.T) {
|
||||||
state := states.BuildState(func(s *states.SyncState) {
|
state := states.BuildState(func(s *states.SyncState) {
|
||||||
s.SetResourceInstanceCurrent(
|
s.SetResourceInstanceCurrent(
|
||||||
|
|
Loading…
Reference in New Issue