]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
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 | } |