github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/node_resource_destroy.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/terraform-plugin-sdk/internal/plans" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/providers" 9 10 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 11 "github.com/hashicorp/terraform-plugin-sdk/internal/configs" 12 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 13 ) 14 15 // NodeDestroyResourceInstance represents a resource instance that is to be 16 // destroyed. 17 type NodeDestroyResourceInstance struct { 18 *NodeAbstractResourceInstance 19 20 // If DeposedKey is set to anything other than states.NotDeposed then 21 // this node destroys a deposed object of the associated instance 22 // rather than its current object. 23 DeposedKey states.DeposedKey 24 25 CreateBeforeDestroyOverride *bool 26 } 27 28 var ( 29 _ GraphNodeResource = (*NodeDestroyResourceInstance)(nil) 30 _ GraphNodeResourceInstance = (*NodeDestroyResourceInstance)(nil) 31 _ GraphNodeDestroyer = (*NodeDestroyResourceInstance)(nil) 32 _ GraphNodeDestroyerCBD = (*NodeDestroyResourceInstance)(nil) 33 _ GraphNodeReferenceable = (*NodeDestroyResourceInstance)(nil) 34 _ GraphNodeReferencer = (*NodeDestroyResourceInstance)(nil) 35 _ GraphNodeEvalable = (*NodeDestroyResourceInstance)(nil) 36 _ GraphNodeProviderConsumer = (*NodeDestroyResourceInstance)(nil) 37 _ GraphNodeProvisionerConsumer = (*NodeDestroyResourceInstance)(nil) 38 ) 39 40 func (n *NodeDestroyResourceInstance) Name() string { 41 if n.DeposedKey != states.NotDeposed { 42 return fmt.Sprintf("%s (destroy deposed %s)", n.ResourceInstanceAddr(), n.DeposedKey) 43 } 44 return n.ResourceInstanceAddr().String() + " (destroy)" 45 } 46 47 // GraphNodeDestroyer 48 func (n *NodeDestroyResourceInstance) DestroyAddr() *addrs.AbsResourceInstance { 49 addr := n.ResourceInstanceAddr() 50 return &addr 51 } 52 53 // GraphNodeDestroyerCBD 54 func (n *NodeDestroyResourceInstance) CreateBeforeDestroy() bool { 55 if n.CreateBeforeDestroyOverride != nil { 56 return *n.CreateBeforeDestroyOverride 57 } 58 59 // If we have no config, we just assume no 60 if n.Config == nil || n.Config.Managed == nil { 61 return false 62 } 63 64 return n.Config.Managed.CreateBeforeDestroy 65 } 66 67 // GraphNodeDestroyerCBD 68 func (n *NodeDestroyResourceInstance) ModifyCreateBeforeDestroy(v bool) error { 69 n.CreateBeforeDestroyOverride = &v 70 return nil 71 } 72 73 // GraphNodeReferenceable, overriding NodeAbstractResource 74 func (n *NodeDestroyResourceInstance) ReferenceableAddrs() []addrs.Referenceable { 75 normalAddrs := n.NodeAbstractResourceInstance.ReferenceableAddrs() 76 destroyAddrs := make([]addrs.Referenceable, len(normalAddrs)) 77 78 phaseType := addrs.ResourceInstancePhaseDestroy 79 if n.CreateBeforeDestroy() { 80 phaseType = addrs.ResourceInstancePhaseDestroyCBD 81 } 82 83 for i, normalAddr := range normalAddrs { 84 switch ta := normalAddr.(type) { 85 case addrs.Resource: 86 destroyAddrs[i] = ta.Phase(phaseType) 87 case addrs.ResourceInstance: 88 destroyAddrs[i] = ta.Phase(phaseType) 89 default: 90 destroyAddrs[i] = normalAddr 91 } 92 } 93 94 return destroyAddrs 95 } 96 97 // GraphNodeReferencer, overriding NodeAbstractResource 98 func (n *NodeDestroyResourceInstance) References() []*addrs.Reference { 99 // If we have a config, then we need to include destroy-time dependencies 100 if c := n.Config; c != nil && c.Managed != nil { 101 var result []*addrs.Reference 102 103 // We include conn info and config for destroy time provisioners 104 // as dependencies that we have. 105 for _, p := range c.Managed.Provisioners { 106 schema := n.ProvisionerSchemas[p.Type] 107 108 if p.When == configs.ProvisionerWhenDestroy { 109 if p.Connection != nil { 110 result = append(result, ReferencesFromConfig(p.Connection.Config, connectionBlockSupersetSchema)...) 111 } 112 result = append(result, ReferencesFromConfig(p.Config, schema)...) 113 } 114 } 115 116 return result 117 } 118 119 return nil 120 } 121 122 // GraphNodeEvalable 123 func (n *NodeDestroyResourceInstance) EvalTree() EvalNode { 124 addr := n.ResourceInstanceAddr() 125 126 // Get our state 127 rs := n.ResourceState 128 var is *states.ResourceInstance 129 if rs != nil { 130 is = rs.Instance(n.InstanceKey) 131 } 132 if is == nil { 133 log.Printf("[WARN] NodeDestroyResourceInstance for %s with no state", addr) 134 } 135 136 var changeApply *plans.ResourceInstanceChange 137 var provider providers.Interface 138 var providerSchema *ProviderSchema 139 var state *states.ResourceInstanceObject 140 var err error 141 return &EvalOpFilter{ 142 Ops: []walkOperation{walkApply, walkDestroy}, 143 Node: &EvalSequence{ 144 Nodes: []EvalNode{ 145 &EvalGetProvider{ 146 Addr: n.ResolvedProvider, 147 Output: &provider, 148 Schema: &providerSchema, 149 }, 150 151 // Get the saved diff for apply 152 &EvalReadDiff{ 153 Addr: addr.Resource, 154 ProviderSchema: &providerSchema, 155 Change: &changeApply, 156 }, 157 158 &EvalReduceDiff{ 159 Addr: addr.Resource, 160 InChange: &changeApply, 161 Destroy: true, 162 OutChange: &changeApply, 163 }, 164 165 // EvalReduceDiff may have simplified our planned change 166 // into a NoOp if it does not require destroying. 167 &EvalIf{ 168 If: func(ctx EvalContext) (bool, error) { 169 if changeApply == nil || changeApply.Action == plans.NoOp { 170 return true, EvalEarlyExitError{} 171 } 172 return true, nil 173 }, 174 Then: EvalNoop{}, 175 }, 176 177 &EvalReadState{ 178 Addr: addr.Resource, 179 Output: &state, 180 Provider: &provider, 181 ProviderSchema: &providerSchema, 182 }, 183 &EvalRequireState{ 184 State: &state, 185 }, 186 187 // Call pre-apply hook 188 &EvalApplyPre{ 189 Addr: addr.Resource, 190 State: &state, 191 Change: &changeApply, 192 }, 193 194 // Run destroy provisioners if not tainted 195 &EvalIf{ 196 If: func(ctx EvalContext) (bool, error) { 197 if state != nil && state.Status == states.ObjectTainted { 198 return false, nil 199 } 200 201 return true, nil 202 }, 203 204 Then: &EvalApplyProvisioners{ 205 Addr: addr.Resource, 206 State: &state, 207 ResourceConfig: n.Config, 208 Error: &err, 209 When: configs.ProvisionerWhenDestroy, 210 }, 211 }, 212 213 // If we have a provisioning error, then we just call 214 // the post-apply hook now. 215 &EvalIf{ 216 If: func(ctx EvalContext) (bool, error) { 217 return err != nil, nil 218 }, 219 220 Then: &EvalApplyPost{ 221 Addr: addr.Resource, 222 State: &state, 223 Error: &err, 224 }, 225 }, 226 227 // Make sure we handle data sources properly. 228 &EvalIf{ 229 If: func(ctx EvalContext) (bool, error) { 230 return addr.Resource.Resource.Mode == addrs.DataResourceMode, nil 231 }, 232 233 Then: &EvalReadDataApply{ 234 Addr: addr.Resource, 235 Config: n.Config, 236 Change: &changeApply, 237 Provider: &provider, 238 ProviderAddr: n.ResolvedProvider, 239 ProviderSchema: &providerSchema, 240 Output: &state, 241 }, 242 Else: &EvalApply{ 243 Addr: addr.Resource, 244 Config: nil, // No configuration because we are destroying 245 State: &state, 246 Change: &changeApply, 247 Provider: &provider, 248 ProviderAddr: n.ResolvedProvider, 249 ProviderSchema: &providerSchema, 250 Output: &state, 251 Error: &err, 252 }, 253 }, 254 &EvalWriteState{ 255 Addr: addr.Resource, 256 ProviderAddr: n.ResolvedProvider, 257 ProviderSchema: &providerSchema, 258 State: &state, 259 }, 260 &EvalApplyPost{ 261 Addr: addr.Resource, 262 State: &state, 263 Error: &err, 264 }, 265 &EvalUpdateStateHook{}, 266 }, 267 }, 268 } 269 } 270 271 // NodeDestroyResourceInstance represents a resource that is to be destroyed. 272 // 273 // Destroying a resource is a state-only operation: it is the individual 274 // instances being destroyed that affects remote objects. During graph 275 // construction, NodeDestroyResource should always depend on any other node 276 // related to the given resource, since it's just a final cleanup to avoid 277 // leaving skeleton resource objects in state after their instances have 278 // all been destroyed. 279 type NodeDestroyResource struct { 280 *NodeAbstractResource 281 } 282 283 var ( 284 _ GraphNodeResource = (*NodeDestroyResource)(nil) 285 _ GraphNodeReferenceable = (*NodeDestroyResource)(nil) 286 _ GraphNodeReferencer = (*NodeDestroyResource)(nil) 287 _ GraphNodeEvalable = (*NodeDestroyResource)(nil) 288 ) 289 290 func (n *NodeDestroyResource) Name() string { 291 return n.ResourceAddr().String() + " (clean up state)" 292 } 293 294 // GraphNodeReferenceable, overriding NodeAbstractResource 295 func (n *NodeDestroyResource) ReferenceableAddrs() []addrs.Referenceable { 296 // NodeDestroyResource doesn't participate in references: the graph 297 // builder that created it should ensure directly that it already depends 298 // on every other node related to its resource, without relying on 299 // references. 300 return nil 301 } 302 303 // GraphNodeReferencer, overriding NodeAbstractResource 304 func (n *NodeDestroyResource) References() []*addrs.Reference { 305 // NodeDestroyResource doesn't participate in references: the graph 306 // builder that created it should ensure directly that it already depends 307 // on every other node related to its resource, without relying on 308 // references. 309 return nil 310 } 311 312 // GraphNodeEvalable 313 func (n *NodeDestroyResource) EvalTree() EvalNode { 314 // This EvalNode will produce an error if the resource isn't already 315 // empty by the time it is called, since it should just be pruning the 316 // leftover husk of a resource in state after all of the child instances 317 // and their objects were destroyed. 318 return &EvalForgetResourceState{ 319 Addr: n.ResourceAddr().Resource, 320 } 321 }