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