9 "github.com/hashicorp/terraform/dag"
12 // RootModuleName is the name given to the root module implicitly.
13 const RootModuleName = "root"
15 // RootModulePath is the path for the root module.
16 var RootModulePath = []string{RootModuleName}
18 // Graph represents the graph that Terraform uses to represent resources
19 // and their dependencies.
21 // Graph is the actual DAG. This is embedded so you can call the DAG
25 // Path is the path in the module tree that this Graph represents.
26 // The root is represented by a single element list containing
30 // debugName is a name for reference in the debug output. This is usually
31 // to indicate what topmost builder was, and if this graph is a shadow or
36 func (g *Graph) DirectedGraph() dag.Grapher {
37 return &g.AcyclicGraph
40 // Walk walks the graph with the given walker for callbacks. The graph
41 // will be walked with full parallelism, so the walker should expect
42 // to be called in concurrently.
43 func (g *Graph) Walk(walker GraphWalker) error {
47 func (g *Graph) walk(walker GraphWalker) error {
48 // The callbacks for enter/exiting a graph
49 ctx := walker.EnterPath(g.Path)
50 defer walker.ExitPath(g.Path)
52 // Get the path for logs
53 path := strings.Join(ctx.Path(), ".")
55 // Determine if our walker is a panic wrapper
56 panicwrap, ok := walker.(GraphWalkerPanicwrapper)
58 panicwrap = nil // just to be sure
61 debugName := "walk-graph.json"
62 if g.debugName != "" {
63 debugName = g.debugName + "-" + debugName
66 debugBuf := dbug.NewFileWriter(debugName)
67 g.SetDebugWriter(debugBuf)
68 defer debugBuf.Close()
71 var walkFn dag.WalkFunc
72 walkFn = func(v dag.Vertex) (rerr error) {
73 log.Printf("[TRACE] vertex '%s.%s': walking", path, dag.VertexName(v))
74 g.DebugVisitInfo(v, g.debugName)
76 // If we have a panic wrap GraphWalker and a panic occurs, recover
77 // and call that. We ensure the return value is an error, however,
78 // so that future nodes are not called.
80 // If no panicwrap, do nothing
85 // If no panic, do nothing
91 // Modify the return value to show the error
92 rerr = fmt.Errorf("vertex %q captured panic: %s\n\n%s",
93 dag.VertexName(v), err, debug.Stack())
95 // Call the panic wrapper
96 panicwrap.Panic(v, err)
100 defer walker.ExitVertex(v, rerr)
102 // vertexCtx is the context that we use when evaluating. This
103 // is normally the context of our graph but can be overridden
104 // with a GraphNodeSubPath impl.
106 if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 {
107 vertexCtx = walker.EnterPath(normalizeModulePath(pn.Path()))
108 defer walker.ExitPath(pn.Path())
111 // If the node is eval-able, then evaluate it.
112 if ev, ok := v.(GraphNodeEvalable); ok {
113 tree := ev.EvalTree()
116 "%s.%s (%T): nil eval tree", path, dag.VertexName(v), v))
119 // Allow the walker to change our tree if needed. Eval,
120 // then callback with the output.
121 log.Printf("[TRACE] vertex '%s.%s': evaluating", path, dag.VertexName(v))
123 g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path))
125 tree = walker.EnterEvalTree(v, tree)
126 output, err := Eval(tree, vertexCtx)
127 if rerr = walker.ExitEvalTree(v, output, err); rerr != nil {
132 // If the node is dynamically expanded, then expand it
133 if ev, ok := v.(GraphNodeDynamicExpandable); ok {
135 "[TRACE] vertex '%s.%s': expanding/walking dynamic subgraph",
139 g.DebugVertexInfo(v, fmt.Sprintf("expanding %T(%s)", v, path))
141 g, err := ev.DynamicExpand(vertexCtx)
148 if rerr = g.walk(walker); rerr != nil {
154 // If the node has a subgraph, then walk the subgraph
155 if sn, ok := v.(GraphNodeSubgraph); ok {
157 "[TRACE] vertex '%s.%s': walking subgraph",
161 g.DebugVertexInfo(v, fmt.Sprintf("subgraph: %T(%s)", v, path))
163 if rerr = sn.Subgraph().(*Graph).walk(walker); rerr != nil {
171 return g.AcyclicGraph.Walk(walkFn)