aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/dag/walk.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/dag/walk.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/dag/walk.go94
1 files changed, 54 insertions, 40 deletions
diff --git a/vendor/github.com/hashicorp/terraform/dag/walk.go b/vendor/github.com/hashicorp/terraform/dag/walk.go
index f03b100..1c926c2 100644
--- a/vendor/github.com/hashicorp/terraform/dag/walk.go
+++ b/vendor/github.com/hashicorp/terraform/dag/walk.go
@@ -2,12 +2,11 @@ package dag
2 2
3import ( 3import (
4 "errors" 4 "errors"
5 "fmt"
6 "log" 5 "log"
7 "sync" 6 "sync"
8 "time" 7 "time"
9 8
10 "github.com/hashicorp/go-multierror" 9 "github.com/hashicorp/terraform/tfdiags"
11) 10)
12 11
13// Walker is used to walk every vertex of a graph in parallel. 12// Walker is used to walk every vertex of a graph in parallel.
@@ -54,10 +53,15 @@ type Walker struct {
54 // if new vertices are added. 53 // if new vertices are added.
55 wait sync.WaitGroup 54 wait sync.WaitGroup
56 55
57 // errMap contains the errors recorded so far for execution. Reading 56 // diagsMap contains the diagnostics recorded so far for execution,
58 // and writing should hold errLock. 57 // and upstreamFailed contains all the vertices whose problems were
59 errMap map[Vertex]error 58 // caused by upstream failures, and thus whose diagnostics should be
60 errLock sync.Mutex 59 // excluded from the final set.
60 //
61 // Readers and writers of either map must hold diagsLock.
62 diagsMap map[Vertex]tfdiags.Diagnostics
63 upstreamFailed map[Vertex]struct{}
64 diagsLock sync.Mutex
61} 65}
62 66
63type walkerVertex struct { 67type walkerVertex struct {
@@ -98,31 +102,30 @@ type walkerVertex struct {
98// user-returned error. 102// user-returned error.
99var errWalkUpstream = errors.New("upstream dependency failed") 103var errWalkUpstream = errors.New("upstream dependency failed")
100 104
101// Wait waits for the completion of the walk and returns any errors ( 105// Wait waits for the completion of the walk and returns diagnostics describing
102// in the form of a multierror) that occurred. Update should be called 106// any problems that arose. Update should be called to populate the walk with
103// to populate the walk with vertices and edges prior to calling this. 107// vertices and edges prior to calling this.
104// 108//
105// Wait will return as soon as all currently known vertices are complete. 109// Wait will return as soon as all currently known vertices are complete.
106// If you plan on calling Update with more vertices in the future, you 110// If you plan on calling Update with more vertices in the future, you
107// should not call Wait until after this is done. 111// should not call Wait until after this is done.
108func (w *Walker) Wait() error { 112func (w *Walker) Wait() tfdiags.Diagnostics {
109 // Wait for completion 113 // Wait for completion
110 w.wait.Wait() 114 w.wait.Wait()
111 115
112 // Grab the error lock 116 var diags tfdiags.Diagnostics
113 w.errLock.Lock() 117 w.diagsLock.Lock()
114 defer w.errLock.Unlock() 118 for v, vDiags := range w.diagsMap {
115 119 if _, upstream := w.upstreamFailed[v]; upstream {
116 // Build the error 120 // Ignore diagnostics for nodes that had failed upstreams, since
117 var result error 121 // the downstream diagnostics are likely to be redundant.
118 for v, err := range w.errMap { 122 continue
119 if err != nil && err != errWalkUpstream {
120 result = multierror.Append(result, fmt.Errorf(
121 "%s: %s", VertexName(v), err))
122 } 123 }
124 diags = diags.Append(vDiags)
123 } 125 }
126 w.diagsLock.Unlock()
124 127
125 return result 128 return diags
126} 129}
127 130
128// Update updates the currently executing walk with the given graph. 131// Update updates the currently executing walk with the given graph.
@@ -136,6 +139,7 @@ func (w *Walker) Wait() error {
136// Multiple Updates can be called in parallel. Update can be called at any 139// Multiple Updates can be called in parallel. Update can be called at any
137// time during a walk. 140// time during a walk.
138func (w *Walker) Update(g *AcyclicGraph) { 141func (w *Walker) Update(g *AcyclicGraph) {
142 log.Print("[TRACE] dag/walk: updating graph")
139 var v, e *Set 143 var v, e *Set
140 if g != nil { 144 if g != nil {
141 v, e = g.vertices, g.edges 145 v, e = g.vertices, g.edges
@@ -381,25 +385,34 @@ func (w *Walker) walkVertex(v Vertex, info *walkerVertex) {
381 } 385 }
382 386
383 // Run our callback or note that our upstream failed 387 // Run our callback or note that our upstream failed
384 var err error 388 var diags tfdiags.Diagnostics
389 var upstreamFailed bool
385 if depsSuccess { 390 if depsSuccess {
386 log.Printf("[TRACE] dag/walk: walking %q", VertexName(v)) 391 log.Printf("[TRACE] dag/walk: visiting %q", VertexName(v))
387 err = w.Callback(v) 392 diags = w.Callback(v)
388 } else { 393 } else {
389 log.Printf("[TRACE] dag/walk: upstream errored, not walking %q", VertexName(v)) 394 log.Printf("[TRACE] dag/walk: upstream of %q errored, so skipping", VertexName(v))
390 err = errWalkUpstream 395 // This won't be displayed to the user because we'll set upstreamFailed,
396 // but we need to ensure there's at least one error in here so that
397 // the failures will cascade downstream.
398 diags = diags.Append(errors.New("upstream dependencies failed"))
399 upstreamFailed = true
391 } 400 }
392 401
393 // Record the error 402 // Record the result (we must do this after execution because we mustn't
394 if err != nil { 403 // hold diagsLock while visiting a vertex.)
395 w.errLock.Lock() 404 w.diagsLock.Lock()
396 defer w.errLock.Unlock() 405 if w.diagsMap == nil {
397 406 w.diagsMap = make(map[Vertex]tfdiags.Diagnostics)
398 if w.errMap == nil { 407 }
399 w.errMap = make(map[Vertex]error) 408 w.diagsMap[v] = diags
400 } 409 if w.upstreamFailed == nil {
401 w.errMap[v] = err 410 w.upstreamFailed = make(map[Vertex]struct{})
402 } 411 }
412 if upstreamFailed {
413 w.upstreamFailed[v] = struct{}{}
414 }
415 w.diagsLock.Unlock()
403} 416}
404 417
405func (w *Walker) waitDeps( 418func (w *Walker) waitDeps(
@@ -407,6 +420,7 @@ func (w *Walker) waitDeps(
407 deps map[Vertex]<-chan struct{}, 420 deps map[Vertex]<-chan struct{},
408 doneCh chan<- bool, 421 doneCh chan<- bool,
409 cancelCh <-chan struct{}) { 422 cancelCh <-chan struct{}) {
423
410 // For each dependency given to us, wait for it to complete 424 // For each dependency given to us, wait for it to complete
411 for dep, depCh := range deps { 425 for dep, depCh := range deps {
412 DepSatisfied: 426 DepSatisfied:
@@ -423,17 +437,17 @@ func (w *Walker) waitDeps(
423 return 437 return
424 438
425 case <-time.After(time.Second * 5): 439 case <-time.After(time.Second * 5):
426 log.Printf("[TRACE] dag/walk: vertex %q, waiting for: %q", 440 log.Printf("[TRACE] dag/walk: vertex %q is waiting for %q",
427 VertexName(v), VertexName(dep)) 441 VertexName(v), VertexName(dep))
428 } 442 }
429 } 443 }
430 } 444 }
431 445
432 // Dependencies satisfied! We need to check if any errored 446 // Dependencies satisfied! We need to check if any errored
433 w.errLock.Lock() 447 w.diagsLock.Lock()
434 defer w.errLock.Unlock() 448 defer w.diagsLock.Unlock()
435 for dep, _ := range deps { 449 for dep := range deps {
436 if w.errMap[dep] != nil { 450 if w.diagsMap[dep].HasErrors() {
437 // One of our dependencies failed, so return false 451 // One of our dependencies failed, so return false
438 doneCh <- false 452 doneCh <- false
439 return 453 return