github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/node_resource_destroy_deposed.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 7 "github.com/hashicorp/terraform-plugin-sdk/internal/dag" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/plans" 9 "github.com/hashicorp/terraform-plugin-sdk/internal/providers" 10 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 11 ) 12 13 // ConcreteResourceInstanceDeposedNodeFunc is a callback type used to convert 14 // an abstract resource instance to a concrete one of some type that has 15 // an associated deposed object key. 16 type ConcreteResourceInstanceDeposedNodeFunc func(*NodeAbstractResourceInstance, states.DeposedKey) dag.Vertex 17 18 type GraphNodeDeposedResourceInstanceObject interface { 19 DeposedInstanceObjectKey() states.DeposedKey 20 } 21 22 // NodePlanDeposedResourceInstanceObject represents deposed resource 23 // instance objects during plan. These are distinct from the primary object 24 // for each resource instance since the only valid operation to do with them 25 // is to destroy them. 26 // 27 // This node type is also used during the refresh walk to ensure that the 28 // record of a deposed object is up-to-date before we plan to destroy it. 29 type NodePlanDeposedResourceInstanceObject struct { 30 *NodeAbstractResourceInstance 31 DeposedKey states.DeposedKey 32 } 33 34 var ( 35 _ GraphNodeDeposedResourceInstanceObject = (*NodePlanDeposedResourceInstanceObject)(nil) 36 _ GraphNodeResource = (*NodePlanDeposedResourceInstanceObject)(nil) 37 _ GraphNodeResourceInstance = (*NodePlanDeposedResourceInstanceObject)(nil) 38 _ GraphNodeReferenceable = (*NodePlanDeposedResourceInstanceObject)(nil) 39 _ GraphNodeReferencer = (*NodePlanDeposedResourceInstanceObject)(nil) 40 _ GraphNodeEvalable = (*NodePlanDeposedResourceInstanceObject)(nil) 41 _ GraphNodeProviderConsumer = (*NodePlanDeposedResourceInstanceObject)(nil) 42 _ GraphNodeProvisionerConsumer = (*NodePlanDeposedResourceInstanceObject)(nil) 43 ) 44 45 func (n *NodePlanDeposedResourceInstanceObject) Name() string { 46 return fmt.Sprintf("%s (deposed %s)", n.ResourceInstanceAddr().String(), n.DeposedKey) 47 } 48 49 func (n *NodePlanDeposedResourceInstanceObject) DeposedInstanceObjectKey() states.DeposedKey { 50 return n.DeposedKey 51 } 52 53 // GraphNodeReferenceable implementation, overriding the one from NodeAbstractResourceInstance 54 func (n *NodePlanDeposedResourceInstanceObject) ReferenceableAddrs() []addrs.Referenceable { 55 // Deposed objects don't participate in references. 56 return nil 57 } 58 59 // GraphNodeReferencer implementation, overriding the one from NodeAbstractResourceInstance 60 func (n *NodePlanDeposedResourceInstanceObject) References() []*addrs.Reference { 61 // We don't evaluate configuration for deposed objects, so they effectively 62 // make no references. 63 return nil 64 } 65 66 // GraphNodeEvalable impl. 67 func (n *NodePlanDeposedResourceInstanceObject) EvalTree() EvalNode { 68 addr := n.ResourceInstanceAddr() 69 70 var provider providers.Interface 71 var providerSchema *ProviderSchema 72 var state *states.ResourceInstanceObject 73 74 seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)} 75 76 // During the refresh walk we will ensure that our record of the deposed 77 // object is up-to-date. If it was already deleted outside of Terraform 78 // then this will remove it from state and thus avoid us planning a 79 // destroy for it during the subsequent plan walk. 80 seq.Nodes = append(seq.Nodes, &EvalOpFilter{ 81 Ops: []walkOperation{walkRefresh}, 82 Node: &EvalSequence{ 83 Nodes: []EvalNode{ 84 &EvalGetProvider{ 85 Addr: n.ResolvedProvider, 86 Output: &provider, 87 Schema: &providerSchema, 88 }, 89 &EvalReadStateDeposed{ 90 Addr: addr.Resource, 91 Provider: &provider, 92 ProviderSchema: &providerSchema, 93 Key: n.DeposedKey, 94 Output: &state, 95 }, 96 &EvalRefresh{ 97 Addr: addr.Resource, 98 ProviderAddr: n.ResolvedProvider, 99 Provider: &provider, 100 ProviderSchema: &providerSchema, 101 State: &state, 102 Output: &state, 103 }, 104 &EvalWriteStateDeposed{ 105 Addr: addr.Resource, 106 Key: n.DeposedKey, 107 ProviderAddr: n.ResolvedProvider, 108 ProviderSchema: &providerSchema, 109 State: &state, 110 }, 111 }, 112 }, 113 }) 114 115 // During the plan walk we always produce a planned destroy change, because 116 // destroying is the only supported action for deposed objects. 117 var change *plans.ResourceInstanceChange 118 seq.Nodes = append(seq.Nodes, &EvalOpFilter{ 119 Ops: []walkOperation{walkPlan, walkPlanDestroy}, 120 Node: &EvalSequence{ 121 Nodes: []EvalNode{ 122 &EvalGetProvider{ 123 Addr: n.ResolvedProvider, 124 Output: &provider, 125 Schema: &providerSchema, 126 }, 127 &EvalReadStateDeposed{ 128 Addr: addr.Resource, 129 Output: &state, 130 Key: n.DeposedKey, 131 Provider: &provider, 132 ProviderSchema: &providerSchema, 133 }, 134 &EvalDiffDestroy{ 135 Addr: addr.Resource, 136 ProviderAddr: n.ResolvedProvider, 137 DeposedKey: n.DeposedKey, 138 State: &state, 139 Output: &change, 140 }, 141 &EvalWriteDiff{ 142 Addr: addr.Resource, 143 DeposedKey: n.DeposedKey, 144 ProviderSchema: &providerSchema, 145 Change: &change, 146 }, 147 // Since deposed objects cannot be referenced by expressions 148 // elsewhere, we don't need to also record the planned new 149 // state in this case. 150 }, 151 }, 152 }) 153 154 return seq 155 } 156 157 // NodeDestroyDeposedResourceInstanceObject represents deposed resource 158 // instance objects during apply. Nodes of this type are inserted by 159 // DiffTransformer when the planned changeset contains "delete" changes for 160 // deposed instance objects, and its only supported operation is to destroy 161 // and then forget the associated object. 162 type NodeDestroyDeposedResourceInstanceObject struct { 163 *NodeAbstractResourceInstance 164 DeposedKey states.DeposedKey 165 } 166 167 var ( 168 _ GraphNodeDeposedResourceInstanceObject = (*NodeDestroyDeposedResourceInstanceObject)(nil) 169 _ GraphNodeResource = (*NodeDestroyDeposedResourceInstanceObject)(nil) 170 _ GraphNodeResourceInstance = (*NodeDestroyDeposedResourceInstanceObject)(nil) 171 _ GraphNodeDestroyer = (*NodeDestroyDeposedResourceInstanceObject)(nil) 172 _ GraphNodeDestroyerCBD = (*NodeDestroyDeposedResourceInstanceObject)(nil) 173 _ GraphNodeReferenceable = (*NodeDestroyDeposedResourceInstanceObject)(nil) 174 _ GraphNodeReferencer = (*NodeDestroyDeposedResourceInstanceObject)(nil) 175 _ GraphNodeEvalable = (*NodeDestroyDeposedResourceInstanceObject)(nil) 176 _ GraphNodeProviderConsumer = (*NodeDestroyDeposedResourceInstanceObject)(nil) 177 _ GraphNodeProvisionerConsumer = (*NodeDestroyDeposedResourceInstanceObject)(nil) 178 ) 179 180 func (n *NodeDestroyDeposedResourceInstanceObject) Name() string { 181 return fmt.Sprintf("%s (destroy deposed %s)", n.Addr.String(), n.DeposedKey) 182 } 183 184 func (n *NodeDestroyDeposedResourceInstanceObject) DeposedInstanceObjectKey() states.DeposedKey { 185 return n.DeposedKey 186 } 187 188 // GraphNodeReferenceable implementation, overriding the one from NodeAbstractResourceInstance 189 func (n *NodeDestroyDeposedResourceInstanceObject) ReferenceableAddrs() []addrs.Referenceable { 190 // Deposed objects don't participate in references. 191 return nil 192 } 193 194 // GraphNodeReferencer implementation, overriding the one from NodeAbstractResourceInstance 195 func (n *NodeDestroyDeposedResourceInstanceObject) References() []*addrs.Reference { 196 // We don't evaluate configuration for deposed objects, so they effectively 197 // make no references. 198 return nil 199 } 200 201 // GraphNodeDestroyer 202 func (n *NodeDestroyDeposedResourceInstanceObject) DestroyAddr() *addrs.AbsResourceInstance { 203 addr := n.ResourceInstanceAddr() 204 return &addr 205 } 206 207 // GraphNodeDestroyerCBD 208 func (n *NodeDestroyDeposedResourceInstanceObject) CreateBeforeDestroy() bool { 209 // A deposed instance is always CreateBeforeDestroy by definition, since 210 // we use deposed only to handle create-before-destroy. 211 return true 212 } 213 214 // GraphNodeDestroyerCBD 215 func (n *NodeDestroyDeposedResourceInstanceObject) ModifyCreateBeforeDestroy(v bool) error { 216 if !v { 217 // Should never happen: deposed instances are _always_ create_before_destroy. 218 return fmt.Errorf("can't deactivate create_before_destroy for a deposed instance") 219 } 220 return nil 221 } 222 223 // GraphNodeEvalable impl. 224 func (n *NodeDestroyDeposedResourceInstanceObject) EvalTree() EvalNode { 225 addr := n.ResourceInstanceAddr() 226 227 var provider providers.Interface 228 var providerSchema *ProviderSchema 229 var state *states.ResourceInstanceObject 230 var change *plans.ResourceInstanceChange 231 var err error 232 233 return &EvalSequence{ 234 Nodes: []EvalNode{ 235 &EvalGetProvider{ 236 Addr: n.ResolvedProvider, 237 Output: &provider, 238 Schema: &providerSchema, 239 }, 240 &EvalReadStateDeposed{ 241 Addr: addr.Resource, 242 Output: &state, 243 Key: n.DeposedKey, 244 Provider: &provider, 245 ProviderSchema: &providerSchema, 246 }, 247 &EvalDiffDestroy{ 248 Addr: addr.Resource, 249 ProviderAddr: n.ResolvedProvider, 250 State: &state, 251 Output: &change, 252 }, 253 // Call pre-apply hook 254 &EvalApplyPre{ 255 Addr: addr.Resource, 256 State: &state, 257 Change: &change, 258 }, 259 &EvalApply{ 260 Addr: addr.Resource, 261 Config: nil, // No configuration because we are destroying 262 State: &state, 263 Change: &change, 264 Provider: &provider, 265 ProviderAddr: n.ResolvedProvider, 266 ProviderSchema: &providerSchema, 267 Output: &state, 268 Error: &err, 269 }, 270 // Always write the resource back to the state deposed... if it 271 // was successfully destroyed it will be pruned. If it was not, it will 272 // be caught on the next run. 273 &EvalWriteStateDeposed{ 274 Addr: addr.Resource, 275 Key: n.DeposedKey, 276 ProviderAddr: n.ResolvedProvider, 277 ProviderSchema: &providerSchema, 278 State: &state, 279 }, 280 &EvalApplyPost{ 281 Addr: addr.Resource, 282 State: &state, 283 Error: &err, 284 }, 285 &EvalReturnError{ 286 Error: &err, 287 }, 288 &EvalUpdateStateHook{}, 289 }, 290 } 291 } 292 293 // GraphNodeDeposer is an optional interface implemented by graph nodes that 294 // might create a single new deposed object for a specific associated resource 295 // instance, allowing a caller to optionally pre-allocate a DeposedKey for 296 // it. 297 type GraphNodeDeposer interface { 298 // SetPreallocatedDeposedKey will be called during graph construction 299 // if a particular node must use a pre-allocated deposed key if/when it 300 // "deposes" the current object of its associated resource instance. 301 SetPreallocatedDeposedKey(key states.DeposedKey) 302 } 303 304 // graphNodeDeposer is an embeddable implementation of GraphNodeDeposer. 305 // Embed it in a node type to get automatic support for it, and then access 306 // the field PreallocatedDeposedKey to access any pre-allocated key. 307 type graphNodeDeposer struct { 308 PreallocatedDeposedKey states.DeposedKey 309 } 310 311 func (n *graphNodeDeposer) SetPreallocatedDeposedKey(key states.DeposedKey) { 312 n.PreallocatedDeposedKey = key 313 }