]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/graph.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / graph.go
1 package terraform
2
3 import (
4 "fmt"
5 "log"
6 "runtime/debug"
7 "strings"
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.
20 type Graph struct {
21 // Graph is the actual DAG. This is embedded so you can call the DAG
22 // methods directly.
23 dag.AcyclicGraph
24
25 // Path is the path in the module tree that this Graph represents.
26 // The root is represented by a single element list containing
27 // RootModuleName
28 Path []string
29
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
32 // not.
33 debugName string
34 }
35
36 func (g *Graph) DirectedGraph() dag.Grapher {
37 return &g.AcyclicGraph
38 }
39
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 {
44 return g.walk(walker)
45 }
46
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)
51
52 // Get the path for logs
53 path := strings.Join(ctx.Path(), ".")
54
55 // Determine if our walker is a panic wrapper
56 panicwrap, ok := walker.(GraphWalkerPanicwrapper)
57 if !ok {
58 panicwrap = nil // just to be sure
59 }
60
61 debugName := "walk-graph.json"
62 if g.debugName != "" {
63 debugName = g.debugName + "-" + debugName
64 }
65
66 debugBuf := dbug.NewFileWriter(debugName)
67 g.SetDebugWriter(debugBuf)
68 defer debugBuf.Close()
69
70 // Walk the graph.
71 var walkFn dag.WalkFunc
72 walkFn = func(v dag.Vertex) (rerr error) {
73 log.Printf("[DEBUG] vertex '%s.%s': walking", path, dag.VertexName(v))
74 g.DebugVisitInfo(v, g.debugName)
75
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.
79 defer func() {
80 // If no panicwrap, do nothing
81 if panicwrap == nil {
82 return
83 }
84
85 // If no panic, do nothing
86 err := recover()
87 if err == nil {
88 return
89 }
90
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())
94
95 // Call the panic wrapper
96 panicwrap.Panic(v, err)
97 }()
98
99 walker.EnterVertex(v)
100 defer walker.ExitVertex(v, rerr)
101
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.
105 vertexCtx := ctx
106 if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 {
107 vertexCtx = walker.EnterPath(normalizeModulePath(pn.Path()))
108 defer walker.ExitPath(pn.Path())
109 }
110
111 // If the node is eval-able, then evaluate it.
112 if ev, ok := v.(GraphNodeEvalable); ok {
113 tree := ev.EvalTree()
114 if tree == nil {
115 panic(fmt.Sprintf(
116 "%s.%s (%T): nil eval tree", path, dag.VertexName(v), v))
117 }
118
119 // Allow the walker to change our tree if needed. Eval,
120 // then callback with the output.
121 log.Printf("[DEBUG] vertex '%s.%s': evaluating", path, dag.VertexName(v))
122
123 g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path))
124
125 tree = walker.EnterEvalTree(v, tree)
126 output, err := Eval(tree, vertexCtx)
127 if rerr = walker.ExitEvalTree(v, output, err); rerr != nil {
128 return
129 }
130 }
131
132 // If the node is dynamically expanded, then expand it
133 if ev, ok := v.(GraphNodeDynamicExpandable); ok {
134 log.Printf(
135 "[DEBUG] vertex '%s.%s': expanding/walking dynamic subgraph",
136 path,
137 dag.VertexName(v))
138
139 g.DebugVertexInfo(v, fmt.Sprintf("expanding %T(%s)", v, path))
140
141 g, err := ev.DynamicExpand(vertexCtx)
142 if err != nil {
143 rerr = err
144 return
145 }
146 if g != nil {
147 // Walk the subgraph
148 if rerr = g.walk(walker); rerr != nil {
149 return
150 }
151 }
152 }
153
154 // If the node has a subgraph, then walk the subgraph
155 if sn, ok := v.(GraphNodeSubgraph); ok {
156 log.Printf(
157 "[DEBUG] vertex '%s.%s': walking subgraph",
158 path,
159 dag.VertexName(v))
160
161 g.DebugVertexInfo(v, fmt.Sprintf("subgraph: %T(%s)", v, path))
162
163 if rerr = sn.Subgraph().(*Graph).walk(walker); rerr != nil {
164 return
165 }
166 }
167
168 return nil
169 }
170
171 return g.AcyclicGraph.Walk(walkFn)
172 }