github.com/hobbeswalsh/terraform@v0.3.7-0.20150619183303-ad17cf55a0fa/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 variabel 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 var defaultString string 218 if v.Default != nil { 219 defaultString = v.Default.(string) 220 } 221 222 // Ask the user for a value for this variable 223 var value string 224 for { 225 var err error 226 value, err = c.uiInput.Input(&InputOpts{ 227 Id: fmt.Sprintf("var.%s", n), 228 Query: fmt.Sprintf("var.%s", n), 229 Default: defaultString, 230 Description: v.Description, 231 }) 232 if err != nil { 233 return fmt.Errorf( 234 "Error asking for %s: %s", n, err) 235 } 236 237 if value == "" && v.Required() { 238 // Redo if it is required. 239 continue 240 } 241 242 if value == "" { 243 // No value, just exit the loop. With no value, we just 244 // use whatever is currently set in variables. 245 break 246 } 247 248 break 249 } 250 251 if value != "" { 252 c.variables[n] = value 253 } 254 } 255 } 256 257 if mode&InputModeProvider != 0 { 258 // Build the graph 259 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 260 if err != nil { 261 return err 262 } 263 264 // Do the walk 265 if _, err := c.walk(graph, walkInput); err != nil { 266 return err 267 } 268 } 269 270 return nil 271 } 272 273 // Apply applies the changes represented by this context and returns 274 // the resulting state. 275 // 276 // In addition to returning the resulting state, this context is updated 277 // with the latest state. 278 func (c *Context) Apply() (*State, error) { 279 v := c.acquireRun() 280 defer c.releaseRun(v) 281 282 // Copy our own state 283 c.state = c.state.DeepCopy() 284 285 // Build the graph 286 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 287 if err != nil { 288 return nil, err 289 } 290 291 // Do the walk 292 _, err = c.walk(graph, walkApply) 293 294 // Clean out any unused things 295 c.state.prune() 296 297 return c.state, err 298 } 299 300 // Plan generates an execution plan for the given context. 301 // 302 // The execution plan encapsulates the context and can be stored 303 // in order to reinstantiate a context later for Apply. 304 // 305 // Plan also updates the diff of this context to be the diff generated 306 // by the plan, so Apply can be called after. 307 func (c *Context) Plan() (*Plan, error) { 308 v := c.acquireRun() 309 defer c.releaseRun(v) 310 311 p := &Plan{ 312 Module: c.module, 313 Vars: c.variables, 314 State: c.state, 315 } 316 317 var operation walkOperation 318 if c.destroy { 319 operation = walkPlanDestroy 320 } else { 321 // Set our state to be something temporary. We do this so that 322 // the plan can update a fake state so that variables work, then 323 // we replace it back with our old state. 324 old := c.state 325 if old == nil { 326 c.state = &State{} 327 c.state.init() 328 } else { 329 c.state = old.DeepCopy() 330 } 331 defer func() { 332 c.state = old 333 }() 334 335 operation = walkPlan 336 } 337 338 // Setup our diff 339 c.diffLock.Lock() 340 c.diff = new(Diff) 341 c.diff.init() 342 c.diffLock.Unlock() 343 344 // Build the graph 345 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 346 if err != nil { 347 return nil, err 348 } 349 350 // Do the walk 351 if _, err := c.walk(graph, operation); err != nil { 352 return nil, err 353 } 354 p.Diff = c.diff 355 356 // Now that we have a diff, we can build the exact graph that Apply will use 357 // and catch any possible cycles during the Plan phase. 358 if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil { 359 return nil, err 360 } 361 362 return p, nil 363 } 364 365 // Refresh goes through all the resources in the state and refreshes them 366 // to their latest state. This will update the state that this context 367 // works with, along with returning it. 368 // 369 // Even in the case an error is returned, the state will be returned and 370 // will potentially be partially updated. 371 func (c *Context) Refresh() (*State, error) { 372 v := c.acquireRun() 373 defer c.releaseRun(v) 374 375 // Copy our own state 376 c.state = c.state.DeepCopy() 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 if _, err := c.walk(graph, walkRefresh); err != nil { 386 return nil, err 387 } 388 389 // Clean out any unused things 390 c.state.prune() 391 392 return c.state, nil 393 } 394 395 // Stop stops the running task. 396 // 397 // Stop will block until the task completes. 398 func (c *Context) Stop() { 399 c.l.Lock() 400 ch := c.runCh 401 402 // If we aren't running, then just return 403 if ch == nil { 404 c.l.Unlock() 405 return 406 } 407 408 // Tell the hook we want to stop 409 c.sh.Stop() 410 411 // Wait for us to stop 412 c.l.Unlock() 413 <-ch 414 } 415 416 // Validate validates the configuration and returns any warnings or errors. 417 func (c *Context) Validate() ([]string, []error) { 418 v := c.acquireRun() 419 defer c.releaseRun(v) 420 421 var errs error 422 423 // Validate the configuration itself 424 if err := c.module.Validate(); err != nil { 425 errs = multierror.Append(errs, err) 426 } 427 428 // This only needs to be done for the root module, since inter-module 429 // variables are validated in the module tree. 430 if config := c.module.Config(); config != nil { 431 // Validate the user variables 432 if err := smcUserVariables(config, c.variables); len(err) > 0 { 433 errs = multierror.Append(errs, err...) 434 } 435 } 436 437 // Build the graph so we can walk it and run Validate on nodes. 438 // We also validate the graph generated here, but this graph doesn't 439 // necessarily match the graph that Plan will generate, so we'll validate the 440 // graph again later after Planning. 441 graph, err := c.Graph(&ContextGraphOpts{Validate: true}) 442 if err != nil { 443 return nil, []error{err} 444 } 445 446 // Walk 447 walker, err := c.walk(graph, walkValidate) 448 if err != nil { 449 return nil, multierror.Append(errs, err).Errors 450 } 451 452 // Return the result 453 rerrs := multierror.Append(errs, walker.ValidationErrors...) 454 return walker.ValidationWarnings, rerrs.Errors 455 } 456 457 // Module returns the module tree associated with this context. 458 func (c *Context) Module() *module.Tree { 459 return c.module 460 } 461 462 // Variables will return the mapping of variables that were defined 463 // for this Context. If Input was called, this mapping may be different 464 // than what was given. 465 func (c *Context) Variables() map[string]string { 466 return c.variables 467 } 468 469 // SetVariable sets a variable after a context has already been built. 470 func (c *Context) SetVariable(k, v string) { 471 c.variables[k] = v 472 } 473 474 func (c *Context) acquireRun() chan<- struct{} { 475 c.l.Lock() 476 defer c.l.Unlock() 477 478 // Wait for no channel to exist 479 for c.runCh != nil { 480 c.l.Unlock() 481 ch := c.runCh 482 <-ch 483 c.l.Lock() 484 } 485 486 ch := make(chan struct{}) 487 c.runCh = ch 488 return ch 489 } 490 491 func (c *Context) releaseRun(ch chan<- struct{}) { 492 c.l.Lock() 493 defer c.l.Unlock() 494 495 close(ch) 496 c.runCh = nil 497 c.sh.Reset() 498 } 499 500 func (c *Context) walk( 501 graph *Graph, operation walkOperation) (*ContextGraphWalker, error) { 502 // Walk the graph 503 log.Printf("[INFO] Starting graph walk: %s", operation.String()) 504 walker := &ContextGraphWalker{Context: c, Operation: operation} 505 return walker, graph.Walk(walker) 506 }