github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/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 }) 108 109 // Target 110 steps = append(steps, &TargetsTransformer{ 111 ParsedTargets: n.Targets, 112 }) 113 114 // Always end with the root being added 115 steps = append(steps, &RootTransformer{}) 116 117 // Build the graph 118 b := &BasicGraphBuilder{ 119 Steps: steps, 120 Name: "NodeResourceDestroy", 121 } 122 return b.Build(ctx.Path()) 123 } 124 125 // GraphNodeEvalable 126 func (n *NodeDestroyResource) EvalTree() EvalNode { 127 // stateId is the ID to put into the state 128 stateId := n.Addr.stateId() 129 130 // Build the instance info. More of this will be populated during eval 131 info := &InstanceInfo{ 132 Id: stateId, 133 Type: n.Addr.Type, 134 uniqueExtra: "destroy", 135 } 136 137 // Build the resource for eval 138 addr := n.Addr 139 resource := &Resource{ 140 Name: addr.Name, 141 Type: addr.Type, 142 CountIndex: addr.Index, 143 } 144 if resource.CountIndex < 0 { 145 resource.CountIndex = 0 146 } 147 148 // Get our state 149 rs := n.ResourceState 150 if rs == nil { 151 rs = &ResourceState{} 152 } 153 154 var diffApply *InstanceDiff 155 var provider ResourceProvider 156 var state *InstanceState 157 var err error 158 return &EvalOpFilter{ 159 Ops: []walkOperation{walkApply, walkDestroy}, 160 Node: &EvalSequence{ 161 Nodes: []EvalNode{ 162 // Get the saved diff for apply 163 &EvalReadDiff{ 164 Name: stateId, 165 Diff: &diffApply, 166 }, 167 168 // Filter the diff so we only get the destroy 169 &EvalFilterDiff{ 170 Diff: &diffApply, 171 Output: &diffApply, 172 Destroy: true, 173 }, 174 175 // If we're not destroying, then compare diffs 176 &EvalIf{ 177 If: func(ctx EvalContext) (bool, error) { 178 if diffApply != nil && diffApply.GetDestroy() { 179 return true, nil 180 } 181 182 return true, EvalEarlyExitError{} 183 }, 184 Then: EvalNoop{}, 185 }, 186 187 // Load the instance info so we have the module path set 188 &EvalInstanceInfo{Info: info}, 189 190 &EvalGetProvider{ 191 Name: n.ProvidedBy()[0], 192 Output: &provider, 193 }, 194 &EvalReadState{ 195 Name: stateId, 196 Output: &state, 197 }, 198 &EvalRequireState{ 199 State: &state, 200 }, 201 202 // Call pre-apply hook 203 &EvalApplyPre{ 204 Info: info, 205 State: &state, 206 Diff: &diffApply, 207 }, 208 209 // Run destroy provisioners if not tainted 210 &EvalIf{ 211 If: func(ctx EvalContext) (bool, error) { 212 if state != nil && state.Tainted { 213 return false, nil 214 } 215 216 return true, nil 217 }, 218 219 Then: &EvalApplyProvisioners{ 220 Info: info, 221 State: &state, 222 Resource: n.Config, 223 InterpResource: resource, 224 Error: &err, 225 When: config.ProvisionerWhenDestroy, 226 }, 227 }, 228 229 // If we have a provisioning error, then we just call 230 // the post-apply hook now. 231 &EvalIf{ 232 If: func(ctx EvalContext) (bool, error) { 233 return err != nil, nil 234 }, 235 236 Then: &EvalApplyPost{ 237 Info: info, 238 State: &state, 239 Error: &err, 240 }, 241 }, 242 243 // Make sure we handle data sources properly. 244 &EvalIf{ 245 If: func(ctx EvalContext) (bool, error) { 246 if n.Addr == nil { 247 return false, fmt.Errorf("nil address") 248 } 249 250 if n.Addr.Mode == config.DataResourceMode { 251 return true, nil 252 } 253 254 return false, nil 255 }, 256 257 Then: &EvalReadDataApply{ 258 Info: info, 259 Diff: &diffApply, 260 Provider: &provider, 261 Output: &state, 262 }, 263 Else: &EvalApply{ 264 Info: info, 265 State: &state, 266 Diff: &diffApply, 267 Provider: &provider, 268 Output: &state, 269 Error: &err, 270 }, 271 }, 272 &EvalWriteState{ 273 Name: stateId, 274 ResourceType: n.Addr.Type, 275 Provider: rs.Provider, 276 Dependencies: rs.Dependencies, 277 State: &state, 278 }, 279 &EvalApplyPost{ 280 Info: info, 281 State: &state, 282 Error: &err, 283 }, 284 &EvalUpdateStateHook{}, 285 }, 286 }, 287 } 288 }