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