diff --git a/terraform/context.go b/terraform/context.go index 27aa71d43..3e869e43c 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -613,13 +613,13 @@ func (c *Context) walk( realCtx := c // If we have a shadow graph, walk that as well + var shadowCtx *Context var shadowCh chan error var shadowCloser io.Closer if shadow != nil { // Build the shadow context. In the process, override the real context // with the one that is wrapped so that the shadow context can verify // the results of the real. - var shadowCtx *Context realCtx, shadowCtx, shadowCloser = newShadowContext(c) // Build the graph walker for the shadow. @@ -657,6 +657,11 @@ func (c *Context) walk( c.shadowErr = multierror.Append(c.shadowErr, err) } + // Verify the contexts (compare) + if err := shadowContextVerify(realCtx, shadowCtx); err != nil { + c.shadowErr = multierror.Append(c.shadowErr, err) + } + if c.shadowErr == nil { log.Printf("[INFO] Shadow graph success!") } else { diff --git a/terraform/shadow_context.go b/terraform/shadow_context.go index 60c6e1680..ba3b811c8 100644 --- a/terraform/shadow_context.go +++ b/terraform/shadow_context.go @@ -1,8 +1,11 @@ package terraform import ( + "fmt" "io" + "reflect" + "github.com/hashicorp/go-multierror" "github.com/mitchellh/copystructure" ) @@ -63,6 +66,32 @@ func newShadowContext(c *Context) (*Context, *Context, io.Closer) { } } +// shadowContextVerify takes the real and shadow context and verifies they +// have equal diffs and states. +func shadowContextVerify(real, shadow *Context) error { + var result error + + // Compare the states + if !real.state.Equal(shadow.state) { + result = multierror.Append(result, fmt.Errorf( + "Real and shadow states do not match! "+ + "Real state:\n\n%s\n\n"+ + "Shadow state:\n\n%s\n\n", + real.state, shadow.state)) + } + + // Compare the diffs + if !reflect.DeepEqual(real.diff, shadow.diff) { + result = multierror.Append(result, fmt.Errorf( + "Real and shadow diffs do not match! "+ + "Real diff:\n\n%s\n\n"+ + "Shadow diff:\n\n%s\n\n", + real.diff, shadow.diff)) + } + + return result +} + // shadowContextCloser is the io.Closer returned by newShadowContext that // closes all the shadows and returns the results. type shadowContextCloser struct { diff --git a/terraform/shadow_resource_provider.go b/terraform/shadow_resource_provider.go index b534b0d3e..176164996 100644 --- a/terraform/shadow_resource_provider.go +++ b/terraform/shadow_resource_provider.go @@ -167,6 +167,9 @@ type shadowResourceProviderShadow struct { } type shadowResourceProviderShared struct { + // NOTE: Anytime a value is added here, be sure to add it to + // the Close() method so that it is closed. + CloseErr shadow.Value Input shadow.Value Validate shadow.Value