terraform: state mv tests
This commit is contained in:
parent
05cbb5c0ea
commit
a44c8b8760
|
@ -223,6 +223,87 @@ func TestStateMv_noState(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStateMv_stateOutNew_nestedModule(t *testing.T) {
|
||||||
|
state := &terraform.State{
|
||||||
|
Modules: []*terraform.ModuleState{
|
||||||
|
&terraform.ModuleState{
|
||||||
|
Path: []string{"root"},
|
||||||
|
Resources: map[string]*terraform.ResourceState{},
|
||||||
|
},
|
||||||
|
|
||||||
|
&terraform.ModuleState{
|
||||||
|
Path: []string{"root", "foo"},
|
||||||
|
Resources: map[string]*terraform.ResourceState{},
|
||||||
|
},
|
||||||
|
|
||||||
|
&terraform.ModuleState{
|
||||||
|
Path: []string{"root", "foo", "child1"},
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
&terraform.ModuleState{
|
||||||
|
Path: []string{"root", "foo", "child2"},
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
statePath := testStateFile(t, state)
|
||||||
|
stateOutPath := statePath + ".out"
|
||||||
|
|
||||||
|
p := testProvider()
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &StateMvCommand{
|
||||||
|
Meta: Meta{
|
||||||
|
ContextOpts: testCtxConfig(p),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"-state", statePath,
|
||||||
|
"-state-out", stateOutPath,
|
||||||
|
"module.foo",
|
||||||
|
"module.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, stateOutPath, testStateMvNestedModule_stateOut)
|
||||||
|
testStateOutput(t, statePath, testStateMvNestedModule_stateOutSrc)
|
||||||
|
|
||||||
|
// Test we have backups
|
||||||
|
backups := testStateBackups(t, filepath.Dir(statePath))
|
||||||
|
if len(backups) != 1 {
|
||||||
|
t.Fatalf("bad: %#v", backups)
|
||||||
|
}
|
||||||
|
testStateOutput(t, backups[0], testStateMvNestedModule_stateOutOriginal)
|
||||||
|
}
|
||||||
|
|
||||||
const testStateMvOutputOriginal = `
|
const testStateMvOutputOriginal = `
|
||||||
test_instance.baz:
|
test_instance.baz:
|
||||||
ID = foo
|
ID = foo
|
||||||
|
@ -245,6 +326,41 @@ test_instance.baz:
|
||||||
foo = value
|
foo = value
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testStateMvNestedModule_stateOut = `
|
||||||
|
module.bar:
|
||||||
|
<no state>
|
||||||
|
module.bar.child1:
|
||||||
|
test_instance.foo:
|
||||||
|
ID = bar
|
||||||
|
bar = value
|
||||||
|
foo = value
|
||||||
|
module.bar.child2:
|
||||||
|
test_instance.foo:
|
||||||
|
ID = bar
|
||||||
|
bar = value
|
||||||
|
foo = value
|
||||||
|
`
|
||||||
|
|
||||||
|
const testStateMvNestedModule_stateOutSrc = `
|
||||||
|
<no state>
|
||||||
|
`
|
||||||
|
|
||||||
|
const testStateMvNestedModule_stateOutOriginal = `
|
||||||
|
<no state>
|
||||||
|
module.foo:
|
||||||
|
<no state>
|
||||||
|
module.foo.child1:
|
||||||
|
test_instance.foo:
|
||||||
|
ID = bar
|
||||||
|
bar = value
|
||||||
|
foo = value
|
||||||
|
module.foo.child2:
|
||||||
|
test_instance.foo:
|
||||||
|
ID = bar
|
||||||
|
bar = value
|
||||||
|
foo = value
|
||||||
|
`
|
||||||
|
|
||||||
const testStateMvOutput_stateOut = `
|
const testStateMvOutput_stateOut = `
|
||||||
test_instance.bar:
|
test_instance.bar:
|
||||||
ID = bar
|
ID = bar
|
||||||
|
|
|
@ -808,6 +808,12 @@ func (m *ModuleState) IsRoot() bool {
|
||||||
return reflect.DeepEqual(m.Path, rootModulePath)
|
return reflect.DeepEqual(m.Path, rootModulePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDescendent returns true if other is a descendent of this module.
|
||||||
|
func (m *ModuleState) IsDescendent(other *ModuleState) bool {
|
||||||
|
i := len(m.Path)
|
||||||
|
return len(other.Path) > i && reflect.DeepEqual(other.Path[:i], m.Path)
|
||||||
|
}
|
||||||
|
|
||||||
// Orphans returns a list of keys of resources that are in the State
|
// Orphans returns a list of keys of resources that are in the State
|
||||||
// but aren't present in the configuration itself. Hence, these keys
|
// but aren't present in the configuration itself. Hence, these keys
|
||||||
// represent the state of resources that are orphans.
|
// represent the state of resources that are orphans.
|
||||||
|
|
|
@ -11,6 +11,11 @@ import (
|
||||||
// module cannot be moved to a resource address, however a resource can be
|
// module cannot be moved to a resource address, however a resource can be
|
||||||
// moved to a module address (it retains the same name, under that resource).
|
// moved to a module address (it retains the same name, under that resource).
|
||||||
//
|
//
|
||||||
|
// The item can also be a []*ModuleState, which is the case for nested
|
||||||
|
// modules. In this case, Add will expect the zero-index to be the top-most
|
||||||
|
// module to add and will only nest children from there. For semantics, this
|
||||||
|
// is equivalent to module => module.
|
||||||
|
//
|
||||||
// The full semantics of Add:
|
// The full semantics of Add:
|
||||||
//
|
//
|
||||||
// ┌───────────────────────┬───────────────────────┬───────────────────────┐
|
// ┌───────────────────────┬───────────────────────┬───────────────────────┐
|
||||||
|
@ -65,7 +70,26 @@ func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
|
func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
|
||||||
src := raw.(*ModuleState).deepcopy()
|
// raw can be either *ModuleState or []*ModuleState. The former means
|
||||||
|
// we're moving just one module. The latter means we're moving a module
|
||||||
|
// and children.
|
||||||
|
root := raw
|
||||||
|
var rest []*ModuleState
|
||||||
|
if list, ok := raw.([]*ModuleState); ok {
|
||||||
|
// We need at least one item
|
||||||
|
if len(list) == 0 {
|
||||||
|
return fmt.Errorf("module move with no value to: %s", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first item is always the root
|
||||||
|
root = list[0]
|
||||||
|
if len(list) > 1 {
|
||||||
|
rest = list[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the actual module state
|
||||||
|
src := root.(*ModuleState).deepcopy()
|
||||||
|
|
||||||
// If the target module exists, it is an error
|
// If the target module exists, it is an error
|
||||||
path := append([]string{"root"}, addr.Path...)
|
path := append([]string{"root"}, addr.Path...)
|
||||||
|
@ -97,6 +121,22 @@ func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add all the children if we have them
|
||||||
|
for _, item := range rest {
|
||||||
|
// If item isn't a descendent of our root, then ignore it
|
||||||
|
if !src.IsDescendent(item) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is! Strip the leading prefix and attach that to our address
|
||||||
|
extra := item.Path[len(src.Path)+1:]
|
||||||
|
addrCopy := addr.Copy()
|
||||||
|
addrCopy.Path = append(addrCopy.Path, extra)
|
||||||
|
|
||||||
|
// Add it
|
||||||
|
s.Add(fromAddr.String(), addrCopy.String(), item)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +267,8 @@ func detectValueAddLoc(raw interface{}) stateAddLoc {
|
||||||
switch raw.(type) {
|
switch raw.(type) {
|
||||||
case *ModuleState:
|
case *ModuleState:
|
||||||
return stateAddModule
|
return stateAddModule
|
||||||
|
case []*ModuleState:
|
||||||
|
return stateAddModule
|
||||||
case *ResourceState:
|
case *ResourceState:
|
||||||
return stateAddResource
|
return stateAddResource
|
||||||
case *InstanceState:
|
case *InstanceState:
|
||||||
|
|
|
@ -194,6 +194,85 @@ func TestStateAdd(t *testing.T) {
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"ModuleState with children => Module Addr (new)": {
|
||||||
|
false,
|
||||||
|
"module.foo",
|
||||||
|
"module.bar",
|
||||||
|
|
||||||
|
[]*ModuleState{
|
||||||
|
&ModuleState{
|
||||||
|
Path: rootModulePath,
|
||||||
|
Resources: map[string]*ResourceState{},
|
||||||
|
},
|
||||||
|
|
||||||
|
&ModuleState{
|
||||||
|
Path: []string{"root", "foo", "child1"},
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"test_instance.foo": &ResourceState{
|
||||||
|
Type: "test_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
&ModuleState{
|
||||||
|
Path: []string{"root", "foo", "child2"},
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"test_instance.foo": &ResourceState{
|
||||||
|
Type: "test_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Should be ignored
|
||||||
|
&ModuleState{
|
||||||
|
Path: []string{"root", "bar", "child2"},
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"test_instance.foo": &ResourceState{
|
||||||
|
Type: "test_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
&State{},
|
||||||
|
&State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{
|
||||||
|
Path: []string{"root", "bar", "child1"},
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"test_instance.foo": &ResourceState{
|
||||||
|
Type: "test_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
&ModuleState{
|
||||||
|
Path: []string{"root", "bar", "child2"},
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"test_instance.foo": &ResourceState{
|
||||||
|
Type: "test_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
"ResourceState => Resource Addr (new)": {
|
"ResourceState => Resource Addr (new)": {
|
||||||
false,
|
false,
|
||||||
"aws_instance.bar",
|
"aws_instance.bar",
|
||||||
|
|
Loading…
Reference in New Issue