mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
This new function complements the existing function FindMoveStatements by potentially generating additional "implied" move statements that aren't written explicit in the configuration but that we'll infer by comparing the configuration and te previous run state. The goal here is to infer only enough to replicate the effect of the "count boundary fixup" graph node (terraform.NodeCountBoundary) that we currently use to deal with this concern of preserving the zero-instance when switching between "count" and not "count". This is just dead code for now. A subsequent commit will introduce this into the "terraform" package while also removing terraform.NodeCountBoundary, thus achieving the same effect as before but in a way that'll get reported in the UI as a move, using the same language that we'd use for an explicit move statement.
133 lines
4.7 KiB
Go
133 lines
4.7 KiB
Go
package refactoring
|
|
|
|
import (
|
|
"sort"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
)
|
|
|
|
func TestImpliedMoveStatements(t *testing.T) {
|
|
resourceAddr := func(name string) addrs.AbsResource {
|
|
return addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "foo",
|
|
Name: name,
|
|
}.Absolute(addrs.RootModuleInstance)
|
|
}
|
|
instObjState := func() *states.ResourceInstanceObjectSrc {
|
|
return &states.ResourceInstanceObjectSrc{}
|
|
}
|
|
providerAddr := addrs.AbsProviderConfig{
|
|
Module: addrs.RootModule,
|
|
Provider: addrs.MustParseProviderSourceString("hashicorp/foo"),
|
|
}
|
|
|
|
rootCfg, _ := loadRefactoringFixture(t, "testdata/move-statement-implied")
|
|
prevRunState := states.BuildState(func(s *states.SyncState) {
|
|
s.SetResourceInstanceCurrent(
|
|
resourceAddr("formerly_count").Instance(addrs.IntKey(0)),
|
|
instObjState(),
|
|
providerAddr,
|
|
)
|
|
s.SetResourceInstanceCurrent(
|
|
resourceAddr("formerly_count").Instance(addrs.IntKey(1)),
|
|
instObjState(),
|
|
providerAddr,
|
|
)
|
|
s.SetResourceInstanceCurrent(
|
|
resourceAddr("now_count").Instance(addrs.NoKey),
|
|
instObjState(),
|
|
providerAddr,
|
|
)
|
|
s.SetResourceInstanceCurrent(
|
|
resourceAddr("formerly_count_explicit").Instance(addrs.IntKey(0)),
|
|
instObjState(),
|
|
providerAddr,
|
|
)
|
|
s.SetResourceInstanceCurrent(
|
|
resourceAddr("formerly_count_explicit").Instance(addrs.IntKey(1)),
|
|
instObjState(),
|
|
providerAddr,
|
|
)
|
|
s.SetResourceInstanceCurrent(
|
|
resourceAddr("now_count_explicit").Instance(addrs.NoKey),
|
|
instObjState(),
|
|
providerAddr,
|
|
)
|
|
|
|
// This "ambiguous" resource is representing a rare but possible
|
|
// situation where we end up having a mixture of different index
|
|
// types in the state at the same time. The main way to get into
|
|
// this state would be to remove "count = 1" and then have the
|
|
// provider fail to destroy the zero-key instance even though we
|
|
// already created the no-key instance. Users can also get here
|
|
// by using "terraform state mv" in weird ways.
|
|
s.SetResourceInstanceCurrent(
|
|
resourceAddr("ambiguous").Instance(addrs.NoKey),
|
|
instObjState(),
|
|
providerAddr,
|
|
)
|
|
s.SetResourceInstanceCurrent(
|
|
resourceAddr("ambiguous").Instance(addrs.IntKey(0)),
|
|
instObjState(),
|
|
providerAddr,
|
|
)
|
|
})
|
|
|
|
explicitStmts := FindMoveStatements(rootCfg)
|
|
got := ImpliedMoveStatements(rootCfg, prevRunState, explicitStmts)
|
|
want := []MoveStatement{
|
|
{
|
|
From: addrs.ImpliedMoveStatementEndpoint(resourceAddr("formerly_count").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
|
|
To: addrs.ImpliedMoveStatementEndpoint(resourceAddr("formerly_count").Instance(addrs.NoKey), tfdiags.SourceRange{}),
|
|
Implied: true,
|
|
DeclRange: tfdiags.SourceRange{
|
|
Filename: "testdata/move-statement-implied/move-statement-implied.tf",
|
|
Start: tfdiags.SourcePos{Line: 9, Column: 1, Byte: 232},
|
|
End: tfdiags.SourcePos{Line: 9, Column: 32, Byte: 263},
|
|
},
|
|
},
|
|
{
|
|
From: addrs.ImpliedMoveStatementEndpoint(resourceAddr("now_count").Instance(addrs.NoKey), tfdiags.SourceRange{}),
|
|
To: addrs.ImpliedMoveStatementEndpoint(resourceAddr("now_count").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
|
|
Implied: true,
|
|
DeclRange: tfdiags.SourceRange{
|
|
Filename: "testdata/move-statement-implied/move-statement-implied.tf",
|
|
Start: tfdiags.SourcePos{Line: 14, Column: 11, Byte: 334},
|
|
End: tfdiags.SourcePos{Line: 14, Column: 12, Byte: 335},
|
|
},
|
|
},
|
|
|
|
// We generate foo.ambiguous[0] to foo.ambiguous here, even though
|
|
// there's already a foo.ambiguous in the state, because it's the
|
|
// responsibility of the later ApplyMoves step to deal with the
|
|
// situation where an object wants to move into an address already
|
|
// occupied by another object.
|
|
{
|
|
From: addrs.ImpliedMoveStatementEndpoint(resourceAddr("ambiguous").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
|
|
To: addrs.ImpliedMoveStatementEndpoint(resourceAddr("ambiguous").Instance(addrs.NoKey), tfdiags.SourceRange{}),
|
|
Implied: true,
|
|
DeclRange: tfdiags.SourceRange{
|
|
Filename: "testdata/move-statement-implied/move-statement-implied.tf",
|
|
Start: tfdiags.SourcePos{Line: 42, Column: 1, Byte: 709},
|
|
End: tfdiags.SourcePos{Line: 42, Column: 27, Byte: 735},
|
|
},
|
|
},
|
|
}
|
|
|
|
sort.Slice(got, func(i, j int) bool {
|
|
// This is just an arbitrary sort to make the result consistent
|
|
// regardless of what order the ImpliedMoveStatements function
|
|
// visits the entries in the state/config.
|
|
return got[i].DeclRange.Start.Line < got[j].DeclRange.Start.Line
|
|
})
|
|
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("wrong result\n%s", diff)
|
|
}
|
|
}
|