github.com/burmuley/terraform@v0.11.12-beta1/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 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 }