github.com/ljosa/terraform@v0.7.0-rc2.0.20160617205345-fe540b408f59/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 if c.destroy { 317 _, err = c.walk(graph, walkDestroy) 318 } else { 319 _, err = c.walk(graph, walkApply) 320 } 321 322 // Clean out any unused things 323 c.state.prune() 324 325 return c.state, err 326 } 327 328 // Plan generates an execution plan for the given context. 329 // 330 // The execution plan encapsulates the context and can be stored 331 // in order to reinstantiate a context later for Apply. 332 // 333 // Plan also updates the diff of this context to be the diff generated 334 // by the plan, so Apply can be called after. 335 func (c *Context) Plan() (*Plan, error) { 336 v := c.acquireRun() 337 defer c.releaseRun(v) 338 339 p := &Plan{ 340 Module: c.module, 341 Vars: c.variables, 342 State: c.state, 343 Targets: c.targets, 344 } 345 346 var operation walkOperation 347 if c.destroy { 348 operation = walkPlanDestroy 349 } else { 350 // Set our state to be something temporary. We do this so that 351 // the plan can update a fake state so that variables work, then 352 // we replace it back with our old state. 353 old := c.state 354 if old == nil { 355 c.state = &State{} 356 c.state.init() 357 } else { 358 c.state = old.DeepCopy() 359 } 360 defer func() { 361 c.state = old 362 }() 363 364 operation = walkPlan 365 } 366 367 // Setup our diff 368 c.diffLock.Lock() 369 c.diff = new(Diff) 370 c.diff.init() 371 c.diffLock.Unlock() 372 373 // Build the graph 374 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 375 if err != nil { 376 return nil, err 377 } 378 379 // Do the walk 380 if _, err := c.walk(graph, operation); err != nil { 381 return nil, err 382 } 383 p.Diff = c.diff 384 385 // Now that we have a diff, we can build the exact graph that Apply will use 386 // and catch any possible cycles during the Plan phase. 387 if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil { 388 return nil, err 389 } 390 391 return p, nil 392 } 393 394 // Refresh goes through all the resources in the state and refreshes them 395 // to their latest state. This will update the state that this context 396 // works with, along with returning it. 397 // 398 // Even in the case an error is returned, the state will be returned and 399 // will potentially be partially updated. 400 func (c *Context) Refresh() (*State, error) { 401 v := c.acquireRun() 402 defer c.releaseRun(v) 403 404 // Copy our own state 405 c.state = c.state.DeepCopy() 406 407 // Build the graph 408 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 409 if err != nil { 410 return nil, err 411 } 412 413 // Do the walk 414 if _, err := c.walk(graph, walkRefresh); err != nil { 415 return nil, err 416 } 417 418 // Clean out any unused things 419 c.state.prune() 420 421 return c.state, nil 422 } 423 424 // Stop stops the running task. 425 // 426 // Stop will block until the task completes. 427 func (c *Context) Stop() { 428 c.l.Lock() 429 ch := c.runCh 430 431 // If we aren't running, then just return 432 if ch == nil { 433 c.l.Unlock() 434 return 435 } 436 437 // Tell the hook we want to stop 438 c.sh.Stop() 439 440 // Wait for us to stop 441 c.l.Unlock() 442 <-ch 443 } 444 445 // Validate validates the configuration and returns any warnings or errors. 446 func (c *Context) Validate() ([]string, []error) { 447 v := c.acquireRun() 448 defer c.releaseRun(v) 449 450 var errs error 451 452 // Validate the configuration itself 453 if err := c.module.Validate(); err != nil { 454 errs = multierror.Append(errs, err) 455 } 456 457 // This only needs to be done for the root module, since inter-module 458 // variables are validated in the module tree. 459 if config := c.module.Config(); config != nil { 460 // Validate the user variables 461 if err := smcUserVariables(config, c.variables); len(err) > 0 { 462 errs = multierror.Append(errs, err...) 463 } 464 } 465 466 // If we have errors at this point, the graphing has no chance, 467 // so just bail early. 468 if errs != nil { 469 return nil, []error{errs} 470 } 471 472 // Build the graph so we can walk it and run Validate on nodes. 473 // We also validate the graph generated here, but this graph doesn't 474 // necessarily match the graph that Plan will generate, so we'll validate the 475 // graph again later after Planning. 476 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 477 if err != nil { 478 return nil, []error{err} 479 } 480 481 // Walk 482 walker, err := c.walk(graph, walkValidate) 483 if err != nil { 484 return nil, multierror.Append(errs, err).Errors 485 } 486 487 // Return the result 488 rerrs := multierror.Append(errs, walker.ValidationErrors...) 489 return walker.ValidationWarnings, rerrs.Errors 490 } 491 492 // Module returns the module tree associated with this context. 493 func (c *Context) Module() *module.Tree { 494 return c.module 495 } 496 497 // Variables will return the mapping of variables that were defined 498 // for this Context. If Input was called, this mapping may be different 499 // than what was given. 500 func (c *Context) Variables() map[string]string { 501 return c.variables 502 } 503 504 // SetVariable sets a variable after a context has already been built. 505 func (c *Context) SetVariable(k, v string) { 506 c.variables[k] = v 507 } 508 509 func (c *Context) acquireRun() chan<- struct{} { 510 c.l.Lock() 511 defer c.l.Unlock() 512 513 // Wait for no channel to exist 514 for c.runCh != nil { 515 c.l.Unlock() 516 ch := c.runCh 517 <-ch 518 c.l.Lock() 519 } 520 521 ch := make(chan struct{}) 522 c.runCh = ch 523 return ch 524 } 525 526 func (c *Context) releaseRun(ch chan<- struct{}) { 527 c.l.Lock() 528 defer c.l.Unlock() 529 530 close(ch) 531 c.runCh = nil 532 c.sh.Reset() 533 } 534 535 func (c *Context) walk( 536 graph *Graph, operation walkOperation) (*ContextGraphWalker, error) { 537 // Walk the graph 538 log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) 539 walker := &ContextGraphWalker{Context: c, Operation: operation} 540 return walker, graph.Walk(walker) 541 }