github.com/hobbeswalsh/terraform@v0.3.7-0.20150619183303-ad17cf55a0fa/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  // Remove is the same as dag.Graph.Remove
    58  func (g *Graph) Remove(v dag.Vertex) dag.Vertex {
    59  	g.once.Do(g.init)
    60  
    61  	// If this is a depend-able node, then remove the lookaside info
    62  	if dv, ok := v.(GraphNodeDependable); ok {
    63  		for _, n := range dv.DependableName() {
    64  			delete(g.dependableMap, n)
    65  		}
    66  	}
    67  
    68  	// Call upwards to remove it from the actual graph
    69  	return g.Graph.Remove(v)
    70  }
    71  
    72  // Replace is the same as dag.Graph.Replace
    73  func (g *Graph) Replace(o, n dag.Vertex) bool {
    74  	// Go through and update our lookaside to point to the new vertex
    75  	for k, v := range g.dependableMap {
    76  		if v == o {
    77  			g.dependableMap[k] = n
    78  		}
    79  	}
    80  
    81  	return g.Graph.Replace(o, n)
    82  }
    83  
    84  // ConnectDependent connects a GraphNodeDependent to all of its
    85  // GraphNodeDependables. It returns the list of dependents it was
    86  // unable to connect to.
    87  func (g *Graph) ConnectDependent(raw dag.Vertex) []string {
    88  	v, ok := raw.(GraphNodeDependent)
    89  	if !ok {
    90  		return nil
    91  	}
    92  
    93  	return g.ConnectTo(v, v.DependentOn())
    94  }
    95  
    96  // ConnectDependents goes through the graph, connecting all the
    97  // GraphNodeDependents to GraphNodeDependables. This is safe to call
    98  // multiple times.
    99  //
   100  // To get details on whether dependencies could be found/made, the more
   101  // specific ConnectDependent should be used.
   102  func (g *Graph) ConnectDependents() {
   103  	for _, v := range g.Vertices() {
   104  		if dv, ok := v.(GraphNodeDependent); ok {
   105  			g.ConnectDependent(dv)
   106  		}
   107  	}
   108  }
   109  
   110  // ConnectFrom creates an edge by finding the source from a DependableName
   111  // and connecting it to the specific vertex.
   112  func (g *Graph) ConnectFrom(source string, target dag.Vertex) {
   113  	g.once.Do(g.init)
   114  
   115  	if source := g.dependableMap[source]; source != nil {
   116  		g.Connect(dag.BasicEdge(source, target))
   117  	}
   118  }
   119  
   120  // ConnectTo connects a vertex to a raw string of targets that are the
   121  // result of DependableName, and returns the list of targets that are missing.
   122  func (g *Graph) ConnectTo(v dag.Vertex, targets []string) []string {
   123  	g.once.Do(g.init)
   124  
   125  	var missing []string
   126  	for _, t := range targets {
   127  		if dest := g.dependableMap[t]; dest != nil {
   128  			g.Connect(dag.BasicEdge(v, dest))
   129  		} else {
   130  			missing = append(missing, t)
   131  		}
   132  	}
   133  
   134  	return missing
   135  }
   136  
   137  // Dependable finds the vertices in the graph that have the given dependable
   138  // names and returns them.
   139  func (g *Graph) Dependable(n string) dag.Vertex {
   140  	// TODO: do we need this?
   141  	return nil
   142  }
   143  
   144  // Walk walks the graph with the given walker for callbacks. The graph
   145  // will be walked with full parallelism, so the walker should expect
   146  // to be called in concurrently.
   147  func (g *Graph) Walk(walker GraphWalker) error {
   148  	return g.walk(walker)
   149  }
   150  
   151  func (g *Graph) init() {
   152  	if g.dependableMap == nil {
   153  		g.dependableMap = make(map[string]dag.Vertex)
   154  	}
   155  }
   156  
   157  func (g *Graph) walk(walker GraphWalker) error {
   158  	// The callbacks for enter/exiting a graph
   159  	ctx := walker.EnterPath(g.Path)
   160  	defer walker.ExitPath(g.Path)
   161  
   162  	// Get the path for logs
   163  	path := strings.Join(ctx.Path(), ".")
   164  
   165  	// Walk the graph.
   166  	var walkFn dag.WalkFunc
   167  	walkFn = func(v dag.Vertex) (rerr error) {
   168  		log.Printf("[DEBUG] vertex %s.%s: walking", path, dag.VertexName(v))
   169  
   170  		walker.EnterVertex(v)
   171  		defer func() { walker.ExitVertex(v, rerr) }()
   172  
   173  		// vertexCtx is the context that we use when evaluating. This
   174  		// is normally the context of our graph but can be overridden
   175  		// with a GraphNodeSubPath impl.
   176  		vertexCtx := ctx
   177  		if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 {
   178  			vertexCtx = walker.EnterPath(pn.Path())
   179  			defer walker.ExitPath(pn.Path())
   180  		}
   181  
   182  		// If the node is eval-able, then evaluate it.
   183  		if ev, ok := v.(GraphNodeEvalable); ok {
   184  			tree := ev.EvalTree()
   185  			if tree == nil {
   186  				panic(fmt.Sprintf(
   187  					"%s.%s (%T): nil eval tree", path, dag.VertexName(v), v))
   188  			}
   189  
   190  			// Allow the walker to change our tree if needed. Eval,
   191  			// then callback with the output.
   192  			log.Printf("[DEBUG] vertex %s.%s: evaluating", path, dag.VertexName(v))
   193  			tree = walker.EnterEvalTree(v, tree)
   194  			output, err := Eval(tree, vertexCtx)
   195  			if rerr = walker.ExitEvalTree(v, output, err); rerr != nil {
   196  				return
   197  			}
   198  		}
   199  
   200  		// If the node is dynamically expanded, then expand it
   201  		if ev, ok := v.(GraphNodeDynamicExpandable); ok {
   202  			log.Printf(
   203  				"[DEBUG] vertex %s.%s: expanding/walking dynamic subgraph",
   204  				path,
   205  				dag.VertexName(v))
   206  			g, err := ev.DynamicExpand(vertexCtx)
   207  			if err != nil {
   208  				rerr = err
   209  				return
   210  			}
   211  
   212  			// Walk the subgraph
   213  			if rerr = g.walk(walker); rerr != nil {
   214  				return
   215  			}
   216  		}
   217  
   218  		// If the node has a subgraph, then walk the subgraph
   219  		if sn, ok := v.(GraphNodeSubgraph); ok {
   220  			log.Printf(
   221  				"[DEBUG] vertex %s.%s: walking subgraph",
   222  				path,
   223  				dag.VertexName(v))
   224  
   225  			if rerr = sn.Subgraph().walk(walker); rerr != nil {
   226  				return
   227  			}
   228  		}
   229  
   230  		return nil
   231  	}
   232  
   233  	return g.AcyclicGraph.Walk(walkFn)
   234  }
   235  
   236  // GraphNodeDependable is an interface which says that a node can be
   237  // depended on (an edge can be placed between this node and another) according
   238  // to the well-known name returned by DependableName.
   239  //
   240  // DependableName can return multiple names it is known by.
   241  type GraphNodeDependable interface {
   242  	DependableName() []string
   243  }
   244  
   245  // GraphNodeDependent is an interface which says that a node depends
   246  // on another GraphNodeDependable by some name. By implementing this
   247  // interface, Graph.ConnectDependents() can be called multiple times
   248  // safely and efficiently.
   249  type GraphNodeDependent interface {
   250  	DependentOn() []string
   251  }