github.com/federicobaldo/terraform@v0.6.15-0.20160323222747-b20f680cbf05/terraform/transform_flatten.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform/dag" 7 ) 8 9 // GraphNodeFlatGraph must be implemented by nodes that have subgraphs 10 // that they want flattened into the graph. 11 type GraphNodeFlatGraph interface { 12 FlattenGraph() *Graph 13 } 14 15 // GraphNodeFlattenable must be implemented by all nodes that can be 16 // flattened. If a FlattenGraph returns any nodes that can't be flattened, 17 // it will be an error. 18 // 19 // If Flatten returns nil for the Vertex along with a nil error, it will 20 // removed from the graph. 21 type GraphNodeFlattenable interface { 22 Flatten(path []string) (dag.Vertex, error) 23 } 24 25 // FlattenTransformer is a transformer that goes through the graph, finds 26 // subgraphs that can be flattened, and flattens them into this graph, 27 // removing the prior subgraph node. 28 type FlattenTransformer struct{} 29 30 func (t *FlattenTransformer) Transform(g *Graph) error { 31 for _, v := range g.Vertices() { 32 fn, ok := v.(GraphNodeFlatGraph) 33 if !ok { 34 continue 35 } 36 37 // If we don't want to be flattened, don't do it 38 subgraph := fn.FlattenGraph() 39 if subgraph == nil { 40 continue 41 } 42 43 // Get all the things that depend on this node. We'll re-connect 44 // dependents later. We have to copy these here since the UpEdges 45 // value will be deleted after the Remove below. 46 dependents := make([]dag.Vertex, 0, 5) 47 for _, v := range g.UpEdges(v).List() { 48 dependents = append(dependents, v) 49 } 50 51 // Remove the old node 52 g.Remove(v) 53 54 // Go through the subgraph and flatten all the nodes 55 for _, sv := range subgraph.Vertices() { 56 // If the vertex already has a subpath then we assume it has 57 // already been flattened. Ignore it. 58 if _, ok := sv.(GraphNodeSubPath); ok { 59 continue 60 } 61 62 fn, ok := sv.(GraphNodeFlattenable) 63 if !ok { 64 return fmt.Errorf( 65 "unflattenable node: %s %T", 66 dag.VertexName(sv), sv) 67 } 68 69 v, err := fn.Flatten(subgraph.Path) 70 if err != nil { 71 return fmt.Errorf( 72 "error flattening %s (%T): %s", 73 dag.VertexName(sv), sv, err) 74 } 75 76 if v == nil { 77 subgraph.Remove(v) 78 } else { 79 subgraph.Replace(sv, v) 80 } 81 } 82 83 // Now that we've handled any changes to the graph that are 84 // needed, we can add them all to our graph along with their edges. 85 for _, sv := range subgraph.Vertices() { 86 g.Add(sv) 87 } 88 for _, se := range subgraph.Edges() { 89 g.Connect(se) 90 } 91 92 // Connect the dependencies for all the new nodes that we added. 93 // This will properly connect variables to their sources, for example. 94 for _, sv := range subgraph.Vertices() { 95 g.ConnectDependent(sv) 96 } 97 98 // Re-connect all the things that dependent on the graph 99 // we just flattened. This should connect them back into the 100 // correct nodes if their DependentOn() is setup correctly. 101 for _, v := range dependents { 102 g.ConnectDependent(v) 103 } 104 } 105 106 return nil 107 }