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