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