terraform: ResourceProvider.Diff shadow

This commit is contained in:
Mitchell Hashimoto 2016-09-30 19:04:58 -07:00
parent cbbd492bce
commit 82a1158f55
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
2 changed files with 121 additions and 7 deletions

View File

@ -123,6 +123,21 @@ func (p *shadowResourceProviderReal) Apply(
return result, err return result, err
} }
func (p *shadowResourceProviderReal) Diff(
info *InstanceInfo,
state *InstanceState,
desired *ResourceConfig) (*InstanceDiff, error) {
result, err := p.ResourceProvider.Diff(info, state, desired)
p.Shared.Diff.SetValue(info.HumanId(), &shadowResourceProviderDiff{
State: state,
Desired: desired,
Result: result,
ResultErr: err,
})
return result, err
}
// shadowResourceProviderShadow is the shadow resource provider. Function // shadowResourceProviderShadow is the shadow resource provider. Function
// calls never affect real resources. This is paired with the "real" side // calls never affect real resources. This is paired with the "real" side
// which must be called properly to enable recording. // which must be called properly to enable recording.
@ -143,6 +158,7 @@ type shadowResourceProviderShared struct {
Validate shadow.Value Validate shadow.Value
Configure shadow.Value Configure shadow.Value
Apply shadow.KeyedValue Apply shadow.KeyedValue
Diff shadow.KeyedValue
} }
func (p *shadowResourceProviderShadow) CloseShadow() error { func (p *shadowResourceProviderShadow) CloseShadow() error {
@ -296,6 +312,50 @@ func (p *shadowResourceProviderShadow) Apply(
return result.Result, result.ResultErr return result.Result, result.ResultErr
} }
func (p *shadowResourceProviderShadow) Diff(
info *InstanceInfo,
state *InstanceState,
desired *ResourceConfig) (*InstanceDiff, error) {
// Unique key
key := info.HumanId()
raw := p.Shared.Diff.Value(key)
if raw == nil {
p.ErrorLock.Lock()
defer p.ErrorLock.Unlock()
p.Error = multierror.Append(p.Error, fmt.Errorf(
"Unknown 'diff' call for %q:\n\n%#v\n\n%#v",
key, state, desired))
return nil, nil
}
result, ok := raw.(*shadowResourceProviderDiff)
if !ok {
p.ErrorLock.Lock()
defer p.ErrorLock.Unlock()
p.Error = multierror.Append(p.Error, fmt.Errorf(
"Unknown 'diff' shadow value: %#v", raw))
return nil, nil
}
// Compare the parameters, which should be identical
if !state.Equal(result.State) {
p.ErrorLock.Lock()
p.Error = multierror.Append(p.Error, fmt.Errorf(
"Diff %q had unequal states (real, then shadow):\n\n%#v\n\n%#v",
key, result.State, state))
p.ErrorLock.Unlock()
}
if !desired.Equal(result.Desired) {
p.ErrorLock.Lock()
p.Error = multierror.Append(p.Error, fmt.Errorf(
"Diff %q had unequal states (real, then shadow):\n\n%#v\n\n%#v",
key, result.Desired, desired))
p.ErrorLock.Unlock()
}
return result.Result, result.ResultErr
}
// TODO // TODO
// TODO // TODO
// TODO // TODO
@ -306,13 +366,6 @@ func (p *shadowResourceProviderShadow) ValidateResource(t string, c *ResourceCon
return nil, nil return nil, nil
} }
func (p *shadowResourceProviderShadow) Diff(
info *InstanceInfo,
state *InstanceState,
desired *ResourceConfig) (*InstanceDiff, error) {
return nil, nil
}
func (p *shadowResourceProviderShadow) Refresh( func (p *shadowResourceProviderShadow) Refresh(
info *InstanceInfo, info *InstanceInfo,
s *InstanceState) (*InstanceState, error) { s *InstanceState) (*InstanceState, error) {
@ -365,3 +418,10 @@ type shadowResourceProviderApply struct {
Result *InstanceState Result *InstanceState
ResultErr error ResultErr error
} }
type shadowResourceProviderDiff struct {
State *InstanceState
Desired *ResourceConfig
Result *InstanceDiff
ResultErr error
}

View File

@ -319,3 +319,57 @@ func TestShadowResourceProviderApply(t *testing.T) {
t.Fatalf("bad: %s", err) t.Fatalf("bad: %s", err)
} }
} }
func TestShadowResourceProviderDiff(t *testing.T) {
mock := new(MockResourceProvider)
real, shadow := newShadowResourceProvider(mock)
// Test values
info := &InstanceInfo{Id: "foo"}
state := &InstanceState{ID: "foo"}
desired := testResourceConfig(t, map[string]interface{}{"foo": "bar"})
mockResult := &InstanceDiff{Destroy: true}
// Configure the mock
mock.DiffReturn = mockResult
// Verify that it blocks until the real func is called
var result *InstanceDiff
var err error
doneCh := make(chan struct{})
go func() {
defer close(doneCh)
result, err = shadow.Diff(info, state, desired)
}()
select {
case <-doneCh:
t.Fatal("should block until finished")
case <-time.After(10 * time.Millisecond):
}
// Call the real func
realResult, realErr := real.Diff(info, state, desired)
if !reflect.DeepEqual(realResult, mockResult) {
t.Fatalf("bad: %#v", realResult)
}
if realErr != nil {
t.Fatalf("bad: %#v", realErr)
}
// The shadow should finish now
<-doneCh
// Verify the shadow returned the same values
if !reflect.DeepEqual(result, mockResult) {
t.Fatalf("bad: %#v", result)
}
if err != nil {
t.Fatalf("bad: %#v", err)
}
// Verify we have no errors
if err := shadow.CloseShadow(); err != nil {
t.Fatalf("bad: %s", err)
}
}