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  }