github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/terraform/graph.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/hashicorp/terraform/dag"
    10  )
    11  
    12  // RootModuleName is the name given to the root module implicitly.
    13  const RootModuleName = "root"
    14  
    15  // RootModulePath is the path for the root module.
    16  var RootModulePath = []string{RootModuleName}
    17  
    18  // Graph represents the graph that Terraform uses to represent resources
    19  // and their dependencies. Each graph represents only one module, but it
    20  // can contain further modules, which themselves have their own graph.
    21  type Graph struct {
    22  	// Graph is the actual DAG. This is embedded so you can call the DAG
    23  	// methods directly.
    24  	dag.AcyclicGraph
    25  
    26  	// Path is the path in the module tree that this Graph represents.
    27  	// The root is represented by a single element list containing
    28  	// RootModuleName
    29  	Path []string
    30  
    31  	// dependableMap is a lookaside table for fast lookups for connecting
    32  	// dependencies by their GraphNodeDependable value to avoid O(n^3)-like
    33  	// situations and turn them into O(1) with respect to the number of new
    34  	// edges.
    35  	dependableMap map[string]dag.Vertex
    36  
    37  	once sync.Once
    38  }
    39  
    40  // Add is the same as dag.Graph.Add.
    41  func (g *Graph) Add(v dag.Vertex) dag.Vertex {
    42  	g.once.Do(g.init)
    43  
    44  	// Call upwards to add it to the actual graph
    45  	g.Graph.Add(v)
    46  
    47  	// If this is a depend-able node, then store the lookaside info
    48  	if dv, ok := v.(GraphNodeDependable); ok {
    49  		for _, n := range dv.DependableName() {
    50  			g.dependableMap[n] = v
    51  		}
    52  	}
    53  
    54  	return v
    55  }
    56  
    57  // ConnectDependent connects a GraphNodeDependent to all of its
    58  // GraphNodeDependables. It returns the list of dependents it was
    59  // unable to connect to.
    60  func (g *Graph) ConnectDependent(raw dag.Vertex) []string {
    61  	v, ok := raw.(GraphNodeDependent)
    62  	if !ok {
    63  		return nil
    64  	}
    65  
    66  	return g.ConnectTo(v, v.DependentOn())
    67  }
    68  
    69  // ConnectDependents goes through the graph, connecting all the
    70  // GraphNodeDependents to GraphNodeDependables. This is safe to call
    71  // multiple times.
    72  //
    73  // To get details on whether dependencies could be found/made, the more
    74  // specific ConnectDependent should be used.
    75  func (g *Graph) ConnectDependents() {
    76  	for _, v := range g.Vertices() {
    77  		if dv, ok := v.(GraphNodeDependent); ok {
    78  			g.ConnectDependent(dv)
    79  		}
    80  	}
    81  }
    82  
    83  // ConnectFrom creates an edge by finding the source from a DependableName
    84  // and connecting it to the specific vertex.
    85  func (g *Graph) ConnectFrom(source string, target dag.Vertex) {
    86  	g.once.Do(g.init)
    87  
    88  	if source := g.dependableMap[source]; source != nil {
    89  		g.Connect(dag.BasicEdge(source, target))
    90  	}
    91  }
    92  
    93  // ConnectTo connects a vertex to a raw string of targets that are the
    94  // result of DependableName, and returns the list of targets that are missing.
    95  func (g *Graph) ConnectTo(v dag.Vertex, targets []string) []string {
    96  	g.once.Do(g.init)
    97  
    98  	var missing []string
    99  	for _, t := range targets {
   100  		if dest := g.dependableMap[t]; dest != nil {
   101  			g.Connect(dag.BasicEdge(v, dest))
   102  		} else {
   103  			missing = append(missing, t)
   104  		}
   105  	}
   106  
   107  	return missing
   108  }
   109  
   110  // Dependable finds the vertices in the graph that have the given dependable
   111  // names and returns them.
   112  func (g *Graph) Dependable(n string) dag.Vertex {
   113  	// TODO: do we need this?
   114  	return nil
   115  }
   116  
   117  // Walk walks the graph with the given walker for callbacks. The graph
   118  // will be walked with full parallelism, so the walker should expect
   119  // to be called in concurrently.
   120  func (g *Graph) Walk(walker GraphWalker) error {
   121  	return g.walk(walker)
   122  }
   123  
   124  func (g *Graph) init() {
   125  	if g.dependableMap == nil {
   126  		g.dependableMap = make(map[string]dag.Vertex)
   127  	}
   128  }
   129  
   130  func (g *Graph) walk(walker GraphWalker) error {
   131  	// The callbacks for enter/exiting a graph
   132  	ctx := walker.EnterGraph(g)
   133  	defer walker.ExitGraph(g)
   134  
   135  	// Get the path for logs
   136  	path := strings.Join(ctx.Path(), ".")
   137  
   138  	// Walk the graph.
   139  	var walkFn dag.WalkFunc
   140  	walkFn = func(v dag.Vertex) (rerr error) {
   141  		log.Printf("[DEBUG] vertex %s.%s: walking", path, dag.VertexName(v))
   142  
   143  		walker.EnterVertex(v)
   144  		defer func() { walker.ExitVertex(v, rerr) }()
   145  
   146  		// If the node is eval-able, then evaluate it.
   147  		if ev, ok := v.(GraphNodeEvalable); ok {
   148  			tree := ev.EvalTree()
   149  			if tree == nil {
   150  				panic(fmt.Sprintf(
   151  					"%s.%s (%T): nil eval tree", path, dag.VertexName(v), v))
   152  			}
   153  
   154  			// Allow the walker to change our tree if needed. Eval,
   155  			// then callback with the output.
   156  			log.Printf("[DEBUG] vertex %s.%s: evaluating", path, dag.VertexName(v))
   157  			tree = walker.EnterEvalTree(v, tree)
   158  			output, err := Eval(tree, ctx)
   159  			if rerr = walker.ExitEvalTree(v, output, err); rerr != nil {
   160  				return
   161  			}
   162  		}
   163  
   164  		// If the node is dynamically expanded, then expand it
   165  		if ev, ok := v.(GraphNodeDynamicExpandable); ok {
   166  			log.Printf(
   167  				"[DEBUG] vertex %s.%s: expanding/walking dynamic subgraph",
   168  				path,
   169  				dag.VertexName(v))
   170  			g, err := ev.DynamicExpand(ctx)
   171  			if err != nil {
   172  				rerr = err
   173  				return
   174  			}
   175  
   176  			// Walk the subgraph
   177  			if rerr = g.walk(walker); rerr != nil {
   178  				return
   179  			}
   180  		}
   181  
   182  		// If the node has a subgraph, then walk the subgraph
   183  		if sn, ok := v.(GraphNodeSubgraph); ok {
   184  			log.Printf(
   185  				"[DEBUG] vertex %s.%s: walking subgraph",
   186  				path,
   187  				dag.VertexName(v))
   188  
   189  			if rerr = sn.Subgraph().walk(walker); rerr != nil {
   190  				return
   191  			}
   192  		}
   193  
   194  		return nil
   195  	}
   196  
   197  	return g.AcyclicGraph.Walk(walkFn)
   198  }
   199  
   200  // GraphNodeDependable is an interface which says that a node can be
   201  // depended on (an edge can be placed between this node and another) according
   202  // to the well-known name returned by DependableName.
   203  //
   204  // DependableName can return multiple names it is known by.
   205  type GraphNodeDependable interface {
   206  	DependableName() []string
   207  }
   208  
   209  // GraphNodeDependent is an interface which says that a node depends
   210  // on another GraphNodeDependable by some name. By implementing this
   211  // interface, Graph.ConnectDependents() can be called multiple times
   212  // safely and efficiently.
   213  type GraphNodeDependent interface {
   214  	DependentOn() []string
   215  }