]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "context" | |
5 | "fmt" | |
6 | "log" | |
7 | "strings" | |
8 | "sync" | |
9 | ||
10 | "github.com/hashicorp/terraform/config" | |
11 | ) | |
12 | ||
13 | // BuiltinEvalContext is an EvalContext implementation that is used by | |
14 | // Terraform by default. | |
15 | type BuiltinEvalContext struct { | |
16 | // StopContext is the context used to track whether we're complete | |
17 | StopContext context.Context | |
18 | ||
19 | // PathValue is the Path that this context is operating within. | |
20 | PathValue []string | |
21 | ||
22 | // Interpolater setting below affect the interpolation of variables. | |
23 | // | |
24 | // The InterpolaterVars are the exact value for ${var.foo} values. | |
25 | // The map is shared between all contexts and is a mapping of | |
26 | // PATH to KEY to VALUE. Because it is shared by all contexts as well | |
27 | // as the Interpolater itself, it is protected by InterpolaterVarLock | |
28 | // which must be locked during any access to the map. | |
29 | Interpolater *Interpolater | |
30 | InterpolaterVars map[string]map[string]interface{} | |
31 | InterpolaterVarLock *sync.Mutex | |
32 | ||
33 | Components contextComponentFactory | |
34 | Hooks []Hook | |
35 | InputValue UIInput | |
36 | ProviderCache map[string]ResourceProvider | |
37 | ProviderConfigCache map[string]*ResourceConfig | |
38 | ProviderInputConfig map[string]map[string]interface{} | |
39 | ProviderLock *sync.Mutex | |
40 | ProvisionerCache map[string]ResourceProvisioner | |
41 | ProvisionerLock *sync.Mutex | |
42 | DiffValue *Diff | |
43 | DiffLock *sync.RWMutex | |
44 | StateValue *State | |
45 | StateLock *sync.RWMutex | |
46 | ||
47 | once sync.Once | |
48 | } | |
49 | ||
50 | func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} { | |
51 | // This can happen during tests. During tests, we just block forever. | |
52 | if ctx.StopContext == nil { | |
53 | return nil | |
54 | } | |
55 | ||
56 | return ctx.StopContext.Done() | |
57 | } | |
58 | ||
59 | func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error { | |
60 | for _, h := range ctx.Hooks { | |
61 | action, err := fn(h) | |
62 | if err != nil { | |
63 | return err | |
64 | } | |
65 | ||
66 | switch action { | |
67 | case HookActionContinue: | |
68 | continue | |
69 | case HookActionHalt: | |
70 | // Return an early exit error to trigger an early exit | |
71 | log.Printf("[WARN] Early exit triggered by hook: %T", h) | |
72 | return EvalEarlyExitError{} | |
73 | } | |
74 | } | |
75 | ||
76 | return nil | |
77 | } | |
78 | ||
79 | func (ctx *BuiltinEvalContext) Input() UIInput { | |
80 | return ctx.InputValue | |
81 | } | |
82 | ||
83 | func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) { | |
84 | ctx.once.Do(ctx.init) | |
85 | ||
86 | // If we already initialized, it is an error | |
87 | if p := ctx.Provider(n); p != nil { | |
88 | return nil, fmt.Errorf("Provider '%s' already initialized", n) | |
89 | } | |
90 | ||
91 | // Warning: make sure to acquire these locks AFTER the call to Provider | |
92 | // above, since it also acquires locks. | |
93 | ctx.ProviderLock.Lock() | |
94 | defer ctx.ProviderLock.Unlock() | |
95 | ||
96 | providerPath := make([]string, len(ctx.Path())+1) | |
97 | copy(providerPath, ctx.Path()) | |
98 | providerPath[len(providerPath)-1] = n | |
99 | key := PathCacheKey(providerPath) | |
100 | ||
101 | typeName := strings.SplitN(n, ".", 2)[0] | |
102 | p, err := ctx.Components.ResourceProvider(typeName, key) | |
103 | if err != nil { | |
104 | return nil, err | |
105 | } | |
106 | ||
107 | ctx.ProviderCache[key] = p | |
108 | return p, nil | |
109 | } | |
110 | ||
111 | func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider { | |
112 | ctx.once.Do(ctx.init) | |
113 | ||
114 | ctx.ProviderLock.Lock() | |
115 | defer ctx.ProviderLock.Unlock() | |
116 | ||
117 | providerPath := make([]string, len(ctx.Path())+1) | |
118 | copy(providerPath, ctx.Path()) | |
119 | providerPath[len(providerPath)-1] = n | |
120 | ||
121 | return ctx.ProviderCache[PathCacheKey(providerPath)] | |
122 | } | |
123 | ||
124 | func (ctx *BuiltinEvalContext) CloseProvider(n string) error { | |
125 | ctx.once.Do(ctx.init) | |
126 | ||
127 | ctx.ProviderLock.Lock() | |
128 | defer ctx.ProviderLock.Unlock() | |
129 | ||
130 | providerPath := make([]string, len(ctx.Path())+1) | |
131 | copy(providerPath, ctx.Path()) | |
132 | providerPath[len(providerPath)-1] = n | |
133 | ||
134 | var provider interface{} | |
135 | provider = ctx.ProviderCache[PathCacheKey(providerPath)] | |
136 | if provider != nil { | |
137 | if p, ok := provider.(ResourceProviderCloser); ok { | |
138 | delete(ctx.ProviderCache, PathCacheKey(providerPath)) | |
139 | return p.Close() | |
140 | } | |
141 | } | |
142 | ||
143 | return nil | |
144 | } | |
145 | ||
146 | func (ctx *BuiltinEvalContext) ConfigureProvider( | |
147 | n string, cfg *ResourceConfig) error { | |
148 | p := ctx.Provider(n) | |
149 | if p == nil { | |
150 | return fmt.Errorf("Provider '%s' not initialized", n) | |
151 | } | |
152 | ||
153 | if err := ctx.SetProviderConfig(n, cfg); err != nil { | |
154 | return nil | |
155 | } | |
156 | ||
157 | return p.Configure(cfg) | |
158 | } | |
159 | ||
160 | func (ctx *BuiltinEvalContext) SetProviderConfig( | |
161 | n string, cfg *ResourceConfig) error { | |
162 | providerPath := make([]string, len(ctx.Path())+1) | |
163 | copy(providerPath, ctx.Path()) | |
164 | providerPath[len(providerPath)-1] = n | |
165 | ||
166 | // Save the configuration | |
167 | ctx.ProviderLock.Lock() | |
168 | ctx.ProviderConfigCache[PathCacheKey(providerPath)] = cfg | |
169 | ctx.ProviderLock.Unlock() | |
170 | ||
171 | return nil | |
172 | } | |
173 | ||
174 | func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} { | |
175 | ctx.ProviderLock.Lock() | |
176 | defer ctx.ProviderLock.Unlock() | |
177 | ||
178 | // Make a copy of the path so we can safely edit it | |
179 | path := ctx.Path() | |
180 | pathCopy := make([]string, len(path)+1) | |
181 | copy(pathCopy, path) | |
182 | ||
183 | // Go up the tree. | |
184 | for i := len(path) - 1; i >= 0; i-- { | |
185 | pathCopy[i+1] = n | |
186 | k := PathCacheKey(pathCopy[:i+2]) | |
187 | if v, ok := ctx.ProviderInputConfig[k]; ok { | |
188 | return v | |
189 | } | |
190 | } | |
191 | ||
192 | return nil | |
193 | } | |
194 | ||
195 | func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface{}) { | |
196 | providerPath := make([]string, len(ctx.Path())+1) | |
197 | copy(providerPath, ctx.Path()) | |
198 | providerPath[len(providerPath)-1] = n | |
199 | ||
200 | // Save the configuration | |
201 | ctx.ProviderLock.Lock() | |
202 | ctx.ProviderInputConfig[PathCacheKey(providerPath)] = c | |
203 | ctx.ProviderLock.Unlock() | |
204 | } | |
205 | ||
206 | func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig { | |
207 | ctx.ProviderLock.Lock() | |
208 | defer ctx.ProviderLock.Unlock() | |
209 | ||
210 | // Make a copy of the path so we can safely edit it | |
211 | path := ctx.Path() | |
212 | pathCopy := make([]string, len(path)+1) | |
213 | copy(pathCopy, path) | |
214 | ||
215 | // Go up the tree. | |
216 | for i := len(path) - 1; i >= 0; i-- { | |
217 | pathCopy[i+1] = n | |
218 | k := PathCacheKey(pathCopy[:i+2]) | |
219 | if v, ok := ctx.ProviderConfigCache[k]; ok { | |
220 | return v | |
221 | } | |
222 | } | |
223 | ||
224 | return nil | |
225 | } | |
226 | ||
227 | func (ctx *BuiltinEvalContext) InitProvisioner( | |
228 | n string) (ResourceProvisioner, error) { | |
229 | ctx.once.Do(ctx.init) | |
230 | ||
231 | // If we already initialized, it is an error | |
232 | if p := ctx.Provisioner(n); p != nil { | |
233 | return nil, fmt.Errorf("Provisioner '%s' already initialized", n) | |
234 | } | |
235 | ||
236 | // Warning: make sure to acquire these locks AFTER the call to Provisioner | |
237 | // above, since it also acquires locks. | |
238 | ctx.ProvisionerLock.Lock() | |
239 | defer ctx.ProvisionerLock.Unlock() | |
240 | ||
241 | provPath := make([]string, len(ctx.Path())+1) | |
242 | copy(provPath, ctx.Path()) | |
243 | provPath[len(provPath)-1] = n | |
244 | key := PathCacheKey(provPath) | |
245 | ||
246 | p, err := ctx.Components.ResourceProvisioner(n, key) | |
247 | if err != nil { | |
248 | return nil, err | |
249 | } | |
250 | ||
251 | ctx.ProvisionerCache[key] = p | |
252 | return p, nil | |
253 | } | |
254 | ||
255 | func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner { | |
256 | ctx.once.Do(ctx.init) | |
257 | ||
258 | ctx.ProvisionerLock.Lock() | |
259 | defer ctx.ProvisionerLock.Unlock() | |
260 | ||
261 | provPath := make([]string, len(ctx.Path())+1) | |
262 | copy(provPath, ctx.Path()) | |
263 | provPath[len(provPath)-1] = n | |
264 | ||
265 | return ctx.ProvisionerCache[PathCacheKey(provPath)] | |
266 | } | |
267 | ||
268 | func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error { | |
269 | ctx.once.Do(ctx.init) | |
270 | ||
271 | ctx.ProvisionerLock.Lock() | |
272 | defer ctx.ProvisionerLock.Unlock() | |
273 | ||
274 | provPath := make([]string, len(ctx.Path())+1) | |
275 | copy(provPath, ctx.Path()) | |
276 | provPath[len(provPath)-1] = n | |
277 | ||
278 | var prov interface{} | |
279 | prov = ctx.ProvisionerCache[PathCacheKey(provPath)] | |
280 | if prov != nil { | |
281 | if p, ok := prov.(ResourceProvisionerCloser); ok { | |
282 | delete(ctx.ProvisionerCache, PathCacheKey(provPath)) | |
283 | return p.Close() | |
284 | } | |
285 | } | |
286 | ||
287 | return nil | |
288 | } | |
289 | ||
290 | func (ctx *BuiltinEvalContext) Interpolate( | |
291 | cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) { | |
292 | if cfg != nil { | |
293 | scope := &InterpolationScope{ | |
294 | Path: ctx.Path(), | |
295 | Resource: r, | |
296 | } | |
297 | ||
298 | vs, err := ctx.Interpolater.Values(scope, cfg.Variables) | |
299 | if err != nil { | |
300 | return nil, err | |
301 | } | |
302 | ||
303 | // Do the interpolation | |
304 | if err := cfg.Interpolate(vs); err != nil { | |
305 | return nil, err | |
306 | } | |
307 | } | |
308 | ||
309 | result := NewResourceConfig(cfg) | |
310 | result.interpolateForce() | |
311 | return result, nil | |
312 | } | |
313 | ||
314 | func (ctx *BuiltinEvalContext) Path() []string { | |
315 | return ctx.PathValue | |
316 | } | |
317 | ||
318 | func (ctx *BuiltinEvalContext) SetVariables(n string, vs map[string]interface{}) { | |
319 | ctx.InterpolaterVarLock.Lock() | |
320 | defer ctx.InterpolaterVarLock.Unlock() | |
321 | ||
322 | path := make([]string, len(ctx.Path())+1) | |
323 | copy(path, ctx.Path()) | |
324 | path[len(path)-1] = n | |
325 | key := PathCacheKey(path) | |
326 | ||
327 | vars := ctx.InterpolaterVars[key] | |
328 | if vars == nil { | |
329 | vars = make(map[string]interface{}) | |
330 | ctx.InterpolaterVars[key] = vars | |
331 | } | |
332 | ||
333 | for k, v := range vs { | |
334 | vars[k] = v | |
335 | } | |
336 | } | |
337 | ||
338 | func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) { | |
339 | return ctx.DiffValue, ctx.DiffLock | |
340 | } | |
341 | ||
342 | func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) { | |
343 | return ctx.StateValue, ctx.StateLock | |
344 | } | |
345 | ||
346 | func (ctx *BuiltinEvalContext) init() { | |
347 | } |