github.com/bpineau/terraform@v0.8.0-rc1.0.20161126184705-a8886012d185/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{
    78  		Steps: steps,
    79  		Name:  "NodeResourceDestroy",
    80  	}
    81  	return b.Build(ctx.Path())
    82  }
    83  
    84  // GraphNodeEvalable
    85  func (n *NodeDestroyResource) EvalTree() EvalNode {
    86  	// stateId is the ID to put into the state
    87  	stateId := n.Addr.stateId()
    88  	if n.Addr.Index > -1 {
    89  		stateId = fmt.Sprintf("%s.%d", stateId, n.Addr.Index)
    90  	}
    91  
    92  	// Build the instance info. More of this will be populated during eval
    93  	info := &InstanceInfo{
    94  		Id:          stateId,
    95  		Type:        n.Addr.Type,
    96  		uniqueExtra: "destroy",
    97  	}
    98  
    99  	// Get our state
   100  	rs := n.ResourceState
   101  	if rs == nil {
   102  		rs = &ResourceState{}
   103  	}
   104  
   105  	var diffApply *InstanceDiff
   106  	var provider ResourceProvider
   107  	var state *InstanceState
   108  	var err error
   109  	return &EvalOpFilter{
   110  		Ops: []walkOperation{walkApply, walkDestroy},
   111  		Node: &EvalSequence{
   112  			Nodes: []EvalNode{
   113  				// Get the saved diff for apply
   114  				&EvalReadDiff{
   115  					Name: stateId,
   116  					Diff: &diffApply,
   117  				},
   118  
   119  				// Filter the diff so we only get the destroy
   120  				&EvalFilterDiff{
   121  					Diff:    &diffApply,
   122  					Output:  &diffApply,
   123  					Destroy: true,
   124  				},
   125  
   126  				// If we're not destroying, then compare diffs
   127  				&EvalIf{
   128  					If: func(ctx EvalContext) (bool, error) {
   129  						if diffApply != nil && diffApply.GetDestroy() {
   130  							return true, nil
   131  						}
   132  
   133  						return true, EvalEarlyExitError{}
   134  					},
   135  					Then: EvalNoop{},
   136  				},
   137  
   138  				// Load the instance info so we have the module path set
   139  				&EvalInstanceInfo{Info: info},
   140  
   141  				&EvalGetProvider{
   142  					Name:   n.ProvidedBy()[0],
   143  					Output: &provider,
   144  				},
   145  				&EvalReadState{
   146  					Name:   stateId,
   147  					Output: &state,
   148  				},
   149  				&EvalRequireState{
   150  					State: &state,
   151  				},
   152  				// Make sure we handle data sources properly.
   153  				&EvalIf{
   154  					If: func(ctx EvalContext) (bool, error) {
   155  						if n.Addr == nil {
   156  							return false, fmt.Errorf("nil address")
   157  						}
   158  
   159  						if n.Addr.Mode == config.DataResourceMode {
   160  							return true, nil
   161  						}
   162  
   163  						return false, nil
   164  					},
   165  
   166  					Then: &EvalReadDataApply{
   167  						Info:     info,
   168  						Diff:     &diffApply,
   169  						Provider: &provider,
   170  						Output:   &state,
   171  					},
   172  					Else: &EvalApply{
   173  						Info:     info,
   174  						State:    &state,
   175  						Diff:     &diffApply,
   176  						Provider: &provider,
   177  						Output:   &state,
   178  						Error:    &err,
   179  					},
   180  				},
   181  				&EvalWriteState{
   182  					Name:         stateId,
   183  					ResourceType: n.Addr.Type,
   184  					Provider:     rs.Provider,
   185  					Dependencies: rs.Dependencies,
   186  					State:        &state,
   187  				},
   188  				&EvalApplyPost{
   189  					Info:  info,
   190  					State: &state,
   191  					Error: &err,
   192  				},
   193  				&EvalUpdateStateHook{},
   194  			},
   195  		},
   196  	}
   197  }