diff --git a/terraform/shadow_resource_provider.go b/terraform/shadow_resource_provider.go index 9d7caafb6..a9a1b2dc7 100644 --- a/terraform/shadow_resource_provider.go +++ b/terraform/shadow_resource_provider.go @@ -98,6 +98,16 @@ func (p *shadowResourceProviderReal) Validate(c *ResourceConfig) ([]string, []er return warns, errs } +func (p *shadowResourceProviderReal) Configure(c *ResourceConfig) error { + err := p.ResourceProvider.Configure(c) + p.Shared.Configure.SetValue(&shadowResourceProviderConfigure{ + Config: c.DeepCopy(), + Result: err, + }) + + return err +} + // shadowResourceProviderShadow is the shadow resource provider. Function // calls never affect real resources. This is paired with the "real" side // which must be called properly to enable recording. @@ -113,9 +123,10 @@ type shadowResourceProviderShadow struct { } type shadowResourceProviderShared struct { - CloseErr shadow.Value - Input shadow.Value - Validate shadow.Value + CloseErr shadow.Value + Input shadow.Value + Validate shadow.Value + Configure shadow.Value } func (p *shadowResourceProviderShadow) CloseShadow() error { @@ -201,6 +212,35 @@ func (p *shadowResourceProviderShadow) Validate(c *ResourceConfig) ([]string, [] return result.ResultWarn, result.ResultErr } +func (p *shadowResourceProviderShadow) Configure(c *ResourceConfig) error { + // Get the result of the call + raw := p.Shared.Configure.Value() + if raw == nil { + return nil + } + + result, ok := raw.(*shadowResourceProviderConfigure) + if !ok { + p.ErrorLock.Lock() + defer p.ErrorLock.Unlock() + p.Error = multierror.Append(p.Error, fmt.Errorf( + "Unknown 'configure' shadow value: %#v", raw)) + return nil + } + + // Compare the parameters, which should be identical + if !c.Equal(result.Config) { + p.ErrorLock.Lock() + p.Error = multierror.Append(p.Error, fmt.Errorf( + "Configure had unequal configurations (real, then shadow):\n\n%#v\n\n%#v", + result.Config, c)) + p.ErrorLock.Unlock() + } + + // Return the results + return result.Result +} + // TODO // TODO // TODO @@ -211,10 +251,6 @@ func (p *shadowResourceProviderShadow) ValidateResource(t string, c *ResourceCon return nil, nil } -func (p *shadowResourceProviderShadow) Configure(c *ResourceConfig) error { - return nil -} - func (p *shadowResourceProviderShadow) Apply( info *InstanceInfo, state *InstanceState, @@ -269,3 +305,8 @@ type shadowResourceProviderValidate struct { ResultWarn []string ResultErr []error } + +type shadowResourceProviderConfigure struct { + Config *ResourceConfig + Result error +} diff --git a/terraform/shadow_resource_provider_test.go b/terraform/shadow_resource_provider_test.go index b2811aa9a..9c36fd546 100644 --- a/terraform/shadow_resource_provider_test.go +++ b/terraform/shadow_resource_provider_test.go @@ -194,3 +194,74 @@ func TestShadowResourceProviderValidate_badInput(t *testing.T) { t.Fatal("should have error") } } + +func TestShadowResourceProviderConfigure(t *testing.T) { + mock := new(MockResourceProvider) + real, shadow := newShadowResourceProvider(mock) + + // Test values + config := testResourceConfig(t, map[string]interface{}{ + "foo": "bar", + }) + returnErr := fmt.Errorf("bar") + + // Configure the mock + mock.ConfigureReturnError = returnErr + + // Verify that it blocks until the real func is called + var err error + doneCh := make(chan struct{}) + go func() { + defer close(doneCh) + err = shadow.Configure(config) + }() + + select { + case <-doneCh: + t.Fatal("should block until finished") + case <-time.After(10 * time.Millisecond): + } + + // Call the real func + realErr := real.Configure(config) + if !reflect.DeepEqual(realErr, returnErr) { + t.Fatalf("bad: %#v", realErr) + } + + // The shadow should finish now + <-doneCh + + // Verify the shadow returned the same values + if !reflect.DeepEqual(err, returnErr) { + t.Fatalf("bad: %#v", err) + } + + // Verify we have no errors + if err := shadow.CloseShadow(); err != nil { + t.Fatalf("bad: %s", err) + } +} + +func TestShadowResourceProviderConfigure_badInput(t *testing.T) { + mock := new(MockResourceProvider) + real, shadow := newShadowResourceProvider(mock) + + // Test values + config := testResourceConfig(t, map[string]interface{}{ + "foo": "bar", + }) + configBad := testResourceConfig(t, map[string]interface{}{ + "foo": "nope", + }) + + // Call the real with one + real.Configure(config) + + // Call the shadow with another + shadow.Configure(configBad) + + // Verify we have an error + if err := shadow.CloseShadow(); err == nil { + t.Fatal("should have error") + } +}