github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/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  	StateFutureAllowed bool
    45  	Providers          map[string]ResourceProviderFactory
    46  	Provisioners       map[string]ResourceProvisionerFactory
    47  	Targets            []string
    48  	Variables          map[string]string
    49  
    50  	UIInput UIInput
    51  }
    52  
    53  // Context represents all the context that Terraform needs in order to
    54  // perform operations on infrastructure. This structure is built using
    55  // NewContext. See the documentation for that.
    56  //
    57  // Extra functions on Context can be found in context_*.go files.
    58  type Context struct {
    59  	destroy      bool
    60  	diff         *Diff
    61  	diffLock     sync.RWMutex
    62  	hooks        []Hook
    63  	module       *module.Tree
    64  	providers    map[string]ResourceProviderFactory
    65  	provisioners map[string]ResourceProvisionerFactory
    66  	sh           *stopHook
    67  	state        *State
    68  	stateLock    sync.RWMutex
    69  	targets      []string
    70  	uiInput      UIInput
    71  	variables    map[string]string
    72  
    73  	l                   sync.Mutex // Lock acquired during any task
    74  	parallelSem         Semaphore
    75  	providerInputConfig map[string]map[string]interface{}
    76  	runCh               <-chan struct{}
    77  }
    78  
    79  // NewContext creates a new Context structure.
    80  //
    81  // Once a Context is creator, the pointer values within ContextOpts
    82  // should not be mutated in any way, since the pointers are copied, not
    83  // the values themselves.
    84  func NewContext(opts *ContextOpts) (*Context, error) {
    85  	// Copy all the hooks and add our stop hook. We don't append directly
    86  	// to the Config so that we're not modifying that in-place.
    87  	sh := new(stopHook)
    88  	hooks := make([]Hook, len(opts.Hooks)+1)
    89  	copy(hooks, opts.Hooks)
    90  	hooks[len(opts.Hooks)] = sh
    91  
    92  	state := opts.State
    93  	if state == nil {
    94  		state = new(State)
    95  		state.init()
    96  	}
    97  
    98  	// If our state is from the future, then error. Callers can avoid
    99  	// this error by explicitly setting `StateFutureAllowed`.
   100  	if !opts.StateFutureAllowed && state.FromFutureTerraform() {
   101  		return nil, fmt.Errorf(
   102  			"Terraform doesn't allow running any operations against a state\n"+
   103  				"that was written by a future Terraform version. The state is\n"+
   104  				"reporting it is written by Terraform '%s'.\n\n"+
   105  				"Please run at least that version of Terraform to continue.",
   106  			state.TFVersion)
   107  	}
   108  
   109  	// Explicitly reset our state version to our current version so that
   110  	// any operations we do will write out that our latest version
   111  	// has run.
   112  	state.TFVersion = Version
   113  
   114  	// Determine parallelism, default to 10. We do this both to limit
   115  	// CPU pressure but also to have an extra guard against rate throttling
   116  	// from providers.
   117  	par := opts.Parallelism
   118  	if par == 0 {
   119  		par = 10
   120  	}
   121  
   122  	// Setup the variables. We first take the variables given to us.
   123  	// We then merge in the variables set in the environment.
   124  	variables := make(map[string]string)
   125  	for _, v := range os.Environ() {
   126  		if !strings.HasPrefix(v, VarEnvPrefix) {
   127  			continue
   128  		}
   129  
   130  		// Strip off the prefix and get the value after the first "="
   131  		idx := strings.Index(v, "=")
   132  		k := v[len(VarEnvPrefix):idx]
   133  		v = v[idx+1:]
   134  
   135  		// Override the command-line set variable
   136  		variables[k] = v
   137  	}
   138  	for k, v := range opts.Variables {
   139  		variables[k] = v
   140  	}
   141  
   142  	return &Context{
   143  		destroy:      opts.Destroy,
   144  		diff:         opts.Diff,
   145  		hooks:        hooks,
   146  		module:       opts.Module,
   147  		providers:    opts.Providers,
   148  		provisioners: opts.Provisioners,
   149  		state:        state,
   150  		targets:      opts.Targets,
   151  		uiInput:      opts.UIInput,
   152  		variables:    variables,
   153  
   154  		parallelSem:         NewSemaphore(par),
   155  		providerInputConfig: make(map[string]map[string]interface{}),
   156  		sh:                  sh,
   157  	}, nil
   158  }
   159  
   160  type ContextGraphOpts struct {
   161  	Validate bool
   162  	Verbose  bool
   163  }
   164  
   165  // Graph returns the graph for this config.
   166  func (c *Context) Graph(g *ContextGraphOpts) (*Graph, error) {
   167  	return c.graphBuilder(g).Build(RootModulePath)
   168  }
   169  
   170  // GraphBuilder returns the GraphBuilder that will be used to create
   171  // the graphs for this context.
   172  func (c *Context) graphBuilder(g *ContextGraphOpts) GraphBuilder {
   173  	// TODO test
   174  	providers := make([]string, 0, len(c.providers))
   175  	for k, _ := range c.providers {
   176  		providers = append(providers, k)
   177  	}
   178  
   179  	provisioners := make([]string, 0, len(c.provisioners))
   180  	for k, _ := range c.provisioners {
   181  		provisioners = append(provisioners, k)
   182  	}
   183  
   184  	return &BuiltinGraphBuilder{
   185  		Root:         c.module,
   186  		Diff:         c.diff,
   187  		Providers:    providers,
   188  		Provisioners: provisioners,
   189  		State:        c.state,
   190  		Targets:      c.targets,
   191  		Destroy:      c.destroy,
   192  		Validate:     g.Validate,
   193  		Verbose:      g.Verbose,
   194  	}
   195  }
   196  
   197  // Input asks for input to fill variables and provider configurations.
   198  // This modifies the configuration in-place, so asking for Input twice
   199  // may result in different UI output showing different current values.
   200  func (c *Context) Input(mode InputMode) error {
   201  	v := c.acquireRun()
   202  	defer c.releaseRun(v)
   203  
   204  	if mode&InputModeVar != 0 {
   205  		// Walk the variables first for the root module. We walk them in
   206  		// alphabetical order for UX reasons.
   207  		rootConf := c.module.Config()
   208  		names := make([]string, len(rootConf.Variables))
   209  		m := make(map[string]*config.Variable)
   210  		for i, v := range rootConf.Variables {
   211  			names[i] = v.Name
   212  			m[v.Name] = v
   213  		}
   214  		sort.Strings(names)
   215  		for _, n := range names {
   216  			// If we only care about unset variables, then if the variable
   217  			// is set, continue on.
   218  			if mode&InputModeVarUnset != 0 {
   219  				if _, ok := c.variables[n]; ok {
   220  					continue
   221  				}
   222  			}
   223  
   224  			v := m[n]
   225  			switch v.Type() {
   226  			case config.VariableTypeUnknown:
   227  				continue
   228  			case config.VariableTypeMap:
   229  				continue
   230  			case config.VariableTypeList:
   231  				continue
   232  			case config.VariableTypeString:
   233  				// Good!
   234  			default:
   235  				panic(fmt.Sprintf("Unknown variable type: %#v", v.Type()))
   236  			}
   237  
   238  			// If the variable is not already set, and the variable defines a
   239  			// default, use that for the value.
   240  			if _, ok := c.variables[n]; !ok {
   241  				if v.Default != nil {
   242  					c.variables[n] = v.Default.(string)
   243  					continue
   244  				}
   245  			}
   246  
   247  			// Ask the user for a value for this variable
   248  			var value string
   249  			for {
   250  				var err error
   251  				value, err = c.uiInput.Input(&InputOpts{
   252  					Id:          fmt.Sprintf("var.%s", n),
   253  					Query:       fmt.Sprintf("var.%s", n),
   254  					Description: v.Description,
   255  				})
   256  				if err != nil {
   257  					return fmt.Errorf(
   258  						"Error asking for %s: %s", n, err)
   259  				}
   260  
   261  				if value == "" && v.Required() {
   262  					// Redo if it is required.
   263  					continue
   264  				}
   265  
   266  				if value == "" {
   267  					// No value, just exit the loop. With no value, we just
   268  					// use whatever is currently set in variables.
   269  					break
   270  				}
   271  
   272  				break
   273  			}
   274  
   275  			if value != "" {
   276  				c.variables[n] = value
   277  			}
   278  		}
   279  	}
   280  
   281  	if mode&InputModeProvider != 0 {
   282  		// Build the graph
   283  		graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   284  		if err != nil {
   285  			return err
   286  		}
   287  
   288  		// Do the walk
   289  		if _, err := c.walk(graph, walkInput); err != nil {
   290  			return err
   291  		}
   292  	}
   293  
   294  	return nil
   295  }
   296  
   297  // Apply applies the changes represented by this context and returns
   298  // the resulting state.
   299  //
   300  // In addition to returning the resulting state, this context is updated
   301  // with the latest state.
   302  func (c *Context) Apply() (*State, error) {
   303  	v := c.acquireRun()
   304  	defer c.releaseRun(v)
   305  
   306  	// Copy our own state
   307  	c.state = c.state.DeepCopy()
   308  
   309  	// Build the graph
   310  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  
   315  	// Do the walk
   316  	var walker *ContextGraphWalker
   317  	if c.destroy {
   318  		walker, err = c.walk(graph, walkDestroy)
   319  	} else {
   320  		walker, err = c.walk(graph, walkApply)
   321  	}
   322  
   323  	if len(walker.ValidationErrors) > 0 {
   324  		err = multierror.Append(err, walker.ValidationErrors...)
   325  	}
   326  
   327  	// Clean out any unused things
   328  	c.state.prune()
   329  
   330  	return c.state, err
   331  }
   332  
   333  // Plan generates an execution plan for the given context.
   334  //
   335  // The execution plan encapsulates the context and can be stored
   336  // in order to reinstantiate a context later for Apply.
   337  //
   338  // Plan also updates the diff of this context to be the diff generated
   339  // by the plan, so Apply can be called after.
   340  func (c *Context) Plan() (*Plan, error) {
   341  	v := c.acquireRun()
   342  	defer c.releaseRun(v)
   343  
   344  	p := &Plan{
   345  		Module:  c.module,
   346  		Vars:    c.variables,
   347  		State:   c.state,
   348  		Targets: c.targets,
   349  	}
   350  
   351  	var operation walkOperation
   352  	if c.destroy {
   353  		operation = walkPlanDestroy
   354  	} else {
   355  		// Set our state to be something temporary. We do this so that
   356  		// the plan can update a fake state so that variables work, then
   357  		// we replace it back with our old state.
   358  		old := c.state
   359  		if old == nil {
   360  			c.state = &State{}
   361  			c.state.init()
   362  		} else {
   363  			c.state = old.DeepCopy()
   364  		}
   365  		defer func() {
   366  			c.state = old
   367  		}()
   368  
   369  		operation = walkPlan
   370  	}
   371  
   372  	// Setup our diff
   373  	c.diffLock.Lock()
   374  	c.diff = new(Diff)
   375  	c.diff.init()
   376  	c.diffLock.Unlock()
   377  
   378  	// Build the graph
   379  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   380  	if err != nil {
   381  		return nil, err
   382  	}
   383  
   384  	// Do the walk
   385  	walker, err := c.walk(graph, operation)
   386  	if err != nil {
   387  		return nil, err
   388  	}
   389  	p.Diff = c.diff
   390  
   391  	// Now that we have a diff, we can build the exact graph that Apply will use
   392  	// and catch any possible cycles during the Plan phase.
   393  	if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil {
   394  		return nil, err
   395  	}
   396  	var errs error
   397  	if len(walker.ValidationErrors) > 0 {
   398  		errs = multierror.Append(errs, walker.ValidationErrors...)
   399  	}
   400  	return p, errs
   401  }
   402  
   403  // Refresh goes through all the resources in the state and refreshes them
   404  // to their latest state. This will update the state that this context
   405  // works with, along with returning it.
   406  //
   407  // Even in the case an error is returned, the state will be returned and
   408  // will potentially be partially updated.
   409  func (c *Context) Refresh() (*State, error) {
   410  	v := c.acquireRun()
   411  	defer c.releaseRun(v)
   412  
   413  	// Copy our own state
   414  	c.state = c.state.DeepCopy()
   415  
   416  	// Build the graph
   417  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   418  	if err != nil {
   419  		return nil, err
   420  	}
   421  
   422  	// Do the walk
   423  	if _, err := c.walk(graph, walkRefresh); err != nil {
   424  		return nil, err
   425  	}
   426  
   427  	// Clean out any unused things
   428  	c.state.prune()
   429  
   430  	return c.state, nil
   431  }
   432  
   433  // Stop stops the running task.
   434  //
   435  // Stop will block until the task completes.
   436  func (c *Context) Stop() {
   437  	c.l.Lock()
   438  	ch := c.runCh
   439  
   440  	// If we aren't running, then just return
   441  	if ch == nil {
   442  		c.l.Unlock()
   443  		return
   444  	}
   445  
   446  	// Tell the hook we want to stop
   447  	c.sh.Stop()
   448  
   449  	// Wait for us to stop
   450  	c.l.Unlock()
   451  	<-ch
   452  }
   453  
   454  // Validate validates the configuration and returns any warnings or errors.
   455  func (c *Context) Validate() ([]string, []error) {
   456  	v := c.acquireRun()
   457  	defer c.releaseRun(v)
   458  
   459  	var errs error
   460  
   461  	// Validate the configuration itself
   462  	if err := c.module.Validate(); err != nil {
   463  		errs = multierror.Append(errs, err)
   464  	}
   465  
   466  	// This only needs to be done for the root module, since inter-module
   467  	// variables are validated in the module tree.
   468  	if config := c.module.Config(); config != nil {
   469  		// Validate the user variables
   470  		if err := smcUserVariables(config, c.variables); len(err) > 0 {
   471  			errs = multierror.Append(errs, err...)
   472  		}
   473  	}
   474  
   475  	// If we have errors at this point, the graphing has no chance,
   476  	// so just bail early.
   477  	if errs != nil {
   478  		return nil, []error{errs}
   479  	}
   480  
   481  	// Build the graph so we can walk it and run Validate on nodes.
   482  	// We also validate the graph generated here, but this graph doesn't
   483  	// necessarily match the graph that Plan will generate, so we'll validate the
   484  	// graph again later after Planning.
   485  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   486  	if err != nil {
   487  		return nil, []error{err}
   488  	}
   489  
   490  	// Walk
   491  	walker, err := c.walk(graph, walkValidate)
   492  	if err != nil {
   493  		return nil, multierror.Append(errs, err).Errors
   494  	}
   495  
   496  	// Return the result
   497  	rerrs := multierror.Append(errs, walker.ValidationErrors...)
   498  	return walker.ValidationWarnings, rerrs.Errors
   499  }
   500  
   501  // Module returns the module tree associated with this context.
   502  func (c *Context) Module() *module.Tree {
   503  	return c.module
   504  }
   505  
   506  // Variables will return the mapping of variables that were defined
   507  // for this Context. If Input was called, this mapping may be different
   508  // than what was given.
   509  func (c *Context) Variables() map[string]string {
   510  	return c.variables
   511  }
   512  
   513  // SetVariable sets a variable after a context has already been built.
   514  func (c *Context) SetVariable(k, v string) {
   515  	c.variables[k] = v
   516  }
   517  
   518  func (c *Context) acquireRun() chan<- struct{} {
   519  	c.l.Lock()
   520  	defer c.l.Unlock()
   521  
   522  	// Wait for no channel to exist
   523  	for c.runCh != nil {
   524  		c.l.Unlock()
   525  		ch := c.runCh
   526  		<-ch
   527  		c.l.Lock()
   528  	}
   529  
   530  	ch := make(chan struct{})
   531  	c.runCh = ch
   532  	return ch
   533  }
   534  
   535  func (c *Context) releaseRun(ch chan<- struct{}) {
   536  	c.l.Lock()
   537  	defer c.l.Unlock()
   538  
   539  	close(ch)
   540  	c.runCh = nil
   541  	c.sh.Reset()
   542  }
   543  
   544  func (c *Context) walk(
   545  	graph *Graph, operation walkOperation) (*ContextGraphWalker, error) {
   546  	// Walk the graph
   547  	log.Printf("[DEBUG] Starting graph walk: %s", operation.String())
   548  	walker := &ContextGraphWalker{Context: c, Operation: operation}
   549  	return walker, graph.Walk(walker)
   550  }