github.com/kwoods/terraform@v0.6.11-0.20160809170336-13497db7138e/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 v := m[n] 321 switch v.Type() { 322 case config.VariableTypeUnknown: 323 continue 324 case config.VariableTypeMap: 325 continue 326 case config.VariableTypeList: 327 continue 328 case config.VariableTypeString: 329 // Good! 330 default: 331 panic(fmt.Sprintf("Unknown variable type: %#v", v.Type())) 332 } 333 334 // If the variable is not already set, and the variable defines a 335 // default, use that for the value. 336 if _, ok := c.variables[n]; !ok { 337 if v.Default != nil { 338 c.variables[n] = v.Default.(string) 339 continue 340 } 341 } 342 343 // Ask the user for a value for this variable 344 var value string 345 retry := 0 346 for { 347 var err error 348 value, err = c.uiInput.Input(&InputOpts{ 349 Id: fmt.Sprintf("var.%s", n), 350 Query: fmt.Sprintf("var.%s", n), 351 Description: v.Description, 352 }) 353 if err != nil { 354 return fmt.Errorf( 355 "Error asking for %s: %s", n, err) 356 } 357 358 if value == "" && v.Required() { 359 // Redo if it is required, but abort if we keep getting 360 // blank entries 361 if retry > 2 { 362 return fmt.Errorf("missing required value for %q", n) 363 } 364 retry++ 365 continue 366 } 367 368 if value == "" { 369 // No value, just exit the loop. With no value, we just 370 // use whatever is currently set in variables. 371 break 372 } 373 374 break 375 } 376 377 if value != "" { 378 c.variables[n] = value 379 } 380 } 381 } 382 383 if mode&InputModeProvider != 0 { 384 // Build the graph 385 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 386 if err != nil { 387 return err 388 } 389 390 // Do the walk 391 if _, err := c.walk(graph, walkInput); err != nil { 392 return err 393 } 394 } 395 396 return nil 397 } 398 399 // Apply applies the changes represented by this context and returns 400 // the resulting state. 401 // 402 // In addition to returning the resulting state, this context is updated 403 // with the latest state. 404 func (c *Context) Apply() (*State, error) { 405 v := c.acquireRun() 406 defer c.releaseRun(v) 407 408 // Copy our own state 409 c.state = c.state.DeepCopy() 410 411 // Build the graph 412 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 413 if err != nil { 414 return nil, err 415 } 416 417 // Do the walk 418 var walker *ContextGraphWalker 419 if c.destroy { 420 walker, err = c.walk(graph, walkDestroy) 421 } else { 422 walker, err = c.walk(graph, walkApply) 423 } 424 425 if len(walker.ValidationErrors) > 0 { 426 err = multierror.Append(err, walker.ValidationErrors...) 427 } 428 429 // Clean out any unused things 430 c.state.prune() 431 432 return c.state, err 433 } 434 435 // Plan generates an execution plan for the given context. 436 // 437 // The execution plan encapsulates the context and can be stored 438 // in order to reinstantiate a context later for Apply. 439 // 440 // Plan also updates the diff of this context to be the diff generated 441 // by the plan, so Apply can be called after. 442 func (c *Context) Plan() (*Plan, error) { 443 v := c.acquireRun() 444 defer c.releaseRun(v) 445 446 p := &Plan{ 447 Module: c.module, 448 Vars: c.variables, 449 State: c.state, 450 Targets: c.targets, 451 } 452 453 var operation walkOperation 454 if c.destroy { 455 operation = walkPlanDestroy 456 } else { 457 // Set our state to be something temporary. We do this so that 458 // the plan can update a fake state so that variables work, then 459 // we replace it back with our old state. 460 old := c.state 461 if old == nil { 462 c.state = &State{} 463 c.state.init() 464 } else { 465 c.state = old.DeepCopy() 466 } 467 defer func() { 468 c.state = old 469 }() 470 471 operation = walkPlan 472 } 473 474 // Setup our diff 475 c.diffLock.Lock() 476 c.diff = new(Diff) 477 c.diff.init() 478 c.diffLock.Unlock() 479 480 // Build the graph 481 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 482 if err != nil { 483 return nil, err 484 } 485 486 // Do the walk 487 walker, err := c.walk(graph, operation) 488 if err != nil { 489 return nil, err 490 } 491 p.Diff = c.diff 492 493 // Now that we have a diff, we can build the exact graph that Apply will use 494 // and catch any possible cycles during the Plan phase. 495 if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil { 496 return nil, err 497 } 498 var errs error 499 if len(walker.ValidationErrors) > 0 { 500 errs = multierror.Append(errs, walker.ValidationErrors...) 501 } 502 return p, errs 503 } 504 505 // Refresh goes through all the resources in the state and refreshes them 506 // to their latest state. This will update the state that this context 507 // works with, along with returning it. 508 // 509 // Even in the case an error is returned, the state will be returned and 510 // will potentially be partially updated. 511 func (c *Context) Refresh() (*State, error) { 512 v := c.acquireRun() 513 defer c.releaseRun(v) 514 515 // Copy our own state 516 c.state = c.state.DeepCopy() 517 518 // Build the graph 519 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 520 if err != nil { 521 return nil, err 522 } 523 524 // Do the walk 525 if _, err := c.walk(graph, walkRefresh); err != nil { 526 return nil, err 527 } 528 529 // Clean out any unused things 530 c.state.prune() 531 532 return c.state, nil 533 } 534 535 // Stop stops the running task. 536 // 537 // Stop will block until the task completes. 538 func (c *Context) Stop() { 539 c.l.Lock() 540 ch := c.runCh 541 542 // If we aren't running, then just return 543 if ch == nil { 544 c.l.Unlock() 545 return 546 } 547 548 // Tell the hook we want to stop 549 c.sh.Stop() 550 551 // Wait for us to stop 552 c.l.Unlock() 553 <-ch 554 } 555 556 // Validate validates the configuration and returns any warnings or errors. 557 func (c *Context) Validate() ([]string, []error) { 558 v := c.acquireRun() 559 defer c.releaseRun(v) 560 561 var errs error 562 563 // Validate the configuration itself 564 if err := c.module.Validate(); err != nil { 565 errs = multierror.Append(errs, err) 566 } 567 568 // This only needs to be done for the root module, since inter-module 569 // variables are validated in the module tree. 570 if config := c.module.Config(); config != nil { 571 // Validate the user variables 572 if err := smcUserVariables(config, c.variables); len(err) > 0 { 573 errs = multierror.Append(errs, err...) 574 } 575 } 576 577 // If we have errors at this point, the graphing has no chance, 578 // so just bail early. 579 if errs != nil { 580 return nil, []error{errs} 581 } 582 583 // Build the graph so we can walk it and run Validate on nodes. 584 // We also validate the graph generated here, but this graph doesn't 585 // necessarily match the graph that Plan will generate, so we'll validate the 586 // graph again later after Planning. 587 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 588 if err != nil { 589 return nil, []error{err} 590 } 591 592 // Walk 593 walker, err := c.walk(graph, walkValidate) 594 if err != nil { 595 return nil, multierror.Append(errs, err).Errors 596 } 597 598 // Return the result 599 rerrs := multierror.Append(errs, walker.ValidationErrors...) 600 return walker.ValidationWarnings, rerrs.Errors 601 } 602 603 // Module returns the module tree associated with this context. 604 func (c *Context) Module() *module.Tree { 605 return c.module 606 } 607 608 // Variables will return the mapping of variables that were defined 609 // for this Context. If Input was called, this mapping may be different 610 // than what was given. 611 func (c *Context) Variables() map[string]interface{} { 612 return c.variables 613 } 614 615 // SetVariable sets a variable after a context has already been built. 616 func (c *Context) SetVariable(k string, v interface{}) { 617 c.variables[k] = v 618 } 619 620 func (c *Context) acquireRun() chan<- struct{} { 621 c.l.Lock() 622 defer c.l.Unlock() 623 624 // Wait for no channel to exist 625 for c.runCh != nil { 626 c.l.Unlock() 627 ch := c.runCh 628 <-ch 629 c.l.Lock() 630 } 631 632 ch := make(chan struct{}) 633 c.runCh = ch 634 return ch 635 } 636 637 func (c *Context) releaseRun(ch chan<- struct{}) { 638 c.l.Lock() 639 defer c.l.Unlock() 640 641 close(ch) 642 c.runCh = nil 643 c.sh.Reset() 644 } 645 646 func (c *Context) walk( 647 graph *Graph, operation walkOperation) (*ContextGraphWalker, error) { 648 // Walk the graph 649 log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) 650 walker := &ContextGraphWalker{Context: c, Operation: operation} 651 return walker, graph.Walk(walker) 652 } 653 654 // parseVariableAsHCL parses the value of a single variable as would have been specified 655 // on the command line via -var or in an environment variable named TF_VAR_x, where x is 656 // the name of the variable. In order to get around the restriction of HCL requiring a 657 // top level object, we prepend a sentinel key, decode the user-specified value as its 658 // value and pull the value back out of the resulting map. 659 func parseVariableAsHCL(name string, input interface{}, targetType config.VariableType) (interface{}, error) { 660 if targetType == config.VariableTypeString { 661 return input, nil 662 } 663 664 const sentinelValue = "SENTINEL_TERRAFORM_VAR_OVERRIDE_KEY" 665 inputWithSentinal := fmt.Sprintf("%s = %s", sentinelValue, input) 666 667 var decoded map[string]interface{} 668 err := hcl.Decode(&decoded, inputWithSentinal) 669 if err != nil { 670 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL: %s", name, input, err) 671 } 672 673 if len(decoded) != 1 { 674 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. Only one value may be specified.", name, input) 675 } 676 677 parsedValue, ok := decoded[sentinelValue] 678 if !ok { 679 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input) 680 } 681 682 switch targetType { 683 case config.VariableTypeList: 684 return parsedValue, nil 685 case config.VariableTypeMap: 686 if list, ok := parsedValue.([]map[string]interface{}); ok { 687 return list[0], nil 688 } 689 690 return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input) 691 default: 692 panic(fmt.Errorf("unknown type %s", targetType.Printable())) 693 } 694 }