mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
If when refreshing an orphaned instance the provider indicates it has already been deleted, there is no reason to create a change for that instance. A NoOp change should only represent an object that exists and is not changing. This was likely left in before in order to try and provide a record of the change for external consumers of the plan, but newer plans also contain all changes made outside of Terraform which better accounts for the difference. The NoOp change now can cause problems, because it may represent an instance with conditions to check even though that instance does not exist.
144 lines
4.3 KiB
Go
144 lines
4.3 KiB
Go
package terraform
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/configs/configschema"
|
|
"github.com/hashicorp/terraform/internal/instances"
|
|
"github.com/hashicorp/terraform/internal/plans"
|
|
"github.com/hashicorp/terraform/internal/providers"
|
|
"github.com/hashicorp/terraform/internal/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()
|
|
p.ConfigureProvider(providers.ConfigureProviderRequest{})
|
|
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.ConfigureProvider(providers.ConfigureProviderRequest{})
|
|
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("there should be no change for the %s instance, got %s", addr, got.Action)
|
|
}
|
|
}
|