github.com/pbthorste/terraform@v0.8.6-0.20170127005045-deb56bd93da2/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  // GraphNodeDestroyerCBD
    34  func (n *NodeDestroyResource) ModifyCreateBeforeDestroy(v bool) error {
    35  	// If we have no config, do nothing since it won't affect the
    36  	// create step anyways.
    37  	if n.Config == nil {
    38  		return nil
    39  	}
    40  
    41  	// Set CBD to true
    42  	n.Config.Lifecycle.CreateBeforeDestroy = true
    43  
    44  	return nil
    45  }
    46  
    47  // GraphNodeReferenceable, overriding NodeAbstractResource
    48  func (n *NodeDestroyResource) ReferenceableName() []string {
    49  	result := n.NodeAbstractResource.ReferenceableName()
    50  	for i, v := range result {
    51  		result[i] = v + ".destroy"
    52  	}
    53  
    54  	return result
    55  }
    56  
    57  // GraphNodeReferencer, overriding NodeAbstractResource
    58  func (n *NodeDestroyResource) References() []string {
    59  	return nil
    60  }
    61  
    62  // GraphNodeDynamicExpandable
    63  func (n *NodeDestroyResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
    64  	// If we have no config we do nothing
    65  	if n.Addr == nil {
    66  		return nil, nil
    67  	}
    68  
    69  	state, lock := ctx.State()
    70  	lock.RLock()
    71  	defer lock.RUnlock()
    72  
    73  	// Start creating the steps
    74  	steps := make([]GraphTransformer, 0, 5)
    75  
    76  	// We want deposed resources in the state to be destroyed
    77  	steps = append(steps, &DeposedTransformer{
    78  		State: state,
    79  		View:  n.Addr.stateId(),
    80  	})
    81  
    82  	// Target
    83  	steps = append(steps, &TargetsTransformer{
    84  		ParsedTargets: n.Targets,
    85  	})
    86  
    87  	// Always end with the root being added
    88  	steps = append(steps, &RootTransformer{})
    89  
    90  	// Build the graph
    91  	b := &BasicGraphBuilder{
    92  		Steps: steps,
    93  		Name:  "NodeResourceDestroy",
    94  	}
    95  	return b.Build(ctx.Path())
    96  }
    97  
    98  // GraphNodeEvalable
    99  func (n *NodeDestroyResource) EvalTree() EvalNode {
   100  	// stateId is the ID to put into the state
   101  	stateId := n.Addr.stateId()
   102  
   103  	// Build the instance info. More of this will be populated during eval
   104  	info := &InstanceInfo{
   105  		Id:          stateId,
   106  		Type:        n.Addr.Type,
   107  		uniqueExtra: "destroy",
   108  	}
   109  
   110  	// Build the resource for eval
   111  	addr := n.Addr
   112  	resource := &Resource{
   113  		Name:       addr.Name,
   114  		Type:       addr.Type,
   115  		CountIndex: addr.Index,
   116  	}
   117  	if resource.CountIndex < 0 {
   118  		resource.CountIndex = 0
   119  	}
   120  
   121  	// Get our state
   122  	rs := n.ResourceState
   123  	if rs == nil {
   124  		rs = &ResourceState{}
   125  	}
   126  
   127  	var diffApply *InstanceDiff
   128  	var provider ResourceProvider
   129  	var state *InstanceState
   130  	var err error
   131  	return &EvalOpFilter{
   132  		Ops: []walkOperation{walkApply, walkDestroy},
   133  		Node: &EvalSequence{
   134  			Nodes: []EvalNode{
   135  				// Get the saved diff for apply
   136  				&EvalReadDiff{
   137  					Name: stateId,
   138  					Diff: &diffApply,
   139  				},
   140  
   141  				// Filter the diff so we only get the destroy
   142  				&EvalFilterDiff{
   143  					Diff:    &diffApply,
   144  					Output:  &diffApply,
   145  					Destroy: true,
   146  				},
   147  
   148  				// If we're not destroying, then compare diffs
   149  				&EvalIf{
   150  					If: func(ctx EvalContext) (bool, error) {
   151  						if diffApply != nil && diffApply.GetDestroy() {
   152  							return true, nil
   153  						}
   154  
   155  						return true, EvalEarlyExitError{}
   156  					},
   157  					Then: EvalNoop{},
   158  				},
   159  
   160  				// Load the instance info so we have the module path set
   161  				&EvalInstanceInfo{Info: info},
   162  
   163  				&EvalGetProvider{
   164  					Name:   n.ProvidedBy()[0],
   165  					Output: &provider,
   166  				},
   167  				&EvalReadState{
   168  					Name:   stateId,
   169  					Output: &state,
   170  				},
   171  				&EvalRequireState{
   172  					State: &state,
   173  				},
   174  
   175  				// Call pre-apply hook
   176  				&EvalApplyPre{
   177  					Info:  info,
   178  					State: &state,
   179  					Diff:  &diffApply,
   180  				},
   181  
   182  				// Run destroy provisioners if not tainted
   183  				&EvalIf{
   184  					If: func(ctx EvalContext) (bool, error) {
   185  						if state != nil && state.Tainted {
   186  							return false, nil
   187  						}
   188  
   189  						return true, nil
   190  					},
   191  
   192  					Then: &EvalApplyProvisioners{
   193  						Info:           info,
   194  						State:          &state,
   195  						Resource:       n.Config,
   196  						InterpResource: resource,
   197  						Error:          &err,
   198  						When:           config.ProvisionerWhenDestroy,
   199  					},
   200  				},
   201  
   202  				// If we have a provisioning error, then we just call
   203  				// the post-apply hook now.
   204  				&EvalIf{
   205  					If: func(ctx EvalContext) (bool, error) {
   206  						return err != nil, nil
   207  					},
   208  
   209  					Then: &EvalApplyPost{
   210  						Info:  info,
   211  						State: &state,
   212  						Error: &err,
   213  					},
   214  				},
   215  
   216  				// Make sure we handle data sources properly.
   217  				&EvalIf{
   218  					If: func(ctx EvalContext) (bool, error) {
   219  						if n.Addr == nil {
   220  							return false, fmt.Errorf("nil address")
   221  						}
   222  
   223  						if n.Addr.Mode == config.DataResourceMode {
   224  							return true, nil
   225  						}
   226  
   227  						return false, nil
   228  					},
   229  
   230  					Then: &EvalReadDataApply{
   231  						Info:     info,
   232  						Diff:     &diffApply,
   233  						Provider: &provider,
   234  						Output:   &state,
   235  					},
   236  					Else: &EvalApply{
   237  						Info:     info,
   238  						State:    &state,
   239  						Diff:     &diffApply,
   240  						Provider: &provider,
   241  						Output:   &state,
   242  						Error:    &err,
   243  					},
   244  				},
   245  				&EvalWriteState{
   246  					Name:         stateId,
   247  					ResourceType: n.Addr.Type,
   248  					Provider:     rs.Provider,
   249  					Dependencies: rs.Dependencies,
   250  					State:        &state,
   251  				},
   252  				&EvalApplyPost{
   253  					Info:  info,
   254  					State: &state,
   255  					Error: &err,
   256  				},
   257  				&EvalUpdateStateHook{},
   258  			},
   259  		},
   260  	}
   261  }