github.com/aspring/terraform@v0.8.2-0.20161216122603-6a8619a5db2e/terraform/transform_destroy.go (about) 1 package terraform 2 3 import ( 4 "github.com/hashicorp/terraform/config" 5 "github.com/hashicorp/terraform/dag" 6 ) 7 8 // GraphNodeDestroyable is the interface that nodes that can be destroyed 9 // must implement. This is used to automatically handle the creation of 10 // destroy nodes in the graph and the dependency ordering of those destroys. 11 type GraphNodeDestroyable interface { 12 // DestroyNode returns the node used for the destroy with the given 13 // mode. If this returns nil, then a destroy node for that mode 14 // will not be added. 15 DestroyNode() GraphNodeDestroy 16 } 17 18 // GraphNodeDestroy is the interface that must implemented by 19 // nodes that destroy. 20 type GraphNodeDestroy interface { 21 dag.Vertex 22 23 // CreateBeforeDestroy is called to check whether this node 24 // should be created before it is destroyed. The CreateBeforeDestroy 25 // transformer uses this information to setup the graph. 26 CreateBeforeDestroy() bool 27 28 // CreateNode returns the node used for the create side of this 29 // destroy. This must already exist within the graph. 30 CreateNode() dag.Vertex 31 } 32 33 // GraphNodeDestroyPrunable is the interface that can be implemented to 34 // signal that this node can be pruned depending on state. 35 type GraphNodeDestroyPrunable interface { 36 // DestroyInclude is called to check if this node should be included 37 // with the given state. The state and diff must NOT be modified. 38 DestroyInclude(*ModuleDiff, *ModuleState) bool 39 } 40 41 // GraphNodeEdgeInclude can be implemented to not include something 42 // as an edge within the destroy graph. This is usually done because it 43 // might cause unnecessary cycles. 44 type GraphNodeDestroyEdgeInclude interface { 45 DestroyEdgeInclude(dag.Vertex) bool 46 } 47 48 // DestroyTransformer is a GraphTransformer that creates the destruction 49 // nodes for things that _might_ be destroyed. 50 type DestroyTransformer struct { 51 FullDestroy bool 52 } 53 54 func (t *DestroyTransformer) Transform(g *Graph) error { 55 var connect, remove []dag.Edge 56 nodeToCn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices())) 57 nodeToDn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices())) 58 for _, v := range g.Vertices() { 59 // If it is not a destroyable, we don't care 60 cn, ok := v.(GraphNodeDestroyable) 61 if !ok { 62 continue 63 } 64 65 // Grab the destroy side of the node and connect it through 66 n := cn.DestroyNode() 67 if n == nil { 68 continue 69 } 70 71 // Store it 72 nodeToCn[n] = cn 73 nodeToDn[cn] = n 74 75 // If the creation node is equal to the destroy node, then 76 // don't do any of the edge jump rope below. 77 if n.(interface{}) == cn.(interface{}) { 78 continue 79 } 80 81 // Add it to the graph 82 g.Add(n) 83 84 // Inherit all the edges from the old node 85 downEdges := g.DownEdges(v).List() 86 for _, edgeRaw := range downEdges { 87 // If this thing specifically requests to not be depended on 88 // by destroy nodes, then don't. 89 if i, ok := edgeRaw.(GraphNodeDestroyEdgeInclude); ok && 90 !i.DestroyEdgeInclude(v) { 91 continue 92 } 93 94 g.Connect(dag.BasicEdge(n, edgeRaw.(dag.Vertex))) 95 } 96 97 // Add a new edge to connect the node to be created to 98 // the destroy node. 99 connect = append(connect, dag.BasicEdge(v, n)) 100 } 101 102 // Go through the nodes we added and determine if they depend 103 // on any nodes with a destroy node. If so, depend on that instead. 104 for n, _ := range nodeToCn { 105 for _, downRaw := range g.DownEdges(n).List() { 106 target := downRaw.(dag.Vertex) 107 cn2, ok := target.(GraphNodeDestroyable) 108 if !ok { 109 continue 110 } 111 112 newTarget := nodeToDn[cn2] 113 if newTarget == nil { 114 continue 115 } 116 117 // Make the new edge and transpose 118 connect = append(connect, dag.BasicEdge(newTarget, n)) 119 120 // Remove the old edge 121 remove = append(remove, dag.BasicEdge(n, target)) 122 } 123 } 124 125 // Atomatically add/remove the edges 126 for _, e := range connect { 127 g.Connect(e) 128 } 129 for _, e := range remove { 130 g.RemoveEdge(e) 131 } 132 133 return nil 134 } 135 136 // CreateBeforeDestroyTransformer is a GraphTransformer that modifies 137 // the destroys of some nodes so that the creation happens before the 138 // destroy. 139 type CreateBeforeDestroyTransformer struct{} 140 141 func (t *CreateBeforeDestroyTransformer) Transform(g *Graph) error { 142 // We "stage" the edge connections/destroys in these slices so that 143 // while we're doing the edge transformations (transpositions) in 144 // the graph, we're not affecting future edge transpositions. These 145 // slices let us stage ALL the changes that WILL happen so that all 146 // of the transformations happen atomically. 147 var connect, destroy []dag.Edge 148 149 for _, v := range g.Vertices() { 150 // We only care to use the destroy nodes 151 dn, ok := v.(GraphNodeDestroy) 152 if !ok { 153 continue 154 } 155 156 // If the node doesn't need to create before destroy, then continue 157 if !dn.CreateBeforeDestroy() { 158 if noCreateBeforeDestroyAncestors(g, dn) { 159 continue 160 } 161 162 // PURPOSELY HACKY FIX SINCE THIS TRANSFORM IS DEPRECATED. 163 // This is a hacky way to fix GH-10439. For a detailed description 164 // of the fix, see CBDEdgeTransformer, which is the equivalent 165 // transform used by the new graphs. 166 // 167 // This transform is deprecated because it is only used by the 168 // old graphs which are going to be removed. 169 var update *config.Resource 170 if dn, ok := v.(*graphNodeResourceDestroy); ok { 171 update = dn.Original.Resource 172 } 173 if dn, ok := v.(*graphNodeResourceDestroyFlat); ok { 174 update = dn.Original.Resource 175 } 176 if update != nil { 177 update.Lifecycle.CreateBeforeDestroy = true 178 } 179 } 180 181 // Get the creation side of this node 182 cn := dn.CreateNode() 183 184 // Take all the things which depend on the creation node and 185 // make them dependencies on the destruction. Clarifying this 186 // with an example: if you have a web server and a load balancer 187 // and the load balancer depends on the web server, then when we 188 // do a create before destroy, we want to make sure the steps are: 189 // 190 // 1.) Create new web server 191 // 2.) Update load balancer 192 // 3.) Delete old web server 193 // 194 // This ensures that. 195 for _, sourceRaw := range g.UpEdges(cn).List() { 196 source := sourceRaw.(dag.Vertex) 197 198 // If the graph has a "root" node (one added by a RootTransformer and not 199 // just a resource that happens to have no ancestors), we don't want to 200 // add any edges to it, because then it ceases to be a root. 201 if _, ok := source.(graphNodeRoot); ok { 202 continue 203 } 204 205 connect = append(connect, dag.BasicEdge(dn, source)) 206 } 207 208 // Swap the edge so that the destroy depends on the creation 209 // happening... 210 connect = append(connect, dag.BasicEdge(dn, cn)) 211 destroy = append(destroy, dag.BasicEdge(cn, dn)) 212 } 213 214 for _, edge := range connect { 215 g.Connect(edge) 216 } 217 for _, edge := range destroy { 218 g.RemoveEdge(edge) 219 } 220 221 return nil 222 } 223 224 // noCreateBeforeDestroyAncestors verifies that a vertex has no ancestors that 225 // are CreateBeforeDestroy. 226 // If this vertex has an ancestor with CreateBeforeDestroy, we will need to 227 // inherit that behavior and re-order the edges even if this node type doesn't 228 // directly implement CreateBeforeDestroy. 229 func noCreateBeforeDestroyAncestors(g *Graph, v dag.Vertex) bool { 230 s, _ := g.Ancestors(v) 231 if s == nil { 232 return true 233 } 234 for _, v := range s.List() { 235 dn, ok := v.(GraphNodeDestroy) 236 if !ok { 237 continue 238 } 239 240 if dn.CreateBeforeDestroy() { 241 // some ancestor is CreateBeforeDestroy, so we need to follow suit 242 return false 243 } 244 } 245 return true 246 } 247 248 // PruneDestroyTransformer is a GraphTransformer that removes the destroy 249 // nodes that aren't in the diff. 250 type PruneDestroyTransformer struct { 251 Diff *Diff 252 State *State 253 } 254 255 func (t *PruneDestroyTransformer) Transform(g *Graph) error { 256 for _, v := range g.Vertices() { 257 // If it is not a destroyer, we don't care 258 dn, ok := v.(GraphNodeDestroyPrunable) 259 if !ok { 260 continue 261 } 262 263 path := g.Path 264 if pn, ok := v.(GraphNodeSubPath); ok { 265 path = pn.Path() 266 } 267 268 var modDiff *ModuleDiff 269 var modState *ModuleState 270 if t.Diff != nil { 271 modDiff = t.Diff.ModuleByPath(path) 272 } 273 if t.State != nil { 274 modState = t.State.ModuleByPath(path) 275 } 276 277 // Remove it if we should 278 if !dn.DestroyInclude(modDiff, modState) { 279 g.Remove(v) 280 } 281 } 282 283 return nil 284 }