github.com/posener/terraform@v0.11.0-beta1.0.20171103235147-645df36af025/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 } 154 155 var diffApply *InstanceDiff 156 var provider ResourceProvider 157 var state *InstanceState 158 var err error 159 return &EvalOpFilter{ 160 Ops: []walkOperation{walkApply, walkDestroy}, 161 Node: &EvalSequence{ 162 Nodes: []EvalNode{ 163 // Get the saved diff for apply 164 &EvalReadDiff{ 165 Name: stateId, 166 Diff: &diffApply, 167 }, 168 169 // Filter the diff so we only get the destroy 170 &EvalFilterDiff{ 171 Diff: &diffApply, 172 Output: &diffApply, 173 Destroy: true, 174 }, 175 176 // If we're not destroying, then compare diffs 177 &EvalIf{ 178 If: func(ctx EvalContext) (bool, error) { 179 if diffApply != nil && diffApply.GetDestroy() { 180 return true, nil 181 } 182 183 return true, EvalEarlyExitError{} 184 }, 185 Then: EvalNoop{}, 186 }, 187 188 // Load the instance info so we have the module path set 189 &EvalInstanceInfo{Info: info}, 190 191 &EvalGetProvider{ 192 Name: n.ResolvedProvider, 193 Output: &provider, 194 }, 195 &EvalReadState{ 196 Name: stateId, 197 Output: &state, 198 }, 199 &EvalRequireState{ 200 State: &state, 201 }, 202 203 // Call pre-apply hook 204 &EvalApplyPre{ 205 Info: info, 206 State: &state, 207 Diff: &diffApply, 208 }, 209 210 // Run destroy provisioners if not tainted 211 &EvalIf{ 212 If: func(ctx EvalContext) (bool, error) { 213 if state != nil && state.Tainted { 214 return false, nil 215 } 216 217 return true, nil 218 }, 219 220 Then: &EvalApplyProvisioners{ 221 Info: info, 222 State: &state, 223 Resource: n.Config, 224 InterpResource: resource, 225 Error: &err, 226 When: config.ProvisionerWhenDestroy, 227 }, 228 }, 229 230 // If we have a provisioning error, then we just call 231 // the post-apply hook now. 232 &EvalIf{ 233 If: func(ctx EvalContext) (bool, error) { 234 return err != nil, nil 235 }, 236 237 Then: &EvalApplyPost{ 238 Info: info, 239 State: &state, 240 Error: &err, 241 }, 242 }, 243 244 // Make sure we handle data sources properly. 245 &EvalIf{ 246 If: func(ctx EvalContext) (bool, error) { 247 if n.Addr == nil { 248 return false, fmt.Errorf("nil address") 249 } 250 251 if n.Addr.Mode == config.DataResourceMode { 252 return true, nil 253 } 254 255 return false, nil 256 }, 257 258 Then: &EvalReadDataApply{ 259 Info: info, 260 Diff: &diffApply, 261 Provider: &provider, 262 Output: &state, 263 }, 264 Else: &EvalApply{ 265 Info: info, 266 State: &state, 267 Diff: &diffApply, 268 Provider: &provider, 269 Output: &state, 270 Error: &err, 271 }, 272 }, 273 &EvalWriteState{ 274 Name: stateId, 275 ResourceType: n.Addr.Type, 276 Provider: rs.Provider, 277 Dependencies: rs.Dependencies, 278 State: &state, 279 }, 280 &EvalApplyPost{ 281 Info: info, 282 State: &state, 283 Error: &err, 284 }, 285 &EvalUpdateStateHook{}, 286 }, 287 }, 288 } 289 }