diff --git a/terraform/context.go b/terraform/context.go index c1e52f03a..110ba4772 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -375,7 +375,7 @@ func (c *Context) Apply() (*State, error) { if c.destroy { walker, err = c.walk(graph, nil, walkDestroy) } else { - walker, err = c.walk(graph, nil, walkApply) + walker, err = c.walk(graph, graph, walkApply) } if len(walker.ValidationErrors) > 0 { @@ -641,6 +641,7 @@ func (c *Context) walk( // on the real walk so it is fine to start first. shadowCh = make(chan error) go func() { + log.Printf("[INFO] Starting shadow graph walk: %s", operation.String()) shadowCh <- shadow.Walk(shadowWalker) }() } @@ -660,14 +661,21 @@ func (c *Context) walk( } // Wait for the walk to end + log.Printf("[DEBUG] Waiting for shadow graph to complete...") if err := <-shadowCh; err != nil { c.shadowErr = multierror.Append(c.shadowErr, err) } - // If we're supposed to fail on shadow errors, then report it - if contextFailOnShadowError && c.shadowErr != nil { - realErr = multierror.Append(realErr, multierror.Prefix( - c.shadowErr, "shadow graph:")) + if c.shadowErr == nil { + log.Printf("[INFO] Shadow graph success!") + } else { + log.Printf("[ERROR] Shadow graph error: %s", c.shadowErr) + + // If we're supposed to fail on shadow errors, then report it + if contextFailOnShadowError { + realErr = multierror.Append(realErr, multierror.Prefix( + c.shadowErr, "shadow graph:")) + } } } diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 0296ef487..6a5de2a34 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -14,7 +14,7 @@ import ( "github.com/hashicorp/terraform/config/module" ) -func TestContext2Apply(t *testing.T) { +func TestContext2Apply_basic(t *testing.T) { m := testModule(t, "apply-good") p := testProvider("aws") p.ApplyFn = testApplyFn diff --git a/terraform/shadow_context.go b/terraform/shadow_context.go index 8bbebc00d..05a0f9c67 100644 --- a/terraform/shadow_context.go +++ b/terraform/shadow_context.go @@ -19,13 +19,13 @@ import ( // copied, no real providers or resources are used, etc. func newShadowContext(c *Context) (*Context, *Context, io.Closer) { // Copy the targets - targetRaw, err := copystructure.Config{Lock: true}.Copy(c.targets) + targetRaw, err := copystructure.Copy(c.targets) if err != nil { panic(err) } // Copy the variables - varRaw, err := copystructure.Config{Lock: true}.Copy(c.variables) + varRaw, err := copystructure.Copy(c.variables) if err != nil { panic(err) } @@ -45,6 +45,12 @@ func newShadowContext(c *Context) (*Context, *Context, io.Closer) { targets: targetRaw.([]string), uiInput: nil, // TODO variables: varRaw.(map[string]interface{}), + + // Hardcoded to 4 since parallelism in the shadow doesn't matter + // a ton since we're doing far less compared to the real side + // and our operations are MUCH faster. + parallelSem: NewSemaphore(4), + providerInputConfig: make(map[string]map[string]interface{}), } // Create the real context. This is effectively just a copy of @@ -53,5 +59,18 @@ func newShadowContext(c *Context) (*Context, *Context, io.Closer) { real := *c real.providers = providerFactory.RealMap() - return &real, shadow, nil + return &real, shadow, &shadowContextCloser{ + Providers: providerFactory, + } +} + +// shadowContextCloser is the io.Closer returned by newShadowContext that +// closes all the shadows and returns the results. +type shadowContextCloser struct { + Providers interface{} +} + +// Close closes the shadow context. +func (c *shadowContextCloser) Close() error { + return nil }