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