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