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