diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/dag/walk.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/dag/walk.go | 94 |
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 | ||
3 | import ( | 3 | import ( |
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 | ||
63 | type walkerVertex struct { | 67 | type walkerVertex struct { |
@@ -98,31 +102,30 @@ type walkerVertex struct { | |||
98 | // user-returned error. | 102 | // user-returned error. |
99 | var errWalkUpstream = errors.New("upstream dependency failed") | 103 | var 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. |
108 | func (w *Walker) Wait() error { | 112 | func (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. |
138 | func (w *Walker) Update(g *AcyclicGraph) { | 141 | func (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 | ||
405 | func (w *Walker) waitDeps( | 418 | func (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 |