github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/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/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]string 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]string 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 // Setup the variables. We first take the variables given to us. 123 // We then merge in the variables set in the environment. 124 variables := make(map[string]string) 125 for _, v := range os.Environ() { 126 if !strings.HasPrefix(v, VarEnvPrefix) { 127 continue 128 } 129 130 // Strip off the prefix and get the value after the first "=" 131 idx := strings.Index(v, "=") 132 k := v[len(VarEnvPrefix):idx] 133 v = v[idx+1:] 134 135 // Override the command-line set variable 136 variables[k] = v 137 } 138 for k, v := range opts.Variables { 139 variables[k] = v 140 } 141 142 return &Context{ 143 destroy: opts.Destroy, 144 diff: opts.Diff, 145 hooks: hooks, 146 module: opts.Module, 147 providers: opts.Providers, 148 provisioners: opts.Provisioners, 149 state: state, 150 targets: opts.Targets, 151 uiInput: opts.UIInput, 152 variables: variables, 153 154 parallelSem: NewSemaphore(par), 155 providerInputConfig: make(map[string]map[string]interface{}), 156 sh: sh, 157 }, nil 158 } 159 160 type ContextGraphOpts struct { 161 Validate bool 162 Verbose bool 163 } 164 165 // Graph returns the graph for this config. 166 func (c *Context) Graph(g *ContextGraphOpts) (*Graph, error) { 167 return c.graphBuilder(g).Build(RootModulePath) 168 } 169 170 // GraphBuilder returns the GraphBuilder that will be used to create 171 // the graphs for this context. 172 func (c *Context) graphBuilder(g *ContextGraphOpts) GraphBuilder { 173 // TODO test 174 providers := make([]string, 0, len(c.providers)) 175 for k, _ := range c.providers { 176 providers = append(providers, k) 177 } 178 179 provisioners := make([]string, 0, len(c.provisioners)) 180 for k, _ := range c.provisioners { 181 provisioners = append(provisioners, k) 182 } 183 184 return &BuiltinGraphBuilder{ 185 Root: c.module, 186 Diff: c.diff, 187 Providers: providers, 188 Provisioners: provisioners, 189 State: c.state, 190 Targets: c.targets, 191 Destroy: c.destroy, 192 Validate: g.Validate, 193 Verbose: g.Verbose, 194 } 195 } 196 197 // Input asks for input to fill variables and provider configurations. 198 // This modifies the configuration in-place, so asking for Input twice 199 // may result in different UI output showing different current values. 200 func (c *Context) Input(mode InputMode) error { 201 v := c.acquireRun() 202 defer c.releaseRun(v) 203 204 if mode&InputModeVar != 0 { 205 // Walk the variables first for the root module. We walk them in 206 // alphabetical order for UX reasons. 207 rootConf := c.module.Config() 208 names := make([]string, len(rootConf.Variables)) 209 m := make(map[string]*config.Variable) 210 for i, v := range rootConf.Variables { 211 names[i] = v.Name 212 m[v.Name] = v 213 } 214 sort.Strings(names) 215 for _, n := range names { 216 // If we only care about unset variables, then if the variable 217 // is set, continue on. 218 if mode&InputModeVarUnset != 0 { 219 if _, ok := c.variables[n]; ok { 220 continue 221 } 222 } 223 224 v := m[n] 225 switch v.Type() { 226 case config.VariableTypeUnknown: 227 continue 228 case config.VariableTypeMap: 229 continue 230 case config.VariableTypeList: 231 continue 232 case config.VariableTypeString: 233 // Good! 234 default: 235 panic(fmt.Sprintf("Unknown variable type: %#v", v.Type())) 236 } 237 238 // If the variable is not already set, and the variable defines a 239 // default, use that for the value. 240 if _, ok := c.variables[n]; !ok { 241 if v.Default != nil { 242 c.variables[n] = v.Default.(string) 243 continue 244 } 245 } 246 247 // Ask the user for a value for this variable 248 var value string 249 for { 250 var err error 251 value, err = c.uiInput.Input(&InputOpts{ 252 Id: fmt.Sprintf("var.%s", n), 253 Query: fmt.Sprintf("var.%s", n), 254 Description: v.Description, 255 }) 256 if err != nil { 257 return fmt.Errorf( 258 "Error asking for %s: %s", n, err) 259 } 260 261 if value == "" && v.Required() { 262 // Redo if it is required. 263 continue 264 } 265 266 if value == "" { 267 // No value, just exit the loop. With no value, we just 268 // use whatever is currently set in variables. 269 break 270 } 271 272 break 273 } 274 275 if value != "" { 276 c.variables[n] = value 277 } 278 } 279 } 280 281 if mode&InputModeProvider != 0 { 282 // Build the graph 283 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 284 if err != nil { 285 return err 286 } 287 288 // Do the walk 289 if _, err := c.walk(graph, walkInput); err != nil { 290 return err 291 } 292 } 293 294 return nil 295 } 296 297 // Apply applies the changes represented by this context and returns 298 // the resulting state. 299 // 300 // In addition to returning the resulting state, this context is updated 301 // with the latest state. 302 func (c *Context) Apply() (*State, error) { 303 v := c.acquireRun() 304 defer c.releaseRun(v) 305 306 // Copy our own state 307 c.state = c.state.DeepCopy() 308 309 // Build the graph 310 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 311 if err != nil { 312 return nil, err 313 } 314 315 // Do the walk 316 var walker *ContextGraphWalker 317 if c.destroy { 318 walker, err = c.walk(graph, walkDestroy) 319 } else { 320 walker, err = c.walk(graph, walkApply) 321 } 322 323 if len(walker.ValidationErrors) > 0 { 324 err = multierror.Append(err, walker.ValidationErrors...) 325 } 326 327 // Clean out any unused things 328 c.state.prune() 329 330 return c.state, err 331 } 332 333 // Plan generates an execution plan for the given context. 334 // 335 // The execution plan encapsulates the context and can be stored 336 // in order to reinstantiate a context later for Apply. 337 // 338 // Plan also updates the diff of this context to be the diff generated 339 // by the plan, so Apply can be called after. 340 func (c *Context) Plan() (*Plan, error) { 341 v := c.acquireRun() 342 defer c.releaseRun(v) 343 344 p := &Plan{ 345 Module: c.module, 346 Vars: c.variables, 347 State: c.state, 348 Targets: c.targets, 349 } 350 351 var operation walkOperation 352 if c.destroy { 353 operation = walkPlanDestroy 354 } else { 355 // Set our state to be something temporary. We do this so that 356 // the plan can update a fake state so that variables work, then 357 // we replace it back with our old state. 358 old := c.state 359 if old == nil { 360 c.state = &State{} 361 c.state.init() 362 } else { 363 c.state = old.DeepCopy() 364 } 365 defer func() { 366 c.state = old 367 }() 368 369 operation = walkPlan 370 } 371 372 // Setup our diff 373 c.diffLock.Lock() 374 c.diff = new(Diff) 375 c.diff.init() 376 c.diffLock.Unlock() 377 378 // Build the graph 379 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 380 if err != nil { 381 return nil, err 382 } 383 384 // Do the walk 385 walker, err := c.walk(graph, operation) 386 if err != nil { 387 return nil, err 388 } 389 p.Diff = c.diff 390 391 // Now that we have a diff, we can build the exact graph that Apply will use 392 // and catch any possible cycles during the Plan phase. 393 if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil { 394 return nil, err 395 } 396 var errs error 397 if len(walker.ValidationErrors) > 0 { 398 errs = multierror.Append(errs, walker.ValidationErrors...) 399 } 400 return p, errs 401 } 402 403 // Refresh goes through all the resources in the state and refreshes them 404 // to their latest state. This will update the state that this context 405 // works with, along with returning it. 406 // 407 // Even in the case an error is returned, the state will be returned and 408 // will potentially be partially updated. 409 func (c *Context) Refresh() (*State, error) { 410 v := c.acquireRun() 411 defer c.releaseRun(v) 412 413 // Copy our own state 414 c.state = c.state.DeepCopy() 415 416 // Build the graph 417 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 418 if err != nil { 419 return nil, err 420 } 421 422 // Do the walk 423 if _, err := c.walk(graph, walkRefresh); err != nil { 424 return nil, err 425 } 426 427 // Clean out any unused things 428 c.state.prune() 429 430 return c.state, nil 431 } 432 433 // Stop stops the running task. 434 // 435 // Stop will block until the task completes. 436 func (c *Context) Stop() { 437 c.l.Lock() 438 ch := c.runCh 439 440 // If we aren't running, then just return 441 if ch == nil { 442 c.l.Unlock() 443 return 444 } 445 446 // Tell the hook we want to stop 447 c.sh.Stop() 448 449 // Wait for us to stop 450 c.l.Unlock() 451 <-ch 452 } 453 454 // Validate validates the configuration and returns any warnings or errors. 455 func (c *Context) Validate() ([]string, []error) { 456 v := c.acquireRun() 457 defer c.releaseRun(v) 458 459 var errs error 460 461 // Validate the configuration itself 462 if err := c.module.Validate(); err != nil { 463 errs = multierror.Append(errs, err) 464 } 465 466 // This only needs to be done for the root module, since inter-module 467 // variables are validated in the module tree. 468 if config := c.module.Config(); config != nil { 469 // Validate the user variables 470 if err := smcUserVariables(config, c.variables); len(err) > 0 { 471 errs = multierror.Append(errs, err...) 472 } 473 } 474 475 // If we have errors at this point, the graphing has no chance, 476 // so just bail early. 477 if errs != nil { 478 return nil, []error{errs} 479 } 480 481 // Build the graph so we can walk it and run Validate on nodes. 482 // We also validate the graph generated here, but this graph doesn't 483 // necessarily match the graph that Plan will generate, so we'll validate the 484 // graph again later after Planning. 485 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 486 if err != nil { 487 return nil, []error{err} 488 } 489 490 // Walk 491 walker, err := c.walk(graph, walkValidate) 492 if err != nil { 493 return nil, multierror.Append(errs, err).Errors 494 } 495 496 // Return the result 497 rerrs := multierror.Append(errs, walker.ValidationErrors...) 498 return walker.ValidationWarnings, rerrs.Errors 499 } 500 501 // Module returns the module tree associated with this context. 502 func (c *Context) Module() *module.Tree { 503 return c.module 504 } 505 506 // Variables will return the mapping of variables that were defined 507 // for this Context. If Input was called, this mapping may be different 508 // than what was given. 509 func (c *Context) Variables() map[string]string { 510 return c.variables 511 } 512 513 // SetVariable sets a variable after a context has already been built. 514 func (c *Context) SetVariable(k, v string) { 515 c.variables[k] = v 516 } 517 518 func (c *Context) acquireRun() chan<- struct{} { 519 c.l.Lock() 520 defer c.l.Unlock() 521 522 // Wait for no channel to exist 523 for c.runCh != nil { 524 c.l.Unlock() 525 ch := c.runCh 526 <-ch 527 c.l.Lock() 528 } 529 530 ch := make(chan struct{}) 531 c.runCh = ch 532 return ch 533 } 534 535 func (c *Context) releaseRun(ch chan<- struct{}) { 536 c.l.Lock() 537 defer c.l.Unlock() 538 539 close(ch) 540 c.runCh = nil 541 c.sh.Reset() 542 } 543 544 func (c *Context) walk( 545 graph *Graph, operation walkOperation) (*ContextGraphWalker, error) { 546 // Walk the graph 547 log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) 548 walker := &ContextGraphWalker{Context: c, Operation: operation} 549 return walker, graph.Walk(walker) 550 }