github.com/alexissmirnov/terraform@v0.4.3-0.20150423153700-1ef9731a2f14/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.VariableTypeUnknown: 179 continue 180 case config.VariableTypeMap: 181 continue 182 case config.VariableTypeString: 183 // Good! 184 default: 185 panic(fmt.Sprintf("Unknown variable type: %#v", v.Type())) 186 } 187 188 var defaultString string 189 if v.Default != nil { 190 defaultString = v.Default.(string) 191 } 192 193 // Ask the user for a value for this variable 194 var value string 195 for { 196 var err error 197 value, err = c.uiInput.Input(&InputOpts{ 198 Id: fmt.Sprintf("var.%s", n), 199 Query: fmt.Sprintf("var.%s", n), 200 Default: defaultString, 201 Description: v.Description, 202 }) 203 if err != nil { 204 return fmt.Errorf( 205 "Error asking for %s: %s", n, err) 206 } 207 208 if value == "" && v.Required() { 209 // Redo if it is required. 210 continue 211 } 212 213 if value == "" { 214 // No value, just exit the loop. With no value, we just 215 // use whatever is currently set in variables. 216 break 217 } 218 219 break 220 } 221 222 if value != "" { 223 c.variables[n] = value 224 } 225 } 226 } 227 228 if mode&InputModeProvider != 0 { 229 // Do the walk 230 if _, err := c.walk(walkInput); err != nil { 231 return err 232 } 233 } 234 235 return nil 236 } 237 238 // Apply applies the changes represented by this context and returns 239 // the resulting state. 240 // 241 // In addition to returning the resulting state, this context is updated 242 // with the latest state. 243 func (c *Context) Apply() (*State, error) { 244 v := c.acquireRun() 245 defer c.releaseRun(v) 246 247 // Copy our own state 248 c.state = c.state.DeepCopy() 249 250 // Do the walk 251 _, err := c.walk(walkApply) 252 253 // Clean out any unused things 254 c.state.prune() 255 256 return c.state, err 257 } 258 259 // Plan generates an execution plan for the given context. 260 // 261 // The execution plan encapsulates the context and can be stored 262 // in order to reinstantiate a context later for Apply. 263 // 264 // Plan also updates the diff of this context to be the diff generated 265 // by the plan, so Apply can be called after. 266 func (c *Context) Plan() (*Plan, error) { 267 v := c.acquireRun() 268 defer c.releaseRun(v) 269 270 p := &Plan{ 271 Module: c.module, 272 Vars: c.variables, 273 State: c.state, 274 } 275 276 var operation walkOperation 277 if c.destroy { 278 operation = walkPlanDestroy 279 } else { 280 // Set our state to be something temporary. We do this so that 281 // the plan can update a fake state so that variables work, then 282 // we replace it back with our old state. 283 old := c.state 284 if old == nil { 285 c.state = &State{} 286 c.state.init() 287 } else { 288 c.state = old.DeepCopy() 289 } 290 defer func() { 291 c.state = old 292 }() 293 294 operation = walkPlan 295 } 296 297 // Setup our diff 298 c.diffLock.Lock() 299 c.diff = new(Diff) 300 c.diff.init() 301 c.diffLock.Unlock() 302 303 // Do the walk 304 if _, err := c.walk(operation); err != nil { 305 return nil, err 306 } 307 p.Diff = c.diff 308 309 return p, nil 310 } 311 312 // Refresh goes through all the resources in the state and refreshes them 313 // to their latest state. This will update the state that this context 314 // works with, along with returning it. 315 // 316 // Even in the case an error is returned, the state will be returned and 317 // will potentially be partially updated. 318 func (c *Context) Refresh() (*State, error) { 319 v := c.acquireRun() 320 defer c.releaseRun(v) 321 322 // Copy our own state 323 c.state = c.state.DeepCopy() 324 325 // Do the walk 326 if _, err := c.walk(walkRefresh); err != nil { 327 return nil, err 328 } 329 330 // Clean out any unused things 331 c.state.prune() 332 333 return c.state, nil 334 } 335 336 // Stop stops the running task. 337 // 338 // Stop will block until the task completes. 339 func (c *Context) Stop() { 340 c.l.Lock() 341 ch := c.runCh 342 343 // If we aren't running, then just return 344 if ch == nil { 345 c.l.Unlock() 346 return 347 } 348 349 // Tell the hook we want to stop 350 c.sh.Stop() 351 352 // Wait for us to stop 353 c.l.Unlock() 354 <-ch 355 } 356 357 // Validate validates the configuration and returns any warnings or errors. 358 func (c *Context) Validate() ([]string, []error) { 359 v := c.acquireRun() 360 defer c.releaseRun(v) 361 362 var errs error 363 364 // Validate the configuration itself 365 if err := c.module.Validate(); err != nil { 366 errs = multierror.Append(errs, err) 367 } 368 369 // This only needs to be done for the root module, since inter-module 370 // variables are validated in the module tree. 371 if config := c.module.Config(); config != nil { 372 // Validate the user variables 373 if err := smcUserVariables(config, c.variables); len(err) > 0 { 374 errs = multierror.Append(errs, err...) 375 } 376 } 377 378 // Walk 379 walker, err := c.walk(walkValidate) 380 if err != nil { 381 return nil, multierror.Append(errs, err).Errors 382 } 383 384 // Return the result 385 rerrs := multierror.Append(errs, walker.ValidationErrors...) 386 return walker.ValidationWarnings, rerrs.Errors 387 } 388 389 // Module returns the module tree associated with this context. 390 func (c *Context) Module() *module.Tree { 391 return c.module 392 } 393 394 // Variables will return the mapping of variables that were defined 395 // for this Context. If Input was called, this mapping may be different 396 // than what was given. 397 func (c *Context) Variables() map[string]string { 398 return c.variables 399 } 400 401 // SetVariable sets a variable after a context has already been built. 402 func (c *Context) SetVariable(k, v string) { 403 c.variables[k] = v 404 } 405 406 func (c *Context) acquireRun() chan<- struct{} { 407 c.l.Lock() 408 defer c.l.Unlock() 409 410 // Wait for no channel to exist 411 for c.runCh != nil { 412 c.l.Unlock() 413 ch := c.runCh 414 <-ch 415 c.l.Lock() 416 } 417 418 ch := make(chan struct{}) 419 c.runCh = ch 420 return ch 421 } 422 423 func (c *Context) releaseRun(ch chan<- struct{}) { 424 c.l.Lock() 425 defer c.l.Unlock() 426 427 close(ch) 428 c.runCh = nil 429 c.sh.Reset() 430 } 431 432 func (c *Context) walk(operation walkOperation) (*ContextGraphWalker, error) { 433 // Build the graph 434 graph, err := c.GraphBuilder().Build(RootModulePath) 435 if err != nil { 436 return nil, err 437 } 438 439 // Walk the graph 440 log.Printf("[INFO] Starting graph walk: %s", operation.String()) 441 walker := &ContextGraphWalker{Context: c, Operation: operation} 442 return walker, graph.Walk(walker) 443 }