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