]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "context" | |
5 | "fmt" | |
6 | "log" | |
bae9f6d2 JC |
7 | "sync" |
8 | ||
107c1cdb ND |
9 | "github.com/hashicorp/terraform/plans" |
10 | "github.com/hashicorp/terraform/providers" | |
11 | "github.com/hashicorp/terraform/provisioners" | |
12 | "github.com/hashicorp/terraform/version" | |
13 | ||
14 | "github.com/hashicorp/terraform/states" | |
15 | ||
16 | "github.com/hashicorp/hcl2/hcl" | |
17 | "github.com/hashicorp/terraform/configs/configschema" | |
18 | "github.com/hashicorp/terraform/lang" | |
19 | "github.com/hashicorp/terraform/tfdiags" | |
20 | ||
21 | "github.com/hashicorp/terraform/addrs" | |
22 | "github.com/zclconf/go-cty/cty" | |
bae9f6d2 JC |
23 | ) |
24 | ||
25 | // BuiltinEvalContext is an EvalContext implementation that is used by | |
26 | // Terraform by default. | |
27 | type BuiltinEvalContext struct { | |
28 | // StopContext is the context used to track whether we're complete | |
29 | StopContext context.Context | |
30 | ||
31 | // PathValue is the Path that this context is operating within. | |
107c1cdb | 32 | PathValue addrs.ModuleInstance |
bae9f6d2 | 33 | |
107c1cdb ND |
34 | // Evaluator is used for evaluating expressions within the scope of this |
35 | // eval context. | |
36 | Evaluator *Evaluator | |
37 | ||
38 | // Schemas is a repository of all of the schemas we should need to | |
39 | // decode configuration blocks and expressions. This must be constructed by | |
40 | // the caller to include schemas for all of the providers, resource types, | |
41 | // data sources and provisioners used by the given configuration and | |
42 | // state. | |
bae9f6d2 | 43 | // |
107c1cdb ND |
44 | // This must not be mutated during evaluation. |
45 | Schemas *Schemas | |
46 | ||
47 | // VariableValues contains the variable values across all modules. This | |
48 | // structure is shared across the entire containing context, and so it | |
49 | // may be accessed only when holding VariableValuesLock. | |
50 | // The keys of the first level of VariableValues are the string | |
51 | // representations of addrs.ModuleInstance values. The second-level keys | |
52 | // are variable names within each module instance. | |
53 | VariableValues map[string]map[string]cty.Value | |
54 | VariableValuesLock *sync.Mutex | |
bae9f6d2 JC |
55 | |
56 | Components contextComponentFactory | |
57 | Hooks []Hook | |
58 | InputValue UIInput | |
107c1cdb ND |
59 | ProviderCache map[string]providers.Interface |
60 | ProviderInputConfig map[string]map[string]cty.Value | |
bae9f6d2 | 61 | ProviderLock *sync.Mutex |
107c1cdb | 62 | ProvisionerCache map[string]provisioners.Interface |
bae9f6d2 | 63 | ProvisionerLock *sync.Mutex |
107c1cdb ND |
64 | ChangesValue *plans.ChangesSync |
65 | StateValue *states.SyncState | |
bae9f6d2 JC |
66 | |
67 | once sync.Once | |
68 | } | |
69 | ||
107c1cdb ND |
70 | // BuiltinEvalContext implements EvalContext |
71 | var _ EvalContext = (*BuiltinEvalContext)(nil) | |
72 | ||
bae9f6d2 JC |
73 | func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} { |
74 | // This can happen during tests. During tests, we just block forever. | |
75 | if ctx.StopContext == nil { | |
76 | return nil | |
77 | } | |
78 | ||
79 | return ctx.StopContext.Done() | |
80 | } | |
81 | ||
82 | func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error { | |
83 | for _, h := range ctx.Hooks { | |
84 | action, err := fn(h) | |
85 | if err != nil { | |
86 | return err | |
87 | } | |
88 | ||
89 | switch action { | |
90 | case HookActionContinue: | |
91 | continue | |
92 | case HookActionHalt: | |
93 | // Return an early exit error to trigger an early exit | |
94 | log.Printf("[WARN] Early exit triggered by hook: %T", h) | |
95 | return EvalEarlyExitError{} | |
96 | } | |
97 | } | |
98 | ||
99 | return nil | |
100 | } | |
101 | ||
102 | func (ctx *BuiltinEvalContext) Input() UIInput { | |
103 | return ctx.InputValue | |
104 | } | |
105 | ||
107c1cdb | 106 | func (ctx *BuiltinEvalContext) InitProvider(typeName string, addr addrs.ProviderConfig) (providers.Interface, error) { |
bae9f6d2 | 107 | ctx.once.Do(ctx.init) |
107c1cdb | 108 | absAddr := addr.Absolute(ctx.Path()) |
bae9f6d2 JC |
109 | |
110 | // If we already initialized, it is an error | |
107c1cdb ND |
111 | if p := ctx.Provider(absAddr); p != nil { |
112 | return nil, fmt.Errorf("%s is already initialized", addr) | |
bae9f6d2 JC |
113 | } |
114 | ||
115 | // Warning: make sure to acquire these locks AFTER the call to Provider | |
116 | // above, since it also acquires locks. | |
117 | ctx.ProviderLock.Lock() | |
118 | defer ctx.ProviderLock.Unlock() | |
119 | ||
107c1cdb ND |
120 | key := absAddr.String() |
121 | ||
122 | p, err := ctx.Components.ResourceProvider(typeName, key) | |
bae9f6d2 JC |
123 | if err != nil { |
124 | return nil, err | |
125 | } | |
126 | ||
107c1cdb ND |
127 | log.Printf("[TRACE] BuiltinEvalContext: Initialized %q provider for %s", typeName, absAddr) |
128 | ctx.ProviderCache[key] = p | |
129 | ||
bae9f6d2 JC |
130 | return p, nil |
131 | } | |
132 | ||
107c1cdb | 133 | func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface { |
bae9f6d2 JC |
134 | ctx.once.Do(ctx.init) |
135 | ||
136 | ctx.ProviderLock.Lock() | |
137 | defer ctx.ProviderLock.Unlock() | |
138 | ||
107c1cdb ND |
139 | return ctx.ProviderCache[addr.String()] |
140 | } | |
141 | ||
142 | func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *ProviderSchema { | |
143 | ctx.once.Do(ctx.init) | |
144 | ||
145 | return ctx.Schemas.ProviderSchema(addr.ProviderConfig.Type) | |
bae9f6d2 JC |
146 | } |
147 | ||
107c1cdb | 148 | func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.ProviderConfig) error { |
bae9f6d2 JC |
149 | ctx.once.Do(ctx.init) |
150 | ||
151 | ctx.ProviderLock.Lock() | |
152 | defer ctx.ProviderLock.Unlock() | |
153 | ||
107c1cdb ND |
154 | key := addr.Absolute(ctx.Path()).String() |
155 | provider := ctx.ProviderCache[key] | |
bae9f6d2 | 156 | if provider != nil { |
107c1cdb ND |
157 | delete(ctx.ProviderCache, key) |
158 | return provider.Close() | |
bae9f6d2 JC |
159 | } |
160 | ||
161 | return nil | |
162 | } | |
163 | ||
107c1cdb ND |
164 | func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.ProviderConfig, cfg cty.Value) tfdiags.Diagnostics { |
165 | var diags tfdiags.Diagnostics | |
166 | absAddr := addr.Absolute(ctx.Path()) | |
167 | p := ctx.Provider(absAddr) | |
bae9f6d2 | 168 | if p == nil { |
107c1cdb ND |
169 | diags = diags.Append(fmt.Errorf("%s not initialized", addr)) |
170 | return diags | |
bae9f6d2 | 171 | } |
107c1cdb ND |
172 | |
173 | providerSchema := ctx.ProviderSchema(absAddr) | |
174 | if providerSchema == nil { | |
175 | diags = diags.Append(fmt.Errorf("schema for %s is not available", absAddr)) | |
176 | return diags | |
177 | } | |
178 | ||
179 | req := providers.ConfigureRequest{ | |
180 | TerraformVersion: version.String(), | |
181 | Config: cfg, | |
182 | } | |
183 | ||
184 | resp := p.Configure(req) | |
185 | return resp.Diagnostics | |
bae9f6d2 JC |
186 | } |
187 | ||
107c1cdb | 188 | func (ctx *BuiltinEvalContext) ProviderInput(pc addrs.ProviderConfig) map[string]cty.Value { |
bae9f6d2 JC |
189 | ctx.ProviderLock.Lock() |
190 | defer ctx.ProviderLock.Unlock() | |
191 | ||
107c1cdb ND |
192 | if !ctx.Path().IsRoot() { |
193 | // Only root module provider configurations can have input. | |
194 | return nil | |
bae9f6d2 JC |
195 | } |
196 | ||
107c1cdb | 197 | return ctx.ProviderInputConfig[pc.String()] |
bae9f6d2 JC |
198 | } |
199 | ||
107c1cdb ND |
200 | func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.ProviderConfig, c map[string]cty.Value) { |
201 | absProvider := pc.Absolute(ctx.Path()) | |
202 | ||
203 | if !ctx.Path().IsRoot() { | |
204 | // Only root module provider configurations can have input. | |
205 | log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module") | |
206 | return | |
207 | } | |
bae9f6d2 JC |
208 | |
209 | // Save the configuration | |
210 | ctx.ProviderLock.Lock() | |
107c1cdb | 211 | ctx.ProviderInputConfig[absProvider.String()] = c |
bae9f6d2 JC |
212 | ctx.ProviderLock.Unlock() |
213 | } | |
214 | ||
107c1cdb | 215 | func (ctx *BuiltinEvalContext) InitProvisioner(n string) (provisioners.Interface, error) { |
bae9f6d2 JC |
216 | ctx.once.Do(ctx.init) |
217 | ||
218 | // If we already initialized, it is an error | |
219 | if p := ctx.Provisioner(n); p != nil { | |
220 | return nil, fmt.Errorf("Provisioner '%s' already initialized", n) | |
221 | } | |
222 | ||
223 | // Warning: make sure to acquire these locks AFTER the call to Provisioner | |
224 | // above, since it also acquires locks. | |
225 | ctx.ProvisionerLock.Lock() | |
226 | defer ctx.ProvisionerLock.Unlock() | |
227 | ||
107c1cdb | 228 | key := PathObjectCacheKey(ctx.Path(), n) |
bae9f6d2 JC |
229 | |
230 | p, err := ctx.Components.ResourceProvisioner(n, key) | |
231 | if err != nil { | |
232 | return nil, err | |
233 | } | |
234 | ||
235 | ctx.ProvisionerCache[key] = p | |
107c1cdb | 236 | |
bae9f6d2 JC |
237 | return p, nil |
238 | } | |
239 | ||
107c1cdb | 240 | func (ctx *BuiltinEvalContext) Provisioner(n string) provisioners.Interface { |
bae9f6d2 JC |
241 | ctx.once.Do(ctx.init) |
242 | ||
243 | ctx.ProvisionerLock.Lock() | |
244 | defer ctx.ProvisionerLock.Unlock() | |
245 | ||
107c1cdb ND |
246 | key := PathObjectCacheKey(ctx.Path(), n) |
247 | return ctx.ProvisionerCache[key] | |
248 | } | |
249 | ||
250 | func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) *configschema.Block { | |
251 | ctx.once.Do(ctx.init) | |
bae9f6d2 | 252 | |
107c1cdb | 253 | return ctx.Schemas.ProvisionerConfig(n) |
bae9f6d2 JC |
254 | } |
255 | ||
256 | func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error { | |
257 | ctx.once.Do(ctx.init) | |
258 | ||
259 | ctx.ProvisionerLock.Lock() | |
260 | defer ctx.ProvisionerLock.Unlock() | |
261 | ||
107c1cdb | 262 | key := PathObjectCacheKey(ctx.Path(), n) |
bae9f6d2 | 263 | |
107c1cdb | 264 | prov := ctx.ProvisionerCache[key] |
bae9f6d2 | 265 | if prov != nil { |
107c1cdb | 266 | return prov.Close() |
bae9f6d2 JC |
267 | } |
268 | ||
269 | return nil | |
270 | } | |
271 | ||
107c1cdb ND |
272 | func (ctx *BuiltinEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) { |
273 | var diags tfdiags.Diagnostics | |
274 | scope := ctx.EvaluationScope(self, keyData) | |
275 | body, evalDiags := scope.ExpandBlock(body, schema) | |
276 | diags = diags.Append(evalDiags) | |
277 | val, evalDiags := scope.EvalBlock(body, schema) | |
278 | diags = diags.Append(evalDiags) | |
279 | return val, body, diags | |
15c0b25d AP |
280 | } |
281 | ||
107c1cdb ND |
282 | func (ctx *BuiltinEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) { |
283 | scope := ctx.EvaluationScope(self, EvalDataForNoInstanceKey) | |
284 | return scope.EvalExpr(expr, wantType) | |
285 | } | |
bae9f6d2 | 286 | |
107c1cdb ND |
287 | func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope { |
288 | data := &evaluationStateData{ | |
289 | Evaluator: ctx.Evaluator, | |
290 | ModulePath: ctx.PathValue, | |
291 | InstanceKeyData: keyData, | |
292 | Operation: ctx.Evaluator.Operation, | |
bae9f6d2 | 293 | } |
107c1cdb | 294 | return ctx.Evaluator.Scope(data, self) |
bae9f6d2 JC |
295 | } |
296 | ||
107c1cdb | 297 | func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { |
bae9f6d2 JC |
298 | return ctx.PathValue |
299 | } | |
300 | ||
107c1cdb ND |
301 | func (ctx *BuiltinEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance, vals map[string]cty.Value) { |
302 | ctx.VariableValuesLock.Lock() | |
303 | defer ctx.VariableValuesLock.Unlock() | |
bae9f6d2 | 304 | |
107c1cdb ND |
305 | childPath := n.ModuleInstance(ctx.PathValue) |
306 | key := childPath.String() | |
bae9f6d2 | 307 | |
107c1cdb ND |
308 | args := ctx.VariableValues[key] |
309 | if args == nil { | |
310 | args = make(map[string]cty.Value) | |
311 | ctx.VariableValues[key] = vals | |
312 | return | |
bae9f6d2 JC |
313 | } |
314 | ||
107c1cdb ND |
315 | for k, v := range vals { |
316 | args[k] = v | |
bae9f6d2 JC |
317 | } |
318 | } | |
319 | ||
107c1cdb ND |
320 | func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync { |
321 | return ctx.ChangesValue | |
bae9f6d2 JC |
322 | } |
323 | ||
107c1cdb ND |
324 | func (ctx *BuiltinEvalContext) State() *states.SyncState { |
325 | return ctx.StateValue | |
bae9f6d2 JC |
326 | } |
327 | ||
328 | func (ctx *BuiltinEvalContext) init() { | |
329 | } |