From e2c415a87eff10ee120f3cf8fa4008e89e3daa24 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 17 Oct 2016 16:29:24 -0700 Subject: [PATCH] terraform: resource provider must never return pointers to same data This is a requirement for the parallelism of Terraform to work sanely. We could deep copy every result but I think this would be unrealistic and impose a performance cost when it isn't necessary in most cases. --- terraform/context_plan_test.go | 4 ++++ terraform/resource_provider.go | 6 ++++++ terraform/resource_provider_mock.go | 20 ++++++++++++++------ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/terraform/context_plan_test.go b/terraform/context_plan_test.go index 8778e4221..e58dbdf9f 100644 --- a/terraform/context_plan_test.go +++ b/terraform/context_plan_test.go @@ -1088,6 +1088,10 @@ func TestContext2Plan_dataResourceBecomesComputed(t *testing.T) { t.Fatalf("missing diff for data.aws_data_resource.foo") } + // This is added by the diff but we want to verify that we got + // the same diff as above minus the dynamic stuff. + delete(iDiff.Attributes, "id") + if same, _ := p.ReadDataDiffReturn.Same(iDiff); !same { t.Fatalf( "incorrect diff for data.data_resource.foo\ngot: %#v\nwant: %#v", diff --git a/terraform/resource_provider.go b/terraform/resource_provider.go index 37cd1d5c3..542f14a61 100644 --- a/terraform/resource_provider.go +++ b/terraform/resource_provider.go @@ -3,6 +3,12 @@ package terraform // ResourceProvider is an interface that must be implemented by any // resource provider: the thing that creates and manages the resources in // a Terraform configuration. +// +// Important implementation note: All returned pointers, such as +// *ResourceConfig, *InstanceState, *InstanceDiff, etc. must not point to +// shared data. Terraform is highly parallel and assumes that this data is safe +// to read/write in parallel so it must be unique references. Note that it is +// safe to return arguments as results, however. type ResourceProvider interface { /********************************************************************* * Functions related to the provider diff --git a/terraform/resource_provider_mock.go b/terraform/resource_provider_mock.go index 8389fd0ae..f8acfafa9 100644 --- a/terraform/resource_provider_mock.go +++ b/terraform/resource_provider_mock.go @@ -157,7 +157,7 @@ func (p *MockResourceProvider) Apply( return p.ApplyFn(info, state, diff) } - return p.ApplyReturn, p.ApplyReturnError + return p.ApplyReturn.DeepCopy(), p.ApplyReturnError } func (p *MockResourceProvider) Diff( @@ -175,7 +175,7 @@ func (p *MockResourceProvider) Diff( return p.DiffFn(info, state, desired) } - return p.DiffReturn, p.DiffReturnError + return p.DiffReturn.DeepCopy(), p.DiffReturnError } func (p *MockResourceProvider) Refresh( @@ -192,7 +192,7 @@ func (p *MockResourceProvider) Refresh( return p.RefreshFn(info, s) } - return p.RefreshReturn, p.RefreshReturnError + return p.RefreshReturn.DeepCopy(), p.RefreshReturnError } func (p *MockResourceProvider) Resources() []ResourceType { @@ -214,7 +214,15 @@ func (p *MockResourceProvider) ImportState(info *InstanceInfo, id string) ([]*In return p.ImportStateFn(info, id) } - return p.ImportStateReturn, p.ImportStateReturnError + var result []*InstanceState + if p.ImportStateReturn != nil { + result = make([]*InstanceState, len(p.ImportStateReturn)) + for i, v := range p.ImportStateReturn { + result[i] = v.DeepCopy() + } + } + + return result, p.ImportStateReturnError } func (p *MockResourceProvider) ValidateDataSource(t string, c *ResourceConfig) ([]string, []error) { @@ -245,7 +253,7 @@ func (p *MockResourceProvider) ReadDataDiff( return p.ReadDataDiffFn(info, desired) } - return p.ReadDataDiffReturn, p.ReadDataDiffReturnError + return p.ReadDataDiffReturn.DeepCopy(), p.ReadDataDiffReturnError } func (p *MockResourceProvider) ReadDataApply( @@ -262,7 +270,7 @@ func (p *MockResourceProvider) ReadDataApply( return p.ReadDataApplyFn(info, d) } - return p.ReadDataApplyReturn, p.ReadDataApplyReturnError + return p.ReadDataApplyReturn.DeepCopy(), p.ReadDataApplyReturnError } func (p *MockResourceProvider) DataSources() []DataSource {