]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
107c1cdb | 4 | "bytes" |
bae9f6d2 JC |
5 | "context" |
6 | "fmt" | |
7 | "log" | |
bae9f6d2 JC |
8 | "strings" |
9 | "sync" | |
10 | ||
bae9f6d2 | 11 | "github.com/hashicorp/hcl" |
107c1cdb ND |
12 | "github.com/zclconf/go-cty/cty" |
13 | ||
14 | "github.com/hashicorp/terraform/addrs" | |
bae9f6d2 | 15 | "github.com/hashicorp/terraform/config" |
107c1cdb ND |
16 | "github.com/hashicorp/terraform/configs" |
17 | "github.com/hashicorp/terraform/lang" | |
18 | "github.com/hashicorp/terraform/plans" | |
19 | "github.com/hashicorp/terraform/providers" | |
20 | "github.com/hashicorp/terraform/provisioners" | |
21 | "github.com/hashicorp/terraform/states" | |
22 | "github.com/hashicorp/terraform/states/statefile" | |
23 | "github.com/hashicorp/terraform/tfdiags" | |
bae9f6d2 JC |
24 | ) |
25 | ||
26 | // InputMode defines what sort of input will be asked for when Input | |
27 | // is called on Context. | |
28 | type InputMode byte | |
29 | ||
30 | const ( | |
31 | // InputModeVar asks for all variables | |
32 | InputModeVar InputMode = 1 << iota | |
33 | ||
34 | // InputModeVarUnset asks for variables which are not set yet. | |
35 | // InputModeVar must be set for this to have an effect. | |
36 | InputModeVarUnset | |
37 | ||
38 | // InputModeProvider asks for provider variables | |
39 | InputModeProvider | |
40 | ||
41 | // InputModeStd is the standard operating mode and asks for both variables | |
42 | // and providers. | |
43 | InputModeStd = InputModeVar | InputModeProvider | |
44 | ) | |
45 | ||
46 | var ( | |
47 | // contextFailOnShadowError will cause Context operations to return | |
48 | // errors when shadow operations fail. This is only used for testing. | |
49 | contextFailOnShadowError = false | |
50 | ||
51 | // contextTestDeepCopyOnPlan will perform a Diff DeepCopy on every | |
52 | // Plan operation, effectively testing the Diff DeepCopy whenever | |
53 | // a Plan occurs. This is enabled for tests. | |
54 | contextTestDeepCopyOnPlan = false | |
55 | ) | |
56 | ||
57 | // ContextOpts are the user-configurable options to create a context with | |
58 | // NewContext. | |
59 | type ContextOpts struct { | |
107c1cdb ND |
60 | Config *configs.Config |
61 | Changes *plans.Changes | |
62 | State *states.State | |
63 | Targets []addrs.Targetable | |
64 | Variables InputValues | |
65 | Meta *ContextMeta | |
66 | Destroy bool | |
67 | ||
68 | Hooks []Hook | |
69 | Parallelism int | |
70 | ProviderResolver providers.Resolver | |
71 | Provisioners map[string]ProvisionerFactory | |
bae9f6d2 | 72 | |
c680a8e1 RS |
73 | // If non-nil, will apply as additional constraints on the provider |
74 | // plugins that will be requested from the provider resolver. | |
75 | ProviderSHA256s map[string][]byte | |
76 | SkipProviderVerify bool | |
77 | ||
bae9f6d2 JC |
78 | UIInput UIInput |
79 | } | |
80 | ||
81 | // ContextMeta is metadata about the running context. This is information | |
82 | // that this package or structure cannot determine on its own but exposes | |
83 | // into Terraform in various ways. This must be provided by the Context | |
84 | // initializer. | |
85 | type ContextMeta struct { | |
86 | Env string // Env is the state environment | |
87 | } | |
88 | ||
89 | // Context represents all the context that Terraform needs in order to | |
90 | // perform operations on infrastructure. This structure is built using | |
107c1cdb | 91 | // NewContext. |
bae9f6d2 | 92 | type Context struct { |
107c1cdb ND |
93 | config *configs.Config |
94 | changes *plans.Changes | |
95 | state *states.State | |
96 | targets []addrs.Targetable | |
97 | variables InputValues | |
98 | meta *ContextMeta | |
99 | destroy bool | |
bae9f6d2 | 100 | |
bae9f6d2 | 101 | hooks []Hook |
107c1cdb ND |
102 | components contextComponentFactory |
103 | schemas *Schemas | |
bae9f6d2 | 104 | sh *stopHook |
bae9f6d2 | 105 | uiInput UIInput |
bae9f6d2 JC |
106 | |
107 | l sync.Mutex // Lock acquired during any task | |
108 | parallelSem Semaphore | |
107c1cdb | 109 | providerInputConfig map[string]map[string]cty.Value |
c680a8e1 | 110 | providerSHA256s map[string][]byte |
bae9f6d2 JC |
111 | runLock sync.Mutex |
112 | runCond *sync.Cond | |
113 | runContext context.Context | |
114 | runContextCancel context.CancelFunc | |
115 | shadowErr error | |
116 | } | |
117 | ||
107c1cdb ND |
118 | // (additional methods on Context can be found in context_*.go files.) |
119 | ||
bae9f6d2 JC |
120 | // NewContext creates a new Context structure. |
121 | // | |
107c1cdb ND |
122 | // Once a Context is created, the caller must not access or mutate any of |
123 | // the objects referenced (directly or indirectly) by the ContextOpts fields. | |
124 | // | |
125 | // If the returned diagnostics contains errors then the resulting context is | |
126 | // invalid and must not be used. | |
127 | func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) { | |
128 | log.Printf("[TRACE] terraform.NewContext: starting") | |
129 | diags := CheckCoreVersionRequirements(opts.Config) | |
130 | // If version constraints are not met then we'll bail early since otherwise | |
131 | // we're likely to just see a bunch of other errors related to | |
132 | // incompatibilities, which could be overwhelming for the user. | |
133 | if diags.HasErrors() { | |
134 | return nil, diags | |
bae9f6d2 JC |
135 | } |
136 | ||
137 | // Copy all the hooks and add our stop hook. We don't append directly | |
138 | // to the Config so that we're not modifying that in-place. | |
139 | sh := new(stopHook) | |
140 | hooks := make([]Hook, len(opts.Hooks)+1) | |
141 | copy(hooks, opts.Hooks) | |
142 | hooks[len(opts.Hooks)] = sh | |
143 | ||
144 | state := opts.State | |
145 | if state == nil { | |
107c1cdb | 146 | state = states.NewState() |
bae9f6d2 JC |
147 | } |
148 | ||
bae9f6d2 JC |
149 | // Determine parallelism, default to 10. We do this both to limit |
150 | // CPU pressure but also to have an extra guard against rate throttling | |
151 | // from providers. | |
152 | par := opts.Parallelism | |
153 | if par == 0 { | |
154 | par = 10 | |
155 | } | |
156 | ||
157 | // Set up the variables in the following sequence: | |
158 | // 0 - Take default values from the configuration | |
159 | // 1 - Take values from TF_VAR_x environment variables | |
160 | // 2 - Take values specified in -var flags, overriding values | |
161 | // set by environment variables if necessary. This includes | |
162 | // values taken from -var-file in addition. | |
107c1cdb ND |
163 | var variables InputValues |
164 | if opts.Config != nil { | |
165 | // Default variables from the configuration seed our map. | |
166 | variables = DefaultVariableValues(opts.Config.Module.Variables) | |
bae9f6d2 | 167 | } |
107c1cdb ND |
168 | // Variables provided by the caller (from CLI, environment, etc) can |
169 | // override the defaults. | |
170 | variables = variables.Override(opts.Variables) | |
bae9f6d2 | 171 | |
c680a8e1 | 172 | // Bind available provider plugins to the constraints in config |
107c1cdb | 173 | var providerFactories map[string]providers.Factory |
c680a8e1 | 174 | if opts.ProviderResolver != nil { |
107c1cdb | 175 | deps := ConfigTreeDependencies(opts.Config, state) |
c680a8e1 RS |
176 | reqd := deps.AllPluginRequirements() |
177 | if opts.ProviderSHA256s != nil && !opts.SkipProviderVerify { | |
178 | reqd.LockExecutables(opts.ProviderSHA256s) | |
179 | } | |
107c1cdb ND |
180 | log.Printf("[TRACE] terraform.NewContext: resolving provider version selections") |
181 | ||
182 | var providerDiags tfdiags.Diagnostics | |
183 | providerFactories, providerDiags = resourceProviderFactories(opts.ProviderResolver, reqd) | |
184 | diags = diags.Append(providerDiags) | |
185 | ||
186 | if diags.HasErrors() { | |
187 | return nil, diags | |
c680a8e1 RS |
188 | } |
189 | } else { | |
107c1cdb | 190 | providerFactories = make(map[string]providers.Factory) |
c680a8e1 RS |
191 | } |
192 | ||
107c1cdb ND |
193 | components := &basicComponentFactory{ |
194 | providers: providerFactories, | |
195 | provisioners: opts.Provisioners, | |
bae9f6d2 JC |
196 | } |
197 | ||
107c1cdb ND |
198 | log.Printf("[TRACE] terraform.NewContext: loading provider schemas") |
199 | schemas, err := LoadSchemas(opts.Config, opts.State, components) | |
200 | if err != nil { | |
201 | diags = diags.Append(err) | |
202 | return nil, diags | |
203 | } | |
204 | ||
205 | changes := opts.Changes | |
206 | if changes == nil { | |
207 | changes = plans.NewChanges() | |
208 | } | |
209 | ||
210 | config := opts.Config | |
211 | if config == nil { | |
212 | config = configs.NewEmptyConfig() | |
213 | } | |
214 | ||
215 | log.Printf("[TRACE] terraform.NewContext: complete") | |
216 | ||
bae9f6d2 | 217 | return &Context{ |
107c1cdb ND |
218 | components: components, |
219 | schemas: schemas, | |
220 | destroy: opts.Destroy, | |
221 | changes: changes, | |
222 | hooks: hooks, | |
223 | meta: opts.Meta, | |
224 | config: config, | |
225 | state: state, | |
226 | targets: opts.Targets, | |
227 | uiInput: opts.UIInput, | |
228 | variables: variables, | |
bae9f6d2 JC |
229 | |
230 | parallelSem: NewSemaphore(par), | |
107c1cdb | 231 | providerInputConfig: make(map[string]map[string]cty.Value), |
c680a8e1 | 232 | providerSHA256s: opts.ProviderSHA256s, |
bae9f6d2 JC |
233 | sh: sh, |
234 | }, nil | |
235 | } | |
236 | ||
107c1cdb ND |
237 | func (c *Context) Schemas() *Schemas { |
238 | return c.schemas | |
239 | } | |
240 | ||
bae9f6d2 JC |
241 | type ContextGraphOpts struct { |
242 | // If true, validates the graph structure (checks for cycles). | |
243 | Validate bool | |
244 | ||
245 | // Legacy graphs only: won't prune the graph | |
246 | Verbose bool | |
247 | } | |
248 | ||
249 | // Graph returns the graph used for the given operation type. | |
250 | // | |
251 | // The most extensive or complex graph type is GraphTypePlan. | |
107c1cdb | 252 | func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags.Diagnostics) { |
bae9f6d2 JC |
253 | if opts == nil { |
254 | opts = &ContextGraphOpts{Validate: true} | |
255 | } | |
256 | ||
257 | log.Printf("[INFO] terraform: building graph: %s", typ) | |
258 | switch typ { | |
259 | case GraphTypeApply: | |
260 | return (&ApplyGraphBuilder{ | |
107c1cdb ND |
261 | Config: c.config, |
262 | Changes: c.changes, | |
263 | State: c.state, | |
264 | Components: c.components, | |
265 | Schemas: c.schemas, | |
266 | Targets: c.targets, | |
267 | Destroy: c.destroy, | |
268 | Validate: opts.Validate, | |
269 | }).Build(addrs.RootModuleInstance) | |
270 | ||
bae9f6d2 JC |
271 | case GraphTypeValidate: |
272 | // The validate graph is just a slightly modified plan graph | |
273 | fallthrough | |
274 | case GraphTypePlan: | |
275 | // Create the plan graph builder | |
276 | p := &PlanGraphBuilder{ | |
107c1cdb ND |
277 | Config: c.config, |
278 | State: c.state, | |
279 | Components: c.components, | |
280 | Schemas: c.schemas, | |
281 | Targets: c.targets, | |
282 | Validate: opts.Validate, | |
bae9f6d2 JC |
283 | } |
284 | ||
285 | // Some special cases for other graph types shared with plan currently | |
286 | var b GraphBuilder = p | |
287 | switch typ { | |
bae9f6d2 | 288 | case GraphTypeValidate: |
bae9f6d2 JC |
289 | b = ValidateGraphBuilder(p) |
290 | } | |
291 | ||
107c1cdb | 292 | return b.Build(addrs.RootModuleInstance) |
bae9f6d2 JC |
293 | |
294 | case GraphTypePlanDestroy: | |
295 | return (&DestroyPlanGraphBuilder{ | |
107c1cdb ND |
296 | Config: c.config, |
297 | State: c.state, | |
298 | Components: c.components, | |
299 | Schemas: c.schemas, | |
300 | Targets: c.targets, | |
301 | Validate: opts.Validate, | |
302 | }).Build(addrs.RootModuleInstance) | |
bae9f6d2 JC |
303 | |
304 | case GraphTypeRefresh: | |
305 | return (&RefreshGraphBuilder{ | |
107c1cdb ND |
306 | Config: c.config, |
307 | State: c.state, | |
308 | Components: c.components, | |
309 | Schemas: c.schemas, | |
310 | Targets: c.targets, | |
311 | Validate: opts.Validate, | |
312 | }).Build(addrs.RootModuleInstance) | |
313 | ||
314 | case GraphTypeEval: | |
315 | return (&EvalGraphBuilder{ | |
316 | Config: c.config, | |
317 | State: c.state, | |
318 | Components: c.components, | |
319 | Schemas: c.schemas, | |
320 | }).Build(addrs.RootModuleInstance) | |
bae9f6d2 | 321 | |
107c1cdb ND |
322 | default: |
323 | // Should never happen, because the above is exhaustive for all graph types. | |
324 | panic(fmt.Errorf("unsupported graph type %s", typ)) | |
325 | } | |
bae9f6d2 JC |
326 | } |
327 | ||
328 | // ShadowError returns any errors caught during a shadow operation. | |
329 | // | |
330 | // A shadow operation is an operation run in parallel to a real operation | |
331 | // that performs the same tasks using new logic on copied state. The results | |
332 | // are compared to ensure that the new logic works the same as the old logic. | |
333 | // The shadow never affects the real operation or return values. | |
334 | // | |
335 | // The result of the shadow operation are only available through this function | |
336 | // call after a real operation is complete. | |
337 | // | |
338 | // For API consumers of Context, you can safely ignore this function | |
339 | // completely if you have no interest in helping report experimental feature | |
340 | // errors to Terraform maintainers. Otherwise, please call this function | |
341 | // after every operation and report this to the user. | |
342 | // | |
343 | // IMPORTANT: Shadow errors are _never_ critical: they _never_ affect | |
344 | // the real state or result of a real operation. They are purely informational | |
345 | // to assist in future Terraform versions being more stable. Please message | |
346 | // this effectively to the end user. | |
347 | // | |
348 | // This must be called only when no other operation is running (refresh, | |
349 | // plan, etc.). The result can be used in parallel to any other operation | |
350 | // running. | |
351 | func (c *Context) ShadowError() error { | |
352 | return c.shadowErr | |
353 | } | |
354 | ||
355 | // State returns a copy of the current state associated with this context. | |
356 | // | |
357 | // This cannot safely be called in parallel with any other Context function. | |
107c1cdb | 358 | func (c *Context) State() *states.State { |
bae9f6d2 JC |
359 | return c.state.DeepCopy() |
360 | } | |
361 | ||
107c1cdb ND |
362 | // Eval produces a scope in which expressions can be evaluated for |
363 | // the given module path. | |
364 | // | |
365 | // This method must first evaluate any ephemeral values (input variables, local | |
366 | // values, and output values) in the configuration. These ephemeral values are | |
367 | // not included in the persisted state, so they must be re-computed using other | |
368 | // values in the state before they can be properly evaluated. The updated | |
369 | // values are retained in the main state associated with the receiving context. | |
370 | // | |
371 | // This function takes no action against remote APIs but it does need access | |
372 | // to all provider and provisioner instances in order to obtain their schemas | |
373 | // for type checking. | |
374 | // | |
375 | // The result is an evaluation scope that can be used to resolve references | |
376 | // against the root module. If the returned diagnostics contains errors then | |
377 | // the returned scope may be nil. If it is not nil then it may still be used | |
378 | // to attempt expression evaluation or other analysis, but some expressions | |
379 | // may not behave as expected. | |
380 | func (c *Context) Eval(path addrs.ModuleInstance) (*lang.Scope, tfdiags.Diagnostics) { | |
381 | // This is intended for external callers such as the "terraform console" | |
382 | // command. Internally, we create an evaluator in c.walk before walking | |
383 | // the graph, and create scopes in ContextGraphWalker. | |
bae9f6d2 | 384 | |
107c1cdb ND |
385 | var diags tfdiags.Diagnostics |
386 | defer c.acquireRun("eval")() | |
bae9f6d2 | 387 | |
107c1cdb ND |
388 | // Start with a copy of state so that we don't affect any instances |
389 | // that other methods may have already returned. | |
390 | c.state = c.state.DeepCopy() | |
391 | var walker *ContextGraphWalker | |
392 | ||
393 | graph, graphDiags := c.Graph(GraphTypeEval, nil) | |
394 | diags = diags.Append(graphDiags) | |
395 | if !diags.HasErrors() { | |
396 | var walkDiags tfdiags.Diagnostics | |
397 | walker, walkDiags = c.walk(graph, walkEval) | |
398 | diags = diags.Append(walker.NonFatalDiagnostics) | |
399 | diags = diags.Append(walkDiags) | |
400 | } | |
401 | ||
402 | if walker == nil { | |
403 | // If we skipped walking the graph (due to errors) then we'll just | |
404 | // use a placeholder graph walker here, which'll refer to the | |
405 | // unmodified state. | |
406 | walker = c.graphWalker(walkEval) | |
407 | } | |
408 | ||
409 | // This is a bit weird since we don't normally evaluate outside of | |
410 | // the context of a walk, but we'll "re-enter" our desired path here | |
411 | // just to get hold of an EvalContext for it. GraphContextBuiltin | |
412 | // caches its contexts, so we should get hold of the context that was | |
413 | // previously used for evaluation here, unless we skipped walking. | |
414 | evalCtx := walker.EnterPath(path) | |
415 | return evalCtx.EvaluationScope(nil, EvalDataForNoInstanceKey), diags | |
416 | } | |
bae9f6d2 | 417 | |
107c1cdb ND |
418 | // Interpolater is no longer used. Use Evaluator instead. |
419 | // | |
420 | // The interpolator returned from this function will return an error on any use. | |
421 | func (c *Context) Interpolater() *Interpolater { | |
422 | // FIXME: Remove this once all callers are updated to no longer use it. | |
423 | return &Interpolater{} | |
bae9f6d2 JC |
424 | } |
425 | ||
426 | // Apply applies the changes represented by this context and returns | |
427 | // the resulting state. | |
428 | // | |
429 | // Even in the case an error is returned, the state may be returned and will | |
430 | // potentially be partially updated. In addition to returning the resulting | |
431 | // state, this context is updated with the latest state. | |
432 | // | |
433 | // If the state is required after an error, the caller should call | |
434 | // Context.State, rather than rely on the return value. | |
435 | // | |
436 | // TODO: Apply and Refresh should either always return a state, or rely on the | |
437 | // State() method. Currently the helper/resource testing framework relies | |
438 | // on the absence of a returned state to determine if Destroy can be | |
439 | // called, so that will need to be refactored before this can be changed. | |
107c1cdb | 440 | func (c *Context) Apply() (*states.State, tfdiags.Diagnostics) { |
bae9f6d2 JC |
441 | defer c.acquireRun("apply")() |
442 | ||
443 | // Copy our own state | |
444 | c.state = c.state.DeepCopy() | |
445 | ||
446 | // Build the graph. | |
107c1cdb ND |
447 | graph, diags := c.Graph(GraphTypeApply, nil) |
448 | if diags.HasErrors() { | |
449 | return nil, diags | |
bae9f6d2 JC |
450 | } |
451 | ||
452 | // Determine the operation | |
453 | operation := walkApply | |
454 | if c.destroy { | |
455 | operation = walkDestroy | |
456 | } | |
457 | ||
458 | // Walk the graph | |
107c1cdb ND |
459 | walker, walkDiags := c.walk(graph, operation) |
460 | diags = diags.Append(walker.NonFatalDiagnostics) | |
461 | diags = diags.Append(walkDiags) | |
462 | ||
463 | if c.destroy && !diags.HasErrors() { | |
464 | // If we know we were trying to destroy objects anyway, and we | |
465 | // completed without any errors, then we'll also prune out any | |
466 | // leftover empty resource husks (left after all of the instances | |
467 | // of a resource with "count" or "for_each" are destroyed) to | |
468 | // help ensure we end up with an _actually_ empty state, assuming | |
469 | // we weren't destroying with -target here. | |
470 | // | |
471 | // (This doesn't actually take into account -target, but that should | |
472 | // be okay because it doesn't throw away anything we can't recompute | |
473 | // on a subsequent "terraform plan" run, if the resources are still | |
474 | // present in the configuration. However, this _will_ cause "count = 0" | |
475 | // resources to read as unknown during the next refresh walk, which | |
476 | // may cause some additional churn if used in a data resource or | |
477 | // provider block, until we remove refreshing as a separate walk and | |
478 | // just do it as part of the plan walk.) | |
479 | c.state.PruneResourceHusks() | |
480 | } | |
481 | ||
482 | return c.state, diags | |
bae9f6d2 JC |
483 | } |
484 | ||
485 | // Plan generates an execution plan for the given context. | |
486 | // | |
487 | // The execution plan encapsulates the context and can be stored | |
488 | // in order to reinstantiate a context later for Apply. | |
489 | // | |
490 | // Plan also updates the diff of this context to be the diff generated | |
491 | // by the plan, so Apply can be called after. | |
107c1cdb | 492 | func (c *Context) Plan() (*plans.Plan, tfdiags.Diagnostics) { |
bae9f6d2 | 493 | defer c.acquireRun("plan")() |
107c1cdb | 494 | c.changes = plans.NewChanges() |
bae9f6d2 | 495 | |
107c1cdb ND |
496 | var diags tfdiags.Diagnostics |
497 | ||
498 | varVals := make(map[string]plans.DynamicValue, len(c.variables)) | |
499 | for k, iv := range c.variables { | |
500 | // We use cty.DynamicPseudoType here so that we'll save both the | |
501 | // value _and_ its dynamic type in the plan, so we can recover | |
502 | // exactly the same value later. | |
503 | dv, err := plans.NewDynamicValue(iv.Value, cty.DynamicPseudoType) | |
504 | if err != nil { | |
505 | diags = diags.Append(tfdiags.Sourceless( | |
506 | tfdiags.Error, | |
507 | "Failed to prepare variable value for plan", | |
508 | fmt.Sprintf("The value for variable %q could not be serialized to store in the plan: %s.", k, err), | |
509 | )) | |
510 | continue | |
15c0b25d | 511 | } |
107c1cdb | 512 | varVals[k] = dv |
15c0b25d AP |
513 | } |
514 | ||
107c1cdb ND |
515 | p := &plans.Plan{ |
516 | VariableValues: varVals, | |
517 | TargetAddrs: c.targets, | |
518 | ProviderSHA256s: c.providerSHA256s, | |
bae9f6d2 JC |
519 | } |
520 | ||
521 | var operation walkOperation | |
522 | if c.destroy { | |
523 | operation = walkPlanDestroy | |
524 | } else { | |
525 | // Set our state to be something temporary. We do this so that | |
526 | // the plan can update a fake state so that variables work, then | |
527 | // we replace it back with our old state. | |
528 | old := c.state | |
529 | if old == nil { | |
107c1cdb | 530 | c.state = states.NewState() |
bae9f6d2 JC |
531 | } else { |
532 | c.state = old.DeepCopy() | |
533 | } | |
534 | defer func() { | |
535 | c.state = old | |
536 | }() | |
537 | ||
538 | operation = walkPlan | |
539 | } | |
540 | ||
bae9f6d2 JC |
541 | // Build the graph. |
542 | graphType := GraphTypePlan | |
543 | if c.destroy { | |
544 | graphType = GraphTypePlanDestroy | |
545 | } | |
107c1cdb ND |
546 | graph, graphDiags := c.Graph(graphType, nil) |
547 | diags = diags.Append(graphDiags) | |
548 | if graphDiags.HasErrors() { | |
549 | return nil, diags | |
bae9f6d2 JC |
550 | } |
551 | ||
552 | // Do the walk | |
107c1cdb ND |
553 | walker, walkDiags := c.walk(graph, operation) |
554 | diags = diags.Append(walker.NonFatalDiagnostics) | |
555 | diags = diags.Append(walkDiags) | |
556 | if walkDiags.HasErrors() { | |
557 | return nil, diags | |
bae9f6d2 | 558 | } |
107c1cdb | 559 | p.Changes = c.changes |
bae9f6d2 | 560 | |
107c1cdb | 561 | return p, diags |
bae9f6d2 JC |
562 | } |
563 | ||
564 | // Refresh goes through all the resources in the state and refreshes them | |
565 | // to their latest state. This will update the state that this context | |
566 | // works with, along with returning it. | |
567 | // | |
568 | // Even in the case an error is returned, the state may be returned and | |
569 | // will potentially be partially updated. | |
107c1cdb | 570 | func (c *Context) Refresh() (*states.State, tfdiags.Diagnostics) { |
bae9f6d2 JC |
571 | defer c.acquireRun("refresh")() |
572 | ||
573 | // Copy our own state | |
574 | c.state = c.state.DeepCopy() | |
575 | ||
107c1cdb ND |
576 | // Refresh builds a partial changeset as part of its work because it must |
577 | // create placeholder stubs for any resource instances that'll be created | |
578 | // in subsequent plan so that provider configurations and data resources | |
579 | // can interpolate from them. This plan is always thrown away after | |
580 | // the operation completes, restoring any existing changeset. | |
581 | oldChanges := c.changes | |
582 | defer func() { c.changes = oldChanges }() | |
583 | c.changes = plans.NewChanges() | |
584 | ||
bae9f6d2 | 585 | // Build the graph. |
107c1cdb ND |
586 | graph, diags := c.Graph(GraphTypeRefresh, nil) |
587 | if diags.HasErrors() { | |
588 | return nil, diags | |
bae9f6d2 JC |
589 | } |
590 | ||
591 | // Do the walk | |
107c1cdb ND |
592 | _, walkDiags := c.walk(graph, walkRefresh) |
593 | diags = diags.Append(walkDiags) | |
594 | if walkDiags.HasErrors() { | |
595 | return nil, diags | |
596 | } | |
597 | ||
598 | // During our walk we will have created planned object placeholders in | |
599 | // state for resource instances that are in configuration but not yet | |
600 | // created. These were created only to allow expression evaluation to | |
601 | // work properly in provider and data blocks during the walk and must | |
602 | // now be discarded, since a subsequent plan walk is responsible for | |
603 | // creating these "for real". | |
604 | // TODO: Consolidate refresh and plan into a single walk, so that the | |
605 | // refresh walk doesn't need to emulate various aspects of the plan | |
606 | // walk in order to properly evaluate provider and data blocks. | |
607 | c.state.SyncWrapper().RemovePlannedResourceInstanceObjects() | |
608 | ||
609 | return c.state, diags | |
bae9f6d2 JC |
610 | } |
611 | ||
612 | // Stop stops the running task. | |
613 | // | |
614 | // Stop will block until the task completes. | |
615 | func (c *Context) Stop() { | |
616 | log.Printf("[WARN] terraform: Stop called, initiating interrupt sequence") | |
617 | ||
618 | c.l.Lock() | |
619 | defer c.l.Unlock() | |
620 | ||
621 | // If we're running, then stop | |
622 | if c.runContextCancel != nil { | |
623 | log.Printf("[WARN] terraform: run context exists, stopping") | |
624 | ||
625 | // Tell the hook we want to stop | |
626 | c.sh.Stop() | |
627 | ||
628 | // Stop the context | |
629 | c.runContextCancel() | |
630 | c.runContextCancel = nil | |
631 | } | |
632 | ||
633 | // Grab the condition var before we exit | |
634 | if cond := c.runCond; cond != nil { | |
107c1cdb | 635 | log.Printf("[INFO] terraform: waiting for graceful stop to complete") |
bae9f6d2 JC |
636 | cond.Wait() |
637 | } | |
638 | ||
639 | log.Printf("[WARN] terraform: stop complete") | |
640 | } | |
641 | ||
107c1cdb ND |
642 | // Validate performs semantic validation of the configuration, and returning |
643 | // any warnings or errors. | |
644 | // | |
645 | // Syntax and structural checks are performed by the configuration loader, | |
646 | // and so are not repeated here. | |
15c0b25d | 647 | func (c *Context) Validate() tfdiags.Diagnostics { |
bae9f6d2 JC |
648 | defer c.acquireRun("validate")() |
649 | ||
15c0b25d | 650 | var diags tfdiags.Diagnostics |
bae9f6d2 | 651 | |
107c1cdb ND |
652 | // Validate input variables. We do this only for the values supplied |
653 | // by the root module, since child module calls are validated when we | |
654 | // visit their graph nodes. | |
655 | if c.config != nil { | |
656 | varDiags := checkInputVariables(c.config.Module.Variables, c.variables) | |
657 | diags = diags.Append(varDiags) | |
bae9f6d2 JC |
658 | } |
659 | ||
107c1cdb ND |
660 | // If we have errors at this point then we probably won't be able to |
661 | // construct a graph without producing redundant errors, so we'll halt early. | |
15c0b25d AP |
662 | if diags.HasErrors() { |
663 | return diags | |
bae9f6d2 JC |
664 | } |
665 | ||
666 | // Build the graph so we can walk it and run Validate on nodes. | |
667 | // We also validate the graph generated here, but this graph doesn't | |
668 | // necessarily match the graph that Plan will generate, so we'll validate the | |
669 | // graph again later after Planning. | |
107c1cdb ND |
670 | graph, graphDiags := c.Graph(GraphTypeValidate, nil) |
671 | diags = diags.Append(graphDiags) | |
672 | if graphDiags.HasErrors() { | |
15c0b25d | 673 | return diags |
bae9f6d2 JC |
674 | } |
675 | ||
676 | // Walk | |
107c1cdb ND |
677 | walker, walkDiags := c.walk(graph, walkValidate) |
678 | diags = diags.Append(walker.NonFatalDiagnostics) | |
679 | diags = diags.Append(walkDiags) | |
680 | if walkDiags.HasErrors() { | |
681 | return diags | |
15c0b25d AP |
682 | } |
683 | ||
684 | return diags | |
bae9f6d2 JC |
685 | } |
686 | ||
107c1cdb ND |
687 | // Config returns the configuration tree associated with this context. |
688 | func (c *Context) Config() *configs.Config { | |
689 | return c.config | |
bae9f6d2 JC |
690 | } |
691 | ||
692 | // Variables will return the mapping of variables that were defined | |
693 | // for this Context. If Input was called, this mapping may be different | |
694 | // than what was given. | |
107c1cdb | 695 | func (c *Context) Variables() InputValues { |
bae9f6d2 JC |
696 | return c.variables |
697 | } | |
698 | ||
699 | // SetVariable sets a variable after a context has already been built. | |
107c1cdb ND |
700 | func (c *Context) SetVariable(k string, v cty.Value) { |
701 | c.variables[k] = &InputValue{ | |
702 | Value: v, | |
703 | SourceType: ValueFromCaller, | |
704 | } | |
bae9f6d2 JC |
705 | } |
706 | ||
707 | func (c *Context) acquireRun(phase string) func() { | |
708 | // With the run lock held, grab the context lock to make changes | |
709 | // to the run context. | |
710 | c.l.Lock() | |
711 | defer c.l.Unlock() | |
712 | ||
713 | // Wait until we're no longer running | |
714 | for c.runCond != nil { | |
715 | c.runCond.Wait() | |
716 | } | |
717 | ||
718 | // Build our lock | |
719 | c.runCond = sync.NewCond(&c.l) | |
720 | ||
bae9f6d2 JC |
721 | // Create a new run context |
722 | c.runContext, c.runContextCancel = context.WithCancel(context.Background()) | |
723 | ||
724 | // Reset the stop hook so we're not stopped | |
725 | c.sh.Reset() | |
726 | ||
727 | // Reset the shadow errors | |
728 | c.shadowErr = nil | |
729 | ||
730 | return c.releaseRun | |
731 | } | |
732 | ||
733 | func (c *Context) releaseRun() { | |
734 | // Grab the context lock so that we can make modifications to fields | |
735 | c.l.Lock() | |
736 | defer c.l.Unlock() | |
737 | ||
bae9f6d2 JC |
738 | // End our run. We check if runContext is non-nil because it can be |
739 | // set to nil if it was cancelled via Stop() | |
740 | if c.runContextCancel != nil { | |
741 | c.runContextCancel() | |
742 | } | |
743 | ||
744 | // Unlock all waiting our condition | |
745 | cond := c.runCond | |
746 | c.runCond = nil | |
747 | cond.Broadcast() | |
748 | ||
749 | // Unset the context | |
750 | c.runContext = nil | |
751 | } | |
752 | ||
107c1cdb | 753 | func (c *Context) walk(graph *Graph, operation walkOperation) (*ContextGraphWalker, tfdiags.Diagnostics) { |
bae9f6d2 JC |
754 | log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) |
755 | ||
107c1cdb | 756 | walker := c.graphWalker(operation) |
bae9f6d2 JC |
757 | |
758 | // Watch for a stop so we can call the provider Stop() API. | |
759 | watchStop, watchWait := c.watchStop(walker) | |
760 | ||
761 | // Walk the real graph, this will block until it completes | |
107c1cdb | 762 | diags := graph.Walk(walker) |
bae9f6d2 JC |
763 | |
764 | // Close the channel so the watcher stops, and wait for it to return. | |
765 | close(watchStop) | |
766 | <-watchWait | |
767 | ||
107c1cdb ND |
768 | return walker, diags |
769 | } | |
770 | ||
771 | func (c *Context) graphWalker(operation walkOperation) *ContextGraphWalker { | |
772 | return &ContextGraphWalker{ | |
773 | Context: c, | |
774 | State: c.state.SyncWrapper(), | |
775 | Changes: c.changes.SyncWrapper(), | |
776 | Operation: operation, | |
777 | StopContext: c.runContext, | |
778 | RootVariableValues: c.variables, | |
779 | } | |
bae9f6d2 JC |
780 | } |
781 | ||
782 | // watchStop immediately returns a `stop` and a `wait` chan after dispatching | |
783 | // the watchStop goroutine. This will watch the runContext for cancellation and | |
784 | // stop the providers accordingly. When the watch is no longer needed, the | |
785 | // `stop` chan should be closed before waiting on the `wait` chan. | |
786 | // The `wait` chan is important, because without synchronizing with the end of | |
787 | // the watchStop goroutine, the runContext may also be closed during the select | |
788 | // incorrectly causing providers to be stopped. Even if the graph walk is done | |
789 | // at that point, stopping a provider permanently cancels its StopContext which | |
790 | // can cause later actions to fail. | |
791 | func (c *Context) watchStop(walker *ContextGraphWalker) (chan struct{}, <-chan struct{}) { | |
792 | stop := make(chan struct{}) | |
793 | wait := make(chan struct{}) | |
794 | ||
795 | // get the runContext cancellation channel now, because releaseRun will | |
796 | // write to the runContext field. | |
797 | done := c.runContext.Done() | |
798 | ||
799 | go func() { | |
800 | defer close(wait) | |
801 | // Wait for a stop or completion | |
802 | select { | |
803 | case <-done: | |
804 | // done means the context was canceled, so we need to try and stop | |
805 | // providers. | |
806 | case <-stop: | |
807 | // our own stop channel was closed. | |
808 | return | |
809 | } | |
810 | ||
811 | // If we're here, we're stopped, trigger the call. | |
107c1cdb | 812 | log.Printf("[TRACE] Context: requesting providers and provisioners to gracefully stop") |
bae9f6d2 JC |
813 | |
814 | { | |
815 | // Copy the providers so that a misbehaved blocking Stop doesn't | |
816 | // completely hang Terraform. | |
817 | walker.providerLock.Lock() | |
107c1cdb | 818 | ps := make([]providers.Interface, 0, len(walker.providerCache)) |
bae9f6d2 JC |
819 | for _, p := range walker.providerCache { |
820 | ps = append(ps, p) | |
821 | } | |
822 | defer walker.providerLock.Unlock() | |
823 | ||
824 | for _, p := range ps { | |
825 | // We ignore the error for now since there isn't any reasonable | |
826 | // action to take if there is an error here, since the stop is still | |
827 | // advisory: Terraform will exit once the graph node completes. | |
828 | p.Stop() | |
829 | } | |
830 | } | |
831 | ||
832 | { | |
833 | // Call stop on all the provisioners | |
834 | walker.provisionerLock.Lock() | |
107c1cdb | 835 | ps := make([]provisioners.Interface, 0, len(walker.provisionerCache)) |
bae9f6d2 JC |
836 | for _, p := range walker.provisionerCache { |
837 | ps = append(ps, p) | |
838 | } | |
839 | defer walker.provisionerLock.Unlock() | |
840 | ||
841 | for _, p := range ps { | |
842 | // We ignore the error for now since there isn't any reasonable | |
843 | // action to take if there is an error here, since the stop is still | |
844 | // advisory: Terraform will exit once the graph node completes. | |
845 | p.Stop() | |
846 | } | |
847 | } | |
848 | }() | |
849 | ||
850 | return stop, wait | |
851 | } | |
852 | ||
853 | // parseVariableAsHCL parses the value of a single variable as would have been specified | |
854 | // on the command line via -var or in an environment variable named TF_VAR_x, where x is | |
855 | // the name of the variable. In order to get around the restriction of HCL requiring a | |
856 | // top level object, we prepend a sentinel key, decode the user-specified value as its | |
857 | // value and pull the value back out of the resulting map. | |
858 | func parseVariableAsHCL(name string, input string, targetType config.VariableType) (interface{}, error) { | |
859 | // expecting a string so don't decode anything, just strip quotes | |
860 | if targetType == config.VariableTypeString { | |
861 | return strings.Trim(input, `"`), nil | |
862 | } | |
863 | ||
864 | // return empty types | |
865 | if strings.TrimSpace(input) == "" { | |
866 | switch targetType { | |
867 | case config.VariableTypeList: | |
868 | return []interface{}{}, nil | |
869 | case config.VariableTypeMap: | |
870 | return make(map[string]interface{}), nil | |
871 | } | |
872 | } | |
873 | ||
874 | const sentinelValue = "SENTINEL_TERRAFORM_VAR_OVERRIDE_KEY" | |
875 | inputWithSentinal := fmt.Sprintf("%s = %s", sentinelValue, input) | |
876 | ||
877 | var decoded map[string]interface{} | |
878 | err := hcl.Decode(&decoded, inputWithSentinal) | |
879 | if err != nil { | |
880 | return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL: %s", name, input, err) | |
881 | } | |
882 | ||
883 | if len(decoded) != 1 { | |
884 | return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. Only one value may be specified.", name, input) | |
885 | } | |
886 | ||
887 | parsedValue, ok := decoded[sentinelValue] | |
888 | if !ok { | |
889 | return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input) | |
890 | } | |
891 | ||
892 | switch targetType { | |
893 | case config.VariableTypeList: | |
894 | return parsedValue, nil | |
895 | case config.VariableTypeMap: | |
896 | if list, ok := parsedValue.([]map[string]interface{}); ok { | |
897 | return list[0], nil | |
898 | } | |
899 | ||
900 | return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input) | |
901 | default: | |
902 | panic(fmt.Errorf("unknown type %s", targetType.Printable())) | |
903 | } | |
904 | } | |
107c1cdb ND |
905 | |
906 | // ShimLegacyState is a helper that takes the legacy state type and | |
907 | // converts it to the new state type. | |
908 | // | |
909 | // This is implemented as a state file upgrade, so it will not preserve | |
910 | // parts of the state structure that are not included in a serialized state, | |
911 | // such as the resolved results of any local values, outputs in non-root | |
912 | // modules, etc. | |
913 | func ShimLegacyState(legacy *State) (*states.State, error) { | |
914 | if legacy == nil { | |
915 | return nil, nil | |
916 | } | |
917 | var buf bytes.Buffer | |
918 | err := WriteState(legacy, &buf) | |
919 | if err != nil { | |
920 | return nil, err | |
921 | } | |
922 | f, err := statefile.Read(&buf) | |
923 | if err != nil { | |
924 | return nil, err | |
925 | } | |
926 | return f.State, err | |
927 | } | |
928 | ||
929 | // MustShimLegacyState is a wrapper around ShimLegacyState that panics if | |
930 | // the conversion does not succeed. This is primarily intended for tests where | |
931 | // the given legacy state is an object constructed within the test. | |
932 | func MustShimLegacyState(legacy *State) *states.State { | |
933 | ret, err := ShimLegacyState(legacy) | |
934 | if err != nil { | |
935 | panic(err) | |
936 | } | |
937 | return ret | |
938 | } |