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