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 }