mirror of
https://github.com/opentffoundation/opentf.git
synced 2026-03-04 08:03:40 -05:00
These both effectively had the behavior of ResourceInstancePrior embedded in them, reading something from the state and change its address as a single compound operation. In the case of ManagedDepose we need to split these up for the CreateThenDestroy variant of "replace", because we want to make sure the final plans are valid before we depose anything and we need the prior state to produce the final plan. (Actually using that will follow in a subsequent commit.) This isn't actually necessary for ManageChangeAddr, but splitting it keeps these two operations consistent in how they interact with the rest of the operations. Due to how the existing states.SyncState works we're not actually making good use of the data flow of these objects right now, but in a future world where we're no longer using the old state models hopefully the state API will switch to an approach that's more aligned with how the execgraph operations are modeled. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
204 lines
6.5 KiB
Go
204 lines
6.5 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package planning
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
"github.com/opentofu/opentofu/internal/engine/internal/exec"
|
|
"github.com/opentofu/opentofu/internal/engine/internal/execgraph"
|
|
"github.com/opentofu/opentofu/internal/plans"
|
|
)
|
|
|
|
// TestExecGraphBuilder_ManagedResourceInstanceSubgraph is a unit test for
|
|
// the ManagedResourceInstanceSubgraph method in particular, focused only on
|
|
// the items and relationships that function produces.
|
|
//
|
|
// Interactions between this method and others should be tested elsewhere.
|
|
func TestExecGraphBuilder_ManagedResourceInstanceSubgraph(t *testing.T) {
|
|
// instAddr is the resource instance address that each test should use
|
|
// for the resource instance object whose result is returned from the
|
|
// "Build" function. We set the return value as the result for this
|
|
// resource instance so that it'll appear in the graph DebugRepr for
|
|
// comparison.
|
|
instAddr := addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test",
|
|
Name: "placeholder",
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
|
|
|
|
tests := map[string]struct {
|
|
Build func(b *execGraphBuilder, providerClientRef execgraph.ResultRef[*exec.ProviderClient]) execgraph.ResourceInstanceResultRef
|
|
WantRepr string
|
|
}{
|
|
"create": {
|
|
func(b *execGraphBuilder, providerClientRef execgraph.ResultRef[*exec.ProviderClient]) execgraph.ResourceInstanceResultRef {
|
|
return b.ManagedResourceInstanceSubgraph(
|
|
&plans.ResourceInstanceChange{
|
|
Addr: instAddr,
|
|
PrevRunAddr: instAddr,
|
|
Change: plans.Change{
|
|
Action: plans.Create,
|
|
Before: cty.NullVal(cty.EmptyObject),
|
|
After: cty.EmptyObjectVal,
|
|
},
|
|
},
|
|
providerClientRef,
|
|
addrs.MakeSet[addrs.AbsResourceInstance](),
|
|
)
|
|
},
|
|
`
|
|
v[0] = cty.EmptyObjectVal;
|
|
|
|
r[0] = ResourceInstanceDesired(test.placeholder, await());
|
|
r[1] = ManagedFinalPlan(r[0], nil, v[0], nil);
|
|
r[2] = ManagedApply(r[1], nil, nil, await());
|
|
|
|
test.placeholder = r[2];
|
|
`,
|
|
},
|
|
"update": {
|
|
func(b *execGraphBuilder, providerClientRef execgraph.ResultRef[*exec.ProviderClient]) execgraph.ResourceInstanceResultRef {
|
|
return b.ManagedResourceInstanceSubgraph(
|
|
&plans.ResourceInstanceChange{
|
|
Addr: instAddr,
|
|
PrevRunAddr: instAddr,
|
|
Change: plans.Change{
|
|
Action: plans.Update,
|
|
Before: cty.StringVal("before"),
|
|
After: cty.StringVal("after"),
|
|
},
|
|
},
|
|
providerClientRef,
|
|
addrs.MakeSet[addrs.AbsResourceInstance](),
|
|
)
|
|
},
|
|
`
|
|
v[0] = cty.StringVal("after");
|
|
|
|
r[0] = ResourceInstancePrior(test.placeholder);
|
|
r[1] = ResourceInstanceDesired(test.placeholder, await());
|
|
r[2] = ManagedFinalPlan(r[1], r[0], v[0], nil);
|
|
r[3] = ManagedApply(r[2], nil, nil, await());
|
|
|
|
test.placeholder = r[3];
|
|
`,
|
|
},
|
|
"update with move": {
|
|
func(b *execGraphBuilder, providerClientRef execgraph.ResultRef[*exec.ProviderClient]) execgraph.ResourceInstanceResultRef {
|
|
oldInstAddr := addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test",
|
|
Name: "old",
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
|
|
return b.ManagedResourceInstanceSubgraph(
|
|
&plans.ResourceInstanceChange{
|
|
Addr: instAddr,
|
|
PrevRunAddr: oldInstAddr,
|
|
Change: plans.Change{
|
|
Action: plans.Update,
|
|
Before: cty.StringVal("before"),
|
|
After: cty.StringVal("after"),
|
|
},
|
|
},
|
|
providerClientRef,
|
|
addrs.MakeSet[addrs.AbsResourceInstance](),
|
|
)
|
|
},
|
|
`
|
|
v[0] = cty.StringVal("after");
|
|
|
|
r[0] = ResourceInstancePrior(test.old);
|
|
r[1] = ManagedChangeAddr(r[0], test.placeholder);
|
|
r[2] = ResourceInstanceDesired(test.placeholder, await());
|
|
r[3] = ManagedFinalPlan(r[2], r[1], v[0], nil);
|
|
r[4] = ManagedApply(r[3], nil, nil, await());
|
|
|
|
test.placeholder = r[4];
|
|
`,
|
|
},
|
|
"delete": {
|
|
func(b *execGraphBuilder, providerClientRef execgraph.ResultRef[*exec.ProviderClient]) execgraph.ResourceInstanceResultRef {
|
|
return b.ManagedResourceInstanceSubgraph(
|
|
&plans.ResourceInstanceChange{
|
|
Addr: instAddr,
|
|
PrevRunAddr: instAddr,
|
|
Change: plans.Change{
|
|
Action: plans.Delete,
|
|
Before: cty.EmptyObjectVal,
|
|
After: cty.NullVal(cty.EmptyObject),
|
|
},
|
|
},
|
|
providerClientRef,
|
|
addrs.MakeSet[addrs.AbsResourceInstance](),
|
|
)
|
|
},
|
|
`
|
|
v[0] = cty.NullVal(cty.EmptyObject);
|
|
|
|
r[0] = ResourceInstancePrior(test.placeholder);
|
|
r[1] = ManagedFinalPlan(nil, r[0], v[0], nil);
|
|
r[2] = ManagedApply(r[1], nil, nil, await());
|
|
|
|
test.placeholder = r[2];
|
|
`,
|
|
},
|
|
"delete then create": {
|
|
func(b *execGraphBuilder, providerClientRef execgraph.ResultRef[*exec.ProviderClient]) execgraph.ResourceInstanceResultRef {
|
|
return b.ManagedResourceInstanceSubgraph(
|
|
&plans.ResourceInstanceChange{
|
|
Addr: instAddr,
|
|
PrevRunAddr: instAddr,
|
|
Change: plans.Change{
|
|
Action: plans.DeleteThenCreate,
|
|
Before: cty.StringVal("before"),
|
|
After: cty.StringVal("after"),
|
|
},
|
|
},
|
|
providerClientRef,
|
|
addrs.MakeSet[addrs.AbsResourceInstance](),
|
|
)
|
|
},
|
|
`
|
|
v[0] = cty.StringVal("after");
|
|
v[1] = cty.NullVal(cty.String);
|
|
|
|
r[0] = ResourceInstancePrior(test.placeholder);
|
|
r[1] = ResourceInstanceDesired(test.placeholder, await());
|
|
r[2] = ManagedFinalPlan(r[1], nil, v[0], nil);
|
|
r[3] = ManagedFinalPlan(nil, r[0], v[1], nil);
|
|
r[4] = ManagedApply(r[3], nil, nil, await(r[2]));
|
|
r[5] = ManagedApply(r[2], nil, nil, await(r[4]));
|
|
|
|
test.placeholder = r[5];
|
|
`,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
builder := newExecGraphBuilder()
|
|
// This test is focused only on the resource instance subgraphs,
|
|
// so we just use a placeholder nil result for the provider client.
|
|
providerClientRef := execgraph.NilResultRef[*exec.ProviderClient]()
|
|
resultRef := test.Build(builder, providerClientRef)
|
|
builder.lower.SetResourceInstanceFinalStateResult(instAddr, resultRef)
|
|
|
|
graph := builder.Finish()
|
|
gotGraphRepr := strings.TrimSpace(graph.DebugRepr())
|
|
wantGraphRepr := strings.TrimSpace(stripCommonLeadingTabs(test.WantRepr))
|
|
if diff := cmp.Diff(wantGraphRepr, gotGraphRepr); diff != "" {
|
|
t.Error("wrong result\n" + diff)
|
|
}
|
|
})
|
|
}
|
|
}
|