]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/context.go
Merge pull request #27 from terraform-providers/go-modules-2019-02-22
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / context.go
1 package terraform
2
3 import (
4 "context"
5 "fmt"
6 "log"
7 "sort"
8 "strings"
9 "sync"
10
11 "github.com/hashicorp/terraform/tfdiags"
12
13 "github.com/hashicorp/go-multierror"
14 "github.com/hashicorp/hcl"
15 "github.com/hashicorp/terraform/config"
16 "github.com/hashicorp/terraform/config/module"
17 "github.com/hashicorp/terraform/version"
18 )
19
20 // InputMode defines what sort of input will be asked for when Input
21 // is called on Context.
22 type InputMode byte
23
24 const (
25 // InputModeVar asks for all variables
26 InputModeVar InputMode = 1 << iota
27
28 // InputModeVarUnset asks for variables which are not set yet.
29 // InputModeVar must be set for this to have an effect.
30 InputModeVarUnset
31
32 // InputModeProvider asks for provider variables
33 InputModeProvider
34
35 // InputModeStd is the standard operating mode and asks for both variables
36 // and providers.
37 InputModeStd = InputModeVar | InputModeProvider
38 )
39
40 var (
41 // contextFailOnShadowError will cause Context operations to return
42 // errors when shadow operations fail. This is only used for testing.
43 contextFailOnShadowError = false
44
45 // contextTestDeepCopyOnPlan will perform a Diff DeepCopy on every
46 // Plan operation, effectively testing the Diff DeepCopy whenever
47 // a Plan occurs. This is enabled for tests.
48 contextTestDeepCopyOnPlan = false
49 )
50
51 // ContextOpts are the user-configurable options to create a context with
52 // NewContext.
53 type ContextOpts struct {
54 Meta *ContextMeta
55 Destroy bool
56 Diff *Diff
57 Hooks []Hook
58 Module *module.Tree
59 Parallelism int
60 State *State
61 StateFutureAllowed bool
62 ProviderResolver ResourceProviderResolver
63 Provisioners map[string]ResourceProvisionerFactory
64 Shadow bool
65 Targets []string
66 Variables map[string]interface{}
67
68 // If non-nil, will apply as additional constraints on the provider
69 // plugins that will be requested from the provider resolver.
70 ProviderSHA256s map[string][]byte
71 SkipProviderVerify bool
72
73 UIInput UIInput
74 }
75
76 // ContextMeta is metadata about the running context. This is information
77 // that this package or structure cannot determine on its own but exposes
78 // into Terraform in various ways. This must be provided by the Context
79 // initializer.
80 type ContextMeta struct {
81 Env string // Env is the state environment
82 }
83
84 // Context represents all the context that Terraform needs in order to
85 // perform operations on infrastructure. This structure is built using
86 // NewContext. See the documentation for that.
87 //
88 // Extra functions on Context can be found in context_*.go files.
89 type Context struct {
90 // Maintainer note: Anytime this struct is changed, please verify
91 // that newShadowContext still does the right thing. Tests should
92 // fail regardless but putting this note here as well.
93
94 components contextComponentFactory
95 destroy bool
96 diff *Diff
97 diffLock sync.RWMutex
98 hooks []Hook
99 meta *ContextMeta
100 module *module.Tree
101 sh *stopHook
102 shadow bool
103 state *State
104 stateLock sync.RWMutex
105 targets []string
106 uiInput UIInput
107 variables map[string]interface{}
108
109 l sync.Mutex // Lock acquired during any task
110 parallelSem Semaphore
111 providerInputConfig map[string]map[string]interface{}
112 providerSHA256s map[string][]byte
113 runLock sync.Mutex
114 runCond *sync.Cond
115 runContext context.Context
116 runContextCancel context.CancelFunc
117 shadowErr error
118 }
119
120 // NewContext creates a new Context structure.
121 //
122 // Once a Context is creator, the pointer values within ContextOpts
123 // should not be mutated in any way, since the pointers are copied, not
124 // the values themselves.
125 func NewContext(opts *ContextOpts) (*Context, error) {
126 // Validate the version requirement if it is given
127 if opts.Module != nil {
128 if err := CheckRequiredVersion(opts.Module); err != nil {
129 return nil, err
130 }
131 }
132
133 // Copy all the hooks and add our stop hook. We don't append directly
134 // to the Config so that we're not modifying that in-place.
135 sh := new(stopHook)
136 hooks := make([]Hook, len(opts.Hooks)+1)
137 copy(hooks, opts.Hooks)
138 hooks[len(opts.Hooks)] = sh
139
140 state := opts.State
141 if state == nil {
142 state = new(State)
143 state.init()
144 }
145
146 // If our state is from the future, then error. Callers can avoid
147 // this error by explicitly setting `StateFutureAllowed`.
148 if err := CheckStateVersion(state); err != nil && !opts.StateFutureAllowed {
149 return nil, err
150 }
151
152 // Explicitly reset our state version to our current version so that
153 // any operations we do will write out that our latest version
154 // has run.
155 state.TFVersion = version.Version
156
157 // Determine parallelism, default to 10. We do this both to limit
158 // CPU pressure but also to have an extra guard against rate throttling
159 // from providers.
160 par := opts.Parallelism
161 if par == 0 {
162 par = 10
163 }
164
165 // Set up the variables in the following sequence:
166 // 0 - Take default values from the configuration
167 // 1 - Take values from TF_VAR_x environment variables
168 // 2 - Take values specified in -var flags, overriding values
169 // set by environment variables if necessary. This includes
170 // values taken from -var-file in addition.
171 variables := make(map[string]interface{})
172 if opts.Module != nil {
173 var err error
174 variables, err = Variables(opts.Module, opts.Variables)
175 if err != nil {
176 return nil, err
177 }
178 }
179
180 // Bind available provider plugins to the constraints in config
181 var providers map[string]ResourceProviderFactory
182 if opts.ProviderResolver != nil {
183 var err error
184 deps := ModuleTreeDependencies(opts.Module, state)
185 reqd := deps.AllPluginRequirements()
186 if opts.ProviderSHA256s != nil && !opts.SkipProviderVerify {
187 reqd.LockExecutables(opts.ProviderSHA256s)
188 }
189 providers, err = resourceProviderFactories(opts.ProviderResolver, reqd)
190 if err != nil {
191 return nil, err
192 }
193 } else {
194 providers = make(map[string]ResourceProviderFactory)
195 }
196
197 diff := opts.Diff
198 if diff == nil {
199 diff = &Diff{}
200 }
201
202 return &Context{
203 components: &basicComponentFactory{
204 providers: providers,
205 provisioners: opts.Provisioners,
206 },
207 destroy: opts.Destroy,
208 diff: diff,
209 hooks: hooks,
210 meta: opts.Meta,
211 module: opts.Module,
212 shadow: opts.Shadow,
213 state: state,
214 targets: opts.Targets,
215 uiInput: opts.UIInput,
216 variables: variables,
217
218 parallelSem: NewSemaphore(par),
219 providerInputConfig: make(map[string]map[string]interface{}),
220 providerSHA256s: opts.ProviderSHA256s,
221 sh: sh,
222 }, nil
223 }
224
225 type ContextGraphOpts struct {
226 // If true, validates the graph structure (checks for cycles).
227 Validate bool
228
229 // Legacy graphs only: won't prune the graph
230 Verbose bool
231 }
232
233 // Graph returns the graph used for the given operation type.
234 //
235 // The most extensive or complex graph type is GraphTypePlan.
236 func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, error) {
237 if opts == nil {
238 opts = &ContextGraphOpts{Validate: true}
239 }
240
241 log.Printf("[INFO] terraform: building graph: %s", typ)
242 switch typ {
243 case GraphTypeApply:
244 return (&ApplyGraphBuilder{
245 Module: c.module,
246 Diff: c.diff,
247 State: c.state,
248 Providers: c.components.ResourceProviders(),
249 Provisioners: c.components.ResourceProvisioners(),
250 Targets: c.targets,
251 Destroy: c.destroy,
252 Validate: opts.Validate,
253 }).Build(RootModulePath)
254
255 case GraphTypeInput:
256 // The input graph is just a slightly modified plan graph
257 fallthrough
258 case GraphTypeValidate:
259 // The validate graph is just a slightly modified plan graph
260 fallthrough
261 case GraphTypePlan:
262 // Create the plan graph builder
263 p := &PlanGraphBuilder{
264 Module: c.module,
265 State: c.state,
266 Providers: c.components.ResourceProviders(),
267 Targets: c.targets,
268 Validate: opts.Validate,
269 }
270
271 // Some special cases for other graph types shared with plan currently
272 var b GraphBuilder = p
273 switch typ {
274 case GraphTypeInput:
275 b = InputGraphBuilder(p)
276 case GraphTypeValidate:
277 // We need to set the provisioners so those can be validated
278 p.Provisioners = c.components.ResourceProvisioners()
279
280 b = ValidateGraphBuilder(p)
281 }
282
283 return b.Build(RootModulePath)
284
285 case GraphTypePlanDestroy:
286 return (&DestroyPlanGraphBuilder{
287 Module: c.module,
288 State: c.state,
289 Targets: c.targets,
290 Validate: opts.Validate,
291 }).Build(RootModulePath)
292
293 case GraphTypeRefresh:
294 return (&RefreshGraphBuilder{
295 Module: c.module,
296 State: c.state,
297 Providers: c.components.ResourceProviders(),
298 Targets: c.targets,
299 Validate: opts.Validate,
300 }).Build(RootModulePath)
301 }
302
303 return nil, fmt.Errorf("unknown graph type: %s", typ)
304 }
305
306 // ShadowError returns any errors caught during a shadow operation.
307 //
308 // A shadow operation is an operation run in parallel to a real operation
309 // that performs the same tasks using new logic on copied state. The results
310 // are compared to ensure that the new logic works the same as the old logic.
311 // The shadow never affects the real operation or return values.
312 //
313 // The result of the shadow operation are only available through this function
314 // call after a real operation is complete.
315 //
316 // For API consumers of Context, you can safely ignore this function
317 // completely if you have no interest in helping report experimental feature
318 // errors to Terraform maintainers. Otherwise, please call this function
319 // after every operation and report this to the user.
320 //
321 // IMPORTANT: Shadow errors are _never_ critical: they _never_ affect
322 // the real state or result of a real operation. They are purely informational
323 // to assist in future Terraform versions being more stable. Please message
324 // this effectively to the end user.
325 //
326 // This must be called only when no other operation is running (refresh,
327 // plan, etc.). The result can be used in parallel to any other operation
328 // running.
329 func (c *Context) ShadowError() error {
330 return c.shadowErr
331 }
332
333 // State returns a copy of the current state associated with this context.
334 //
335 // This cannot safely be called in parallel with any other Context function.
336 func (c *Context) State() *State {
337 return c.state.DeepCopy()
338 }
339
340 // Interpolater returns an Interpolater built on a copy of the state
341 // that can be used to test interpolation values.
342 func (c *Context) Interpolater() *Interpolater {
343 var varLock sync.Mutex
344 var stateLock sync.RWMutex
345 return &Interpolater{
346 Operation: walkApply,
347 Meta: c.meta,
348 Module: c.module,
349 State: c.state.DeepCopy(),
350 StateLock: &stateLock,
351 VariableValues: c.variables,
352 VariableValuesLock: &varLock,
353 }
354 }
355
356 // Input asks for input to fill variables and provider configurations.
357 // This modifies the configuration in-place, so asking for Input twice
358 // may result in different UI output showing different current values.
359 func (c *Context) Input(mode InputMode) error {
360 defer c.acquireRun("input")()
361
362 if mode&InputModeVar != 0 {
363 // Walk the variables first for the root module. We walk them in
364 // alphabetical order for UX reasons.
365 rootConf := c.module.Config()
366 names := make([]string, len(rootConf.Variables))
367 m := make(map[string]*config.Variable)
368 for i, v := range rootConf.Variables {
369 names[i] = v.Name
370 m[v.Name] = v
371 }
372 sort.Strings(names)
373 for _, n := range names {
374 // If we only care about unset variables, then if the variable
375 // is set, continue on.
376 if mode&InputModeVarUnset != 0 {
377 if _, ok := c.variables[n]; ok {
378 continue
379 }
380 }
381
382 var valueType config.VariableType
383
384 v := m[n]
385 switch valueType = v.Type(); valueType {
386 case config.VariableTypeUnknown:
387 continue
388 case config.VariableTypeMap:
389 // OK
390 case config.VariableTypeList:
391 // OK
392 case config.VariableTypeString:
393 // OK
394 default:
395 panic(fmt.Sprintf("Unknown variable type: %#v", v.Type()))
396 }
397
398 // If the variable is not already set, and the variable defines a
399 // default, use that for the value.
400 if _, ok := c.variables[n]; !ok {
401 if v.Default != nil {
402 c.variables[n] = v.Default.(string)
403 continue
404 }
405 }
406
407 // this should only happen during tests
408 if c.uiInput == nil {
409 log.Println("[WARN] Content.uiInput is nil")
410 continue
411 }
412
413 // Ask the user for a value for this variable
414 var value string
415 retry := 0
416 for {
417 var err error
418 value, err = c.uiInput.Input(&InputOpts{
419 Id: fmt.Sprintf("var.%s", n),
420 Query: fmt.Sprintf("var.%s", n),
421 Description: v.Description,
422 })
423 if err != nil {
424 return fmt.Errorf(
425 "Error asking for %s: %s", n, err)
426 }
427
428 if value == "" && v.Required() {
429 // Redo if it is required, but abort if we keep getting
430 // blank entries
431 if retry > 2 {
432 return fmt.Errorf("missing required value for %q", n)
433 }
434 retry++
435 continue
436 }
437
438 break
439 }
440
441 // no value provided, so don't set the variable at all
442 if value == "" {
443 continue
444 }
445
446 decoded, err := parseVariableAsHCL(n, value, valueType)
447 if err != nil {
448 return err
449 }
450
451 if decoded != nil {
452 c.variables[n] = decoded
453 }
454 }
455 }
456
457 if mode&InputModeProvider != 0 {
458 // Build the graph
459 graph, err := c.Graph(GraphTypeInput, nil)
460 if err != nil {
461 return err
462 }
463
464 // Do the walk
465 if _, err := c.walk(graph, walkInput); err != nil {
466 return err
467 }
468 }
469
470 return nil
471 }
472
473 // Apply applies the changes represented by this context and returns
474 // the resulting state.
475 //
476 // Even in the case an error is returned, the state may be returned and will
477 // potentially be partially updated. In addition to returning the resulting
478 // state, this context is updated with the latest state.
479 //
480 // If the state is required after an error, the caller should call
481 // Context.State, rather than rely on the return value.
482 //
483 // TODO: Apply and Refresh should either always return a state, or rely on the
484 // State() method. Currently the helper/resource testing framework relies
485 // on the absence of a returned state to determine if Destroy can be
486 // called, so that will need to be refactored before this can be changed.
487 func (c *Context) Apply() (*State, error) {
488 defer c.acquireRun("apply")()
489
490 // Check there are no empty target parameter values
491 for _, target := range c.targets {
492 if target == "" {
493 return nil, fmt.Errorf("Target parameter must not have empty value")
494 }
495 }
496
497 // Copy our own state
498 c.state = c.state.DeepCopy()
499
500 // Build the graph.
501 graph, err := c.Graph(GraphTypeApply, nil)
502 if err != nil {
503 return nil, err
504 }
505
506 // Determine the operation
507 operation := walkApply
508 if c.destroy {
509 operation = walkDestroy
510 }
511
512 // Walk the graph
513 walker, err := c.walk(graph, operation)
514 if len(walker.ValidationErrors) > 0 {
515 err = multierror.Append(err, walker.ValidationErrors...)
516 }
517
518 // Clean out any unused things
519 c.state.prune()
520
521 return c.state, err
522 }
523
524 // Plan generates an execution plan for the given context.
525 //
526 // The execution plan encapsulates the context and can be stored
527 // in order to reinstantiate a context later for Apply.
528 //
529 // Plan also updates the diff of this context to be the diff generated
530 // by the plan, so Apply can be called after.
531 func (c *Context) Plan() (*Plan, error) {
532 defer c.acquireRun("plan")()
533
534 // Check there are no empty target parameter values
535 for _, target := range c.targets {
536 if target == "" {
537 return nil, fmt.Errorf("Target parameter must not have empty value")
538 }
539 }
540
541 p := &Plan{
542 Module: c.module,
543 Vars: c.variables,
544 State: c.state,
545 Targets: c.targets,
546
547 TerraformVersion: version.String(),
548 ProviderSHA256s: c.providerSHA256s,
549 }
550
551 var operation walkOperation
552 if c.destroy {
553 operation = walkPlanDestroy
554 p.Destroy = true
555 } else {
556 // Set our state to be something temporary. We do this so that
557 // the plan can update a fake state so that variables work, then
558 // we replace it back with our old state.
559 old := c.state
560 if old == nil {
561 c.state = &State{}
562 c.state.init()
563 } else {
564 c.state = old.DeepCopy()
565 }
566 defer func() {
567 c.state = old
568 }()
569
570 operation = walkPlan
571 }
572
573 // Setup our diff
574 c.diffLock.Lock()
575 c.diff = new(Diff)
576 c.diff.init()
577 c.diffLock.Unlock()
578
579 // Build the graph.
580 graphType := GraphTypePlan
581 if c.destroy {
582 graphType = GraphTypePlanDestroy
583 }
584 graph, err := c.Graph(graphType, nil)
585 if err != nil {
586 return nil, err
587 }
588
589 // Do the walk
590 walker, err := c.walk(graph, operation)
591 if err != nil {
592 return nil, err
593 }
594 p.Diff = c.diff
595
596 // If this is true, it means we're running unit tests. In this case,
597 // we perform a deep copy just to ensure that all context tests also
598 // test that a diff is copy-able. This will panic if it fails. This
599 // is enabled during unit tests.
600 //
601 // This should never be true during production usage, but even if it is,
602 // it can't do any real harm.
603 if contextTestDeepCopyOnPlan {
604 p.Diff.DeepCopy()
605 }
606
607 /*
608 // We don't do the reverification during the new destroy plan because
609 // it will use a different apply process.
610 if X_legacyGraph {
611 // Now that we have a diff, we can build the exact graph that Apply will use
612 // and catch any possible cycles during the Plan phase.
613 if _, err := c.Graph(GraphTypeLegacy, nil); err != nil {
614 return nil, err
615 }
616 }
617 */
618
619 var errs error
620 if len(walker.ValidationErrors) > 0 {
621 errs = multierror.Append(errs, walker.ValidationErrors...)
622 }
623 return p, errs
624 }
625
626 // Refresh goes through all the resources in the state and refreshes them
627 // to their latest state. This will update the state that this context
628 // works with, along with returning it.
629 //
630 // Even in the case an error is returned, the state may be returned and
631 // will potentially be partially updated.
632 func (c *Context) Refresh() (*State, error) {
633 defer c.acquireRun("refresh")()
634
635 // Copy our own state
636 c.state = c.state.DeepCopy()
637
638 // Build the graph.
639 graph, err := c.Graph(GraphTypeRefresh, nil)
640 if err != nil {
641 return nil, err
642 }
643
644 // Do the walk
645 if _, err := c.walk(graph, walkRefresh); err != nil {
646 return nil, err
647 }
648
649 // Clean out any unused things
650 c.state.prune()
651
652 return c.state, nil
653 }
654
655 // Stop stops the running task.
656 //
657 // Stop will block until the task completes.
658 func (c *Context) Stop() {
659 log.Printf("[WARN] terraform: Stop called, initiating interrupt sequence")
660
661 c.l.Lock()
662 defer c.l.Unlock()
663
664 // If we're running, then stop
665 if c.runContextCancel != nil {
666 log.Printf("[WARN] terraform: run context exists, stopping")
667
668 // Tell the hook we want to stop
669 c.sh.Stop()
670
671 // Stop the context
672 c.runContextCancel()
673 c.runContextCancel = nil
674 }
675
676 // Grab the condition var before we exit
677 if cond := c.runCond; cond != nil {
678 cond.Wait()
679 }
680
681 log.Printf("[WARN] terraform: stop complete")
682 }
683
684 // Validate validates the configuration and returns any warnings or errors.
685 func (c *Context) Validate() tfdiags.Diagnostics {
686 defer c.acquireRun("validate")()
687
688 var diags tfdiags.Diagnostics
689
690 // Validate the configuration itself
691 diags = diags.Append(c.module.Validate())
692
693 // This only needs to be done for the root module, since inter-module
694 // variables are validated in the module tree.
695 if config := c.module.Config(); config != nil {
696 // Validate the user variables
697 for _, err := range smcUserVariables(config, c.variables) {
698 diags = diags.Append(err)
699 }
700 }
701
702 // If we have errors at this point, the graphing has no chance,
703 // so just bail early.
704 if diags.HasErrors() {
705 return diags
706 }
707
708 // Build the graph so we can walk it and run Validate on nodes.
709 // We also validate the graph generated here, but this graph doesn't
710 // necessarily match the graph that Plan will generate, so we'll validate the
711 // graph again later after Planning.
712 graph, err := c.Graph(GraphTypeValidate, nil)
713 if err != nil {
714 diags = diags.Append(err)
715 return diags
716 }
717
718 // Walk
719 walker, err := c.walk(graph, walkValidate)
720 if err != nil {
721 diags = diags.Append(err)
722 }
723
724 sort.Strings(walker.ValidationWarnings)
725 sort.Slice(walker.ValidationErrors, func(i, j int) bool {
726 return walker.ValidationErrors[i].Error() < walker.ValidationErrors[j].Error()
727 })
728
729 for _, warn := range walker.ValidationWarnings {
730 diags = diags.Append(tfdiags.SimpleWarning(warn))
731 }
732 for _, err := range walker.ValidationErrors {
733 diags = diags.Append(err)
734 }
735
736 return diags
737 }
738
739 // Module returns the module tree associated with this context.
740 func (c *Context) Module() *module.Tree {
741 return c.module
742 }
743
744 // Variables will return the mapping of variables that were defined
745 // for this Context. If Input was called, this mapping may be different
746 // than what was given.
747 func (c *Context) Variables() map[string]interface{} {
748 return c.variables
749 }
750
751 // SetVariable sets a variable after a context has already been built.
752 func (c *Context) SetVariable(k string, v interface{}) {
753 c.variables[k] = v
754 }
755
756 func (c *Context) acquireRun(phase string) func() {
757 // With the run lock held, grab the context lock to make changes
758 // to the run context.
759 c.l.Lock()
760 defer c.l.Unlock()
761
762 // Wait until we're no longer running
763 for c.runCond != nil {
764 c.runCond.Wait()
765 }
766
767 // Build our lock
768 c.runCond = sync.NewCond(&c.l)
769
770 // Setup debugging
771 dbug.SetPhase(phase)
772
773 // Create a new run context
774 c.runContext, c.runContextCancel = context.WithCancel(context.Background())
775
776 // Reset the stop hook so we're not stopped
777 c.sh.Reset()
778
779 // Reset the shadow errors
780 c.shadowErr = nil
781
782 return c.releaseRun
783 }
784
785 func (c *Context) releaseRun() {
786 // Grab the context lock so that we can make modifications to fields
787 c.l.Lock()
788 defer c.l.Unlock()
789
790 // setting the phase to "INVALID" lets us easily detect if we have
791 // operations happening outside of a run, or we missed setting the proper
792 // phase
793 dbug.SetPhase("INVALID")
794
795 // End our run. We check if runContext is non-nil because it can be
796 // set to nil if it was cancelled via Stop()
797 if c.runContextCancel != nil {
798 c.runContextCancel()
799 }
800
801 // Unlock all waiting our condition
802 cond := c.runCond
803 c.runCond = nil
804 cond.Broadcast()
805
806 // Unset the context
807 c.runContext = nil
808 }
809
810 func (c *Context) walk(graph *Graph, operation walkOperation) (*ContextGraphWalker, error) {
811 // Keep track of the "real" context which is the context that does
812 // the real work: talking to real providers, modifying real state, etc.
813 realCtx := c
814
815 log.Printf("[DEBUG] Starting graph walk: %s", operation.String())
816
817 walker := &ContextGraphWalker{
818 Context: realCtx,
819 Operation: operation,
820 StopContext: c.runContext,
821 }
822
823 // Watch for a stop so we can call the provider Stop() API.
824 watchStop, watchWait := c.watchStop(walker)
825
826 // Walk the real graph, this will block until it completes
827 realErr := graph.Walk(walker)
828
829 // Close the channel so the watcher stops, and wait for it to return.
830 close(watchStop)
831 <-watchWait
832
833 return walker, realErr
834 }
835
836 // watchStop immediately returns a `stop` and a `wait` chan after dispatching
837 // the watchStop goroutine. This will watch the runContext for cancellation and
838 // stop the providers accordingly. When the watch is no longer needed, the
839 // `stop` chan should be closed before waiting on the `wait` chan.
840 // The `wait` chan is important, because without synchronizing with the end of
841 // the watchStop goroutine, the runContext may also be closed during the select
842 // incorrectly causing providers to be stopped. Even if the graph walk is done
843 // at that point, stopping a provider permanently cancels its StopContext which
844 // can cause later actions to fail.
845 func (c *Context) watchStop(walker *ContextGraphWalker) (chan struct{}, <-chan struct{}) {
846 stop := make(chan struct{})
847 wait := make(chan struct{})
848
849 // get the runContext cancellation channel now, because releaseRun will
850 // write to the runContext field.
851 done := c.runContext.Done()
852
853 go func() {
854 defer close(wait)
855 // Wait for a stop or completion
856 select {
857 case <-done:
858 // done means the context was canceled, so we need to try and stop
859 // providers.
860 case <-stop:
861 // our own stop channel was closed.
862 return
863 }
864
865 // If we're here, we're stopped, trigger the call.
866
867 {
868 // Copy the providers so that a misbehaved blocking Stop doesn't
869 // completely hang Terraform.
870 walker.providerLock.Lock()
871 ps := make([]ResourceProvider, 0, len(walker.providerCache))
872 for _, p := range walker.providerCache {
873 ps = append(ps, p)
874 }
875 defer walker.providerLock.Unlock()
876
877 for _, p := range ps {
878 // We ignore the error for now since there isn't any reasonable
879 // action to take if there is an error here, since the stop is still
880 // advisory: Terraform will exit once the graph node completes.
881 p.Stop()
882 }
883 }
884
885 {
886 // Call stop on all the provisioners
887 walker.provisionerLock.Lock()
888 ps := make([]ResourceProvisioner, 0, len(walker.provisionerCache))
889 for _, p := range walker.provisionerCache {
890 ps = append(ps, p)
891 }
892 defer walker.provisionerLock.Unlock()
893
894 for _, p := range ps {
895 // We ignore the error for now since there isn't any reasonable
896 // action to take if there is an error here, since the stop is still
897 // advisory: Terraform will exit once the graph node completes.
898 p.Stop()
899 }
900 }
901 }()
902
903 return stop, wait
904 }
905
906 // parseVariableAsHCL parses the value of a single variable as would have been specified
907 // on the command line via -var or in an environment variable named TF_VAR_x, where x is
908 // the name of the variable. In order to get around the restriction of HCL requiring a
909 // top level object, we prepend a sentinel key, decode the user-specified value as its
910 // value and pull the value back out of the resulting map.
911 func parseVariableAsHCL(name string, input string, targetType config.VariableType) (interface{}, error) {
912 // expecting a string so don't decode anything, just strip quotes
913 if targetType == config.VariableTypeString {
914 return strings.Trim(input, `"`), nil
915 }
916
917 // return empty types
918 if strings.TrimSpace(input) == "" {
919 switch targetType {
920 case config.VariableTypeList:
921 return []interface{}{}, nil
922 case config.VariableTypeMap:
923 return make(map[string]interface{}), nil
924 }
925 }
926
927 const sentinelValue = "SENTINEL_TERRAFORM_VAR_OVERRIDE_KEY"
928 inputWithSentinal := fmt.Sprintf("%s = %s", sentinelValue, input)
929
930 var decoded map[string]interface{}
931 err := hcl.Decode(&decoded, inputWithSentinal)
932 if err != nil {
933 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL: %s", name, input, err)
934 }
935
936 if len(decoded) != 1 {
937 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. Only one value may be specified.", name, input)
938 }
939
940 parsedValue, ok := decoded[sentinelValue]
941 if !ok {
942 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input)
943 }
944
945 switch targetType {
946 case config.VariableTypeList:
947 return parsedValue, nil
948 case config.VariableTypeMap:
949 if list, ok := parsedValue.([]map[string]interface{}); ok {
950 return list[0], nil
951 }
952
953 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input)
954 default:
955 panic(fmt.Errorf("unknown type %s", targetType.Printable()))
956 }
957 }