diff --git a/terraform/context_refresh_test.go b/terraform/context_refresh_test.go index 77535d735..b8efe480c 100644 --- a/terraform/context_refresh_test.go +++ b/terraform/context_refresh_test.go @@ -1743,3 +1743,54 @@ resource "aws_instance" "bar" { t.Fatal("create_before_destroy not updated in instance state") } } + +func TestContext2Refresh_dataSourceOrphan(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ``, + }) + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.DataResourceMode, + Type: "test_data_source", + Name: "foo", + }.Instance(addrs.NoKey), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), + Dependencies: []addrs.ConfigResource{}, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + p := testProvider("test") + p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { + resp.State = cty.NullVal(req.Config.Type()) + return + } + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + + _, diags := ctx.Refresh() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + if p.ReadResourceCalled { + t.Fatal("there are no managed resources to read") + } + + if p.ReadDataSourceCalled { + t.Fatal("orphaned data source instance should not be read") + } +} diff --git a/terraform/node_resource_plan_instance.go b/terraform/node_resource_plan_instance.go index 3ffe99460..3810484d0 100644 --- a/terraform/node_resource_plan_instance.go +++ b/terraform/node_resource_plan_instance.go @@ -36,7 +36,7 @@ func (n *NodePlannableResourceInstance) Execute(ctx EvalContext, op walkOperatio // Eval info is different depending on what kind of resource this is switch addr.Resource.Resource.Mode { case addrs.ManagedResourceMode: - return n.managedResourceExecute(ctx, n.skipRefresh) + return n.managedResourceExecute(ctx) case addrs.DataResourceMode: return n.dataResourceExecute(ctx) default: @@ -123,7 +123,7 @@ func (n *NodePlannableResourceInstance) dataResourceExecute(ctx EvalContext) err return err } -func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext, skipRefresh bool) error { +func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext) error { config := n.Config addr := n.ResourceInstanceAddr() @@ -162,7 +162,7 @@ func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext, } // Refresh, maybe - if !skipRefresh { + if !n.skipRefresh { refresh := &EvalRefresh{ Addr: addr.Resource, ProviderAddr: n.ResolvedProvider, diff --git a/terraform/node_resource_plan_orphan.go b/terraform/node_resource_plan_orphan.go index 7469473e3..515caec58 100644 --- a/terraform/node_resource_plan_orphan.go +++ b/terraform/node_resource_plan_orphan.go @@ -1,6 +1,10 @@ package terraform import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/states" ) @@ -32,6 +36,28 @@ func (n *NodePlannableResourceInstanceOrphan) Name() string { func (n *NodePlannableResourceInstanceOrphan) Execute(ctx EvalContext, op walkOperation) error { addr := n.ResourceInstanceAddr() + // Eval info is different depending on what kind of resource this is + switch addr.Resource.Resource.Mode { + case addrs.ManagedResourceMode: + return n.managedResourceExecute(ctx) + case addrs.DataResourceMode: + return n.dataResourceExecute(ctx) + default: + panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) + } +} + +func (n *NodePlannableResourceInstanceOrphan) dataResourceExecute(ctx EvalContext) error { + // A data source that is no longer in the config is removed from the state + log.Printf("[TRACE] NodePlannableResourceInstanceOrphan: removing state object for %s", n.Addr) + state := ctx.RefreshState() + state.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) + return nil +} + +func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalContext) error { + addr := n.ResourceInstanceAddr() + // Declare a bunch of variables that are used for state during // evaluation. These are written to by-address below. var change *plans.ResourceInstanceChange