github.com/r3vit/terraform@v0.11.9-beta1.0.20181016131357-87d05607d3c5/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  	// We modify our referenceable name to have the suffix of ".destroy"
    50  	// since depending on the creation side doesn't necessarilly mean
    51  	// depending on destruction.
    52  	suffix := ".destroy"
    53  
    54  	// If we're CBD, we also append "-cbd". This is because CBD will setup
    55  	// its own edges (in CBDEdgeTransformer). Depending on the "destroy"
    56  	// side generally doesn't mean depending on CBD as well. See GH-11349
    57  	if n.CreateBeforeDestroy() {
    58  		suffix += "-cbd"
    59  	}
    60  
    61  	result := n.NodeAbstractResource.ReferenceableName()
    62  	for i, v := range result {
    63  		result[i] = v + suffix
    64  	}
    65  
    66  	return result
    67  }
    68  
    69  // GraphNodeReferencer, overriding NodeAbstractResource
    70  func (n *NodeDestroyResource) References() []string {
    71  	// If we have a config, then we need to include destroy-time dependencies
    72  	if c := n.Config; c != nil {
    73  		var result []string
    74  		for _, p := range c.Provisioners {
    75  			// We include conn info and config for destroy time provisioners
    76  			// as dependencies that we have.
    77  			if p.When == config.ProvisionerWhenDestroy {
    78  				result = append(result, ReferencesFromConfig(p.ConnInfo)...)
    79  				result = append(result, ReferencesFromConfig(p.RawConfig)...)
    80  			}
    81  		}
    82  
    83  		return result
    84  	}
    85  
    86  	return nil
    87  }
    88  
    89  // GraphNodeDynamicExpandable
    90  func (n *NodeDestroyResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
    91  	// If we have no config we do nothing
    92  	if n.Addr == nil {
    93  		return nil, nil
    94  	}
    95  
    96  	state, lock := ctx.State()
    97  	lock.RLock()
    98  	defer lock.RUnlock()
    99  
   100  	// Start creating the steps
   101  	steps := make([]GraphTransformer, 0, 5)
   102  
   103  	// We want deposed resources in the state to be destroyed
   104  	steps = append(steps, &DeposedTransformer{
   105  		State:            state,
   106  		View:             n.Addr.stateId(),
   107  		ResolvedProvider: n.ResolvedProvider,
   108  	})
   109  
   110  	// Target
   111  	steps = append(steps, &TargetsTransformer{
   112  		ParsedTargets: n.Targets,
   113  	})
   114  
   115  	// Always end with the root being added
   116  	steps = append(steps, &RootTransformer{})
   117  
   118  	// Build the graph
   119  	b := &BasicGraphBuilder{
   120  		Steps: steps,
   121  		Name:  "NodeResourceDestroy",
   122  	}
   123  	return b.Build(ctx.Path())
   124  }
   125  
   126  // GraphNodeEvalable
   127  func (n *NodeDestroyResource) EvalTree() EvalNode {
   128  	// stateId is the ID to put into the state
   129  	stateId := n.Addr.stateId()
   130  
   131  	// Build the instance info. More of this will be populated during eval
   132  	info := &InstanceInfo{
   133  		Id:          stateId,
   134  		Type:        n.Addr.Type,
   135  		uniqueExtra: "destroy",
   136  	}
   137  
   138  	// Build the resource for eval
   139  	addr := n.Addr
   140  	resource := &Resource{
   141  		Name:       addr.Name,
   142  		Type:       addr.Type,
   143  		CountIndex: addr.Index,
   144  	}
   145  	if resource.CountIndex < 0 {
   146  		resource.CountIndex = 0
   147  	}
   148  
   149  	// Get our state
   150  	rs := n.ResourceState
   151  	if rs == nil {
   152  		rs = &ResourceState{
   153  			Provider: n.ResolvedProvider,
   154  		}
   155  	}
   156  
   157  	var diffApply *InstanceDiff
   158  	var provider ResourceProvider
   159  	var state *InstanceState
   160  	var err error
   161  	return &EvalOpFilter{
   162  		Ops: []walkOperation{walkApply, walkDestroy},
   163  		Node: &EvalSequence{
   164  			Nodes: []EvalNode{
   165  				// Get the saved diff for apply
   166  				&EvalReadDiff{
   167  					Name: stateId,
   168  					Diff: &diffApply,
   169  				},
   170  
   171  				// Filter the diff so we only get the destroy
   172  				&EvalFilterDiff{
   173  					Diff:    &diffApply,
   174  					Output:  &diffApply,
   175  					Destroy: true,
   176  				},
   177  
   178  				// If we're not destroying, then compare diffs
   179  				&EvalIf{
   180  					If: func(ctx EvalContext) (bool, error) {
   181  						if diffApply != nil && diffApply.GetDestroy() {
   182  							return true, nil
   183  						}
   184  
   185  						return true, EvalEarlyExitError{}
   186  					},
   187  					Then: EvalNoop{},
   188  				},
   189  
   190  				// Load the instance info so we have the module path set
   191  				&EvalInstanceInfo{Info: info},
   192  
   193  				&EvalGetProvider{
   194  					Name:   n.ResolvedProvider,
   195  					Output: &provider,
   196  				},
   197  				&EvalReadState{
   198  					Name:   stateId,
   199  					Output: &state,
   200  				},
   201  				&EvalRequireState{
   202  					State: &state,
   203  				},
   204  
   205  				// Call pre-apply hook
   206  				&EvalApplyPre{
   207  					Info:  info,
   208  					State: &state,
   209  					Diff:  &diffApply,
   210  				},
   211  
   212  				// Run destroy provisioners if not tainted
   213  				&EvalIf{
   214  					If: func(ctx EvalContext) (bool, error) {
   215  						if state != nil && state.Tainted {
   216  							return false, nil
   217  						}
   218  
   219  						return true, nil
   220  					},
   221  
   222  					Then: &EvalApplyProvisioners{
   223  						Info:           info,
   224  						State:          &state,
   225  						Resource:       n.Config,
   226  						InterpResource: resource,
   227  						Error:          &err,
   228  						When:           config.ProvisionerWhenDestroy,
   229  					},
   230  				},
   231  
   232  				// If we have a provisioning error, then we just call
   233  				// the post-apply hook now.
   234  				&EvalIf{
   235  					If: func(ctx EvalContext) (bool, error) {
   236  						return err != nil, nil
   237  					},
   238  
   239  					Then: &EvalApplyPost{
   240  						Info:  info,
   241  						State: &state,
   242  						Error: &err,
   243  					},
   244  				},
   245  
   246  				// Make sure we handle data sources properly.
   247  				&EvalIf{
   248  					If: func(ctx EvalContext) (bool, error) {
   249  						if n.Addr == nil {
   250  							return false, fmt.Errorf("nil address")
   251  						}
   252  
   253  						if n.Addr.Mode == config.DataResourceMode {
   254  							return true, nil
   255  						}
   256  
   257  						return false, nil
   258  					},
   259  
   260  					Then: &EvalReadDataApply{
   261  						Info:     info,
   262  						Diff:     &diffApply,
   263  						Provider: &provider,
   264  						Output:   &state,
   265  					},
   266  					Else: &EvalApply{
   267  						Info:     info,
   268  						State:    &state,
   269  						Diff:     &diffApply,
   270  						Provider: &provider,
   271  						Output:   &state,
   272  						Error:    &err,
   273  					},
   274  				},
   275  				&EvalWriteState{
   276  					Name:         stateId,
   277  					ResourceType: n.Addr.Type,
   278  					Provider:     n.ResolvedProvider,
   279  					Dependencies: rs.Dependencies,
   280  					State:        &state,
   281  				},
   282  				&EvalApplyPost{
   283  					Info:  info,
   284  					State: &state,
   285  					Error: &err,
   286  				},
   287  				&EvalUpdateStateHook{},
   288  			},
   289  		},
   290  	}
   291  }