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