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