diff --git a/terraform/context.go b/terraform/context.go index 511d30c0a2..f1326dae7b 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -59,6 +59,7 @@ type Context struct { variables map[string]string l sync.Mutex // Lock acquired during any task + parallelSem Semaphore providerInputConfig map[string]map[string]interface{} runCh <-chan struct{} } @@ -82,17 +83,27 @@ func NewContext(opts *ContextOpts) *Context { state.init() } + // Determine parallelism, default to 10. We do this both to limit + // CPU pressure but also to have an extra guard against rate throttling + // from providers. + par := opts.Parallelism + if par == 0 { + par = 10 + } + return &Context{ - diff: opts.Diff, - hooks: hooks, - module: opts.Module, - providers: opts.Providers, + diff: opts.Diff, + hooks: hooks, + module: opts.Module, + providers: opts.Providers, + provisioners: opts.Provisioners, + state: state, + uiInput: opts.UIInput, + variables: opts.Variables, + + parallelSem: NewSemaphore(par), providerInputConfig: make(map[string]map[string]interface{}), - provisioners: opts.Provisioners, sh: sh, - state: state, - uiInput: opts.UIInput, - variables: opts.Variables, } } diff --git a/terraform/graph_walk_context.go b/terraform/graph_walk_context.go index f99ca60dab..64a9f3d02c 100644 --- a/terraform/graph_walk_context.go +++ b/terraform/graph_walk_context.go @@ -84,6 +84,9 @@ func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext { } func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode { + // Acquire a lock on the semaphore + w.Context.parallelSem.Acquire() + // We want to filter the evaluation tree to only include operations // that belong in this operation. return EvalFilter(n, EvalNodeFilterOp(w.Operation)) @@ -91,6 +94,9 @@ func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode { func (w *ContextGraphWalker) ExitEvalTree( v dag.Vertex, output interface{}, err error) error { + // Release the semaphore + w.Context.parallelSem.Release() + if err == nil { return nil }