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