9 "github.com/hashicorp/go-multierror"
10 "github.com/hashicorp/terraform/helper/shadow"
13 // shadowResourceProvisioner implements ResourceProvisioner for the shadow
14 // eval context defined in eval_context_shadow.go.
16 // This is used to verify behavior with a real provisioner. This shouldn't
18 type shadowResourceProvisioner interface {
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,
33 // Create the real provisioner that does actual work
34 real := &shadowResourceProvisionerReal{
35 ResourceProvisioner: p,
39 // Create the shadow that watches the real value
40 shadow := &shadowResourceProvisionerShadow{
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 {
53 Shared *shadowResourceProvisionerShared
56 func (p *shadowResourceProvisionerReal) Close() error {
58 if c, ok := p.ResourceProvisioner.(ResourceProvisionerCloser); ok {
62 p.Shared.CloseErr.SetValue(result)
66 func (p *shadowResourceProvisionerReal) Validate(c *ResourceConfig) ([]string, []error) {
67 warns, errs := p.ResourceProvisioner.Validate(c)
68 p.Shared.Validate.SetValue(&shadowResourceProvisionerValidate{
77 func (p *shadowResourceProvisionerReal) Apply(
78 output UIOutput, s *InstanceState, c *ResourceConfig) error {
79 err := p.ResourceProvisioner.Apply(output, s, c)
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()
87 raw, ok := p.Shared.Apply.ValueOk(key)
90 raw = &shadow.ComparedValue{
91 Func: shadowResourceProvisionerApplyCompare,
95 p.Shared.Apply.SetValue(key, raw)
98 compareVal, ok := raw.(*shadow.ComparedValue)
100 // Just log and return so that we don't cause the real side
102 log.Printf("[ERROR] unknown value in 'apply': %#v", raw)
106 // Write the resulting value
107 compareVal.SetValue(&shadowResourceProvisionerApply{
115 func (p *shadowResourceProvisionerReal) Stop() error {
116 return p.ResourceProvisioner.Stop()
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
125 Error error // Error is the list of errors from the shadow
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.
133 CloseErr shadow.Value
134 Validate shadow.ComparedValue
135 Apply shadow.KeyedValue
136 ApplyLock sync.Mutex // For writing only
139 func (p *shadowResourceProvisionerShared) Close() error {
140 closers := []io.Closer{
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 {
156 func (p *shadowResourceProvisionerShadow) CloseShadow() error {
157 err := p.Shared.Close()
159 err = fmt.Errorf("close error: %s", err)
165 func (p *shadowResourceProvisionerShadow) ShadowError() error {
169 func (p *shadowResourceProvisionerShadow) Close() error {
170 v := p.Shared.CloseErr.Value()
178 func (p *shadowResourceProvisionerShadow) Validate(c *ResourceConfig) ([]string, []error) {
179 // Get the result of the validate call
180 raw := p.Shared.Validate.Value(c)
185 result, ok := raw.(*shadowResourceProvisionerValidate)
188 defer p.ErrorLock.Unlock()
189 p.Error = multierror.Append(p.Error, fmt.Errorf(
190 "Unknown 'validate' shadow value: %#v", raw))
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
199 func (p *shadowResourceProvisionerShadow) Apply(
200 output UIOutput, s *InstanceState, c *ResourceConfig) error {
201 // Get the value based on the key
203 raw := p.Shared.Apply.Value(key)
208 compareVal, ok := raw.(*shadow.ComparedValue)
211 defer p.ErrorLock.Unlock()
212 p.Error = multierror.Append(p.Error, fmt.Errorf(
213 "Unknown 'apply' shadow value: %#v", raw))
217 // With the compared value, we compare against our config
218 raw = compareVal.Value(c)
223 result, ok := raw.(*shadowResourceProvisionerApply)
226 defer p.ErrorLock.Unlock()
227 p.Error = multierror.Append(p.Error, fmt.Errorf(
228 "Unknown 'apply' shadow value: %#v", raw))
232 return result.ResultErr
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
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.
245 type shadowResourceProvisionerValidate struct {
246 Config *ResourceConfig
251 type shadowResourceProvisionerApply struct {
252 Config *ResourceConfig
256 func shadowResourceProvisionerValidateCompare(k, v interface{}) bool {
257 c, ok := k.(*ResourceConfig)
262 result, ok := v.(*shadowResourceProvisionerValidate)
267 return c.Equal(result.Config)
270 func shadowResourceProvisionerApplyCompare(k, v interface{}) bool {
271 c, ok := k.(*ResourceConfig)
276 result, ok := v.(*shadowResourceProvisionerApply)
281 return c.Equal(result.Config)