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