github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/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  }