github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/terraform/shadow_components.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/hashicorp/go-multierror" 8 "github.com/hashicorp/terraform/helper/shadow" 9 ) 10 11 // newShadowComponentFactory creates a shadowed contextComponentFactory 12 // so that requests to create new components result in both a real and 13 // shadow side. 14 func newShadowComponentFactory( 15 f contextComponentFactory) (contextComponentFactory, *shadowComponentFactory) { 16 // Create the shared data 17 shared := &shadowComponentFactoryShared{contextComponentFactory: f} 18 19 // Create the real side 20 real := &shadowComponentFactory{ 21 shadowComponentFactoryShared: shared, 22 } 23 24 // Create the shadow 25 shadow := &shadowComponentFactory{ 26 shadowComponentFactoryShared: shared, 27 Shadow: true, 28 } 29 30 return real, shadow 31 } 32 33 // shadowComponentFactory is the shadow side. Any components created 34 // with this factory are fake and will not cause real work to happen. 35 // 36 // Unlike other shadowers, the shadow component factory will allow the 37 // shadow to create _any_ component even if it is never requested on the 38 // real side. This is because errors will happen later downstream as function 39 // calls are made to the shadows that are never matched on the real side. 40 type shadowComponentFactory struct { 41 *shadowComponentFactoryShared 42 43 Shadow bool // True if this should return the shadow 44 lock sync.Mutex 45 } 46 47 func (f *shadowComponentFactory) ResourceProvider( 48 n, uid string) (ResourceProvider, error) { 49 f.lock.Lock() 50 defer f.lock.Unlock() 51 52 real, shadow, err := f.shadowComponentFactoryShared.ResourceProvider(n, uid) 53 var result ResourceProvider = real 54 if f.Shadow { 55 result = shadow 56 } 57 58 return result, err 59 } 60 61 func (f *shadowComponentFactory) ResourceProvisioner( 62 n, uid string) (ResourceProvisioner, error) { 63 f.lock.Lock() 64 defer f.lock.Unlock() 65 66 real, shadow, err := f.shadowComponentFactoryShared.ResourceProvisioner(n, uid) 67 var result ResourceProvisioner = real 68 if f.Shadow { 69 result = shadow 70 } 71 72 return result, err 73 } 74 75 // CloseShadow is called when the _real_ side is complete. This will cause 76 // all future blocking operations to return immediately on the shadow to 77 // ensure the shadow also completes. 78 func (f *shadowComponentFactory) CloseShadow() error { 79 // If we aren't the shadow, just return 80 if !f.Shadow { 81 return nil 82 } 83 84 // Lock ourselves so we don't modify state 85 f.lock.Lock() 86 defer f.lock.Unlock() 87 88 // Grab our shared state 89 shared := f.shadowComponentFactoryShared 90 91 // If we're already closed, its an error 92 if shared.closed { 93 return fmt.Errorf("component factory shadow already closed") 94 } 95 96 // Close all the providers and provisioners and return the error 97 var result error 98 for _, n := range shared.providerKeys { 99 _, shadow, err := shared.ResourceProvider(n, n) 100 if err == nil && shadow != nil { 101 if err := shadow.CloseShadow(); err != nil { 102 result = multierror.Append(result, err) 103 } 104 } 105 } 106 107 for _, n := range shared.provisionerKeys { 108 _, shadow, err := shared.ResourceProvisioner(n, n) 109 if err == nil && shadow != nil { 110 if err := shadow.CloseShadow(); err != nil { 111 result = multierror.Append(result, err) 112 } 113 } 114 } 115 116 // Mark ourselves as closed 117 shared.closed = true 118 119 return result 120 } 121 122 func (f *shadowComponentFactory) ShadowError() error { 123 // If we aren't the shadow, just return 124 if !f.Shadow { 125 return nil 126 } 127 128 // Lock ourselves so we don't modify state 129 f.lock.Lock() 130 defer f.lock.Unlock() 131 132 // Grab our shared state 133 shared := f.shadowComponentFactoryShared 134 135 // If we're not closed, its an error 136 if !shared.closed { 137 return fmt.Errorf("component factory must be closed to retrieve errors") 138 } 139 140 // Close all the providers and provisioners and return the error 141 var result error 142 for _, n := range shared.providerKeys { 143 _, shadow, err := shared.ResourceProvider(n, n) 144 if err == nil && shadow != nil { 145 if err := shadow.ShadowError(); err != nil { 146 result = multierror.Append(result, err) 147 } 148 } 149 } 150 151 for _, n := range shared.provisionerKeys { 152 _, shadow, err := shared.ResourceProvisioner(n, n) 153 if err == nil && shadow != nil { 154 if err := shadow.ShadowError(); err != nil { 155 result = multierror.Append(result, err) 156 } 157 } 158 } 159 160 return result 161 } 162 163 // shadowComponentFactoryShared is shared data between the two factories. 164 // 165 // It is NOT SAFE to run any function on this struct in parallel. Lock 166 // access to this struct. 167 type shadowComponentFactoryShared struct { 168 contextComponentFactory 169 170 closed bool 171 providers shadow.KeyedValue 172 providerKeys []string 173 provisioners shadow.KeyedValue 174 provisionerKeys []string 175 } 176 177 // shadowResourceProviderFactoryEntry is the entry that is stored in 178 // the Shadows key/value for a provider. 179 type shadowComponentFactoryProviderEntry struct { 180 Real ResourceProvider 181 Shadow shadowResourceProvider 182 Err error 183 } 184 185 type shadowComponentFactoryProvisionerEntry struct { 186 Real ResourceProvisioner 187 Shadow shadowResourceProvisioner 188 Err error 189 } 190 191 func (f *shadowComponentFactoryShared) ResourceProvider( 192 n, uid string) (ResourceProvider, shadowResourceProvider, error) { 193 // Determine if we already have a value 194 raw, ok := f.providers.ValueOk(uid) 195 if !ok { 196 // Build the entry 197 var entry shadowComponentFactoryProviderEntry 198 199 // No value, initialize. Create the original 200 p, err := f.contextComponentFactory.ResourceProvider(n, uid) 201 if err != nil { 202 entry.Err = err 203 p = nil // Just to be sure 204 } 205 206 if p != nil { 207 // Create the shadow 208 real, shadow := newShadowResourceProvider(p) 209 entry.Real = real 210 entry.Shadow = shadow 211 212 if f.closed { 213 shadow.CloseShadow() 214 } 215 } 216 217 // Store the value 218 f.providers.SetValue(uid, &entry) 219 f.providerKeys = append(f.providerKeys, uid) 220 raw = &entry 221 } 222 223 // Read the entry 224 entry, ok := raw.(*shadowComponentFactoryProviderEntry) 225 if !ok { 226 return nil, nil, fmt.Errorf("Unknown value for shadow provider: %#v", raw) 227 } 228 229 // Return 230 return entry.Real, entry.Shadow, entry.Err 231 } 232 233 func (f *shadowComponentFactoryShared) ResourceProvisioner( 234 n, uid string) (ResourceProvisioner, shadowResourceProvisioner, error) { 235 // Determine if we already have a value 236 raw, ok := f.provisioners.ValueOk(uid) 237 if !ok { 238 // Build the entry 239 var entry shadowComponentFactoryProvisionerEntry 240 241 // No value, initialize. Create the original 242 p, err := f.contextComponentFactory.ResourceProvisioner(n, uid) 243 if err != nil { 244 entry.Err = err 245 p = nil // Just to be sure 246 } 247 248 if p != nil { 249 // For now, just create a mock since we don't support provisioners yet 250 real, shadow := newShadowResourceProvisioner(p) 251 entry.Real = real 252 entry.Shadow = shadow 253 254 if f.closed { 255 shadow.CloseShadow() 256 } 257 } 258 259 // Store the value 260 f.provisioners.SetValue(uid, &entry) 261 f.provisionerKeys = append(f.provisionerKeys, uid) 262 raw = &entry 263 } 264 265 // Read the entry 266 entry, ok := raw.(*shadowComponentFactoryProvisionerEntry) 267 if !ok { 268 return nil, nil, fmt.Errorf("Unknown value for shadow provisioner: %#v", raw) 269 } 270 271 // Return 272 return entry.Real, entry.Shadow, entry.Err 273 }