8 "github.com/hashicorp/go-multierror"
9 "github.com/hashicorp/terraform/helper/shadow"
12 // shadowResourceProvider implements ResourceProvider for the shadow
13 // eval context defined in eval_context_shadow.go.
15 // This is used to verify behavior with a real provider. This shouldn't
17 type shadowResourceProvider interface {
22 // newShadowResourceProvider creates a new shadowed ResourceProvider.
24 // This will assume a well behaved real ResourceProvider. For example,
25 // it assumes that the `Resources` call underneath doesn't change values
26 // since once it is called on the real provider, it will be cached and
27 // returned in the shadow since number of calls to that shouldn't affect
30 // However, with calls like Apply, call order is taken into account,
31 // parameters are checked for equality, etc.
32 func newShadowResourceProvider(p ResourceProvider) (ResourceProvider, shadowResourceProvider) {
33 // Create the shared data
34 shared := shadowResourceProviderShared{}
36 // Create the real provider that does actual work
37 real := &shadowResourceProviderReal{
42 // Create the shadow that watches the real value
43 shadow := &shadowResourceProviderShadow{
46 resources: p.Resources(),
47 dataSources: p.DataSources(),
53 // shadowResourceProviderReal is the real resource provider. Function calls
54 // to this will perform real work. This records the parameters and return
55 // values and call order for the shadow to reproduce.
56 type shadowResourceProviderReal struct {
59 Shared *shadowResourceProviderShared
62 func (p *shadowResourceProviderReal) Close() error {
64 if c, ok := p.ResourceProvider.(ResourceProviderCloser); ok {
68 p.Shared.CloseErr.SetValue(result)
72 func (p *shadowResourceProviderReal) Input(
73 input UIInput, c *ResourceConfig) (*ResourceConfig, error) {
76 result, err := p.ResourceProvider.Input(input, c)
77 p.Shared.Input.SetValue(&shadowResourceProviderInput{
79 Result: result.DeepCopy(),
86 func (p *shadowResourceProviderReal) Validate(c *ResourceConfig) ([]string, []error) {
87 warns, errs := p.ResourceProvider.Validate(c)
88 p.Shared.Validate.SetValue(&shadowResourceProviderValidate{
97 func (p *shadowResourceProviderReal) Configure(c *ResourceConfig) error {
100 err := p.ResourceProvider.Configure(c)
101 p.Shared.Configure.SetValue(&shadowResourceProviderConfigure{
109 func (p *shadowResourceProviderReal) Stop() error {
110 return p.ResourceProvider.Stop()
113 func (p *shadowResourceProviderReal) ValidateResource(
114 t string, c *ResourceConfig) ([]string, []error) {
116 configCopy := c.DeepCopy()
119 warns, errs := p.ResourceProvider.ValidateResource(t, c)
121 // Initialize to ensure we always have a wrapper with a lock
122 p.Shared.ValidateResource.Init(
123 key, &shadowResourceProviderValidateResourceWrapper{})
126 raw := p.Shared.ValidateResource.Value(key)
127 wrapper, ok := raw.(*shadowResourceProviderValidateResourceWrapper)
129 // If this fails then we just continue with our day... the shadow
130 // will fail to but there isn't much we can do.
132 "[ERROR] unknown value in ValidateResource shadow value: %#v", raw)
136 // Lock the wrapper for writing and record our call
138 defer wrapper.Unlock()
140 wrapper.Calls = append(wrapper.Calls, &shadowResourceProviderValidateResource{
146 // With it locked, call SetValue again so that it triggers WaitForChange
147 p.Shared.ValidateResource.SetValue(key, wrapper)
153 func (p *shadowResourceProviderReal) Apply(
155 state *InstanceState,
156 diff *InstanceDiff) (*InstanceState, error) {
157 // Thse have to be copied before the call since call can modify
158 stateCopy := state.DeepCopy()
159 diffCopy := diff.DeepCopy()
161 result, err := p.ResourceProvider.Apply(info, state, diff)
162 p.Shared.Apply.SetValue(info.uniqueId(), &shadowResourceProviderApply{
165 Result: result.DeepCopy(),
172 func (p *shadowResourceProviderReal) Diff(
174 state *InstanceState,
175 desired *ResourceConfig) (*InstanceDiff, error) {
176 // Thse have to be copied before the call since call can modify
177 stateCopy := state.DeepCopy()
178 desiredCopy := desired.DeepCopy()
180 result, err := p.ResourceProvider.Diff(info, state, desired)
181 p.Shared.Diff.SetValue(info.uniqueId(), &shadowResourceProviderDiff{
183 Desired: desiredCopy,
184 Result: result.DeepCopy(),
191 func (p *shadowResourceProviderReal) Refresh(
193 state *InstanceState) (*InstanceState, error) {
194 // Thse have to be copied before the call since call can modify
195 stateCopy := state.DeepCopy()
197 result, err := p.ResourceProvider.Refresh(info, state)
198 p.Shared.Refresh.SetValue(info.uniqueId(), &shadowResourceProviderRefresh{
200 Result: result.DeepCopy(),
207 func (p *shadowResourceProviderReal) ValidateDataSource(
208 t string, c *ResourceConfig) ([]string, []error) {
210 configCopy := c.DeepCopy()
213 warns, errs := p.ResourceProvider.ValidateDataSource(t, c)
216 p.Shared.ValidateDataSource.Init(
217 key, &shadowResourceProviderValidateDataSourceWrapper{})
220 raw := p.Shared.ValidateDataSource.Value(key)
221 wrapper, ok := raw.(*shadowResourceProviderValidateDataSourceWrapper)
223 // If this fails then we just continue with our day... the shadow
224 // will fail to but there isn't much we can do.
226 "[ERROR] unknown value in ValidateDataSource shadow value: %#v", raw)
230 // Lock the wrapper for writing and record our call
232 defer wrapper.Unlock()
234 wrapper.Calls = append(wrapper.Calls, &shadowResourceProviderValidateDataSource{
241 p.Shared.ValidateDataSource.SetValue(key, wrapper)
247 func (p *shadowResourceProviderReal) ReadDataDiff(
249 desired *ResourceConfig) (*InstanceDiff, error) {
250 // These have to be copied before the call since call can modify
251 desiredCopy := desired.DeepCopy()
253 result, err := p.ResourceProvider.ReadDataDiff(info, desired)
254 p.Shared.ReadDataDiff.SetValue(info.uniqueId(), &shadowResourceProviderReadDataDiff{
255 Desired: desiredCopy,
256 Result: result.DeepCopy(),
263 func (p *shadowResourceProviderReal) ReadDataApply(
265 diff *InstanceDiff) (*InstanceState, error) {
266 // Thse have to be copied before the call since call can modify
267 diffCopy := diff.DeepCopy()
269 result, err := p.ResourceProvider.ReadDataApply(info, diff)
270 p.Shared.ReadDataApply.SetValue(info.uniqueId(), &shadowResourceProviderReadDataApply{
272 Result: result.DeepCopy(),
279 // shadowResourceProviderShadow is the shadow resource provider. Function
280 // calls never affect real resources. This is paired with the "real" side
281 // which must be called properly to enable recording.
282 type shadowResourceProviderShadow struct {
283 Shared *shadowResourceProviderShared
285 // Cached values that are expected to not change
286 resources []ResourceType
287 dataSources []DataSource
289 Error error // Error is the list of errors from the shadow
293 type shadowResourceProviderShared struct {
294 // NOTE: Anytime a value is added here, be sure to add it to
295 // the Close() method so that it is closed.
297 CloseErr shadow.Value
299 Validate shadow.Value
300 Configure shadow.Value
301 ValidateResource shadow.KeyedValue
302 Apply shadow.KeyedValue
303 Diff shadow.KeyedValue
304 Refresh shadow.KeyedValue
305 ValidateDataSource shadow.KeyedValue
306 ReadDataDiff shadow.KeyedValue
307 ReadDataApply shadow.KeyedValue
310 func (p *shadowResourceProviderShared) Close() error {
311 return shadow.Close(p)
314 func (p *shadowResourceProviderShadow) CloseShadow() error {
315 err := p.Shared.Close()
317 err = fmt.Errorf("close error: %s", err)
323 func (p *shadowResourceProviderShadow) ShadowError() error {
327 func (p *shadowResourceProviderShadow) Resources() []ResourceType {
331 func (p *shadowResourceProviderShadow) DataSources() []DataSource {
335 func (p *shadowResourceProviderShadow) Close() error {
336 v := p.Shared.CloseErr.Value()
344 func (p *shadowResourceProviderShadow) Input(
345 input UIInput, c *ResourceConfig) (*ResourceConfig, error) {
346 // Get the result of the input call
347 raw := p.Shared.Input.Value()
352 result, ok := raw.(*shadowResourceProviderInput)
355 defer p.ErrorLock.Unlock()
356 p.Error = multierror.Append(p.Error, fmt.Errorf(
357 "Unknown 'input' shadow value: %#v", raw))
361 // Compare the parameters, which should be identical
362 if !c.Equal(result.Config) {
364 p.Error = multierror.Append(p.Error, fmt.Errorf(
365 "Input had unequal configurations (real, then shadow):\n\n%#v\n\n%#v",
370 // Return the results
371 return result.Result, result.ResultErr
374 func (p *shadowResourceProviderShadow) Validate(c *ResourceConfig) ([]string, []error) {
375 // Get the result of the validate call
376 raw := p.Shared.Validate.Value()
381 result, ok := raw.(*shadowResourceProviderValidate)
384 defer p.ErrorLock.Unlock()
385 p.Error = multierror.Append(p.Error, fmt.Errorf(
386 "Unknown 'validate' shadow value: %#v", raw))
390 // Compare the parameters, which should be identical
391 if !c.Equal(result.Config) {
393 p.Error = multierror.Append(p.Error, fmt.Errorf(
394 "Validate had unequal configurations (real, then shadow):\n\n%#v\n\n%#v",
399 // Return the results
400 return result.ResultWarn, result.ResultErr
403 func (p *shadowResourceProviderShadow) Configure(c *ResourceConfig) error {
404 // Get the result of the call
405 raw := p.Shared.Configure.Value()
410 result, ok := raw.(*shadowResourceProviderConfigure)
413 defer p.ErrorLock.Unlock()
414 p.Error = multierror.Append(p.Error, fmt.Errorf(
415 "Unknown 'configure' shadow value: %#v", raw))
419 // Compare the parameters, which should be identical
420 if !c.Equal(result.Config) {
422 p.Error = multierror.Append(p.Error, fmt.Errorf(
423 "Configure had unequal configurations (real, then shadow):\n\n%#v\n\n%#v",
428 // Return the results
432 // Stop returns immediately.
433 func (p *shadowResourceProviderShadow) Stop() error {
437 func (p *shadowResourceProviderShadow) ValidateResource(t string, c *ResourceConfig) ([]string, []error) {
441 // Get the initial value
442 raw := p.Shared.ValidateResource.Value(key)
444 // Find a validation with our configuration
445 var result *shadowResourceProviderValidateResource
450 defer p.ErrorLock.Unlock()
451 p.Error = multierror.Append(p.Error, fmt.Errorf(
452 "Unknown 'ValidateResource' call for %q:\n\n%#v",
457 wrapper, ok := raw.(*shadowResourceProviderValidateResourceWrapper)
460 defer p.ErrorLock.Unlock()
461 p.Error = multierror.Append(p.Error, fmt.Errorf(
462 "Unknown 'ValidateResource' shadow value for %q: %#v", key, raw))
466 // Look for the matching call with our configuration
468 for _, call := range wrapper.Calls {
469 if call.Config.Equal(c) {
476 // If we found a result, exit
481 // Wait for a change so we can get the wrapper again
482 raw = p.Shared.ValidateResource.WaitForChange(key)
485 return result.Warns, result.Errors
488 func (p *shadowResourceProviderShadow) Apply(
490 state *InstanceState,
491 diff *InstanceDiff) (*InstanceState, error) {
493 key := info.uniqueId()
494 raw := p.Shared.Apply.Value(key)
497 defer p.ErrorLock.Unlock()
498 p.Error = multierror.Append(p.Error, fmt.Errorf(
499 "Unknown 'apply' call for %q:\n\n%#v\n\n%#v",
504 result, ok := raw.(*shadowResourceProviderApply)
507 defer p.ErrorLock.Unlock()
508 p.Error = multierror.Append(p.Error, fmt.Errorf(
509 "Unknown 'apply' shadow value for %q: %#v", key, raw))
513 // Compare the parameters, which should be identical
514 if !state.Equal(result.State) {
516 p.Error = multierror.Append(p.Error, fmt.Errorf(
517 "Apply %q: state had unequal states (real, then shadow):\n\n%#v\n\n%#v",
518 key, result.State, state))
522 if !diff.Equal(result.Diff) {
524 p.Error = multierror.Append(p.Error, fmt.Errorf(
525 "Apply %q: unequal diffs (real, then shadow):\n\n%#v\n\n%#v",
526 key, result.Diff, diff))
530 return result.Result, result.ResultErr
533 func (p *shadowResourceProviderShadow) Diff(
535 state *InstanceState,
536 desired *ResourceConfig) (*InstanceDiff, error) {
538 key := info.uniqueId()
539 raw := p.Shared.Diff.Value(key)
542 defer p.ErrorLock.Unlock()
543 p.Error = multierror.Append(p.Error, fmt.Errorf(
544 "Unknown 'diff' call for %q:\n\n%#v\n\n%#v",
545 key, state, desired))
549 result, ok := raw.(*shadowResourceProviderDiff)
552 defer p.ErrorLock.Unlock()
553 p.Error = multierror.Append(p.Error, fmt.Errorf(
554 "Unknown 'diff' shadow value for %q: %#v", key, raw))
558 // Compare the parameters, which should be identical
559 if !state.Equal(result.State) {
561 p.Error = multierror.Append(p.Error, fmt.Errorf(
562 "Diff %q had unequal states (real, then shadow):\n\n%#v\n\n%#v",
563 key, result.State, state))
566 if !desired.Equal(result.Desired) {
568 p.Error = multierror.Append(p.Error, fmt.Errorf(
569 "Diff %q had unequal states (real, then shadow):\n\n%#v\n\n%#v",
570 key, result.Desired, desired))
574 return result.Result, result.ResultErr
577 func (p *shadowResourceProviderShadow) Refresh(
579 state *InstanceState) (*InstanceState, error) {
581 key := info.uniqueId()
582 raw := p.Shared.Refresh.Value(key)
585 defer p.ErrorLock.Unlock()
586 p.Error = multierror.Append(p.Error, fmt.Errorf(
587 "Unknown 'refresh' call for %q:\n\n%#v",
592 result, ok := raw.(*shadowResourceProviderRefresh)
595 defer p.ErrorLock.Unlock()
596 p.Error = multierror.Append(p.Error, fmt.Errorf(
597 "Unknown 'refresh' shadow value: %#v", raw))
601 // Compare the parameters, which should be identical
602 if !state.Equal(result.State) {
604 p.Error = multierror.Append(p.Error, fmt.Errorf(
605 "Refresh %q had unequal states (real, then shadow):\n\n%#v\n\n%#v",
606 key, result.State, state))
610 return result.Result, result.ResultErr
613 func (p *shadowResourceProviderShadow) ValidateDataSource(
614 t string, c *ResourceConfig) ([]string, []error) {
618 // Get the initial value
619 raw := p.Shared.ValidateDataSource.Value(key)
621 // Find a validation with our configuration
622 var result *shadowResourceProviderValidateDataSource
627 defer p.ErrorLock.Unlock()
628 p.Error = multierror.Append(p.Error, fmt.Errorf(
629 "Unknown 'ValidateDataSource' call for %q:\n\n%#v",
634 wrapper, ok := raw.(*shadowResourceProviderValidateDataSourceWrapper)
637 defer p.ErrorLock.Unlock()
638 p.Error = multierror.Append(p.Error, fmt.Errorf(
639 "Unknown 'ValidateDataSource' shadow value: %#v", raw))
643 // Look for the matching call with our configuration
645 for _, call := range wrapper.Calls {
646 if call.Config.Equal(c) {
653 // If we found a result, exit
658 // Wait for a change so we can get the wrapper again
659 raw = p.Shared.ValidateDataSource.WaitForChange(key)
662 return result.Warns, result.Errors
665 func (p *shadowResourceProviderShadow) ReadDataDiff(
667 desired *ResourceConfig) (*InstanceDiff, error) {
669 key := info.uniqueId()
670 raw := p.Shared.ReadDataDiff.Value(key)
673 defer p.ErrorLock.Unlock()
674 p.Error = multierror.Append(p.Error, fmt.Errorf(
675 "Unknown 'ReadDataDiff' call for %q:\n\n%#v",
680 result, ok := raw.(*shadowResourceProviderReadDataDiff)
683 defer p.ErrorLock.Unlock()
684 p.Error = multierror.Append(p.Error, fmt.Errorf(
685 "Unknown 'ReadDataDiff' shadow value for %q: %#v", key, raw))
689 // Compare the parameters, which should be identical
690 if !desired.Equal(result.Desired) {
692 p.Error = multierror.Append(p.Error, fmt.Errorf(
693 "ReadDataDiff %q had unequal configs (real, then shadow):\n\n%#v\n\n%#v",
694 key, result.Desired, desired))
698 return result.Result, result.ResultErr
701 func (p *shadowResourceProviderShadow) ReadDataApply(
703 d *InstanceDiff) (*InstanceState, error) {
705 key := info.uniqueId()
706 raw := p.Shared.ReadDataApply.Value(key)
709 defer p.ErrorLock.Unlock()
710 p.Error = multierror.Append(p.Error, fmt.Errorf(
711 "Unknown 'ReadDataApply' call for %q:\n\n%#v",
716 result, ok := raw.(*shadowResourceProviderReadDataApply)
719 defer p.ErrorLock.Unlock()
720 p.Error = multierror.Append(p.Error, fmt.Errorf(
721 "Unknown 'ReadDataApply' shadow value for %q: %#v", key, raw))
725 // Compare the parameters, which should be identical
726 if !d.Equal(result.Diff) {
728 p.Error = multierror.Append(p.Error, fmt.Errorf(
729 "ReadDataApply: unequal diffs (real, then shadow):\n\n%#v\n\n%#v",
734 return result.Result, result.ResultErr
737 func (p *shadowResourceProviderShadow) ImportState(info *InstanceInfo, id string) ([]*InstanceState, error) {
738 panic("import not supported by shadow graph")
741 // The structs for the various function calls are put below. These structs
742 // are used to carry call information across the real/shadow boundaries.
744 type shadowResourceProviderInput struct {
745 Config *ResourceConfig
746 Result *ResourceConfig
750 type shadowResourceProviderValidate struct {
751 Config *ResourceConfig
756 type shadowResourceProviderConfigure struct {
757 Config *ResourceConfig
761 type shadowResourceProviderValidateResourceWrapper struct {
764 Calls []*shadowResourceProviderValidateResource
767 type shadowResourceProviderValidateResource struct {
768 Config *ResourceConfig
773 type shadowResourceProviderApply struct {
776 Result *InstanceState
780 type shadowResourceProviderDiff struct {
782 Desired *ResourceConfig
787 type shadowResourceProviderRefresh struct {
789 Result *InstanceState
793 type shadowResourceProviderValidateDataSourceWrapper struct {
796 Calls []*shadowResourceProviderValidateDataSource
799 type shadowResourceProviderValidateDataSource struct {
800 Config *ResourceConfig
805 type shadowResourceProviderReadDataDiff struct {
806 Desired *ResourceConfig
811 type shadowResourceProviderReadDataApply struct {
813 Result *InstanceState