github.com/r3labs/terraform@v0.8.4/terraform/graph.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"runtime/debug"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/hashicorp/terraform/dag"
    11  )
    12  
    13  // RootModuleName is the name given to the root module implicitly.
    14  const RootModuleName = "root"
    15  
    16  // RootModulePath is the path for the root module.
    17  var RootModulePath = []string{RootModuleName}
    18  
    19  // Graph represents the graph that Terraform uses to represent resources
    20  // and their dependencies. Each graph represents only one module, but it
    21  // can contain further modules, which themselves have their own graph.
    22  type Graph struct {
    23  	// Graph is the actual DAG. This is embedded so you can call the DAG
    24  	// methods directly.
    25  	dag.AcyclicGraph
    26  
    27  	// Path is the path in the module tree that this Graph represents.
    28  	// The root is represented by a single element list containing
    29  	// RootModuleName
    30  	Path []string
    31  
    32  	// annotations are the annotations that are added to vertices. Annotations
    33  	// are arbitrary metadata taht is used for various logic. Annotations
    34  	// should have unique keys that are referenced via constants.
    35  	annotations map[dag.Vertex]map[string]interface{}
    36  
    37  	// dependableMap is a lookaside table for fast lookups for connecting
    38  	// dependencies by their GraphNodeDependable value to avoid O(n^3)-like
    39  	// situations and turn them into O(1) with respect to the number of new
    40  	// edges.
    41  	dependableMap map[string]dag.Vertex
    42  
    43  	// debugName is a name for reference in the debug output. This is usually
    44  	// to indicate what topmost builder was, and if this graph is a shadow or
    45  	// not.
    46  	debugName string
    47  
    48  	once sync.Once
    49  }
    50  
    51  func (g *Graph) DirectedGraph() dag.Grapher {
    52  	return &g.AcyclicGraph
    53  }
    54  
    55  // Annotations returns the annotations that are configured for the
    56  // given vertex. The map is guaranteed to be non-nil but may be empty.
    57  //
    58  // The returned map may be modified to modify the annotations of the
    59  // vertex.
    60  func (g *Graph) Annotations(v dag.Vertex) map[string]interface{} {
    61  	g.once.Do(g.init)
    62  
    63  	// If this vertex isn't in the graph, then just return an empty map
    64  	if !g.HasVertex(v) {
    65  		return map[string]interface{}{}
    66  	}
    67  
    68  	// Get the map, if it doesn't exist yet then initialize it
    69  	m, ok := g.annotations[v]
    70  	if !ok {
    71  		m = make(map[string]interface{})
    72  		g.annotations[v] = m
    73  	}
    74  
    75  	return m
    76  }
    77  
    78  // Add is the same as dag.Graph.Add.
    79  func (g *Graph) Add(v dag.Vertex) dag.Vertex {
    80  	g.once.Do(g.init)
    81  
    82  	// Call upwards to add it to the actual graph
    83  	g.Graph.Add(v)
    84  
    85  	// If this is a depend-able node, then store the lookaside info
    86  	if dv, ok := v.(GraphNodeDependable); ok {
    87  		for _, n := range dv.DependableName() {
    88  			g.dependableMap[n] = v
    89  		}
    90  	}
    91  
    92  	// If this initializes annotations, then do that
    93  	if av, ok := v.(GraphNodeAnnotationInit); ok {
    94  		as := g.Annotations(v)
    95  		for k, v := range av.AnnotationInit() {
    96  			as[k] = v
    97  		}
    98  	}
    99  
   100  	return v
   101  }
   102  
   103  // Remove is the same as dag.Graph.Remove
   104  func (g *Graph) Remove(v dag.Vertex) dag.Vertex {
   105  	g.once.Do(g.init)
   106  
   107  	// If this is a depend-able node, then remove the lookaside info
   108  	if dv, ok := v.(GraphNodeDependable); ok {
   109  		for _, n := range dv.DependableName() {
   110  			delete(g.dependableMap, n)
   111  		}
   112  	}
   113  
   114  	// Remove the annotations
   115  	delete(g.annotations, v)
   116  
   117  	// Call upwards to remove it from the actual graph
   118  	return g.Graph.Remove(v)
   119  }
   120  
   121  // Replace is the same as dag.Graph.Replace
   122  func (g *Graph) Replace(o, n dag.Vertex) bool {
   123  	g.once.Do(g.init)
   124  
   125  	// Go through and update our lookaside to point to the new vertex
   126  	for k, v := range g.dependableMap {
   127  		if v == o {
   128  			if _, ok := n.(GraphNodeDependable); ok {
   129  				g.dependableMap[k] = n
   130  			} else {
   131  				delete(g.dependableMap, k)
   132  			}
   133  		}
   134  	}
   135  
   136  	// Move the annotation if it exists
   137  	if m, ok := g.annotations[o]; ok {
   138  		g.annotations[n] = m
   139  		delete(g.annotations, o)
   140  	}
   141  
   142  	return g.Graph.Replace(o, n)
   143  }
   144  
   145  // ConnectDependent connects a GraphNodeDependent to all of its
   146  // GraphNodeDependables. It returns the list of dependents it was
   147  // unable to connect to.
   148  func (g *Graph) ConnectDependent(raw dag.Vertex) []string {
   149  	v, ok := raw.(GraphNodeDependent)
   150  	if !ok {
   151  		return nil
   152  	}
   153  
   154  	return g.ConnectTo(v, v.DependentOn())
   155  }
   156  
   157  // ConnectDependents goes through the graph, connecting all the
   158  // GraphNodeDependents to GraphNodeDependables. This is safe to call
   159  // multiple times.
   160  //
   161  // To get details on whether dependencies could be found/made, the more
   162  // specific ConnectDependent should be used.
   163  func (g *Graph) ConnectDependents() {
   164  	for _, v := range g.Vertices() {
   165  		if dv, ok := v.(GraphNodeDependent); ok {
   166  			g.ConnectDependent(dv)
   167  		}
   168  	}
   169  }
   170  
   171  // ConnectFrom creates an edge by finding the source from a DependableName
   172  // and connecting it to the specific vertex.
   173  func (g *Graph) ConnectFrom(source string, target dag.Vertex) {
   174  	g.once.Do(g.init)
   175  
   176  	if source := g.dependableMap[source]; source != nil {
   177  		g.Connect(dag.BasicEdge(source, target))
   178  	}
   179  }
   180  
   181  // ConnectTo connects a vertex to a raw string of targets that are the
   182  // result of DependableName, and returns the list of targets that are missing.
   183  func (g *Graph) ConnectTo(v dag.Vertex, targets []string) []string {
   184  	g.once.Do(g.init)
   185  
   186  	var missing []string
   187  	for _, t := range targets {
   188  		if dest := g.dependableMap[t]; dest != nil {
   189  			g.Connect(dag.BasicEdge(v, dest))
   190  		} else {
   191  			missing = append(missing, t)
   192  		}
   193  	}
   194  
   195  	return missing
   196  }
   197  
   198  // Dependable finds the vertices in the graph that have the given dependable
   199  // names and returns them.
   200  func (g *Graph) Dependable(n string) dag.Vertex {
   201  	// TODO: do we need this?
   202  	return nil
   203  }
   204  
   205  // Walk walks the graph with the given walker for callbacks. The graph
   206  // will be walked with full parallelism, so the walker should expect
   207  // to be called in concurrently.
   208  func (g *Graph) Walk(walker GraphWalker) error {
   209  	return g.walk(walker)
   210  }
   211  
   212  func (g *Graph) init() {
   213  	if g.annotations == nil {
   214  		g.annotations = make(map[dag.Vertex]map[string]interface{})
   215  	}
   216  
   217  	if g.dependableMap == nil {
   218  		g.dependableMap = make(map[string]dag.Vertex)
   219  	}
   220  }
   221  
   222  func (g *Graph) walk(walker GraphWalker) error {
   223  	// The callbacks for enter/exiting a graph
   224  	ctx := walker.EnterPath(g.Path)
   225  	defer walker.ExitPath(g.Path)
   226  
   227  	// Get the path for logs
   228  	path := strings.Join(ctx.Path(), ".")
   229  
   230  	// Determine if our walker is a panic wrapper
   231  	panicwrap, ok := walker.(GraphWalkerPanicwrapper)
   232  	if !ok {
   233  		panicwrap = nil // just to be sure
   234  	}
   235  
   236  	debugName := "walk-graph.json"
   237  	if g.debugName != "" {
   238  		debugName = g.debugName + "-" + debugName
   239  	}
   240  
   241  	debugBuf := dbug.NewFileWriter(debugName)
   242  	g.SetDebugWriter(debugBuf)
   243  	defer debugBuf.Close()
   244  
   245  	// Walk the graph.
   246  	var walkFn dag.WalkFunc
   247  	walkFn = func(v dag.Vertex) (rerr error) {
   248  		log.Printf("[DEBUG] vertex '%s.%s': walking", path, dag.VertexName(v))
   249  		g.DebugVisitInfo(v, g.debugName)
   250  
   251  		// If we have a panic wrap GraphWalker and a panic occurs, recover
   252  		// and call that. We ensure the return value is an error, however,
   253  		// so that future nodes are not called.
   254  		defer func() {
   255  			// If no panicwrap, do nothing
   256  			if panicwrap == nil {
   257  				return
   258  			}
   259  
   260  			// If no panic, do nothing
   261  			err := recover()
   262  			if err == nil {
   263  				return
   264  			}
   265  
   266  			// Modify the return value to show the error
   267  			rerr = fmt.Errorf("vertex %q captured panic: %s\n\n%s",
   268  				dag.VertexName(v), err, debug.Stack())
   269  
   270  			// Call the panic wrapper
   271  			panicwrap.Panic(v, err)
   272  		}()
   273  
   274  		walker.EnterVertex(v)
   275  		defer walker.ExitVertex(v, rerr)
   276  
   277  		// vertexCtx is the context that we use when evaluating. This
   278  		// is normally the context of our graph but can be overridden
   279  		// with a GraphNodeSubPath impl.
   280  		vertexCtx := ctx
   281  		if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 {
   282  			vertexCtx = walker.EnterPath(normalizeModulePath(pn.Path()))
   283  			defer walker.ExitPath(pn.Path())
   284  		}
   285  
   286  		// If the node is eval-able, then evaluate it.
   287  		if ev, ok := v.(GraphNodeEvalable); ok {
   288  			tree := ev.EvalTree()
   289  			if tree == nil {
   290  				panic(fmt.Sprintf(
   291  					"%s.%s (%T): nil eval tree", path, dag.VertexName(v), v))
   292  			}
   293  
   294  			// Allow the walker to change our tree if needed. Eval,
   295  			// then callback with the output.
   296  			log.Printf("[DEBUG] vertex '%s.%s': evaluating", path, dag.VertexName(v))
   297  
   298  			g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path))
   299  
   300  			tree = walker.EnterEvalTree(v, tree)
   301  			output, err := Eval(tree, vertexCtx)
   302  			if rerr = walker.ExitEvalTree(v, output, err); rerr != nil {
   303  				return
   304  			}
   305  		}
   306  
   307  		// If the node is dynamically expanded, then expand it
   308  		if ev, ok := v.(GraphNodeDynamicExpandable); ok {
   309  			log.Printf(
   310  				"[DEBUG] vertex '%s.%s': expanding/walking dynamic subgraph",
   311  				path,
   312  				dag.VertexName(v))
   313  
   314  			g.DebugVertexInfo(v, fmt.Sprintf("expanding %T(%s)", v, path))
   315  
   316  			g, err := ev.DynamicExpand(vertexCtx)
   317  			if err != nil {
   318  				rerr = err
   319  				return
   320  			}
   321  			if g != nil {
   322  				// Walk the subgraph
   323  				if rerr = g.walk(walker); rerr != nil {
   324  					return
   325  				}
   326  			}
   327  		}
   328  
   329  		// If the node has a subgraph, then walk the subgraph
   330  		if sn, ok := v.(GraphNodeSubgraph); ok {
   331  			log.Printf(
   332  				"[DEBUG] vertex '%s.%s': walking subgraph",
   333  				path,
   334  				dag.VertexName(v))
   335  
   336  			g.DebugVertexInfo(v, fmt.Sprintf("subgraph: %T(%s)", v, path))
   337  
   338  			if rerr = sn.Subgraph().(*Graph).walk(walker); rerr != nil {
   339  				return
   340  			}
   341  		}
   342  
   343  		return nil
   344  	}
   345  
   346  	return g.AcyclicGraph.Walk(walkFn)
   347  }
   348  
   349  // GraphNodeAnnotationInit is an interface that allows a node to
   350  // initialize it's annotations.
   351  //
   352  // AnnotationInit will be called _once_ when the node is added to a
   353  // graph for the first time and is expected to return it's initial
   354  // annotations.
   355  type GraphNodeAnnotationInit interface {
   356  	AnnotationInit() map[string]interface{}
   357  }
   358  
   359  // GraphNodeDependable is an interface which says that a node can be
   360  // depended on (an edge can be placed between this node and another) according
   361  // to the well-known name returned by DependableName.
   362  //
   363  // DependableName can return multiple names it is known by.
   364  type GraphNodeDependable interface {
   365  	DependableName() []string
   366  }
   367  
   368  // GraphNodeDependent is an interface which says that a node depends
   369  // on another GraphNodeDependable by some name. By implementing this
   370  // interface, Graph.ConnectDependents() can be called multiple times
   371  // safely and efficiently.
   372  type GraphNodeDependent interface {
   373  	DependentOn() []string
   374  }