github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/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  func (p *shadowResourceProvisionerReal) Stop() error {
   116  	return p.ResourceProvisioner.Stop()
   117  }
   118  
   119  // shadowResourceProvisionerShadow is the shadow resource provisioner. Function
   120  // calls never affect real resources. This is paired with the "real" side
   121  // which must be called properly to enable recording.
   122  type shadowResourceProvisionerShadow struct {
   123  	Shared *shadowResourceProvisionerShared
   124  
   125  	Error     error // Error is the list of errors from the shadow
   126  	ErrorLock sync.Mutex
   127  }
   128  
   129  type shadowResourceProvisionerShared struct {
   130  	// NOTE: Anytime a value is added here, be sure to add it to
   131  	// the Close() method so that it is closed.
   132  
   133  	CloseErr  shadow.Value
   134  	Validate  shadow.ComparedValue
   135  	Apply     shadow.KeyedValue
   136  	ApplyLock sync.Mutex // For writing only
   137  }
   138  
   139  func (p *shadowResourceProvisionerShared) Close() error {
   140  	closers := []io.Closer{
   141  		&p.CloseErr,
   142  	}
   143  
   144  	for _, c := range closers {
   145  		// This should never happen, but we don't panic because a panic
   146  		// could affect the real behavior of Terraform and a shadow should
   147  		// never be able to do that.
   148  		if err := c.Close(); err != nil {
   149  			return err
   150  		}
   151  	}
   152  
   153  	return nil
   154  }
   155  
   156  func (p *shadowResourceProvisionerShadow) CloseShadow() error {
   157  	err := p.Shared.Close()
   158  	if err != nil {
   159  		err = fmt.Errorf("close error: %s", err)
   160  	}
   161  
   162  	return err
   163  }
   164  
   165  func (p *shadowResourceProvisionerShadow) ShadowError() error {
   166  	return p.Error
   167  }
   168  
   169  func (p *shadowResourceProvisionerShadow) Close() error {
   170  	v := p.Shared.CloseErr.Value()
   171  	if v == nil {
   172  		return nil
   173  	}
   174  
   175  	return v.(error)
   176  }
   177  
   178  func (p *shadowResourceProvisionerShadow) Validate(c *ResourceConfig) ([]string, []error) {
   179  	// Get the result of the validate call
   180  	raw := p.Shared.Validate.Value(c)
   181  	if raw == nil {
   182  		return nil, nil
   183  	}
   184  
   185  	result, ok := raw.(*shadowResourceProvisionerValidate)
   186  	if !ok {
   187  		p.ErrorLock.Lock()
   188  		defer p.ErrorLock.Unlock()
   189  		p.Error = multierror.Append(p.Error, fmt.Errorf(
   190  			"Unknown 'validate' shadow value: %#v", raw))
   191  		return nil, nil
   192  	}
   193  
   194  	// We don't need to compare configurations because we key on the
   195  	// configuration so just return right away.
   196  	return result.ResultWarn, result.ResultErr
   197  }
   198  
   199  func (p *shadowResourceProvisionerShadow) Apply(
   200  	output UIOutput, s *InstanceState, c *ResourceConfig) error {
   201  	// Get the value based on the key
   202  	key := s.ID
   203  	raw := p.Shared.Apply.Value(key)
   204  	if raw == nil {
   205  		return nil
   206  	}
   207  
   208  	compareVal, ok := raw.(*shadow.ComparedValue)
   209  	if !ok {
   210  		p.ErrorLock.Lock()
   211  		defer p.ErrorLock.Unlock()
   212  		p.Error = multierror.Append(p.Error, fmt.Errorf(
   213  			"Unknown 'apply' shadow value: %#v", raw))
   214  		return nil
   215  	}
   216  
   217  	// With the compared value, we compare against our config
   218  	raw = compareVal.Value(c)
   219  	if raw == nil {
   220  		return nil
   221  	}
   222  
   223  	result, ok := raw.(*shadowResourceProvisionerApply)
   224  	if !ok {
   225  		p.ErrorLock.Lock()
   226  		defer p.ErrorLock.Unlock()
   227  		p.Error = multierror.Append(p.Error, fmt.Errorf(
   228  			"Unknown 'apply' shadow value: %#v", raw))
   229  		return nil
   230  	}
   231  
   232  	return result.ResultErr
   233  }
   234  
   235  func (p *shadowResourceProvisionerShadow) Stop() error {
   236  	// For the shadow, we always just return nil since a Stop indicates
   237  	// that we were interrupted and shadows are disabled during interrupts
   238  	// anyways.
   239  	return nil
   240  }
   241  
   242  // The structs for the various function calls are put below. These structs
   243  // are used to carry call information across the real/shadow boundaries.
   244  
   245  type shadowResourceProvisionerValidate struct {
   246  	Config     *ResourceConfig
   247  	ResultWarn []string
   248  	ResultErr  []error
   249  }
   250  
   251  type shadowResourceProvisionerApply struct {
   252  	Config    *ResourceConfig
   253  	ResultErr error
   254  }
   255  
   256  func shadowResourceProvisionerValidateCompare(k, v interface{}) bool {
   257  	c, ok := k.(*ResourceConfig)
   258  	if !ok {
   259  		return false
   260  	}
   261  
   262  	result, ok := v.(*shadowResourceProvisionerValidate)
   263  	if !ok {
   264  		return false
   265  	}
   266  
   267  	return c.Equal(result.Config)
   268  }
   269  
   270  func shadowResourceProvisionerApplyCompare(k, v interface{}) bool {
   271  	c, ok := k.(*ResourceConfig)
   272  	if !ok {
   273  		return false
   274  	}
   275  
   276  	result, ok := v.(*shadowResourceProvisionerApply)
   277  	if !ok {
   278  		return false
   279  	}
   280  
   281  	return c.Equal(result.Config)
   282  }