mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-20 21:52:57 -06:00
8d4d333efe
If we don't do this then we can create a situation where refresh detects that an object already doesn't exist but we plan to destroy it anyway, rather than returning "no changes" as expected.
147 lines
4.3 KiB
Go
147 lines
4.3 KiB
Go
package terraform
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/configs/configschema"
|
|
"github.com/hashicorp/terraform/instances"
|
|
"github.com/hashicorp/terraform/plans"
|
|
"github.com/hashicorp/terraform/providers"
|
|
"github.com/hashicorp/terraform/states"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func TestNodeResourcePlanOrphanExecute(t *testing.T) {
|
|
state := states.NewState()
|
|
state.Module(addrs.RootModuleInstance).SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_object",
|
|
Name: "foo",
|
|
}.Instance(addrs.NoKey),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsFlat: map[string]string{
|
|
"test_string": "foo",
|
|
},
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
|
|
p := simpleMockProvider()
|
|
ctx := &MockEvalContext{
|
|
StateState: state.SyncWrapper(),
|
|
RefreshStateState: state.DeepCopy().SyncWrapper(),
|
|
PrevRunStateState: state.DeepCopy().SyncWrapper(),
|
|
InstanceExpanderExpander: instances.NewExpander(),
|
|
ProviderProvider: p,
|
|
ProviderSchemaSchema: &ProviderSchema{
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
"test_object": simpleTestSchema(),
|
|
},
|
|
},
|
|
ChangesChanges: plans.NewChanges().SyncWrapper(),
|
|
}
|
|
|
|
node := NodePlannableResourceInstanceOrphan{
|
|
NodeAbstractResourceInstance: &NodeAbstractResourceInstance{
|
|
NodeAbstractResource: NodeAbstractResource{
|
|
ResolvedProvider: addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
},
|
|
Addr: mustResourceInstanceAddr("test_object.foo"),
|
|
},
|
|
}
|
|
diags := node.Execute(ctx, walkApply)
|
|
if diags.HasErrors() {
|
|
t.Fatalf("unexpected error: %s", diags.Err())
|
|
}
|
|
if !state.Empty() {
|
|
t.Fatalf("expected empty state, got %s", state.String())
|
|
}
|
|
}
|
|
|
|
func TestNodeResourcePlanOrphanExecute_alreadyDeleted(t *testing.T) {
|
|
addr := addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_object",
|
|
Name: "foo",
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
|
|
|
|
state := states.NewState()
|
|
state.Module(addrs.RootModuleInstance).SetResourceInstanceCurrent(
|
|
addr.Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsFlat: map[string]string{
|
|
"test_string": "foo",
|
|
},
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
refreshState := state.DeepCopy()
|
|
prevRunState := state.DeepCopy()
|
|
changes := plans.NewChanges()
|
|
|
|
p := simpleMockProvider()
|
|
p.ReadResourceResponse = &providers.ReadResourceResponse{
|
|
NewState: cty.NullVal(p.GetProviderSchemaResponse.ResourceTypes["test_string"].Block.ImpliedType()),
|
|
}
|
|
ctx := &MockEvalContext{
|
|
StateState: state.SyncWrapper(),
|
|
RefreshStateState: refreshState.SyncWrapper(),
|
|
PrevRunStateState: prevRunState.SyncWrapper(),
|
|
InstanceExpanderExpander: instances.NewExpander(),
|
|
ProviderProvider: p,
|
|
ProviderSchemaSchema: &ProviderSchema{
|
|
ResourceTypes: map[string]*configschema.Block{
|
|
"test_object": simpleTestSchema(),
|
|
},
|
|
},
|
|
ChangesChanges: changes.SyncWrapper(),
|
|
}
|
|
|
|
node := NodePlannableResourceInstanceOrphan{
|
|
NodeAbstractResourceInstance: &NodeAbstractResourceInstance{
|
|
NodeAbstractResource: NodeAbstractResource{
|
|
ResolvedProvider: addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
},
|
|
Addr: mustResourceInstanceAddr("test_object.foo"),
|
|
},
|
|
}
|
|
diags := node.Execute(ctx, walkPlan)
|
|
if diags.HasErrors() {
|
|
t.Fatalf("unexpected error: %s", diags.Err())
|
|
}
|
|
if !state.Empty() {
|
|
t.Fatalf("expected empty state, got %s", state.String())
|
|
}
|
|
|
|
if got := prevRunState.ResourceInstance(addr); got == nil {
|
|
t.Errorf("no entry for %s in the prev run state; should still be present", addr)
|
|
}
|
|
if got := refreshState.ResourceInstance(addr); got != nil {
|
|
t.Errorf("refresh state has entry for %s; should've been removed", addr)
|
|
}
|
|
if got := changes.ResourceInstance(addr); got == nil {
|
|
t.Errorf("no entry for %s in the planned changes; should have a NoOp change", addr)
|
|
} else {
|
|
if got, want := got.Action, plans.NoOp; got != want {
|
|
t.Errorf("planned change for %s has wrong action\ngot: %s\nwant: %s", addr, got, want)
|
|
}
|
|
}
|
|
|
|
}
|