diff --git a/states/sync.go b/states/sync.go index 780f83bb8..a37744673 100644 --- a/states/sync.go +++ b/states/sync.go @@ -179,7 +179,7 @@ func (s *SyncState) ResourceInstanceObject(addr addrs.AbsResourceInstance, gen G if inst == nil { return nil } - return inst.GetGeneration(gen) + return inst.GetGeneration(gen).DeepCopy() } // SetResourceMeta updates the resource-level metadata for the resource at diff --git a/terraform/eval_state.go b/terraform/eval_state.go index d861c07e4..d506ce3fe 100644 --- a/terraform/eval_state.go +++ b/terraform/eval_state.go @@ -212,6 +212,10 @@ func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) { absAddr := n.Addr.Absolute(ctx.Path()) state := ctx.State() + if n.ProviderAddr.ProviderConfig.Type == "" { + return nil, fmt.Errorf("failed to write state for %s, missing provider type", absAddr) + } + obj := *n.State if obj == nil || obj.Value.IsNull() { // No need to encode anything: we'll just write it directly. diff --git a/terraform/node_data_destroy.go b/terraform/node_data_destroy.go index 477fd0aba..6ba39904d 100644 --- a/terraform/node_data_destroy.go +++ b/terraform/node_data_destroy.go @@ -1,23 +1,40 @@ package terraform import ( + "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/states" ) -// NodeDestroyableDataResource represents a resource that is "destroyable": +// NodeDestroyableDataResourceInstance represents a resource that is "destroyable": // it is ready to be destroyed. -type NodeDestroyableDataResource struct { +type NodeDestroyableDataResourceInstance struct { *NodeAbstractResourceInstance } // GraphNodeEvalable -func (n *NodeDestroyableDataResource) EvalTree() EvalNode { +func (n *NodeDestroyableDataResourceInstance) EvalTree() EvalNode { addr := n.ResourceInstanceAddr() + var providerSchema *ProviderSchema + // We don't need the provider, but we're calling EvalGetProvider to load the + // schema. + var provider providers.Interface + // Just destroy it. var state *states.ResourceInstanceObject - return &EvalWriteState{ - Addr: addr.Resource, - State: &state, // state is nil here + return &EvalSequence{ + Nodes: []EvalNode{ + &EvalGetProvider{ + Addr: n.ResolvedProvider, + Output: &provider, + Schema: &providerSchema, + }, + &EvalWriteState{ + Addr: addr.Resource, + State: &state, + ProviderAddr: n.ResolvedProvider, + ProviderSchema: &providerSchema, + }, + }, } } diff --git a/terraform/node_data_refresh.go b/terraform/node_data_refresh.go index 6a008d74b..a086214d4 100644 --- a/terraform/node_data_refresh.go +++ b/terraform/node_data_refresh.go @@ -56,10 +56,11 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er // We also need a destroyable resource for orphans that are a result of a // scaled-in count. concreteResourceDestroyable := func(a *NodeAbstractResourceInstance) dag.Vertex { - // Add the config since we don't do that via transforms + // Add the config and provider since we don't do that via transforms a.Config = n.Config + a.ResolvedProvider = n.ResolvedProvider - return &NodeDestroyableDataResource{ + return &NodeDestroyableDataResourceInstance{ NodeAbstractResourceInstance: a, } } diff --git a/terraform/node_data_refresh_test.go b/terraform/node_data_refresh_test.go index f3e006f1b..6b6059fa2 100644 --- a/terraform/node_data_refresh_test.go +++ b/terraform/node_data_refresh_test.go @@ -128,6 +128,11 @@ func TestNodeRefreshableDataResourceDynamicExpand_scaleIn(t *testing.T) { "foo", ), Config: m.Module.DataResources["data.aws_instance.foo"], + ResolvedProvider: addrs.AbsProviderConfig{ + ProviderConfig: addrs.ProviderConfig{ + Type: "aws", + }, + }, }, } @@ -147,14 +152,29 @@ func TestNodeRefreshableDataResourceDynamicExpand_scaleIn(t *testing.T) { expected := `data.aws_instance.foo[0] - *terraform.NodeRefreshableDataResourceInstance data.aws_instance.foo[1] - *terraform.NodeRefreshableDataResourceInstance data.aws_instance.foo[2] - *terraform.NodeRefreshableDataResourceInstance -data.aws_instance.foo[3] - *terraform.NodeDestroyableDataResource +data.aws_instance.foo[3] - *terraform.NodeDestroyableDataResourceInstance root - terraform.graphNodeRoot data.aws_instance.foo[0] - *terraform.NodeRefreshableDataResourceInstance data.aws_instance.foo[1] - *terraform.NodeRefreshableDataResourceInstance data.aws_instance.foo[2] - *terraform.NodeRefreshableDataResourceInstance - data.aws_instance.foo[3] - *terraform.NodeDestroyableDataResource + data.aws_instance.foo[3] - *terraform.NodeDestroyableDataResourceInstance ` if expected != actual { t.Fatalf("Expected:\n%s\nGot:\n%s", expected, actual) } + + var destroyableDataResource *NodeDestroyableDataResourceInstance + for _, v := range g.Vertices() { + if r, ok := v.(*NodeDestroyableDataResourceInstance); ok { + destroyableDataResource = r + } + } + + if destroyableDataResource == nil { + t.Fatal("failed to find a destroyableDataResource") + } + + if destroyableDataResource.ResolvedProvider.ProviderConfig.Type == "" { + t.Fatal("NodeDestroyableDataResourceInstance missing provider config") + } } diff --git a/terraform/node_resource_refresh.go b/terraform/node_resource_refresh.go index 472eb853d..950602320 100644 --- a/terraform/node_resource_refresh.go +++ b/terraform/node_resource_refresh.go @@ -149,7 +149,7 @@ func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode { NodeAbstractResourceInstance: n.NodeAbstractResourceInstance, } } else { - dn = &NodeDestroyableDataResource{ + dn = &NodeDestroyableDataResourceInstance{ NodeAbstractResourceInstance: n.NodeAbstractResourceInstance, } } diff --git a/terraform/transform_attach_state.go b/terraform/transform_attach_state.go index 0adb05877..3af7b989d 100644 --- a/terraform/transform_attach_state.go +++ b/terraform/transform_attach_state.go @@ -59,7 +59,9 @@ func (t *AttachStateTransformer) Transform(g *Graph) error { continue } - an.AttachResourceState(rs) + // make sure to attach a copy of the state, so instances can modify the + // same ResourceState. + an.AttachResourceState(rs.DeepCopy()) } return nil