opentofu/terraform/node_resource_plan_destroy.go
Martin Atkins 49fa2b3f35 core: Always set ProviderAddr on EvalDiffDestroy
If we don't set it, we end up creating an invalid plan where the destroy
changes don't have a provider address set, which then later fails
decoding when round-tripped through a planfile.

This also includes some extra safety checks in EvalDiff and
EvalDiffDestroy so that we can catch this bug sooner in future.

This change is verified by
TestContext2Apply_plannedDestroyInterpolatedCount, which is now passing.
2018-10-16 19:14:11 -07:00

89 lines
2.7 KiB
Go

package terraform
import (
"fmt"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/providers"
"github.com/hashicorp/terraform/states"
)
// NodePlanDestroyableResourceInstance represents a resource that is ready
// to be planned for destruction.
type NodePlanDestroyableResourceInstance struct {
*NodeAbstractResourceInstance
}
var (
_ GraphNodeSubPath = (*NodePlanDestroyableResourceInstance)(nil)
_ GraphNodeReferenceable = (*NodePlanDestroyableResourceInstance)(nil)
_ GraphNodeReferencer = (*NodePlanDestroyableResourceInstance)(nil)
_ GraphNodeDestroyer = (*NodePlanDestroyableResourceInstance)(nil)
_ GraphNodeResource = (*NodePlanDestroyableResourceInstance)(nil)
_ GraphNodeResourceInstance = (*NodePlanDestroyableResourceInstance)(nil)
_ GraphNodeAttachResourceConfig = (*NodePlanDestroyableResourceInstance)(nil)
_ GraphNodeAttachResourceState = (*NodePlanDestroyableResourceInstance)(nil)
_ GraphNodeEvalable = (*NodePlanDestroyableResourceInstance)(nil)
_ GraphNodeProviderConsumer = (*NodePlanDestroyableResourceInstance)(nil)
)
// GraphNodeDestroyer
func (n *NodePlanDestroyableResourceInstance) DestroyAddr() *addrs.AbsResourceInstance {
addr := n.ResourceInstanceAddr()
return &addr
}
// GraphNodeEvalable
func (n *NodePlanDestroyableResourceInstance) EvalTree() EvalNode {
addr := n.ResourceInstanceAddr()
// Declare a bunch of variables that are used for state during
// evaluation. These are written to by address in the EvalNodes we
// declare below.
var provider providers.Interface
var providerSchema *ProviderSchema
var change *plans.ResourceInstanceChange
var state *states.ResourceInstanceObject
if n.ResolvedProvider.ProviderConfig.Type == "" {
// Should never happen; indicates that the graph was not constructed
// correctly since we didn't get our provider attached.
panic(fmt.Sprintf("%T %q was not assigned a resolved provider", n, dag.VertexName(n)))
}
return &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Addr: n.ResolvedProvider,
Output: &provider,
Schema: &providerSchema,
},
&EvalReadState{
Addr: addr.Resource,
Provider: &provider,
ProviderSchema: &providerSchema,
Output: &state,
},
&EvalDiffDestroy{
Addr: addr.Resource,
ProviderAddr: n.ResolvedProvider,
State: &state,
Output: &change,
},
&EvalCheckPreventDestroy{
Addr: addr.Resource,
Config: n.Config,
Change: &change,
},
&EvalWriteDiff{
Addr: addr.Resource,
ProviderSchema: &providerSchema,
Change: &change,
},
},
}
}