mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
6b8e103d6a
An earlier commit added logic to decode "moved" blocks and do static validation of them. Here we now include that result also in modules produced from those files, which we can then use in Terraform Core to actually implement the moves. This also places the feature behind an active experiment keyword called config_driven_move. For now activating this doesn't actually achieve anything except let you include moved blocks that Terraform will summarily ignore, but we'll expand the scope of this in later commits to eventually reach the point where it's really usable.
209 lines
4.7 KiB
Go
209 lines
4.7 KiB
Go
package configs
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hcltest"
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
)
|
|
|
|
func TestDecodeMovedBlock(t *testing.T) {
|
|
blockRange := hcl.Range{
|
|
Filename: "mock.tf",
|
|
Start: hcl.Pos{Line: 3, Column: 12, Byte: 27},
|
|
End: hcl.Pos{Line: 3, Column: 19, Byte: 34},
|
|
}
|
|
|
|
foo_expr := hcltest.MockExprTraversalSrc("test_instance.foo")
|
|
bar_expr := hcltest.MockExprTraversalSrc("test_instance.bar")
|
|
|
|
foo_index_expr := hcltest.MockExprTraversalSrc("test_instance.foo[1]")
|
|
bar_index_expr := hcltest.MockExprTraversalSrc("test_instance.bar[\"one\"]")
|
|
|
|
mod_foo_expr := hcltest.MockExprTraversalSrc("module.foo")
|
|
mod_bar_expr := hcltest.MockExprTraversalSrc("module.bar")
|
|
|
|
tests := map[string]struct {
|
|
input *hcl.Block
|
|
want *Moved
|
|
err string
|
|
}{
|
|
"success": {
|
|
&hcl.Block{
|
|
Type: "moved",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcl.Attributes{
|
|
"from": {
|
|
Name: "from",
|
|
Expr: foo_expr,
|
|
},
|
|
"to": {
|
|
Name: "to",
|
|
Expr: bar_expr,
|
|
},
|
|
},
|
|
}),
|
|
DefRange: blockRange,
|
|
},
|
|
&Moved{
|
|
From: mustMoveEndpointFromExpr(foo_expr),
|
|
To: mustMoveEndpointFromExpr(bar_expr),
|
|
DeclRange: blockRange,
|
|
},
|
|
``,
|
|
},
|
|
"indexed resources": {
|
|
&hcl.Block{
|
|
Type: "moved",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcl.Attributes{
|
|
"from": {
|
|
Name: "from",
|
|
Expr: foo_index_expr,
|
|
},
|
|
"to": {
|
|
Name: "to",
|
|
Expr: bar_index_expr,
|
|
},
|
|
},
|
|
}),
|
|
DefRange: blockRange,
|
|
},
|
|
&Moved{
|
|
From: mustMoveEndpointFromExpr(foo_index_expr),
|
|
To: mustMoveEndpointFromExpr(bar_index_expr),
|
|
DeclRange: blockRange,
|
|
},
|
|
``,
|
|
},
|
|
"modules": {
|
|
&hcl.Block{
|
|
Type: "moved",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcl.Attributes{
|
|
"from": {
|
|
Name: "from",
|
|
Expr: mod_foo_expr,
|
|
},
|
|
"to": {
|
|
Name: "to",
|
|
Expr: mod_bar_expr,
|
|
},
|
|
},
|
|
}),
|
|
DefRange: blockRange,
|
|
},
|
|
&Moved{
|
|
From: mustMoveEndpointFromExpr(mod_foo_expr),
|
|
To: mustMoveEndpointFromExpr(mod_bar_expr),
|
|
DeclRange: blockRange,
|
|
},
|
|
``,
|
|
},
|
|
"error: missing argument": {
|
|
&hcl.Block{
|
|
Type: "moved",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcl.Attributes{
|
|
"from": {
|
|
Name: "from",
|
|
Expr: foo_expr,
|
|
},
|
|
},
|
|
}),
|
|
DefRange: blockRange,
|
|
},
|
|
&Moved{
|
|
From: mustMoveEndpointFromExpr(foo_expr),
|
|
DeclRange: blockRange,
|
|
},
|
|
"Missing required argument",
|
|
},
|
|
"error: type mismatch": {
|
|
&hcl.Block{
|
|
Type: "moved",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcl.Attributes{
|
|
"to": {
|
|
Name: "to",
|
|
Expr: foo_expr,
|
|
},
|
|
"from": {
|
|
Name: "from",
|
|
Expr: mod_foo_expr,
|
|
},
|
|
},
|
|
}),
|
|
DefRange: blockRange,
|
|
},
|
|
&Moved{
|
|
To: mustMoveEndpointFromExpr(foo_expr),
|
|
From: mustMoveEndpointFromExpr(mod_foo_expr),
|
|
DeclRange: blockRange,
|
|
},
|
|
"Invalid \"moved\" addresses",
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
got, diags := decodeMovedBlock(test.input)
|
|
|
|
if diags.HasErrors() {
|
|
if test.err == "" {
|
|
t.Fatalf("unexpected error: %s", diags.Errs())
|
|
}
|
|
if gotErr := diags[0].Summary; gotErr != test.err {
|
|
t.Errorf("wrong error, got %q, want %q", gotErr, test.err)
|
|
}
|
|
} else if test.err != "" {
|
|
t.Fatal("expected error")
|
|
}
|
|
|
|
if !cmp.Equal(got, test.want, cmp.AllowUnexported(addrs.MoveEndpoint{})) {
|
|
t.Fatalf("wrong result: %s", cmp.Diff(got, test.want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMovedBlocksInModule(t *testing.T) {
|
|
parser := NewParser(nil)
|
|
mod, diags := parser.LoadConfigDir("testdata/valid-modules/moved-blocks")
|
|
if diags.HasErrors() {
|
|
t.Errorf("unexpected error: %s", diags.Error())
|
|
}
|
|
|
|
var gotPairs [][2]string
|
|
for _, mc := range mod.Moved {
|
|
gotPairs = append(gotPairs, [2]string{mc.From.String(), mc.To.String()})
|
|
}
|
|
wantPairs := [][2]string{
|
|
{`test.foo`, `test.bar`},
|
|
{`test.foo`, `test.bar["bloop"]`},
|
|
{`module.a`, `module.b`},
|
|
{`module.a`, `module.a["foo"]`},
|
|
{`test.foo`, `module.a.test.foo`},
|
|
{`data.test.foo`, `data.test.bar`},
|
|
}
|
|
if diff := cmp.Diff(wantPairs, gotPairs); diff != "" {
|
|
t.Errorf("wrong addresses\n%s", diff)
|
|
}
|
|
}
|
|
|
|
func mustMoveEndpointFromExpr(expr hcl.Expression) *addrs.MoveEndpoint {
|
|
traversal, hcldiags := hcl.AbsTraversalForExpr(expr)
|
|
if hcldiags.HasErrors() {
|
|
panic(hcldiags.Errs())
|
|
}
|
|
|
|
ep, diags := addrs.ParseMoveEndpoint(traversal)
|
|
if diags.HasErrors() {
|
|
panic(diags.Err())
|
|
}
|
|
|
|
return ep
|
|
}
|