816 lines
21 KiB
Go
816 lines
21 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
"github.com/hashicorp/terraform/helper/shadow"
|
|
)
|
|
|
|
// shadowResourceProvider implements ResourceProvider for the shadow
|
|
// eval context defined in eval_context_shadow.go.
|
|
//
|
|
// This is used to verify behavior with a real provider. This shouldn't
|
|
// be used directly.
|
|
type shadowResourceProvider interface {
|
|
ResourceProvider
|
|
Shadow
|
|
}
|
|
|
|
// newShadowResourceProvider creates a new shadowed ResourceProvider.
|
|
//
|
|
// This will assume a well behaved real ResourceProvider. For example,
|
|
// it assumes that the `Resources` call underneath doesn't change values
|
|
// since once it is called on the real provider, it will be cached and
|
|
// returned in the shadow since number of calls to that shouldn't affect
|
|
// actual behavior.
|
|
//
|
|
// However, with calls like Apply, call order is taken into account,
|
|
// parameters are checked for equality, etc.
|
|
func newShadowResourceProvider(p ResourceProvider) (ResourceProvider, shadowResourceProvider) {
|
|
// Create the shared data
|
|
shared := shadowResourceProviderShared{}
|
|
|
|
// Create the real provider that does actual work
|
|
real := &shadowResourceProviderReal{
|
|
ResourceProvider: p,
|
|
Shared: &shared,
|
|
}
|
|
|
|
// Create the shadow that watches the real value
|
|
shadow := &shadowResourceProviderShadow{
|
|
Shared: &shared,
|
|
|
|
resources: p.Resources(),
|
|
dataSources: p.DataSources(),
|
|
}
|
|
|
|
return real, shadow
|
|
}
|
|
|
|
// shadowResourceProviderReal is the real resource provider. Function calls
|
|
// to this will perform real work. This records the parameters and return
|
|
// values and call order for the shadow to reproduce.
|
|
type shadowResourceProviderReal struct {
|
|
ResourceProvider
|
|
|
|
Shared *shadowResourceProviderShared
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) Close() error {
|
|
var result error
|
|
if c, ok := p.ResourceProvider.(ResourceProviderCloser); ok {
|
|
result = c.Close()
|
|
}
|
|
|
|
p.Shared.CloseErr.SetValue(result)
|
|
return result
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) Input(
|
|
input UIInput, c *ResourceConfig) (*ResourceConfig, error) {
|
|
cCopy := c.DeepCopy()
|
|
|
|
result, err := p.ResourceProvider.Input(input, c)
|
|
p.Shared.Input.SetValue(&shadowResourceProviderInput{
|
|
Config: cCopy,
|
|
Result: result.DeepCopy(),
|
|
ResultErr: err,
|
|
})
|
|
|
|
return result, err
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) Validate(c *ResourceConfig) ([]string, []error) {
|
|
warns, errs := p.ResourceProvider.Validate(c)
|
|
p.Shared.Validate.SetValue(&shadowResourceProviderValidate{
|
|
Config: c.DeepCopy(),
|
|
ResultWarn: warns,
|
|
ResultErr: errs,
|
|
})
|
|
|
|
return warns, errs
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) Configure(c *ResourceConfig) error {
|
|
cCopy := c.DeepCopy()
|
|
|
|
err := p.ResourceProvider.Configure(c)
|
|
p.Shared.Configure.SetValue(&shadowResourceProviderConfigure{
|
|
Config: cCopy,
|
|
Result: err,
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) Stop() error {
|
|
return p.ResourceProvider.Stop()
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) ValidateResource(
|
|
t string, c *ResourceConfig) ([]string, []error) {
|
|
key := t
|
|
configCopy := c.DeepCopy()
|
|
|
|
// Real operation
|
|
warns, errs := p.ResourceProvider.ValidateResource(t, c)
|
|
|
|
// Initialize to ensure we always have a wrapper with a lock
|
|
p.Shared.ValidateResource.Init(
|
|
key, &shadowResourceProviderValidateResourceWrapper{})
|
|
|
|
// Get the result
|
|
raw := p.Shared.ValidateResource.Value(key)
|
|
wrapper, ok := raw.(*shadowResourceProviderValidateResourceWrapper)
|
|
if !ok {
|
|
// If this fails then we just continue with our day... the shadow
|
|
// will fail to but there isn't much we can do.
|
|
log.Printf(
|
|
"[ERROR] unknown value in ValidateResource shadow value: %#v", raw)
|
|
return warns, errs
|
|
}
|
|
|
|
// Lock the wrapper for writing and record our call
|
|
wrapper.Lock()
|
|
defer wrapper.Unlock()
|
|
|
|
wrapper.Calls = append(wrapper.Calls, &shadowResourceProviderValidateResource{
|
|
Config: configCopy,
|
|
Warns: warns,
|
|
Errors: errs,
|
|
})
|
|
|
|
// With it locked, call SetValue again so that it triggers WaitForChange
|
|
p.Shared.ValidateResource.SetValue(key, wrapper)
|
|
|
|
// Return the result
|
|
return warns, errs
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) Apply(
|
|
info *InstanceInfo,
|
|
state *InstanceState,
|
|
diff *InstanceDiff) (*InstanceState, error) {
|
|
// Thse have to be copied before the call since call can modify
|
|
stateCopy := state.DeepCopy()
|
|
diffCopy := diff.DeepCopy()
|
|
|
|
result, err := p.ResourceProvider.Apply(info, state, diff)
|
|
p.Shared.Apply.SetValue(info.uniqueId(), &shadowResourceProviderApply{
|
|
State: stateCopy,
|
|
Diff: diffCopy,
|
|
Result: result.DeepCopy(),
|
|
ResultErr: err,
|
|
})
|
|
|
|
return result, err
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) Diff(
|
|
info *InstanceInfo,
|
|
state *InstanceState,
|
|
desired *ResourceConfig) (*InstanceDiff, error) {
|
|
// Thse have to be copied before the call since call can modify
|
|
stateCopy := state.DeepCopy()
|
|
desiredCopy := desired.DeepCopy()
|
|
|
|
result, err := p.ResourceProvider.Diff(info, state, desired)
|
|
p.Shared.Diff.SetValue(info.uniqueId(), &shadowResourceProviderDiff{
|
|
State: stateCopy,
|
|
Desired: desiredCopy,
|
|
Result: result.DeepCopy(),
|
|
ResultErr: err,
|
|
})
|
|
|
|
return result, err
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) Refresh(
|
|
info *InstanceInfo,
|
|
state *InstanceState) (*InstanceState, error) {
|
|
// Thse have to be copied before the call since call can modify
|
|
stateCopy := state.DeepCopy()
|
|
|
|
result, err := p.ResourceProvider.Refresh(info, state)
|
|
p.Shared.Refresh.SetValue(info.uniqueId(), &shadowResourceProviderRefresh{
|
|
State: stateCopy,
|
|
Result: result.DeepCopy(),
|
|
ResultErr: err,
|
|
})
|
|
|
|
return result, err
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) ValidateDataSource(
|
|
t string, c *ResourceConfig) ([]string, []error) {
|
|
key := t
|
|
configCopy := c.DeepCopy()
|
|
|
|
// Real operation
|
|
warns, errs := p.ResourceProvider.ValidateDataSource(t, c)
|
|
|
|
// Initialize
|
|
p.Shared.ValidateDataSource.Init(
|
|
key, &shadowResourceProviderValidateDataSourceWrapper{})
|
|
|
|
// Get the result
|
|
raw := p.Shared.ValidateDataSource.Value(key)
|
|
wrapper, ok := raw.(*shadowResourceProviderValidateDataSourceWrapper)
|
|
if !ok {
|
|
// If this fails then we just continue with our day... the shadow
|
|
// will fail to but there isn't much we can do.
|
|
log.Printf(
|
|
"[ERROR] unknown value in ValidateDataSource shadow value: %#v", raw)
|
|
return warns, errs
|
|
}
|
|
|
|
// Lock the wrapper for writing and record our call
|
|
wrapper.Lock()
|
|
defer wrapper.Unlock()
|
|
|
|
wrapper.Calls = append(wrapper.Calls, &shadowResourceProviderValidateDataSource{
|
|
Config: configCopy,
|
|
Warns: warns,
|
|
Errors: errs,
|
|
})
|
|
|
|
// Set it
|
|
p.Shared.ValidateDataSource.SetValue(key, wrapper)
|
|
|
|
// Return the result
|
|
return warns, errs
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) ReadDataDiff(
|
|
info *InstanceInfo,
|
|
desired *ResourceConfig) (*InstanceDiff, error) {
|
|
// These have to be copied before the call since call can modify
|
|
desiredCopy := desired.DeepCopy()
|
|
|
|
result, err := p.ResourceProvider.ReadDataDiff(info, desired)
|
|
p.Shared.ReadDataDiff.SetValue(info.uniqueId(), &shadowResourceProviderReadDataDiff{
|
|
Desired: desiredCopy,
|
|
Result: result.DeepCopy(),
|
|
ResultErr: err,
|
|
})
|
|
|
|
return result, err
|
|
}
|
|
|
|
func (p *shadowResourceProviderReal) ReadDataApply(
|
|
info *InstanceInfo,
|
|
diff *InstanceDiff) (*InstanceState, error) {
|
|
// Thse have to be copied before the call since call can modify
|
|
diffCopy := diff.DeepCopy()
|
|
|
|
result, err := p.ResourceProvider.ReadDataApply(info, diff)
|
|
p.Shared.ReadDataApply.SetValue(info.uniqueId(), &shadowResourceProviderReadDataApply{
|
|
Diff: diffCopy,
|
|
Result: result.DeepCopy(),
|
|
ResultErr: err,
|
|
})
|
|
|
|
return result, 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.
|
|
type shadowResourceProviderShadow struct {
|
|
Shared *shadowResourceProviderShared
|
|
|
|
// Cached values that are expected to not change
|
|
resources []ResourceType
|
|
dataSources []DataSource
|
|
|
|
Error error // Error is the list of errors from the shadow
|
|
ErrorLock sync.Mutex
|
|
}
|
|
|
|
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
|
|
Configure shadow.Value
|
|
ValidateResource shadow.KeyedValue
|
|
Apply shadow.KeyedValue
|
|
Diff shadow.KeyedValue
|
|
Refresh shadow.KeyedValue
|
|
ValidateDataSource shadow.KeyedValue
|
|
ReadDataDiff shadow.KeyedValue
|
|
ReadDataApply shadow.KeyedValue
|
|
}
|
|
|
|
func (p *shadowResourceProviderShared) Close() error {
|
|
return shadow.Close(p)
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) CloseShadow() error {
|
|
err := p.Shared.Close()
|
|
if err != nil {
|
|
err = fmt.Errorf("close error: %s", err)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) ShadowError() error {
|
|
return p.Error
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) Resources() []ResourceType {
|
|
return p.resources
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) DataSources() []DataSource {
|
|
return p.dataSources
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) Close() error {
|
|
v := p.Shared.CloseErr.Value()
|
|
if v == nil {
|
|
return nil
|
|
}
|
|
|
|
return v.(error)
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) Input(
|
|
input UIInput, c *ResourceConfig) (*ResourceConfig, error) {
|
|
// Get the result of the input call
|
|
raw := p.Shared.Input.Value()
|
|
if raw == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
result, ok := raw.(*shadowResourceProviderInput)
|
|
if !ok {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'input' shadow value: %#v", raw))
|
|
return nil, nil
|
|
}
|
|
|
|
// Compare the parameters, which should be identical
|
|
if !c.Equal(result.Config) {
|
|
p.ErrorLock.Lock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Input had unequal configurations (real, then shadow):\n\n%#v\n\n%#v",
|
|
result.Config, c))
|
|
p.ErrorLock.Unlock()
|
|
}
|
|
|
|
// Return the results
|
|
return result.Result, result.ResultErr
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) Validate(c *ResourceConfig) ([]string, []error) {
|
|
// Get the result of the validate call
|
|
raw := p.Shared.Validate.Value()
|
|
if raw == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
result, ok := raw.(*shadowResourceProviderValidate)
|
|
if !ok {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'validate' shadow value: %#v", raw))
|
|
return nil, nil
|
|
}
|
|
|
|
// Compare the parameters, which should be identical
|
|
if !c.Equal(result.Config) {
|
|
p.ErrorLock.Lock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Validate had unequal configurations (real, then shadow):\n\n%#v\n\n%#v",
|
|
result.Config, c))
|
|
p.ErrorLock.Unlock()
|
|
}
|
|
|
|
// Return the results
|
|
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
|
|
}
|
|
|
|
// Stop returns immediately.
|
|
func (p *shadowResourceProviderShadow) Stop() error {
|
|
return nil
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) ValidateResource(t string, c *ResourceConfig) ([]string, []error) {
|
|
// Unique key
|
|
key := t
|
|
|
|
// Get the initial value
|
|
raw := p.Shared.ValidateResource.Value(key)
|
|
|
|
// Find a validation with our configuration
|
|
var result *shadowResourceProviderValidateResource
|
|
for {
|
|
// Get the value
|
|
if raw == nil {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'ValidateResource' call for %q:\n\n%#v",
|
|
key, c))
|
|
return nil, nil
|
|
}
|
|
|
|
wrapper, ok := raw.(*shadowResourceProviderValidateResourceWrapper)
|
|
if !ok {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'ValidateResource' shadow value for %q: %#v", key, raw))
|
|
return nil, nil
|
|
}
|
|
|
|
// Look for the matching call with our configuration
|
|
wrapper.RLock()
|
|
for _, call := range wrapper.Calls {
|
|
if call.Config.Equal(c) {
|
|
result = call
|
|
break
|
|
}
|
|
}
|
|
wrapper.RUnlock()
|
|
|
|
// If we found a result, exit
|
|
if result != nil {
|
|
break
|
|
}
|
|
|
|
// Wait for a change so we can get the wrapper again
|
|
raw = p.Shared.ValidateResource.WaitForChange(key)
|
|
}
|
|
|
|
return result.Warns, result.Errors
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) Apply(
|
|
info *InstanceInfo,
|
|
state *InstanceState,
|
|
diff *InstanceDiff) (*InstanceState, error) {
|
|
// Unique key
|
|
key := info.uniqueId()
|
|
raw := p.Shared.Apply.Value(key)
|
|
if raw == nil {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'apply' call for %q:\n\n%#v\n\n%#v",
|
|
key, state, diff))
|
|
return nil, nil
|
|
}
|
|
|
|
result, ok := raw.(*shadowResourceProviderApply)
|
|
if !ok {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'apply' shadow value for %q: %#v", key, 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(
|
|
"Apply %q: state had unequal states (real, then shadow):\n\n%#v\n\n%#v",
|
|
key, result.State, state))
|
|
p.ErrorLock.Unlock()
|
|
}
|
|
|
|
if !diff.Equal(result.Diff) {
|
|
p.ErrorLock.Lock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Apply %q: unequal diffs (real, then shadow):\n\n%#v\n\n%#v",
|
|
key, result.Diff, diff))
|
|
p.ErrorLock.Unlock()
|
|
}
|
|
|
|
return result.Result, result.ResultErr
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) Diff(
|
|
info *InstanceInfo,
|
|
state *InstanceState,
|
|
desired *ResourceConfig) (*InstanceDiff, error) {
|
|
// Unique key
|
|
key := info.uniqueId()
|
|
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 for %q: %#v", key, 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
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) Refresh(
|
|
info *InstanceInfo,
|
|
state *InstanceState) (*InstanceState, error) {
|
|
// Unique key
|
|
key := info.uniqueId()
|
|
raw := p.Shared.Refresh.Value(key)
|
|
if raw == nil {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'refresh' call for %q:\n\n%#v",
|
|
key, state))
|
|
return nil, nil
|
|
}
|
|
|
|
result, ok := raw.(*shadowResourceProviderRefresh)
|
|
if !ok {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'refresh' 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(
|
|
"Refresh %q had unequal states (real, then shadow):\n\n%#v\n\n%#v",
|
|
key, result.State, state))
|
|
p.ErrorLock.Unlock()
|
|
}
|
|
|
|
return result.Result, result.ResultErr
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) ValidateDataSource(
|
|
t string, c *ResourceConfig) ([]string, []error) {
|
|
// Unique key
|
|
key := t
|
|
|
|
// Get the initial value
|
|
raw := p.Shared.ValidateDataSource.Value(key)
|
|
|
|
// Find a validation with our configuration
|
|
var result *shadowResourceProviderValidateDataSource
|
|
for {
|
|
// Get the value
|
|
if raw == nil {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'ValidateDataSource' call for %q:\n\n%#v",
|
|
key, c))
|
|
return nil, nil
|
|
}
|
|
|
|
wrapper, ok := raw.(*shadowResourceProviderValidateDataSourceWrapper)
|
|
if !ok {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'ValidateDataSource' shadow value: %#v", raw))
|
|
return nil, nil
|
|
}
|
|
|
|
// Look for the matching call with our configuration
|
|
wrapper.RLock()
|
|
for _, call := range wrapper.Calls {
|
|
if call.Config.Equal(c) {
|
|
result = call
|
|
break
|
|
}
|
|
}
|
|
wrapper.RUnlock()
|
|
|
|
// If we found a result, exit
|
|
if result != nil {
|
|
break
|
|
}
|
|
|
|
// Wait for a change so we can get the wrapper again
|
|
raw = p.Shared.ValidateDataSource.WaitForChange(key)
|
|
}
|
|
|
|
return result.Warns, result.Errors
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) ReadDataDiff(
|
|
info *InstanceInfo,
|
|
desired *ResourceConfig) (*InstanceDiff, error) {
|
|
// Unique key
|
|
key := info.uniqueId()
|
|
raw := p.Shared.ReadDataDiff.Value(key)
|
|
if raw == nil {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'ReadDataDiff' call for %q:\n\n%#v",
|
|
key, desired))
|
|
return nil, nil
|
|
}
|
|
|
|
result, ok := raw.(*shadowResourceProviderReadDataDiff)
|
|
if !ok {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'ReadDataDiff' shadow value for %q: %#v", key, raw))
|
|
return nil, nil
|
|
}
|
|
|
|
// Compare the parameters, which should be identical
|
|
if !desired.Equal(result.Desired) {
|
|
p.ErrorLock.Lock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"ReadDataDiff %q had unequal configs (real, then shadow):\n\n%#v\n\n%#v",
|
|
key, result.Desired, desired))
|
|
p.ErrorLock.Unlock()
|
|
}
|
|
|
|
return result.Result, result.ResultErr
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) ReadDataApply(
|
|
info *InstanceInfo,
|
|
d *InstanceDiff) (*InstanceState, error) {
|
|
// Unique key
|
|
key := info.uniqueId()
|
|
raw := p.Shared.ReadDataApply.Value(key)
|
|
if raw == nil {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'ReadDataApply' call for %q:\n\n%#v",
|
|
key, d))
|
|
return nil, nil
|
|
}
|
|
|
|
result, ok := raw.(*shadowResourceProviderReadDataApply)
|
|
if !ok {
|
|
p.ErrorLock.Lock()
|
|
defer p.ErrorLock.Unlock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"Unknown 'ReadDataApply' shadow value for %q: %#v", key, raw))
|
|
return nil, nil
|
|
}
|
|
|
|
// Compare the parameters, which should be identical
|
|
if !d.Equal(result.Diff) {
|
|
p.ErrorLock.Lock()
|
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
|
"ReadDataApply: unequal diffs (real, then shadow):\n\n%#v\n\n%#v",
|
|
result.Diff, d))
|
|
p.ErrorLock.Unlock()
|
|
}
|
|
|
|
return result.Result, result.ResultErr
|
|
}
|
|
|
|
func (p *shadowResourceProviderShadow) ImportState(info *InstanceInfo, id string) ([]*InstanceState, error) {
|
|
panic("import not supported by shadow graph")
|
|
}
|
|
|
|
// The structs for the various function calls are put below. These structs
|
|
// are used to carry call information across the real/shadow boundaries.
|
|
|
|
type shadowResourceProviderInput struct {
|
|
Config *ResourceConfig
|
|
Result *ResourceConfig
|
|
ResultErr error
|
|
}
|
|
|
|
type shadowResourceProviderValidate struct {
|
|
Config *ResourceConfig
|
|
ResultWarn []string
|
|
ResultErr []error
|
|
}
|
|
|
|
type shadowResourceProviderConfigure struct {
|
|
Config *ResourceConfig
|
|
Result error
|
|
}
|
|
|
|
type shadowResourceProviderValidateResourceWrapper struct {
|
|
sync.RWMutex
|
|
|
|
Calls []*shadowResourceProviderValidateResource
|
|
}
|
|
|
|
type shadowResourceProviderValidateResource struct {
|
|
Config *ResourceConfig
|
|
Warns []string
|
|
Errors []error
|
|
}
|
|
|
|
type shadowResourceProviderApply struct {
|
|
State *InstanceState
|
|
Diff *InstanceDiff
|
|
Result *InstanceState
|
|
ResultErr error
|
|
}
|
|
|
|
type shadowResourceProviderDiff struct {
|
|
State *InstanceState
|
|
Desired *ResourceConfig
|
|
Result *InstanceDiff
|
|
ResultErr error
|
|
}
|
|
|
|
type shadowResourceProviderRefresh struct {
|
|
State *InstanceState
|
|
Result *InstanceState
|
|
ResultErr error
|
|
}
|
|
|
|
type shadowResourceProviderValidateDataSourceWrapper struct {
|
|
sync.RWMutex
|
|
|
|
Calls []*shadowResourceProviderValidateDataSource
|
|
}
|
|
|
|
type shadowResourceProviderValidateDataSource struct {
|
|
Config *ResourceConfig
|
|
Warns []string
|
|
Errors []error
|
|
}
|
|
|
|
type shadowResourceProviderReadDataDiff struct {
|
|
Desired *ResourceConfig
|
|
Result *InstanceDiff
|
|
ResultErr error
|
|
}
|
|
|
|
type shadowResourceProviderReadDataApply struct {
|
|
Diff *InstanceDiff
|
|
Result *InstanceState
|
|
ResultErr error
|
|
}
|