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