]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/terraform/context.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / context.go
CommitLineData
bae9f6d2
JC
1package terraform
2
3import (
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.
28type InputMode byte
29
30const (
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
46var (
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.
59type 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.
85type 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 92type 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.
127func 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
237func (c *Context) Schemas() *Schemas {
238 return c.schemas
239}
240
bae9f6d2
JC
241type 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 252func (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.
351func (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 358func (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.
380func (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.
421func (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 440func (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 492func (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 570func (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.
615func (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 647func (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.
688func (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 695func (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
700func (c *Context) SetVariable(k string, v cty.Value) {
701 c.variables[k] = &InputValue{
702 Value: v,
703 SourceType: ValueFromCaller,
704 }
bae9f6d2
JC
705}
706
707func (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
733func (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 753func (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
771func (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.
791func (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.
858func 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.
913func 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.
932func MustShimLegacyState(legacy *State) *states.State {
933 ret, err := ShimLegacyState(legacy)
934 if err != nil {
935 panic(err)
936 }
937 return ret
938}