mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 01:41:48 -06:00
c6fd938fb8
This doesn't cause any practical issues as far as I'm aware (couldn't get any test to fail), but caused shadow errors since it wasn't matching the prior behavior.
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
|
|
}
|