github.com/mkuzmin/terraform@v0.3.7-0.20161118171027-ec4c00ff92a9/terraform/node_resource_destroy.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/config"
     7  )
     8  
     9  // NodeDestroyResource represents a resource that is to be destroyed.
    10  type NodeDestroyResource struct {
    11  	*NodeAbstractResource
    12  }
    13  
    14  func (n *NodeDestroyResource) Name() string {
    15  	return n.NodeAbstractResource.Name() + " (destroy)"
    16  }
    17  
    18  // GraphNodeDestroyer
    19  func (n *NodeDestroyResource) DestroyAddr() *ResourceAddress {
    20  	return n.Addr
    21  }
    22  
    23  // GraphNodeDestroyerCBD
    24  func (n *NodeDestroyResource) CreateBeforeDestroy() bool {
    25  	// If we have no config, we just assume no
    26  	if n.Config == nil {
    27  		return false
    28  	}
    29  
    30  	return n.Config.Lifecycle.CreateBeforeDestroy
    31  }
    32  
    33  // GraphNodeReferenceable, overriding NodeAbstractResource
    34  func (n *NodeDestroyResource) ReferenceableName() []string {
    35  	result := n.NodeAbstractResource.ReferenceableName()
    36  	for i, v := range result {
    37  		result[i] = v + ".destroy"
    38  	}
    39  
    40  	return result
    41  }
    42  
    43  // GraphNodeReferencer, overriding NodeAbstractResource
    44  func (n *NodeDestroyResource) References() []string {
    45  	return nil
    46  }
    47  
    48  // GraphNodeDynamicExpandable
    49  func (n *NodeDestroyResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
    50  	// If we have no config we do nothing
    51  	if n.Config == nil {
    52  		return nil, nil
    53  	}
    54  
    55  	state, lock := ctx.State()
    56  	lock.RLock()
    57  	defer lock.RUnlock()
    58  
    59  	// Start creating the steps
    60  	steps := make([]GraphTransformer, 0, 5)
    61  
    62  	// We want deposed resources in the state to be destroyed
    63  	steps = append(steps, &DeposedTransformer{
    64  		State: state,
    65  		View:  n.Config.Id(),
    66  	})
    67  
    68  	// Target
    69  	steps = append(steps, &TargetsTransformer{
    70  		ParsedTargets: n.Targets,
    71  	})
    72  
    73  	// Always end with the root being added
    74  	steps = append(steps, &RootTransformer{})
    75  
    76  	// Build the graph
    77  	b := &BasicGraphBuilder{Steps: steps}
    78  	return b.Build(ctx.Path())
    79  }
    80  
    81  // GraphNodeEvalable
    82  func (n *NodeDestroyResource) EvalTree() EvalNode {
    83  	// stateId is the ID to put into the state
    84  	stateId := n.Addr.stateId()
    85  	if n.Addr.Index > -1 {
    86  		stateId = fmt.Sprintf("%s.%d", stateId, n.Addr.Index)
    87  	}
    88  
    89  	// Build the instance info. More of this will be populated during eval
    90  	info := &InstanceInfo{
    91  		Id:          stateId,
    92  		Type:        n.Addr.Type,
    93  		uniqueExtra: "destroy",
    94  	}
    95  
    96  	// Get our state
    97  	rs := n.ResourceState
    98  	if rs == nil {
    99  		rs = &ResourceState{}
   100  	}
   101  
   102  	var diffApply *InstanceDiff
   103  	var provider ResourceProvider
   104  	var state *InstanceState
   105  	var err error
   106  	return &EvalOpFilter{
   107  		Ops: []walkOperation{walkApply, walkDestroy},
   108  		Node: &EvalSequence{
   109  			Nodes: []EvalNode{
   110  				// Get the saved diff for apply
   111  				&EvalReadDiff{
   112  					Name: stateId,
   113  					Diff: &diffApply,
   114  				},
   115  
   116  				// Filter the diff so we only get the destroy
   117  				&EvalFilterDiff{
   118  					Diff:    &diffApply,
   119  					Output:  &diffApply,
   120  					Destroy: true,
   121  				},
   122  
   123  				// If we're not destroying, then compare diffs
   124  				&EvalIf{
   125  					If: func(ctx EvalContext) (bool, error) {
   126  						if diffApply != nil && diffApply.GetDestroy() {
   127  							return true, nil
   128  						}
   129  
   130  						return true, EvalEarlyExitError{}
   131  					},
   132  					Then: EvalNoop{},
   133  				},
   134  
   135  				// Load the instance info so we have the module path set
   136  				&EvalInstanceInfo{Info: info},
   137  
   138  				&EvalGetProvider{
   139  					Name:   n.ProvidedBy()[0],
   140  					Output: &provider,
   141  				},
   142  				&EvalReadState{
   143  					Name:   stateId,
   144  					Output: &state,
   145  				},
   146  				&EvalRequireState{
   147  					State: &state,
   148  				},
   149  				// Make sure we handle data sources properly.
   150  				&EvalIf{
   151  					If: func(ctx EvalContext) (bool, error) {
   152  						if n.Addr == nil {
   153  							return false, fmt.Errorf("nil address")
   154  						}
   155  
   156  						if n.Addr.Mode == config.DataResourceMode {
   157  							return true, nil
   158  						}
   159  
   160  						return false, nil
   161  					},
   162  
   163  					Then: &EvalReadDataApply{
   164  						Info:     info,
   165  						Diff:     &diffApply,
   166  						Provider: &provider,
   167  						Output:   &state,
   168  					},
   169  					Else: &EvalApply{
   170  						Info:     info,
   171  						State:    &state,
   172  						Diff:     &diffApply,
   173  						Provider: &provider,
   174  						Output:   &state,
   175  						Error:    &err,
   176  					},
   177  				},
   178  				&EvalWriteState{
   179  					Name:         stateId,
   180  					ResourceType: n.Addr.Type,
   181  					Provider:     rs.Provider,
   182  					Dependencies: rs.Dependencies,
   183  					State:        &state,
   184  				},
   185  				&EvalApplyPost{
   186  					Info:  info,
   187  					State: &state,
   188  					Error: &err,
   189  				},
   190  				&EvalUpdateStateHook{},
   191  			},
   192  		},
   193  	}
   194  }