github.com/ojiry/terraform@v0.8.2-0.20161218223921-e50cec712c4a/terraform/shadow_resource_provisioner.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"sync"
     8  
     9  	"github.com/hashicorp/go-multierror"
    10  	"github.com/hashicorp/terraform/helper/shadow"
    11  )
    12  
    13  // shadowResourceProvisioner implements ResourceProvisioner for the shadow
    14  // eval context defined in eval_context_shadow.go.
    15  //
    16  // This is used to verify behavior with a real provisioner. This shouldn't
    17  // be used directly.
    18  type shadowResourceProvisioner interface {
    19  	ResourceProvisioner
    20  	Shadow
    21  }
    22  
    23  // newShadowResourceProvisioner creates a new shadowed ResourceProvisioner.
    24  func newShadowResourceProvisioner(
    25  	p ResourceProvisioner) (ResourceProvisioner, shadowResourceProvisioner) {
    26  	// Create the shared data
    27  	shared := shadowResourceProvisionerShared{
    28  		Validate: shadow.ComparedValue{
    29  			Func: shadowResourceProvisionerValidateCompare,
    30  		},
    31  	}
    32  
    33  	// Create the real provisioner that does actual work
    34  	real := &shadowResourceProvisionerReal{
    35  		ResourceProvisioner: p,
    36  		Shared:              &shared,
    37  	}
    38  
    39  	// Create the shadow that watches the real value
    40  	shadow := &shadowResourceProvisionerShadow{
    41  		Shared: &shared,
    42  	}
    43  
    44  	return real, shadow
    45  }
    46  
    47  // shadowResourceProvisionerReal is the real resource provisioner. Function calls
    48  // to this will perform real work. This records the parameters and return
    49  // values and call order for the shadow to reproduce.
    50  type shadowResourceProvisionerReal struct {
    51  	ResourceProvisioner
    52  
    53  	Shared *shadowResourceProvisionerShared
    54  }
    55  
    56  func (p *shadowResourceProvisionerReal) Close() error {
    57  	var result error
    58  	if c, ok := p.ResourceProvisioner.(ResourceProvisionerCloser); ok {
    59  		result = c.Close()
    60  	}
    61  
    62  	p.Shared.CloseErr.SetValue(result)
    63  	return result
    64  }
    65  
    66  func (p *shadowResourceProvisionerReal) Validate(c *ResourceConfig) ([]string, []error) {
    67  	warns, errs := p.ResourceProvisioner.Validate(c)
    68  	p.Shared.Validate.SetValue(&shadowResourceProvisionerValidate{
    69  		Config:     c,
    70  		ResultWarn: warns,
    71  		ResultErr:  errs,
    72  	})
    73  
    74  	return warns, errs
    75  }
    76  
    77  func (p *shadowResourceProvisionerReal) Apply(
    78  	output UIOutput, s *InstanceState, c *ResourceConfig) error {
    79  	err := p.ResourceProvisioner.Apply(output, s, c)
    80  
    81  	// Write the result, grab a lock for writing. This should nver
    82  	// block long since the operations below don't block.
    83  	p.Shared.ApplyLock.Lock()
    84  	defer p.Shared.ApplyLock.Unlock()
    85  
    86  	key := s.ID
    87  	raw, ok := p.Shared.Apply.ValueOk(key)
    88  	if !ok {
    89  		// Setup a new value
    90  		raw = &shadow.ComparedValue{
    91  			Func: shadowResourceProvisionerApplyCompare,
    92  		}
    93  
    94  		// Set it
    95  		p.Shared.Apply.SetValue(key, raw)
    96  	}
    97  
    98  	compareVal, ok := raw.(*shadow.ComparedValue)
    99  	if !ok {
   100  		// Just log and return so that we don't cause the real side
   101  		// any side effects.
   102  		log.Printf("[ERROR] unknown value in 'apply': %#v", raw)
   103  		return err
   104  	}
   105  
   106  	// Write the resulting value
   107  	compareVal.SetValue(&shadowResourceProvisionerApply{
   108  		Config:    c,
   109  		ResultErr: err,
   110  	})
   111  
   112  	return err
   113  }
   114  
   115  // shadowResourceProvisionerShadow is the shadow resource provisioner. Function
   116  // calls never affect real resources. This is paired with the "real" side
   117  // which must be called properly to enable recording.
   118  type shadowResourceProvisionerShadow struct {
   119  	Shared *shadowResourceProvisionerShared
   120  
   121  	Error     error // Error is the list of errors from the shadow
   122  	ErrorLock sync.Mutex
   123  }
   124  
   125  type shadowResourceProvisionerShared struct {
   126  	// NOTE: Anytime a value is added here, be sure to add it to
   127  	// the Close() method so that it is closed.
   128  
   129  	CloseErr  shadow.Value
   130  	Validate  shadow.ComparedValue
   131  	Apply     shadow.KeyedValue
   132  	ApplyLock sync.Mutex // For writing only
   133  }
   134  
   135  func (p *shadowResourceProvisionerShared) Close() error {
   136  	closers := []io.Closer{
   137  		&p.CloseErr,
   138  	}
   139  
   140  	for _, c := range closers {
   141  		// This should never happen, but we don't panic because a panic
   142  		// could affect the real behavior of Terraform and a shadow should
   143  		// never be able to do that.
   144  		if err := c.Close(); err != nil {
   145  			return err
   146  		}
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  func (p *shadowResourceProvisionerShadow) CloseShadow() error {
   153  	err := p.Shared.Close()
   154  	if err != nil {
   155  		err = fmt.Errorf("close error: %s", err)
   156  	}
   157  
   158  	return err
   159  }
   160  
   161  func (p *shadowResourceProvisionerShadow) ShadowError() error {
   162  	return p.Error
   163  }
   164  
   165  func (p *shadowResourceProvisionerShadow) Close() error {
   166  	v := p.Shared.CloseErr.Value()
   167  	if v == nil {
   168  		return nil
   169  	}
   170  
   171  	return v.(error)
   172  }
   173  
   174  func (p *shadowResourceProvisionerShadow) Validate(c *ResourceConfig) ([]string, []error) {
   175  	// Get the result of the validate call
   176  	raw := p.Shared.Validate.Value(c)
   177  	if raw == nil {
   178  		return nil, nil
   179  	}
   180  
   181  	result, ok := raw.(*shadowResourceProvisionerValidate)
   182  	if !ok {
   183  		p.ErrorLock.Lock()
   184  		defer p.ErrorLock.Unlock()
   185  		p.Error = multierror.Append(p.Error, fmt.Errorf(
   186  			"Unknown 'validate' shadow value: %#v", raw))
   187  		return nil, nil
   188  	}
   189  
   190  	// We don't need to compare configurations because we key on the
   191  	// configuration so just return right away.
   192  	return result.ResultWarn, result.ResultErr
   193  }
   194  
   195  func (p *shadowResourceProvisionerShadow) Apply(
   196  	output UIOutput, s *InstanceState, c *ResourceConfig) error {
   197  	// Get the value based on the key
   198  	key := s.ID
   199  	raw := p.Shared.Apply.Value(key)
   200  	if raw == nil {
   201  		return nil
   202  	}
   203  
   204  	compareVal, ok := raw.(*shadow.ComparedValue)
   205  	if !ok {
   206  		p.ErrorLock.Lock()
   207  		defer p.ErrorLock.Unlock()
   208  		p.Error = multierror.Append(p.Error, fmt.Errorf(
   209  			"Unknown 'apply' shadow value: %#v", raw))
   210  		return nil
   211  	}
   212  
   213  	// With the compared value, we compare against our config
   214  	raw = compareVal.Value(c)
   215  	if raw == nil {
   216  		return nil
   217  	}
   218  
   219  	result, ok := raw.(*shadowResourceProvisionerApply)
   220  	if !ok {
   221  		p.ErrorLock.Lock()
   222  		defer p.ErrorLock.Unlock()
   223  		p.Error = multierror.Append(p.Error, fmt.Errorf(
   224  			"Unknown 'apply' shadow value: %#v", raw))
   225  		return nil
   226  	}
   227  
   228  	return result.ResultErr
   229  }
   230  
   231  // The structs for the various function calls are put below. These structs
   232  // are used to carry call information across the real/shadow boundaries.
   233  
   234  type shadowResourceProvisionerValidate struct {
   235  	Config     *ResourceConfig
   236  	ResultWarn []string
   237  	ResultErr  []error
   238  }
   239  
   240  type shadowResourceProvisionerApply struct {
   241  	Config    *ResourceConfig
   242  	ResultErr error
   243  }
   244  
   245  func shadowResourceProvisionerValidateCompare(k, v interface{}) bool {
   246  	c, ok := k.(*ResourceConfig)
   247  	if !ok {
   248  		return false
   249  	}
   250  
   251  	result, ok := v.(*shadowResourceProvisionerValidate)
   252  	if !ok {
   253  		return false
   254  	}
   255  
   256  	return c.Equal(result.Config)
   257  }
   258  
   259  func shadowResourceProvisionerApplyCompare(k, v interface{}) bool {
   260  	c, ok := k.(*ResourceConfig)
   261  	if !ok {
   262  		return false
   263  	}
   264  
   265  	result, ok := v.(*shadowResourceProvisionerApply)
   266  	if !ok {
   267  		return false
   268  	}
   269  
   270  	return c.Equal(result.Config)
   271  }