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 }