github.com/ojongerius/terraform@v0.7.1-0.20160811111335-97fcd5f4cc90/terraform/context.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "os" 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 ) 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 InputModeVarUnset 27 28 // InputModeProvider asks for provider variables 29 InputModeProvider 30 31 // InputModeStd is the standard operating mode and asks for both variables 32 // and providers. 33 InputModeStd = InputModeVar | InputModeProvider 34 ) 35 36 // ContextOpts are the user-configurable options to create a context with 37 // NewContext. 38 type ContextOpts struct { 39 Destroy bool 40 Diff *Diff 41 Hooks []Hook 42 Module *module.Tree 43 Parallelism int 44 State *State 45 StateFutureAllowed bool 46 Providers map[string]ResourceProviderFactory 47 Provisioners map[string]ResourceProvisionerFactory 48 Targets []string 49 Variables map[string]interface{} 50 51 UIInput UIInput 52 } 53 54 // Context represents all the context that Terraform needs in order to 55 // perform operations on infrastructure. This structure is built using 56 // NewContext. See the documentation for that. 57 // 58 // Extra functions on Context can be found in context_*.go files. 59 type Context struct { 60 destroy bool 61 diff *Diff 62 diffLock sync.RWMutex 63 hooks []Hook 64 module *module.Tree 65 providers map[string]ResourceProviderFactory 66 provisioners map[string]ResourceProvisionerFactory 67 sh *stopHook 68 state *State 69 stateLock sync.RWMutex 70 targets []string 71 uiInput UIInput 72 variables map[string]interface{} 73 74 l sync.Mutex // Lock acquired during any task 75 parallelSem Semaphore 76 providerInputConfig map[string]map[string]interface{} 77 runCh <-chan struct{} 78 } 79 80 // NewContext creates a new Context structure. 81 // 82 // Once a Context is creator, the pointer values within ContextOpts 83 // should not be mutated in any way, since the pointers are copied, not 84 // the values themselves. 85 func NewContext(opts *ContextOpts) (*Context, error) { 86 // Copy all the hooks and add our stop hook. We don't append directly 87 // to the Config so that we're not modifying that in-place. 88 sh := new(stopHook) 89 hooks := make([]Hook, len(opts.Hooks)+1) 90 copy(hooks, opts.Hooks) 91 hooks[len(opts.Hooks)] = sh 92 93 state := opts.State 94 if state == nil { 95 state = new(State) 96 state.init() 97 } 98 99 // If our state is from the future, then error. Callers can avoid 100 // this error by explicitly setting `StateFutureAllowed`. 101 if !opts.StateFutureAllowed && state.FromFutureTerraform() { 102 return nil, fmt.Errorf( 103 "Terraform doesn't allow running any operations against a state\n"+ 104 "that was written by a future Terraform version. The state is\n"+ 105 "reporting it is written by Terraform '%s'.\n\n"+ 106 "Please run at least that version of Terraform to continue.", 107 state.TFVersion) 108 } 109 110 // Explicitly reset our state version to our current version so that 111 // any operations we do will write out that our latest version 112 // has run. 113 state.TFVersion = Version 114 115 // Determine parallelism, default to 10. We do this both to limit 116 // CPU pressure but also to have an extra guard against rate throttling 117 // from providers. 118 par := opts.Parallelism 119 if par == 0 { 120 par = 10 121 } 122 123 // Set up the variables in the following sequence: 124 // 0 - Take default values from the configuration 125 // 1 - Take values from TF_VAR_x environment variables 126 // 2 - Take values specified in -var flags, overriding values 127 // set by environment variables if necessary. This includes 128 // values taken from -var-file in addition. 129 variables := make(map[string]interface{}) 130 131 if opts.Module != nil { 132 for _, v := range opts.Module.Config().Variables { 133 if v.Default != nil { 134 if v.Type() == config.VariableTypeString { 135 // v.Default has already been parsed as HCL so there may be 136 // some stray ints in there 137 switch typedDefault := v.Default.(type) { 138 case string: 139 if typedDefault == "" { 140 continue 141 } 142 variables[v.Name] = typedDefault 143 case int, int64: 144 variables[v.Name] = fmt.Sprintf("%d", typedDefault) 145 case float32, float64: 146 variables[v.Name] = fmt.Sprintf("%f", typedDefault) 147 case bool: 148 variables[v.Name] = fmt.Sprintf("%t", typedDefault) 149 } 150 } else { 151 variables[v.Name] = v.Default 152 } 153 } 154 } 155 156 for _, v := range os.Environ() { 157 if !strings.HasPrefix(v, VarEnvPrefix) { 158 continue 159 } 160 161 // Strip off the prefix and get the value after the first "=" 162 idx := strings.Index(v, "=") 163 k := v[len(VarEnvPrefix):idx] 164 v = v[idx+1:] 165 166 // Override the configuration-default values. Note that *not* finding the variable 167 // in configuration is OK, as we don't want to preclude people from having multiple 168 // sets of TF_VAR_whatever in their environment even if it is a little weird. 169 for _, schema := range opts.Module.Config().Variables { 170 if schema.Name == k { 171 varType := schema.Type() 172 varVal, err := parseVariableAsHCL(k, v, varType) 173 if err != nil { 174 return nil, err 175 } 176 switch varType { 177 case config.VariableTypeMap: 178 if existing, hasMap := variables[k]; !hasMap { 179 variables[k] = varVal 180 } else { 181 if existingMap, ok := existing.(map[string]interface{}); !ok { 182 panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k)) 183 } else { 184 switch typedV := varVal.(type) { 185 case []map[string]interface{}: 186 for newKey, newVal := range typedV[0] { 187 existingMap[newKey] = newVal 188 } 189 case map[string]interface{}: 190 for newKey, newVal := range typedV { 191 existingMap[newKey] = newVal 192 } 193 default: 194 panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k)) 195 } 196 } 197 } 198 default: 199 variables[k] = varVal 200 } 201 } 202 } 203 } 204 205 for k, v := range opts.Variables { 206 for _, schema := range opts.Module.Config().Variables { 207 if schema.Name == k { 208 switch schema.Type() { 209 case config.VariableTypeMap: 210 if existing, hasMap := variables[k]; !hasMap { 211 variables[k] = v 212 } else { 213 if existingMap, ok := existing.(map[string]interface{}); !ok { 214 panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k)) 215 } else { 216 switch typedV := v.(type) { 217 case []map[string]interface{}: 218 for newKey, newVal := range typedV[0] { 219 existingMap[newKey] = newVal 220 } 221 case map[string]interface{}: 222 for newKey, newVal := range typedV { 223 existingMap[newKey] = newVal 224 } 225 default: 226 panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k)) 227 } 228 } 229 } 230 default: 231 variables[k] = v 232 } 233 } 234 } 235 } 236 } 237 238 return &Context{ 239 destroy: opts.Destroy, 240 diff: opts.Diff, 241 hooks: hooks, 242 module: opts.Module, 243 providers: opts.Providers, 244 provisioners: opts.Provisioners, 245 state: state, 246 targets: opts.Targets, 247 uiInput: opts.UIInput, 248 variables: variables, 249 250 parallelSem: NewSemaphore(par), 251 providerInputConfig: make(map[string]map[string]interface{}), 252 sh: sh, 253 }, nil 254 } 255 256 type ContextGraphOpts struct { 257 Validate bool 258 Verbose bool 259 } 260 261 // Graph returns the graph for this config. 262 func (c *Context) Graph(g *ContextGraphOpts) (*Graph, error) { 263 return c.graphBuilder(g).Build(RootModulePath) 264 } 265 266 // GraphBuilder returns the GraphBuilder that will be used to create 267 // the graphs for this context. 268 func (c *Context) graphBuilder(g *ContextGraphOpts) GraphBuilder { 269 // TODO test 270 providers := make([]string, 0, len(c.providers)) 271 for k, _ := range c.providers { 272 providers = append(providers, k) 273 } 274 275 provisioners := make([]string, 0, len(c.provisioners)) 276 for k, _ := range c.provisioners { 277 provisioners = append(provisioners, k) 278 } 279 280 return &BuiltinGraphBuilder{ 281 Root: c.module, 282 Diff: c.diff, 283 Providers: providers, 284 Provisioners: provisioners, 285 State: c.state, 286 Targets: c.targets, 287 Destroy: c.destroy, 288 Validate: g.Validate, 289 Verbose: g.Verbose, 290 } 291 } 292 293 // Input asks for input to fill variables and provider configurations. 294 // This modifies the configuration in-place, so asking for Input twice 295 // may result in different UI output showing different current values. 296 func (c *Context) Input(mode InputMode) error { 297 v := c.acquireRun() 298 defer c.releaseRun(v) 299 300 if mode&InputModeVar != 0 { 301 // Walk the variables first for the root module. We walk them in 302 // alphabetical order for UX reasons. 303 rootConf := c.module.Config() 304 names := make([]string, len(rootConf.Variables)) 305 m := make(map[string]*config.Variable) 306 for i, v := range rootConf.Variables { 307 names[i] = v.Name 308 m[v.Name] = v 309 } 310 sort.Strings(names) 311 for _, n := range names { 312 // If we only care about unset variables, then if the variable 313 // is set, continue on. 314 if mode&InputModeVarUnset != 0 { 315 if _, ok := c.variables[n]; ok { 316 continue 317 } 318 } 319 320 var valueType config.VariableType 321 322 v := m[n] 323 switch valueType = v.Type(); valueType { 324 case config.VariableTypeUnknown: 325 continue 326 case config.VariableTypeMap: 327 // OK 328 case config.VariableTypeList: 329 // OK 330 case config.VariableTypeString: 331 // OK 332 default: 333 panic(fmt.Sprintf("Unknown variable type: %#v", v.Type())) 334 } 335 336 // If the variable is not already set, and the variable defines a 337 // default, use that for the value. 338 if _, ok := c.variables[n]; !ok { 339 if v.Default != nil { 340 c.variables[n] = v.Default.(string) 341 continue 342 } 343 } 344 345 // this should only happen during tests 346 if c.uiInput == nil { 347 log.Println("[WARN] Content.uiInput is nil") 348 continue 349 } 350 351 // Ask the user for a value for this variable 352 var value string 353 retry := 0 354 for { 355 var err error 356 value, err = c.uiInput.Input(&InputOpts{ 357 Id: fmt.Sprintf("var.%s", n), 358 Query: fmt.Sprintf("var.%s", n), 359 Description: v.Description, 360 }) 361 if err != nil { 362 return fmt.Errorf( 363 "Error asking for %s: %s", n, err) 364 } 365 366 if value == "" && v.Required() { 367 // Redo if it is required, but abort if we keep getting 368 // blank entries 369 if retry > 2 { 370 return fmt.Errorf("missing required value for %q", n) 371 } 372 retry++ 373 continue 374 } 375 376 break 377 } 378 379 // no value provided, so don't set the variable at all 380 if value == "" { 381 continue 382 } 383 384 decoded, err := parseVariableAsHCL(n, value, valueType) 385 if err != nil { 386 return err 387 } 388 389 if decoded != nil { 390 c.variables[n] = decoded 391 } 392 } 393 } 394 395 if mode&InputModeProvider != 0 { 396 // Build the graph 397 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 398 if err != nil { 399 return err 400 } 401 402 // Do the walk 403 if _, err := c.walk(graph, walkInput); err != nil { 404 return err 405 } 406 } 407 408 return nil 409 } 410 411 // Apply applies the changes represented by this context and returns 412 // the resulting state. 413 // 414 // In addition to returning the resulting state, this context is updated 415 // with the latest state. 416 func (c *Context) Apply() (*State, error) { 417 v := c.acquireRun() 418 defer c.releaseRun(v) 419 420 // Copy our own state 421 c.state = c.state.DeepCopy() 422 423 // Build the graph 424 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 425 if err != nil { 426 return nil, err 427 } 428 429 // Do the walk 430 var walker *ContextGraphWalker 431 if c.destroy { 432 walker, err = c.walk(graph, walkDestroy) 433 } else { 434 walker, err = c.walk(graph, walkApply) 435 } 436 437 if len(walker.ValidationErrors) > 0 { 438 err = multierror.Append(err, walker.ValidationErrors...) 439 } 440 441 // Clean out any unused things 442 c.state.prune() 443 444 return c.state, err 445 } 446 447 // Plan generates an execution plan for the given context. 448 // 449 // The execution plan encapsulates the context and can be stored 450 // in order to reinstantiate a context later for Apply. 451 // 452 // Plan also updates the diff of this context to be the diff generated 453 // by the plan, so Apply can be called after. 454 func (c *Context) Plan() (*Plan, error) { 455 v := c.acquireRun() 456 defer c.releaseRun(v) 457 458 p := &Plan{ 459 Module: c.module, 460 Vars: c.variables, 461 State: c.state, 462 Targets: c.targets, 463 } 464 465 var operation walkOperation 466 if c.destroy { 467 operation = walkPlanDestroy 468 } else { 469 // Set our state to be something temporary. We do this so that 470 // the plan can update a fake state so that variables work, then 471 // we replace it back with our old state. 472 old := c.state 473 if old == nil { 474 c.state = &State{} 475 c.state.init() 476 } else { 477 c.state = old.DeepCopy() 478 } 479 defer func() { 480 c.state = old 481 }() 482 483 operation = walkPlan 484 } 485 486 // Setup our diff 487 c.diffLock.Lock() 488 c.diff = new(Diff) 489 c.diff.init() 490 c.diffLock.Unlock() 491 492 // Build the graph 493 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 494 if err != nil { 495 return nil, err 496 } 497 498 // Do the walk 499 walker, err := c.walk(graph, operation) 500 if err != nil { 501 return nil, err 502 } 503 p.Diff = c.diff 504 505 // Now that we have a diff, we can build the exact graph that Apply will use 506 // and catch any possible cycles during the Plan phase. 507 if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil { 508 return nil, err 509 } 510 var errs error 511 if len(walker.ValidationErrors) > 0 { 512 errs = multierror.Append(errs, walker.ValidationErrors...) 513 } 514 return p, errs 515 } 516 517 // Refresh goes through all the resources in the state and refreshes them 518 // to their latest state. This will update the state that this context 519 // works with, along with returning it. 520 // 521 // Even in the case an error is returned, the state will be returned and 522 // will potentially be partially updated. 523 func (c *Context) Refresh() (*State, error) { 524 v := c.acquireRun() 525 defer c.releaseRun(v) 526 527 // Copy our own state 528 c.state = c.state.DeepCopy() 529 530 // Build the graph 531 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 532 if err != nil { 533 return nil, err 534 } 535 536 // Do the walk 537 if _, err := c.walk(graph, walkRefresh); err != nil { 538 return nil, err 539 } 540 541 // Clean out any unused things 542 c.state.prune() 543 544 return c.state, nil 545 } 546 547 // Stop stops the running task. 548 // 549 // Stop will block until the task completes. 550 func (c *Context) Stop() { 551 c.l.Lock() 552 ch := c.runCh 553 554 // If we aren't running, then just return 555 if ch == nil { 556 c.l.Unlock() 557 return 558 } 559 560 // Tell the hook we want to stop 561 c.sh.Stop() 562 563 // Wait for us to stop 564 c.l.Unlock() 565 <-ch 566 } 567 568 // Validate validates the configuration and returns any warnings or errors. 569 func (c *Context) Validate() ([]string, []error) { 570 v := c.acquireRun() 571 defer c.releaseRun(v) 572 573 var errs error 574 575 // Validate the configuration itself 576 if err := c.module.Validate(); err != nil { 577 errs = multierror.Append(errs, err) 578 } 579 580 // This only needs to be done for the root module, since inter-module 581 // variables are validated in the module tree. 582 if config := c.module.Config(); config != nil { 583 // Validate the user variables 584 if err := smcUserVariables(config, c.variables); len(err) > 0 { 585 errs = multierror.Append(errs, err...) 586 } 587 } 588 589 // If we have errors at this point, the graphing has no chance, 590 // so just bail early. 591 if errs != nil { 592 return nil, []error{errs} 593 } 594 595 // Build the graph so we can walk it and run Validate on nodes. 596 // We also validate the graph generated here, but this graph doesn't 597 // necessarily match the graph that Plan will generate, so we'll validate the 598 // graph again later after Planning. 599 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 600 if err != nil { 601 return nil, []error{err} 602 } 603 604 // Walk 605 walker, err := c.walk(graph, walkValidate) 606 if err != nil { 607 return nil, multierror.Append(errs, err).Errors 608 } 609 610 // Return the result 611 rerrs := multierror.Append(errs, walker.ValidationErrors...) 612 return walker.ValidationWarnings, rerrs.Errors 613 } 614 615 // Module returns the module tree associated with this context. 616 func (c *Context) Module() *module.Tree { 617 return c.module 618 } 619 620 // Variables will return the mapping of variables that were defined 621 // for this Context. If Input was called, this mapping may be different 622 // than what was given. 623 func (c *Context) Variables() map[string]interface{} { 624 return c.variables 625 } 626 627 // SetVariable sets a variable after a context has already been built. 628 func (c *Context) SetVariable(k string, v interface{}) { 629 c.variables[k] = v 630 } 631 632 func (c *Context) acquireRun() chan<- struct{} { 633 c.l.Lock() 634 defer c.l.Unlock() 635 636 // Wait for no channel to exist 637 for c.runCh != nil { 638 c.l.Unlock() 639 ch := c.runCh 640 <-ch 641 c.l.Lock() 642 } 643 644 ch := make(chan struct{}) 645 c.runCh = ch 646 return ch 647 } 648 649 func (c *Context) releaseRun(ch chan<- struct{}) { 650 c.l.Lock() 651 defer c.l.Unlock() 652 653 close(ch) 654 c.runCh = nil 655 c.sh.Reset() 656 } 657 658 func (c *Context) walk( 659 graph *Graph, operation walkOperation) (*ContextGraphWalker, error) { 660 // Walk the graph 661 log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) 662 walker := &ContextGraphWalker{Context: c, Operation: operation} 663 return walker, graph.Walk(walker) 664 } 665 666 // parseVariableAsHCL parses the value of a single variable as would have been specified 667 // on the command line via -var or in an environment variable named TF_VAR_x, where x is 668 // the name of the variable. In order to get around the restriction of HCL requiring a 669 // top level object, we prepend a sentinel key, decode the user-specified value as its 670 // value and pull the value back out of the resulting map. 671 func parseVariableAsHCL(name string, input string, targetType config.VariableType) (interface{}, error) { 672 // expecting a string so don't decode anything, just strip quotes 673 if targetType == config.VariableTypeString { 674 return strings.Trim(input, `"`), nil 675 } 676 677 // return empty types 678 if strings.TrimSpace(input) == "" { 679 switch targetType { 680 case config.VariableTypeList: 681 return []interface{}{}, nil 682 case config.VariableTypeMap: 683 return make(map[string]interface{}), nil 684 } 685 } 686 687 const sentinelValue = "SENTINEL_TERRAFORM_VAR_OVERRIDE_KEY" 688 inputWithSentinal := fmt.Sprintf("%s = %s", sentinelValue, input) 689 690 var decoded map[string]interface{} 691 err := hcl.Decode(&decoded, inputWithSentinal) 692 if err != nil { 693 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL: %s", name, input, err) 694 } 695 696 if len(decoded) != 1 { 697 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. Only one value may be specified.", name, input) 698 } 699 700 parsedValue, ok := decoded[sentinelValue] 701 if !ok { 702 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input) 703 } 704 705 switch targetType { 706 case config.VariableTypeList: 707 return parsedValue, nil 708 case config.VariableTypeMap: 709 if list, ok := parsedValue.([]map[string]interface{}); ok { 710 return list[0], nil 711 } 712 713 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input) 714 default: 715 panic(fmt.Errorf("unknown type %s", targetType.Printable())) 716 } 717 }