315 lines
11 KiB
Go
315 lines
11 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"
|
|
)
|
|
|
|
// ConcreteResourceInstanceDeposedNodeFunc is a callback type used to convert
|
|
// an abstract resource instance to a concrete one of some type that has
|
|
// an associated deposed object key.
|
|
type ConcreteResourceInstanceDeposedNodeFunc func(*NodeAbstractResourceInstance, states.DeposedKey) dag.Vertex
|
|
|
|
type GraphNodeDeposedResourceInstanceObject interface {
|
|
DeposedInstanceObjectKey() states.DeposedKey
|
|
}
|
|
|
|
// NodePlanDeposedResourceInstanceObject represents deposed resource
|
|
// instance objects during plan. These are distinct from the primary object
|
|
// for each resource instance since the only valid operation to do with them
|
|
// is to destroy them.
|
|
//
|
|
// This node type is also used during the refresh walk to ensure that the
|
|
// record of a deposed object is up-to-date before we plan to destroy it.
|
|
type NodePlanDeposedResourceInstanceObject struct {
|
|
*NodeAbstractResourceInstance
|
|
DeposedKey states.DeposedKey
|
|
}
|
|
|
|
var (
|
|
_ GraphNodeDeposedResourceInstanceObject = (*NodePlanDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeConfigResource = (*NodePlanDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeResourceInstance = (*NodePlanDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeReferenceable = (*NodePlanDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeReferencer = (*NodePlanDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeEvalable = (*NodePlanDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeProviderConsumer = (*NodePlanDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeProvisionerConsumer = (*NodePlanDeposedResourceInstanceObject)(nil)
|
|
)
|
|
|
|
func (n *NodePlanDeposedResourceInstanceObject) Name() string {
|
|
return fmt.Sprintf("%s (deposed %s)", n.ResourceInstanceAddr().String(), n.DeposedKey)
|
|
}
|
|
|
|
func (n *NodePlanDeposedResourceInstanceObject) DeposedInstanceObjectKey() states.DeposedKey {
|
|
return n.DeposedKey
|
|
}
|
|
|
|
// GraphNodeReferenceable implementation, overriding the one from NodeAbstractResourceInstance
|
|
func (n *NodePlanDeposedResourceInstanceObject) ReferenceableAddrs() []addrs.Referenceable {
|
|
// Deposed objects don't participate in references.
|
|
return nil
|
|
}
|
|
|
|
// GraphNodeReferencer implementation, overriding the one from NodeAbstractResourceInstance
|
|
func (n *NodePlanDeposedResourceInstanceObject) References() []*addrs.Reference {
|
|
// We don't evaluate configuration for deposed objects, so they effectively
|
|
// make no references.
|
|
return nil
|
|
}
|
|
|
|
// GraphNodeEvalable impl.
|
|
func (n *NodePlanDeposedResourceInstanceObject) EvalTree() EvalNode {
|
|
addr := n.ResourceInstanceAddr()
|
|
|
|
var provider providers.Interface
|
|
var providerSchema *ProviderSchema
|
|
var state *states.ResourceInstanceObject
|
|
|
|
seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
|
|
|
|
// During the refresh walk we will ensure that our record of the deposed
|
|
// object is up-to-date. If it was already deleted outside of Terraform
|
|
// then this will remove it from state and thus avoid us planning a
|
|
// destroy for it during the subsequent plan walk.
|
|
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
|
Ops: []walkOperation{walkRefresh},
|
|
Node: &EvalSequence{
|
|
Nodes: []EvalNode{
|
|
&EvalGetProvider{
|
|
Addr: n.ResolvedProvider,
|
|
Output: &provider,
|
|
Schema: &providerSchema,
|
|
},
|
|
&EvalReadStateDeposed{
|
|
Addr: addr.Resource,
|
|
Provider: &provider,
|
|
ProviderSchema: &providerSchema,
|
|
Key: n.DeposedKey,
|
|
Output: &state,
|
|
},
|
|
&EvalRefresh{
|
|
Addr: addr.Resource,
|
|
ProviderAddr: n.ResolvedProvider,
|
|
Provider: &provider,
|
|
ProviderMetas: n.ProviderMetas,
|
|
ProviderSchema: &providerSchema,
|
|
State: &state,
|
|
Output: &state,
|
|
},
|
|
&EvalWriteStateDeposed{
|
|
Addr: addr.Resource,
|
|
Key: n.DeposedKey,
|
|
ProviderAddr: n.ResolvedProvider,
|
|
ProviderSchema: &providerSchema,
|
|
State: &state,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
// During the plan walk we always produce a planned destroy change, because
|
|
// destroying is the only supported action for deposed objects.
|
|
var change *plans.ResourceInstanceChange
|
|
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
|
Ops: []walkOperation{walkPlan, walkPlanDestroy},
|
|
Node: &EvalSequence{
|
|
Nodes: []EvalNode{
|
|
&EvalGetProvider{
|
|
Addr: n.ResolvedProvider,
|
|
Output: &provider,
|
|
Schema: &providerSchema,
|
|
},
|
|
&EvalReadStateDeposed{
|
|
Addr: addr.Resource,
|
|
Output: &state,
|
|
Key: n.DeposedKey,
|
|
Provider: &provider,
|
|
ProviderSchema: &providerSchema,
|
|
},
|
|
&EvalDiffDestroy{
|
|
Addr: addr.Resource,
|
|
ProviderAddr: n.ResolvedProvider,
|
|
DeposedKey: n.DeposedKey,
|
|
State: &state,
|
|
Output: &change,
|
|
},
|
|
&EvalWriteDiff{
|
|
Addr: addr.Resource,
|
|
DeposedKey: n.DeposedKey,
|
|
ProviderSchema: &providerSchema,
|
|
Change: &change,
|
|
},
|
|
// Since deposed objects cannot be referenced by expressions
|
|
// elsewhere, we don't need to also record the planned new
|
|
// state in this case.
|
|
},
|
|
},
|
|
})
|
|
|
|
return seq
|
|
}
|
|
|
|
// NodeDestroyDeposedResourceInstanceObject represents deposed resource
|
|
// instance objects during apply. Nodes of this type are inserted by
|
|
// DiffTransformer when the planned changeset contains "delete" changes for
|
|
// deposed instance objects, and its only supported operation is to destroy
|
|
// and then forget the associated object.
|
|
type NodeDestroyDeposedResourceInstanceObject struct {
|
|
*NodeAbstractResourceInstance
|
|
DeposedKey states.DeposedKey
|
|
}
|
|
|
|
var (
|
|
_ GraphNodeDeposedResourceInstanceObject = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeConfigResource = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeResourceInstance = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeDestroyer = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeDestroyerCBD = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeReferenceable = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeReferencer = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeEvalable = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeProviderConsumer = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
|
_ GraphNodeProvisionerConsumer = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
|
)
|
|
|
|
func (n *NodeDestroyDeposedResourceInstanceObject) Name() string {
|
|
return fmt.Sprintf("%s (destroy deposed %s)", n.ResourceInstanceAddr(), n.DeposedKey)
|
|
}
|
|
|
|
func (n *NodeDestroyDeposedResourceInstanceObject) DeposedInstanceObjectKey() states.DeposedKey {
|
|
return n.DeposedKey
|
|
}
|
|
|
|
// GraphNodeReferenceable implementation, overriding the one from NodeAbstractResourceInstance
|
|
func (n *NodeDestroyDeposedResourceInstanceObject) ReferenceableAddrs() []addrs.Referenceable {
|
|
// Deposed objects don't participate in references.
|
|
return nil
|
|
}
|
|
|
|
// GraphNodeReferencer implementation, overriding the one from NodeAbstractResourceInstance
|
|
func (n *NodeDestroyDeposedResourceInstanceObject) References() []*addrs.Reference {
|
|
// We don't evaluate configuration for deposed objects, so they effectively
|
|
// make no references.
|
|
return nil
|
|
}
|
|
|
|
// GraphNodeDestroyer
|
|
func (n *NodeDestroyDeposedResourceInstanceObject) DestroyAddr() *addrs.AbsResourceInstance {
|
|
addr := n.ResourceInstanceAddr()
|
|
return &addr
|
|
}
|
|
|
|
// GraphNodeDestroyerCBD
|
|
func (n *NodeDestroyDeposedResourceInstanceObject) CreateBeforeDestroy() bool {
|
|
// A deposed instance is always CreateBeforeDestroy by definition, since
|
|
// we use deposed only to handle create-before-destroy.
|
|
return true
|
|
}
|
|
|
|
// GraphNodeDestroyerCBD
|
|
func (n *NodeDestroyDeposedResourceInstanceObject) ModifyCreateBeforeDestroy(v bool) error {
|
|
if !v {
|
|
// Should never happen: deposed instances are _always_ create_before_destroy.
|
|
return fmt.Errorf("can't deactivate create_before_destroy for a deposed instance")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GraphNodeEvalable impl.
|
|
func (n *NodeDestroyDeposedResourceInstanceObject) EvalTree() EvalNode {
|
|
addr := n.ResourceInstanceAddr()
|
|
|
|
var provider providers.Interface
|
|
var providerSchema *ProviderSchema
|
|
var state *states.ResourceInstanceObject
|
|
var change *plans.ResourceInstanceChange
|
|
var err error
|
|
|
|
return &EvalSequence{
|
|
Nodes: []EvalNode{
|
|
&EvalGetProvider{
|
|
Addr: n.ResolvedProvider,
|
|
Output: &provider,
|
|
Schema: &providerSchema,
|
|
},
|
|
&EvalReadStateDeposed{
|
|
Addr: addr.Resource,
|
|
Output: &state,
|
|
Key: n.DeposedKey,
|
|
Provider: &provider,
|
|
ProviderSchema: &providerSchema,
|
|
},
|
|
&EvalDiffDestroy{
|
|
Addr: addr.Resource,
|
|
ProviderAddr: n.ResolvedProvider,
|
|
State: &state,
|
|
Output: &change,
|
|
},
|
|
// Call pre-apply hook
|
|
&EvalApplyPre{
|
|
Addr: addr.Resource,
|
|
State: &state,
|
|
Change: &change,
|
|
},
|
|
&EvalApply{
|
|
Addr: addr.Resource,
|
|
Config: nil, // No configuration because we are destroying
|
|
State: &state,
|
|
Change: &change,
|
|
Provider: &provider,
|
|
ProviderAddr: n.ResolvedProvider,
|
|
ProviderSchema: &providerSchema,
|
|
Output: &state,
|
|
Error: &err,
|
|
},
|
|
// Always write the resource back to the state deposed... if it
|
|
// was successfully destroyed it will be pruned. If it was not, it will
|
|
// be caught on the next run.
|
|
&EvalWriteStateDeposed{
|
|
Addr: addr.Resource,
|
|
Key: n.DeposedKey,
|
|
ProviderAddr: n.ResolvedProvider,
|
|
ProviderSchema: &providerSchema,
|
|
State: &state,
|
|
},
|
|
&EvalApplyPost{
|
|
Addr: addr.Resource,
|
|
State: &state,
|
|
Error: &err,
|
|
},
|
|
&EvalReturnError{
|
|
Error: &err,
|
|
},
|
|
&EvalUpdateStateHook{},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GraphNodeDeposer is an optional interface implemented by graph nodes that
|
|
// might create a single new deposed object for a specific associated resource
|
|
// instance, allowing a caller to optionally pre-allocate a DeposedKey for
|
|
// it.
|
|
type GraphNodeDeposer interface {
|
|
// SetPreallocatedDeposedKey will be called during graph construction
|
|
// if a particular node must use a pre-allocated deposed key if/when it
|
|
// "deposes" the current object of its associated resource instance.
|
|
SetPreallocatedDeposedKey(key states.DeposedKey)
|
|
}
|
|
|
|
// graphNodeDeposer is an embeddable implementation of GraphNodeDeposer.
|
|
// Embed it in a node type to get automatic support for it, and then access
|
|
// the field PreallocatedDeposedKey to access any pre-allocated key.
|
|
type graphNodeDeposer struct {
|
|
PreallocatedDeposedKey states.DeposedKey
|
|
}
|
|
|
|
func (n *graphNodeDeposer) SetPreallocatedDeposedKey(key states.DeposedKey) {
|
|
n.PreallocatedDeposedKey = key
|
|
}
|