github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/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 // DestroyTransformer is a GraphTransformer that creates the destruction 49 // nodes for things that _might_ be destroyed. 50 type DestroyTransformer struct{} 51 52 func (t *DestroyTransformer) Transform(g *Graph) error { 53 var connect, remove []dag.Edge 54 55 modes := []GraphNodeDestroyMode{DestroyPrimary, DestroyTainted} 56 for _, m := range modes { 57 connectMode, removeMode, err := t.transform(g, m) 58 if err != nil { 59 return err 60 } 61 62 connect = append(connect, connectMode...) 63 remove = append(remove, removeMode...) 64 } 65 66 // Atomatically add/remove the edges 67 for _, e := range connect { 68 g.Connect(e) 69 } 70 for _, e := range remove { 71 g.RemoveEdge(e) 72 } 73 74 return nil 75 } 76 77 func (t *DestroyTransformer) transform( 78 g *Graph, mode GraphNodeDestroyMode) ([]dag.Edge, []dag.Edge, error) { 79 var connect, remove []dag.Edge 80 nodeToCn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices())) 81 nodeToDn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices())) 82 for _, v := range g.Vertices() { 83 // If it is not a destroyable, we don't care 84 cn, ok := v.(GraphNodeDestroyable) 85 if !ok { 86 continue 87 } 88 89 // Grab the destroy side of the node and connect it through 90 n := cn.DestroyNode(mode) 91 if n == nil { 92 continue 93 } 94 95 // Store it 96 nodeToCn[n] = cn 97 nodeToDn[cn] = n 98 99 // Add it to the graph 100 g.Add(n) 101 102 // Inherit all the edges from the old node 103 downEdges := g.DownEdges(v).List() 104 for _, edgeRaw := range downEdges { 105 g.Connect(dag.BasicEdge(n, edgeRaw.(dag.Vertex))) 106 } 107 108 // Add a new edge to connect the node to be created to 109 // the destroy node. 110 connect = append(connect, dag.BasicEdge(v, n)) 111 } 112 113 // Go through the nodes we added and determine if they depend 114 // on any nodes with a destroy node. If so, depend on that instead. 115 for n, _ := range nodeToCn { 116 for _, downRaw := range g.DownEdges(n).List() { 117 target := downRaw.(dag.Vertex) 118 cn2, ok := target.(GraphNodeDestroyable) 119 if !ok { 120 continue 121 } 122 123 newTarget := nodeToDn[cn2] 124 if newTarget == nil { 125 continue 126 } 127 128 // Make the new edge and transpose 129 connect = append(connect, dag.BasicEdge(newTarget, n)) 130 131 // Remove the old edge 132 remove = append(remove, dag.BasicEdge(n, target)) 133 } 134 } 135 136 return connect, remove, nil 137 } 138 139 // CreateBeforeDestroyTransformer is a GraphTransformer that modifies 140 // the destroys of some nodes so that the creation happens before the 141 // destroy. 142 type CreateBeforeDestroyTransformer struct{} 143 144 func (t *CreateBeforeDestroyTransformer) Transform(g *Graph) error { 145 // We "stage" the edge connections/destroys in these slices so that 146 // while we're doing the edge transformations (transpositions) in 147 // the graph, we're not affecting future edge transpositions. These 148 // slices let us stage ALL the changes that WILL happen so that all 149 // of the transformations happen atomically. 150 var connect, destroy []dag.Edge 151 152 for _, v := range g.Vertices() { 153 // We only care to use the destroy nodes 154 dn, ok := v.(GraphNodeDestroy) 155 if !ok { 156 continue 157 } 158 159 // If the node doesn't need to create before destroy, then continue 160 if !dn.CreateBeforeDestroy() { 161 continue 162 } 163 164 // Get the creation side of this node 165 cn := dn.CreateNode() 166 167 // Take all the things which depend on the creation node and 168 // make them dependencies on the destruction. Clarifying this 169 // with an example: if you have a web server and a load balancer 170 // and the load balancer depends on the web server, then when we 171 // do a create before destroy, we want to make sure the steps are: 172 // 173 // 1.) Create new web server 174 // 2.) Update load balancer 175 // 3.) Delete old web server 176 // 177 // This ensures that. 178 for _, sourceRaw := range g.UpEdges(cn).List() { 179 source := sourceRaw.(dag.Vertex) 180 connect = append(connect, dag.BasicEdge(dn, source)) 181 } 182 183 // Swap the edge so that the destroy depends on the creation 184 // happening... 185 connect = append(connect, dag.BasicEdge(dn, cn)) 186 destroy = append(destroy, dag.BasicEdge(cn, dn)) 187 } 188 189 for _, edge := range connect { 190 g.Connect(edge) 191 } 192 for _, edge := range destroy { 193 g.RemoveEdge(edge) 194 } 195 196 return nil 197 } 198 199 // PruneDestroyTransformer is a GraphTransformer that removes the destroy 200 // nodes that aren't in the diff. 201 type PruneDestroyTransformer struct { 202 Diff *Diff 203 State *State 204 } 205 206 func (t *PruneDestroyTransformer) Transform(g *Graph) error { 207 var modDiff *ModuleDiff 208 var modState *ModuleState 209 if t.Diff != nil { 210 modDiff = t.Diff.ModuleByPath(g.Path) 211 } 212 if t.State != nil { 213 modState = t.State.ModuleByPath(g.Path) 214 } 215 216 for _, v := range g.Vertices() { 217 // If it is not a destroyer, we don't care 218 dn, ok := v.(GraphNodeDestroyPrunable) 219 if !ok { 220 continue 221 } 222 223 // Remove it if we should 224 if !dn.DestroyInclude(modDiff, modState) { 225 g.Remove(v) 226 } 227 } 228 229 return nil 230 }