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 }