Merge pull request #29330 from hashicorp/jbardin/move
refactoring: CanChainFrom and NestedWithin
This commit is contained in:
commit
11561b22cd
|
@ -37,6 +37,10 @@ func (c ModuleCall) Absolute(moduleAddr ModuleInstance) AbsModuleCall {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c ModuleCall) Equal(other ModuleCall) bool {
|
||||||
|
return c.Name == other.Name
|
||||||
|
}
|
||||||
|
|
||||||
// AbsModuleCall is the address of a "module" block relative to the root
|
// AbsModuleCall is the address of a "module" block relative to the root
|
||||||
// of the configuration.
|
// of the configuration.
|
||||||
//
|
//
|
||||||
|
@ -70,6 +74,10 @@ func (c AbsModuleCall) Instance(key InstanceKey) ModuleInstance {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c AbsModuleCall) Equal(other AbsModuleCall) bool {
|
||||||
|
return c.Module.Equal(other.Module) && c.Call.Equal(other.Call)
|
||||||
|
}
|
||||||
|
|
||||||
type absModuleCallInstanceKey string
|
type absModuleCallInstanceKey string
|
||||||
|
|
||||||
func (c AbsModuleCall) UniqueKey() UniqueKey {
|
func (c AbsModuleCall) UniqueKey() UniqueKey {
|
||||||
|
|
|
@ -192,6 +192,8 @@ func (e *MoveEndpointInModule) SelectsModule(addr ModuleInstance) bool {
|
||||||
if callPart.Name != relAddr.Call.Name {
|
if callPart.Name != relAddr.Call.Name {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
relMatch = relAddr.Module.Child(relAddr.Call.Name, callPart.InstanceKey)
|
||||||
case AbsResource:
|
case AbsResource:
|
||||||
relMatch = relAddr.Module
|
relMatch = relAddr.Module
|
||||||
case AbsResourceInstance:
|
case AbsResourceInstance:
|
||||||
|
@ -203,11 +205,13 @@ func (e *MoveEndpointInModule) SelectsModule(addr ModuleInstance) bool {
|
||||||
if len(relPart) != len(relMatch) {
|
if len(relPart) != len(relMatch) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range relMatch {
|
for i := range relMatch {
|
||||||
if relPart[i] != relMatch[i] {
|
if relPart[i] != relMatch[i] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +222,35 @@ func (e *MoveEndpointInModule) SelectsModule(addr ModuleInstance) bool {
|
||||||
// the reciever is the "to" from one statement and the other given address
|
// the reciever is the "to" from one statement and the other given address
|
||||||
// is the "from" of another statement.
|
// is the "from" of another statement.
|
||||||
func (e *MoveEndpointInModule) CanChainFrom(other *MoveEndpointInModule) bool {
|
func (e *MoveEndpointInModule) CanChainFrom(other *MoveEndpointInModule) bool {
|
||||||
// TODO: implement
|
eSub := e.relSubject
|
||||||
|
oSub := other.relSubject
|
||||||
|
|
||||||
|
switch oSub := oSub.(type) {
|
||||||
|
case AbsModuleCall:
|
||||||
|
switch eSub := eSub.(type) {
|
||||||
|
case AbsModuleCall:
|
||||||
|
return eSub.Equal(oSub)
|
||||||
|
}
|
||||||
|
|
||||||
|
case ModuleInstance:
|
||||||
|
switch eSub := eSub.(type) {
|
||||||
|
case ModuleInstance:
|
||||||
|
return eSub.Equal(oSub)
|
||||||
|
}
|
||||||
|
|
||||||
|
case AbsResource:
|
||||||
|
switch eSub := eSub.(type) {
|
||||||
|
case AbsResource:
|
||||||
|
return eSub.Equal(oSub)
|
||||||
|
}
|
||||||
|
|
||||||
|
case AbsResourceInstance:
|
||||||
|
switch eSub := eSub.(type) {
|
||||||
|
case AbsResourceInstance:
|
||||||
|
return eSub.Equal(oSub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +258,52 @@ func (e *MoveEndpointInModule) CanChainFrom(other *MoveEndpointInModule) bool {
|
||||||
// contained within one of the objects that the given other address could
|
// contained within one of the objects that the given other address could
|
||||||
// select.
|
// select.
|
||||||
func (e *MoveEndpointInModule) NestedWithin(other *MoveEndpointInModule) bool {
|
func (e *MoveEndpointInModule) NestedWithin(other *MoveEndpointInModule) bool {
|
||||||
// TODO: implement
|
eSub := e.relSubject
|
||||||
|
oSub := other.relSubject
|
||||||
|
|
||||||
|
switch oSub := oSub.(type) {
|
||||||
|
case AbsModuleCall:
|
||||||
|
withinModuleCall := func(mod ModuleInstance, call AbsModuleCall) bool {
|
||||||
|
// parent modules don't match at all
|
||||||
|
if !call.Module.IsAncestor(mod) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
rem := mod[len(call.Module):]
|
||||||
|
return rem[0].Name == call.Call.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Module calls can contain module instances, resources, and resource
|
||||||
|
// instances.
|
||||||
|
switch eSub := eSub.(type) {
|
||||||
|
case AbsResource:
|
||||||
|
return withinModuleCall(eSub.Module, oSub)
|
||||||
|
|
||||||
|
case AbsResourceInstance:
|
||||||
|
return withinModuleCall(eSub.Module, oSub)
|
||||||
|
|
||||||
|
case ModuleInstance:
|
||||||
|
return withinModuleCall(eSub, oSub)
|
||||||
|
}
|
||||||
|
|
||||||
|
case ModuleInstance:
|
||||||
|
// Module instances can contain resources and resource instances.
|
||||||
|
switch eSub := eSub.(type) {
|
||||||
|
case AbsResource:
|
||||||
|
return eSub.Module.Equal(oSub) || oSub.IsAncestor(eSub.Module)
|
||||||
|
|
||||||
|
case AbsResourceInstance:
|
||||||
|
return eSub.Module.Equal(oSub) || oSub.IsAncestor(eSub.Module)
|
||||||
|
}
|
||||||
|
|
||||||
|
case AbsResource:
|
||||||
|
// A resource can only contain a resource instance.
|
||||||
|
switch eSub := eSub.(type) {
|
||||||
|
case AbsResourceInstance:
|
||||||
|
return eSub.ContainingResource().Equal(oSub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1074,3 +1074,284 @@ func TestAbsResourceMoveDestination(t *testing.T) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMoveEndpointChainAndNested(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Endpoint, Other AbsMoveable
|
||||||
|
CanChainFrom, NestedWithin bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Endpoint: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Call: ModuleCall{Name: "bar"},
|
||||||
|
},
|
||||||
|
Other: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Call: ModuleCall{Name: "bar"},
|
||||||
|
},
|
||||||
|
CanChainFrom: true,
|
||||||
|
NestedWithin: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Other: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Call: ModuleCall{Name: "bar"},
|
||||||
|
},
|
||||||
|
CanChainFrom: false,
|
||||||
|
NestedWithin: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: mustParseModuleInstanceStr("module.foo[2].module.bar[2]"),
|
||||||
|
Other: AbsModuleCall{
|
||||||
|
Module: RootModuleInstance,
|
||||||
|
Call: ModuleCall{Name: "foo"},
|
||||||
|
},
|
||||||
|
CanChainFrom: false,
|
||||||
|
NestedWithin: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].module.bar.resource.baz").ContainingResource(),
|
||||||
|
Other: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Call: ModuleCall{Name: "bar"},
|
||||||
|
},
|
||||||
|
CanChainFrom: false,
|
||||||
|
NestedWithin: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].module.bar[3].resource.baz[2]"),
|
||||||
|
Other: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Call: ModuleCall{Name: "bar"},
|
||||||
|
},
|
||||||
|
CanChainFrom: false,
|
||||||
|
NestedWithin: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Call: ModuleCall{Name: "bar"},
|
||||||
|
},
|
||||||
|
Other: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
CanChainFrom: false,
|
||||||
|
NestedWithin: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Other: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
CanChainFrom: true,
|
||||||
|
NestedWithin: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
|
||||||
|
Other: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
CanChainFrom: false,
|
||||||
|
NestedWithin: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].module.bar.resource.baz"),
|
||||||
|
Other: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
CanChainFrom: false,
|
||||||
|
NestedWithin: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Call: ModuleCall{Name: "bar"},
|
||||||
|
},
|
||||||
|
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
|
||||||
|
CanChainFrom: false,
|
||||||
|
NestedWithin: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
|
||||||
|
CanChainFrom: false,
|
||||||
|
NestedWithin: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
|
||||||
|
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
|
||||||
|
CanChainFrom: true,
|
||||||
|
NestedWithin: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
|
||||||
|
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz[2]").ContainingResource(),
|
||||||
|
CanChainFrom: false,
|
||||||
|
NestedWithin: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Endpoint: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Call: ModuleCall{Name: "bar"},
|
||||||
|
},
|
||||||
|
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
|
||||||
|
CanChainFrom: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
|
||||||
|
CanChainFrom: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
|
||||||
|
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
|
||||||
|
CanChainFrom: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
|
||||||
|
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
|
||||||
|
CanChainFrom: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("[%02d]%s.CanChainFrom(%s)", i, test.Endpoint, test.Other),
|
||||||
|
func(t *testing.T) {
|
||||||
|
endpoint := &MoveEndpointInModule{
|
||||||
|
relSubject: test.Endpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
other := &MoveEndpointInModule{
|
||||||
|
relSubject: test.Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoint.CanChainFrom(other) != test.CanChainFrom {
|
||||||
|
t.Errorf("expected %s CanChainFrom %s == %t", test.Endpoint, test.Other, test.CanChainFrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoint.NestedWithin(other) != test.NestedWithin {
|
||||||
|
t.Errorf("expected %s NestedWithin %s == %t", test.Endpoint, test.Other, test.NestedWithin)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectsModule(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Endpoint *MoveEndpointInModule
|
||||||
|
Addr ModuleInstance
|
||||||
|
Selects bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Endpoint: &MoveEndpointInModule{
|
||||||
|
relSubject: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.foo[2]"),
|
||||||
|
Call: ModuleCall{Name: "bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Addr: mustParseModuleInstanceStr("module.foo[2].module.bar[1]"),
|
||||||
|
Selects: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: &MoveEndpointInModule{
|
||||||
|
module: mustParseModuleInstanceStr("module.foo").Module(),
|
||||||
|
relSubject: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.bar[2]"),
|
||||||
|
Call: ModuleCall{Name: "baz"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Addr: mustParseModuleInstanceStr("module.foo[2].module.bar[2].module.baz"),
|
||||||
|
Selects: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: &MoveEndpointInModule{
|
||||||
|
module: mustParseModuleInstanceStr("module.foo").Module(),
|
||||||
|
relSubject: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.bar[2]"),
|
||||||
|
Call: ModuleCall{Name: "baz"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Addr: mustParseModuleInstanceStr("module.foo[2].module.bar[1].module.baz"),
|
||||||
|
Selects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: &MoveEndpointInModule{
|
||||||
|
relSubject: AbsModuleCall{
|
||||||
|
Module: mustParseModuleInstanceStr("module.bar"),
|
||||||
|
Call: ModuleCall{Name: "baz"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Addr: mustParseModuleInstanceStr("module.bar[1].module.baz"),
|
||||||
|
Selects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: &MoveEndpointInModule{
|
||||||
|
module: mustParseModuleInstanceStr("module.foo").Module(),
|
||||||
|
relSubject: mustParseAbsResourceInstanceStr(`module.bar.resource.name["key"]`),
|
||||||
|
},
|
||||||
|
Addr: mustParseModuleInstanceStr(`module.foo[1].module.bar`),
|
||||||
|
Selects: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: &MoveEndpointInModule{
|
||||||
|
relSubject: mustParseModuleInstanceStr(`module.bar.module.baz["key"]`),
|
||||||
|
},
|
||||||
|
Addr: mustParseModuleInstanceStr(`module.bar.module.baz["key"]`),
|
||||||
|
Selects: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: &MoveEndpointInModule{
|
||||||
|
relSubject: mustParseAbsResourceInstanceStr(`module.bar.module.baz["key"].resource.name`).ContainingResource(),
|
||||||
|
},
|
||||||
|
Addr: mustParseModuleInstanceStr(`module.bar.module.baz["key"]`),
|
||||||
|
Selects: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: &MoveEndpointInModule{
|
||||||
|
module: mustParseModuleInstanceStr("module.nope").Module(),
|
||||||
|
relSubject: mustParseAbsResourceInstanceStr(`module.bar.resource.name["key"]`),
|
||||||
|
},
|
||||||
|
Addr: mustParseModuleInstanceStr(`module.foo[1].module.bar`),
|
||||||
|
Selects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: &MoveEndpointInModule{
|
||||||
|
relSubject: mustParseModuleInstanceStr(`module.bar.module.baz["key"]`),
|
||||||
|
},
|
||||||
|
Addr: mustParseModuleInstanceStr(`module.bar.module.baz["nope"]`),
|
||||||
|
Selects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Endpoint: &MoveEndpointInModule{
|
||||||
|
relSubject: mustParseAbsResourceInstanceStr(`module.nope.module.baz["key"].resource.name`).ContainingResource(),
|
||||||
|
},
|
||||||
|
Addr: mustParseModuleInstanceStr(`module.bar.module.baz["key"]`),
|
||||||
|
Selects: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("[%02d]%s.SelectsModule(%s)", i, test.Endpoint, test.Addr),
|
||||||
|
func(t *testing.T) {
|
||||||
|
if test.Endpoint.SelectsModule(test.Addr) != test.Selects {
|
||||||
|
t.Errorf("expected %s SelectsModule %s == %t", test.Endpoint, test.Addr, test.Selects)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustParseAbsResourceInstanceStr(s string) AbsResourceInstance {
|
||||||
|
r, diags := ParseAbsResourceInstanceStr(s)
|
||||||
|
if diags.HasErrors() {
|
||||||
|
panic(diags.ErrWithWarnings().Error())
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ type MoveResult struct {
|
||||||
// ApplyMoves expects exclusive access to the given state while it's running.
|
// ApplyMoves expects exclusive access to the given state while it's running.
|
||||||
// Don't read or write any part of the state structure until ApplyMoves returns.
|
// Don't read or write any part of the state structure until ApplyMoves returns.
|
||||||
func ApplyMoves(stmts []MoveStatement, state *states.State) map[addrs.UniqueKey]MoveResult {
|
func ApplyMoves(stmts []MoveStatement, state *states.State) map[addrs.UniqueKey]MoveResult {
|
||||||
|
results := make(map[addrs.UniqueKey]MoveResult)
|
||||||
|
|
||||||
// The methodology here is to construct a small graph of all of the move
|
// The methodology here is to construct a small graph of all of the move
|
||||||
// statements where the edges represent where a particular statement
|
// statements where the edges represent where a particular statement
|
||||||
// is either chained from or nested inside the effect of another statement.
|
// is either chained from or nested inside the effect of another statement.
|
||||||
|
@ -40,19 +42,18 @@ func ApplyMoves(stmts []MoveStatement, state *states.State) map[addrs.UniqueKey]
|
||||||
// at all. The separate validation step should detect this and return
|
// at all. The separate validation step should detect this and return
|
||||||
// an error.
|
// an error.
|
||||||
if len(g.Cycles()) != 0 {
|
if len(g.Cycles()) != 0 {
|
||||||
return nil
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
// The starting nodes are the ones that don't depend on any other nodes.
|
// The starting nodes are the ones that don't depend on any other nodes.
|
||||||
startNodes := make(dag.Set, len(stmts))
|
startNodes := make(dag.Set, len(stmts))
|
||||||
for _, v := range g.Vertices() {
|
for _, v := range g.Vertices() {
|
||||||
if len(g.UpEdges(v)) == 0 {
|
if len(g.DownEdges(v)) == 0 {
|
||||||
startNodes.Add(v)
|
startNodes.Add(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results := make(map[addrs.UniqueKey]MoveResult)
|
g.ReverseDepthFirstWalk(startNodes, func(v dag.Vertex, depth int) error {
|
||||||
g.DepthFirstWalk(startNodes, func(v dag.Vertex, depth int) error {
|
|
||||||
stmt := v.(*MoveStatement)
|
stmt := v.(*MoveStatement)
|
||||||
|
|
||||||
for _, ms := range state.Modules {
|
for _, ms := range state.Modules {
|
||||||
|
@ -147,9 +148,9 @@ func ApplyMoves(stmts []MoveStatement, state *states.State) map[addrs.UniqueKey]
|
||||||
// may contain cycles and other sorts of invalidity.
|
// may contain cycles and other sorts of invalidity.
|
||||||
func buildMoveStatementGraph(stmts []MoveStatement) *dag.AcyclicGraph {
|
func buildMoveStatementGraph(stmts []MoveStatement) *dag.AcyclicGraph {
|
||||||
g := &dag.AcyclicGraph{}
|
g := &dag.AcyclicGraph{}
|
||||||
for _, stmt := range stmts {
|
for i := range stmts {
|
||||||
// The graph nodes are pointers to the actual statements directly.
|
// The graph nodes are pointers to the actual statements directly.
|
||||||
g.Add(&stmt)
|
g.Add(&stmts[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we'll add the edges representing chaining and nesting relationships.
|
// Now we'll add the edges representing chaining and nesting relationships.
|
||||||
|
|
|
@ -15,39 +15,108 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestApplyMoves(t *testing.T) {
|
func TestApplyMoves(t *testing.T) {
|
||||||
// TODO: Renable this once we're ready to implement the intended behaviors
|
|
||||||
// it is describing.
|
|
||||||
t.Skip("ApplyMoves is not yet fully implemented")
|
|
||||||
|
|
||||||
providerAddr := addrs.AbsProviderConfig{
|
providerAddr := addrs.AbsProviderConfig{
|
||||||
Module: addrs.RootModule,
|
Module: addrs.RootModule,
|
||||||
Provider: addrs.MustParseProviderSourceString("example.com/foo/bar"),
|
Provider: addrs.MustParseProviderSourceString("example.com/foo/bar"),
|
||||||
}
|
}
|
||||||
rootNoKeyResourceAddr := [...]addrs.AbsResourceInstance{
|
|
||||||
addrs.Resource{
|
moduleBoo, _ := addrs.ParseModuleInstanceStr("module.boo")
|
||||||
|
moduleBarKey, _ := addrs.ParseModuleInstanceStr("module.bar[0]")
|
||||||
|
|
||||||
|
instAddrs := map[string]addrs.AbsResourceInstance{
|
||||||
|
"foo.from": addrs.Resource{
|
||||||
Mode: addrs.ManagedResourceMode,
|
Mode: addrs.ManagedResourceMode,
|
||||||
Type: "foo",
|
Type: "foo",
|
||||||
Name: "from",
|
Name: "from",
|
||||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||||
addrs.Resource{
|
|
||||||
|
"foo.mid": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "mid",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||||
|
|
||||||
|
"foo.to": addrs.Resource{
|
||||||
Mode: addrs.ManagedResourceMode,
|
Mode: addrs.ManagedResourceMode,
|
||||||
Type: "foo",
|
Type: "foo",
|
||||||
Name: "to",
|
Name: "to",
|
||||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||||
}
|
|
||||||
rootIntKeyResourceAddr := [...]addrs.AbsResourceInstance{
|
"foo.from[0]": addrs.Resource{
|
||||||
addrs.Resource{
|
|
||||||
Mode: addrs.ManagedResourceMode,
|
Mode: addrs.ManagedResourceMode,
|
||||||
Type: "foo",
|
Type: "foo",
|
||||||
Name: "from",
|
Name: "from",
|
||||||
}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
|
}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
|
||||||
addrs.Resource{
|
|
||||||
|
"foo.to[0]": addrs.Resource{
|
||||||
Mode: addrs.ManagedResourceMode,
|
Mode: addrs.ManagedResourceMode,
|
||||||
Type: "foo",
|
Type: "foo",
|
||||||
Name: "to",
|
Name: "to",
|
||||||
}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
|
}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
|
||||||
|
|
||||||
|
"module.boo.foo.from": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "from",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(moduleBoo),
|
||||||
|
|
||||||
|
"module.boo.foo.mid": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "mid",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(moduleBoo),
|
||||||
|
|
||||||
|
"module.boo.foo.to": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "to",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(moduleBoo),
|
||||||
|
|
||||||
|
"module.boo.foo.from[0]": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "from",
|
||||||
|
}.Instance(addrs.IntKey(0)).Absolute(moduleBoo),
|
||||||
|
|
||||||
|
"module.boo.foo.to[0]": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "to",
|
||||||
|
}.Instance(addrs.IntKey(0)).Absolute(moduleBoo),
|
||||||
|
|
||||||
|
"module.bar[0].foo.from": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "from",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(moduleBarKey),
|
||||||
|
|
||||||
|
"module.bar[0].foo.mid": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "mid",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(moduleBarKey),
|
||||||
|
|
||||||
|
"module.bar[0].foo.to": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "to",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(moduleBarKey),
|
||||||
|
|
||||||
|
"module.bar[0].foo.from[0]": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "from",
|
||||||
|
}.Instance(addrs.IntKey(0)).Absolute(moduleBarKey),
|
||||||
|
|
||||||
|
"module.bar[0].foo.to[0]": addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "foo",
|
||||||
|
Name: "to",
|
||||||
|
}.Instance(addrs.IntKey(0)).Absolute(moduleBarKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emptyResults := map[addrs.UniqueKey]MoveResult{}
|
||||||
|
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
Stmts []MoveStatement
|
Stmts []MoveStatement
|
||||||
State *states.State
|
State *states.State
|
||||||
|
@ -58,14 +127,14 @@ func TestApplyMoves(t *testing.T) {
|
||||||
"no moves and empty state": {
|
"no moves and empty state": {
|
||||||
[]MoveStatement{},
|
[]MoveStatement{},
|
||||||
states.NewState(),
|
states.NewState(),
|
||||||
nil,
|
emptyResults,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
"no moves": {
|
"no moves": {
|
||||||
[]MoveStatement{},
|
[]MoveStatement{},
|
||||||
states.BuildState(func(s *states.SyncState) {
|
states.BuildState(func(s *states.SyncState) {
|
||||||
s.SetResourceInstanceCurrent(
|
s.SetResourceInstanceCurrent(
|
||||||
rootNoKeyResourceAddr[0],
|
instAddrs["foo.from"],
|
||||||
&states.ResourceInstanceObjectSrc{
|
&states.ResourceInstanceObjectSrc{
|
||||||
Status: states.ObjectReady,
|
Status: states.ObjectReady,
|
||||||
AttrsJSON: []byte(`{}`),
|
AttrsJSON: []byte(`{}`),
|
||||||
|
@ -73,7 +142,7 @@ func TestApplyMoves(t *testing.T) {
|
||||||
providerAddr,
|
providerAddr,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
nil,
|
emptyResults,
|
||||||
[]string{
|
[]string{
|
||||||
`foo.from`,
|
`foo.from`,
|
||||||
},
|
},
|
||||||
|
@ -84,7 +153,7 @@ func TestApplyMoves(t *testing.T) {
|
||||||
},
|
},
|
||||||
states.BuildState(func(s *states.SyncState) {
|
states.BuildState(func(s *states.SyncState) {
|
||||||
s.SetResourceInstanceCurrent(
|
s.SetResourceInstanceCurrent(
|
||||||
rootNoKeyResourceAddr[0],
|
instAddrs["foo.from"],
|
||||||
&states.ResourceInstanceObjectSrc{
|
&states.ResourceInstanceObjectSrc{
|
||||||
Status: states.ObjectReady,
|
Status: states.ObjectReady,
|
||||||
AttrsJSON: []byte(`{}`),
|
AttrsJSON: []byte(`{}`),
|
||||||
|
@ -93,13 +162,13 @@ func TestApplyMoves(t *testing.T) {
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
map[addrs.UniqueKey]MoveResult{
|
map[addrs.UniqueKey]MoveResult{
|
||||||
rootNoKeyResourceAddr[0].UniqueKey(): {
|
instAddrs["foo.from"].UniqueKey(): {
|
||||||
From: rootNoKeyResourceAddr[0],
|
From: instAddrs["foo.from"],
|
||||||
To: rootNoKeyResourceAddr[1],
|
To: instAddrs["foo.to"],
|
||||||
},
|
},
|
||||||
rootNoKeyResourceAddr[1].UniqueKey(): {
|
instAddrs["foo.to"].UniqueKey(): {
|
||||||
From: rootNoKeyResourceAddr[1],
|
From: instAddrs["foo.from"],
|
||||||
To: rootNoKeyResourceAddr[1],
|
To: instAddrs["foo.to"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
|
@ -112,7 +181,7 @@ func TestApplyMoves(t *testing.T) {
|
||||||
},
|
},
|
||||||
states.BuildState(func(s *states.SyncState) {
|
states.BuildState(func(s *states.SyncState) {
|
||||||
s.SetResourceInstanceCurrent(
|
s.SetResourceInstanceCurrent(
|
||||||
rootIntKeyResourceAddr[0],
|
instAddrs["foo.from[0]"],
|
||||||
&states.ResourceInstanceObjectSrc{
|
&states.ResourceInstanceObjectSrc{
|
||||||
Status: states.ObjectReady,
|
Status: states.ObjectReady,
|
||||||
AttrsJSON: []byte(`{}`),
|
AttrsJSON: []byte(`{}`),
|
||||||
|
@ -121,26 +190,206 @@ func TestApplyMoves(t *testing.T) {
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
map[addrs.UniqueKey]MoveResult{
|
map[addrs.UniqueKey]MoveResult{
|
||||||
rootNoKeyResourceAddr[0].UniqueKey(): {
|
instAddrs["foo.from[0]"].UniqueKey(): {
|
||||||
From: rootIntKeyResourceAddr[0],
|
From: instAddrs["foo.from[0]"],
|
||||||
To: rootIntKeyResourceAddr[1],
|
To: instAddrs["foo.to[0]"],
|
||||||
},
|
},
|
||||||
rootNoKeyResourceAddr[1].UniqueKey(): {
|
instAddrs["foo.to[0]"].UniqueKey(): {
|
||||||
From: rootIntKeyResourceAddr[0],
|
From: instAddrs["foo.from[0]"],
|
||||||
To: rootIntKeyResourceAddr[1],
|
To: instAddrs["foo.to[0]"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
`foo.to[0]`,
|
`foo.to[0]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"chained move of whole singleton resource": {
|
||||||
|
[]MoveStatement{
|
||||||
|
testMoveStatement(t, "", "foo.from", "foo.mid"),
|
||||||
|
testMoveStatement(t, "", "foo.mid", "foo.to"),
|
||||||
|
},
|
||||||
|
states.BuildState(func(s *states.SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
instAddrs["foo.from"],
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{}`),
|
||||||
|
},
|
||||||
|
providerAddr,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
map[addrs.UniqueKey]MoveResult{
|
||||||
|
instAddrs["foo.from"].UniqueKey(): {
|
||||||
|
From: instAddrs["foo.from"],
|
||||||
|
To: instAddrs["foo.mid"],
|
||||||
|
},
|
||||||
|
instAddrs["foo.mid"].UniqueKey(): {
|
||||||
|
From: instAddrs["foo.mid"],
|
||||||
|
To: instAddrs["foo.to"],
|
||||||
|
},
|
||||||
|
instAddrs["foo.to"].UniqueKey(): {
|
||||||
|
From: instAddrs["foo.mid"],
|
||||||
|
To: instAddrs["foo.to"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
`foo.to`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"move whole resource into module": {
|
||||||
|
[]MoveStatement{
|
||||||
|
testMoveStatement(t, "", "foo.from", "module.boo.foo.to"),
|
||||||
|
},
|
||||||
|
states.BuildState(func(s *states.SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
instAddrs["foo.from[0]"],
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{}`),
|
||||||
|
},
|
||||||
|
providerAddr,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
map[addrs.UniqueKey]MoveResult{
|
||||||
|
instAddrs["foo.from[0]"].UniqueKey(): {
|
||||||
|
From: instAddrs["foo.from[0]"],
|
||||||
|
To: instAddrs["module.boo.foo.to[0]"],
|
||||||
|
},
|
||||||
|
instAddrs["module.boo.foo.to[0]"].UniqueKey(): {
|
||||||
|
From: instAddrs["foo.from[0]"],
|
||||||
|
To: instAddrs["module.boo.foo.to[0]"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
`module.boo.foo.to[0]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"move resource instance between modules": {
|
||||||
|
[]MoveStatement{
|
||||||
|
testMoveStatement(t, "", "module.boo.foo.from[0]", "module.bar[0].foo.to[0]"),
|
||||||
|
},
|
||||||
|
states.BuildState(func(s *states.SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
instAddrs["module.boo.foo.from[0]"],
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{}`),
|
||||||
|
},
|
||||||
|
providerAddr,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
map[addrs.UniqueKey]MoveResult{
|
||||||
|
instAddrs["module.boo.foo.from[0]"].UniqueKey(): {
|
||||||
|
From: instAddrs["module.boo.foo.from[0]"],
|
||||||
|
To: instAddrs["module.bar[0].foo.to[0]"],
|
||||||
|
},
|
||||||
|
instAddrs["module.bar[0].foo.to[0]"].UniqueKey(): {
|
||||||
|
From: instAddrs["module.boo.foo.from[0]"],
|
||||||
|
To: instAddrs["module.bar[0].foo.to[0]"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
`module.bar[0].foo.to[0]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"move whole single module to indexed module": {
|
||||||
|
[]MoveStatement{
|
||||||
|
testMoveStatement(t, "", "module.boo", "module.bar[0]"),
|
||||||
|
},
|
||||||
|
states.BuildState(func(s *states.SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
instAddrs["module.boo.foo.from[0]"],
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{}`),
|
||||||
|
},
|
||||||
|
providerAddr,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
map[addrs.UniqueKey]MoveResult{
|
||||||
|
instAddrs["module.boo.foo.from[0]"].UniqueKey(): {
|
||||||
|
From: instAddrs["module.boo.foo.from[0]"],
|
||||||
|
To: instAddrs["module.bar[0].foo.from[0]"],
|
||||||
|
},
|
||||||
|
instAddrs["module.bar[0].foo.from[0]"].UniqueKey(): {
|
||||||
|
From: instAddrs["module.boo.foo.from[0]"],
|
||||||
|
To: instAddrs["module.bar[0].foo.from[0]"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
`module.bar[0].foo.from[0]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"move whole module to within indexed module and instance chained": {
|
||||||
|
[]MoveStatement{
|
||||||
|
testMoveStatement(t, "", "module.boo", "module.bar[0]"),
|
||||||
|
testMoveStatement(t, "module.bar[0]", "foo.from[0]", "foo.too[0]"),
|
||||||
|
},
|
||||||
|
states.BuildState(func(s *states.SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
instAddrs["module.boo.foo.from[0]"],
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{}`),
|
||||||
|
},
|
||||||
|
providerAddr,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
map[addrs.UniqueKey]MoveResult{
|
||||||
|
instAddrs["module.boo.foo.from[0]"].UniqueKey(): {
|
||||||
|
From: instAddrs["module.boo.foo.from[0]"],
|
||||||
|
To: instAddrs["module.bar[0].foo.from[0]"],
|
||||||
|
},
|
||||||
|
instAddrs["module.bar[0].foo.from[0]"].UniqueKey(): {
|
||||||
|
From: instAddrs["module.boo.foo.from[0]"],
|
||||||
|
To: instAddrs["module.bar[0].foo.from[0]"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
`module.bar[0].foo.from[0]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"move instance to indexed module and instance chained": {
|
||||||
|
[]MoveStatement{
|
||||||
|
testMoveStatement(t, "", "module.boo.foo.from[0]", "module.bar[0].foo.from[0]"),
|
||||||
|
testMoveStatement(t, "module.bar[0]", "foo.from[0]", "foo.too[0]"),
|
||||||
|
},
|
||||||
|
states.BuildState(func(s *states.SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
instAddrs["module.boo.foo.from[0]"],
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{}`),
|
||||||
|
},
|
||||||
|
providerAddr,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
map[addrs.UniqueKey]MoveResult{
|
||||||
|
instAddrs["module.boo.foo.from[0]"].UniqueKey(): {
|
||||||
|
From: instAddrs["module.boo.foo.from[0]"],
|
||||||
|
To: instAddrs["module.bar[0].foo.from[0]"],
|
||||||
|
},
|
||||||
|
instAddrs["module.bar[0].foo.from[0]"].UniqueKey(): {
|
||||||
|
From: instAddrs["module.boo.foo.from[0]"],
|
||||||
|
To: instAddrs["module.bar[0].foo.from[0]"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
`module.bar[0].foo.from[0]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
var stmtsBuf strings.Builder
|
var stmtsBuf strings.Builder
|
||||||
for _, stmt := range test.Stmts {
|
for _, stmt := range test.Stmts {
|
||||||
fmt.Fprintf(&stmtsBuf, "- from: %s\n to: %s", stmt.From, stmt.To)
|
fmt.Fprintf(&stmtsBuf, "- from: %s\n to: %s\n", stmt.From, stmt.To)
|
||||||
}
|
}
|
||||||
t.Logf("move statements:\n%s", stmtsBuf.String())
|
t.Logf("move statements:\n%s", stmtsBuf.String())
|
||||||
|
|
||||||
|
|
|
@ -50,3 +50,8 @@ func (s *MoveStatement) ObjectKind() addrs.MoveEndpointKind {
|
||||||
// match it.
|
// match it.
|
||||||
return s.From.ObjectKind()
|
return s.From.ObjectKind()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name is used internally for displaying the statement graph
|
||||||
|
func (s *MoveStatement) Name() string {
|
||||||
|
return fmt.Sprintf("%s->%s", s.From, s.To)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue