mirror of https://github.com/hashicorp/terraform
This method encapsulates the move-processing rules for applying move statements to ModuleInstance addresses. It honors both module call moves and module instance moves by trying to find a subsequence of the input that matches the "from" endpoint and then, if found, replacing it with the "to" endpoint while preserving the prefix and suffix around the match, if any.pull/29253/head
parent
2e82e55268
commit
4d733b4d2d
@ -0,0 +1,305 @@
|
||||
package addrs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
func TestModuleInstanceMoveDestination(t *testing.T) {
|
||||
tests := []struct {
|
||||
DeclModule string
|
||||
StmtFrom, StmtTo string
|
||||
Reciever string
|
||||
WantMatch bool
|
||||
WantResult string
|
||||
}{
|
||||
{
|
||||
``,
|
||||
`module.foo`,
|
||||
`module.bar`,
|
||||
`module.foo`,
|
||||
true,
|
||||
`module.bar`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo`,
|
||||
`module.bar`,
|
||||
`module.foo[1]`,
|
||||
true,
|
||||
`module.bar[1]`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo`,
|
||||
`module.bar`,
|
||||
`module.foo["a"]`,
|
||||
true,
|
||||
`module.bar["a"]`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo`,
|
||||
`module.bar.module.foo`,
|
||||
`module.foo`,
|
||||
true,
|
||||
`module.bar.module.foo`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo.module.bar`,
|
||||
`module.bar`,
|
||||
`module.foo.module.bar`,
|
||||
true,
|
||||
`module.bar`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo[1]`,
|
||||
`module.foo[2]`,
|
||||
`module.foo[1]`,
|
||||
true,
|
||||
`module.foo[2]`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo[1]`,
|
||||
`module.foo`,
|
||||
`module.foo[1]`,
|
||||
true,
|
||||
`module.foo`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo`,
|
||||
`module.foo[1]`,
|
||||
`module.foo`,
|
||||
true,
|
||||
`module.foo[1]`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo`,
|
||||
`module.foo[1]`,
|
||||
`module.foo.module.bar`,
|
||||
true,
|
||||
`module.foo[1].module.bar`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo`,
|
||||
`module.foo[1]`,
|
||||
`module.foo.module.bar[0]`,
|
||||
true,
|
||||
`module.foo[1].module.bar[0]`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo`,
|
||||
`module.bar.module.foo`,
|
||||
`module.foo[0]`,
|
||||
true,
|
||||
`module.bar.module.foo[0]`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo.module.bar`,
|
||||
`module.bar`,
|
||||
`module.foo.module.bar[0]`,
|
||||
true,
|
||||
`module.bar[0]`,
|
||||
},
|
||||
{
|
||||
`foo`,
|
||||
`module.bar`,
|
||||
`module.baz`,
|
||||
`module.foo.module.bar`,
|
||||
true,
|
||||
`module.foo.module.baz`,
|
||||
},
|
||||
{
|
||||
`foo`,
|
||||
`module.bar`,
|
||||
`module.baz`,
|
||||
`module.foo[1].module.bar`,
|
||||
true,
|
||||
`module.foo[1].module.baz`,
|
||||
},
|
||||
{
|
||||
`foo`,
|
||||
`module.bar`,
|
||||
`module.bar[1]`,
|
||||
`module.foo[1].module.bar`,
|
||||
true,
|
||||
`module.foo[1].module.bar[1]`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo[1]`,
|
||||
`module.foo[2]`,
|
||||
`module.foo`,
|
||||
false, // the receiver has a non-matching instance key (NoKey)
|
||||
``,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo[1]`,
|
||||
`module.foo[2]`,
|
||||
`module.foo[2]`,
|
||||
false, // the receiver is already the "to" address
|
||||
``,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.foo`,
|
||||
`module.bar`,
|
||||
``,
|
||||
false, // the root module can never be moved
|
||||
``,
|
||||
},
|
||||
{
|
||||
`foo`,
|
||||
`module.bar`,
|
||||
`module.bar[1]`,
|
||||
`module.boz`,
|
||||
false, // the receiver is outside the declaration module
|
||||
``,
|
||||
},
|
||||
{
|
||||
`foo.bar`,
|
||||
`module.bar`,
|
||||
`module.bar[1]`,
|
||||
`module.boz`,
|
||||
false, // the receiver is outside the declaration module
|
||||
``,
|
||||
},
|
||||
{
|
||||
`foo.bar`,
|
||||
`module.a`,
|
||||
`module.b`,
|
||||
`module.boz`,
|
||||
false, // the receiver is outside the declaration module
|
||||
``,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.a1.module.a2`,
|
||||
`module.b1.module.b2`,
|
||||
`module.c`,
|
||||
false, // the receiver is outside the declaration module
|
||||
``,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.a1.module.a2[0]`,
|
||||
`module.b1.module.b2[1]`,
|
||||
`module.c`,
|
||||
false, // the receiver is outside the declaration module
|
||||
``,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.a1.module.a2`,
|
||||
`module.b1.module.b2`,
|
||||
`module.a1.module.b2`,
|
||||
false, // the receiver is outside the declaration module
|
||||
``,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.a1.module.a2`,
|
||||
`module.b1.module.b2`,
|
||||
`module.b1.module.a2`,
|
||||
false, // the receiver is outside the declaration module
|
||||
``,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`module.a1.module.a2[0]`,
|
||||
`module.b1.module.b2[1]`,
|
||||
`module.a1.module.b2[0]`,
|
||||
false, // the receiver is outside the declaration module
|
||||
``,
|
||||
},
|
||||
{
|
||||
``,
|
||||
`foo_instance.bar`,
|
||||
`foo_instance.baz`,
|
||||
`module.foo`,
|
||||
false, // a resource address can never match a module instance
|
||||
``,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(
|
||||
fmt.Sprintf(
|
||||
"%s: %s to %s with %s",
|
||||
test.DeclModule,
|
||||
test.StmtFrom, test.StmtTo,
|
||||
test.Reciever,
|
||||
),
|
||||
func(t *testing.T) {
|
||||
|
||||
parseStmtEP := func(t *testing.T, input string) *MoveEndpoint {
|
||||
t.Helper()
|
||||
|
||||
traversal, hclDiags := hclsyntax.ParseTraversalAbs([]byte(input), "", hcl.InitialPos)
|
||||
if hclDiags.HasErrors() {
|
||||
// We're not trying to test the HCL parser here, so any
|
||||
// failures at this point are likely to be bugs in the
|
||||
// test case itself.
|
||||
t.Fatalf("syntax error: %s", hclDiags.Error())
|
||||
}
|
||||
|
||||
moveEp, diags := ParseMoveEndpoint(traversal)
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected error: %s", diags.Err().Error())
|
||||
}
|
||||
return moveEp
|
||||
}
|
||||
|
||||
fromEPLocal := parseStmtEP(t, test.StmtFrom)
|
||||
toEPLocal := parseStmtEP(t, test.StmtTo)
|
||||
|
||||
declModule := RootModule
|
||||
if test.DeclModule != "" {
|
||||
declModule = strings.Split(test.DeclModule, ".")
|
||||
}
|
||||
fromEP, toEP := UnifyMoveEndpoints(declModule, fromEPLocal, toEPLocal)
|
||||
if fromEP == nil || toEP == nil {
|
||||
t.Fatalf("invalid test case: non-unifyable endpoints\nfrom: %s\nto: %s", fromEPLocal, toEPLocal)
|
||||
}
|
||||
|
||||
receiverAddr := RootModuleInstance
|
||||
if test.Reciever != "" {
|
||||
var diags tfdiags.Diagnostics
|
||||
receiverAddr, diags = ParseModuleInstanceStr(test.Reciever)
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("invalid reciever address: %s", diags.Err().Error())
|
||||
}
|
||||
}
|
||||
gotAddr, gotMatch := receiverAddr.MoveDestination(fromEP, toEP)
|
||||
if !test.WantMatch {
|
||||
if gotMatch {
|
||||
t.Errorf("unexpected match\nreciever: %s\nfrom: %s\nto: %s\nresult: %s", test.Reciever, fromEP, toEP, gotAddr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !gotMatch {
|
||||
t.Errorf("unexpected non-match\nreciever: %s\nfrom: %s\nto: %s", test.Reciever, fromEP, toEP)
|
||||
}
|
||||
|
||||
if gotStr, wantStr := gotAddr.String(), test.WantResult; gotStr != wantStr {
|
||||
t.Errorf("wrong result\ngot: %s\nwant: %s", gotStr, wantStr)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue