diff --git a/terraform/context.go b/terraform/context.go index fc7e4c994..58119f78a 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -37,6 +37,11 @@ var ( // contextFailOnShadowError will cause Context operations to return // errors when shadow operations fail. This is only used for testing. contextFailOnShadowError = false + + // contextTestDeepCopyOnPlan will perform a Diff DeepCopy on every + // Plan operation, effectively testing the Diff DeepCopy whenever + // a Plan occurs. This is enabled for tests. + contextTestDeepCopyOnPlan = false ) // ContextOpts are the user-configurable options to create a context with @@ -437,6 +442,17 @@ func (c *Context) Plan() (*Plan, error) { } p.Diff = c.diff + // If this is true, it means we're running unit tests. In this case, + // we perform a deep copy just to ensure that all context tests also + // test that a diff is copy-able. This will panic if it fails. This + // is enabled during unit tests. + // + // This should never be true during production usage, but even if it is, + // it can't do any real harm. + if contextTestDeepCopyOnPlan { + p.Diff.DeepCopy() + } + // Now that we have a diff, we can build the exact graph that Apply will use // and catch any possible cycles during the Plan phase. if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil { diff --git a/terraform/diff.go b/terraform/diff.go index 351a3c48d..b43e16ee8 100644 --- a/terraform/diff.go +++ b/terraform/diff.go @@ -9,6 +9,8 @@ import ( "sort" "strings" "sync" + + "github.com/mitchellh/copystructure" ) // DiffChangeType is an enum with the kind of changes a diff has planned. @@ -79,6 +81,17 @@ func (d *Diff) Empty() bool { return true } +// DeepCopy performs a deep copy of all parts of the Diff, making the +// resulting Diff safe to use without modifying this one. +func (d *Diff) DeepCopy() *Diff { + copy, err := copystructure.Config{Lock: true}.Copy(d) + if err != nil { + panic(err) + } + + return copy.(*Diff) +} + func (d *Diff) String() string { var buf bytes.Buffer diff --git a/terraform/terraform_test.go b/terraform/terraform_test.go index 910005d2a..dd6185613 100644 --- a/terraform/terraform_test.go +++ b/terraform/terraform_test.go @@ -35,6 +35,9 @@ func TestMain(m *testing.M) { // Make sure shadow operations fail our real tests contextFailOnShadowError = true + // Always DeepCopy the Diff on every Plan during a test + contextTestDeepCopyOnPlan = true + os.Exit(m.Run()) }