github.com/tomaszheflik/terraform@v0.7.3-0.20160827060421-32f990b41594/terraform/context.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"sort"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/hashicorp/go-multierror"
    11  	"github.com/hashicorp/hcl"
    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]interface{}
    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]interface{}
    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  	// Set up the variables in the following sequence:
   123  	//    0 - Take default values from the configuration
   124  	//    1 - Take values from TF_VAR_x environment variables
   125  	//    2 - Take values specified in -var flags, overriding values
   126  	//        set by environment variables if necessary. This includes
   127  	//        values taken from -var-file in addition.
   128  	variables := make(map[string]interface{})
   129  
   130  	if opts.Module != nil {
   131  		var err error
   132  		variables, err = Variables(opts.Module, opts.Variables)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  	}
   137  
   138  	return &Context{
   139  		destroy:      opts.Destroy,
   140  		diff:         opts.Diff,
   141  		hooks:        hooks,
   142  		module:       opts.Module,
   143  		providers:    opts.Providers,
   144  		provisioners: opts.Provisioners,
   145  		state:        state,
   146  		targets:      opts.Targets,
   147  		uiInput:      opts.UIInput,
   148  		variables:    variables,
   149  
   150  		parallelSem:         NewSemaphore(par),
   151  		providerInputConfig: make(map[string]map[string]interface{}),
   152  		sh:                  sh,
   153  	}, nil
   154  }
   155  
   156  type ContextGraphOpts struct {
   157  	Validate bool
   158  	Verbose  bool
   159  }
   160  
   161  // Graph returns the graph for this config.
   162  func (c *Context) Graph(g *ContextGraphOpts) (*Graph, error) {
   163  	return c.graphBuilder(g).Build(RootModulePath)
   164  }
   165  
   166  // GraphBuilder returns the GraphBuilder that will be used to create
   167  // the graphs for this context.
   168  func (c *Context) graphBuilder(g *ContextGraphOpts) GraphBuilder {
   169  	// TODO test
   170  	providers := make([]string, 0, len(c.providers))
   171  	for k, _ := range c.providers {
   172  		providers = append(providers, k)
   173  	}
   174  
   175  	provisioners := make([]string, 0, len(c.provisioners))
   176  	for k, _ := range c.provisioners {
   177  		provisioners = append(provisioners, k)
   178  	}
   179  
   180  	return &BuiltinGraphBuilder{
   181  		Root:         c.module,
   182  		Diff:         c.diff,
   183  		Providers:    providers,
   184  		Provisioners: provisioners,
   185  		State:        c.state,
   186  		Targets:      c.targets,
   187  		Destroy:      c.destroy,
   188  		Validate:     g.Validate,
   189  		Verbose:      g.Verbose,
   190  	}
   191  }
   192  
   193  // Input asks for input to fill variables and provider configurations.
   194  // This modifies the configuration in-place, so asking for Input twice
   195  // may result in different UI output showing different current values.
   196  func (c *Context) Input(mode InputMode) error {
   197  	v := c.acquireRun()
   198  	defer c.releaseRun(v)
   199  
   200  	if mode&InputModeVar != 0 {
   201  		// Walk the variables first for the root module. We walk them in
   202  		// alphabetical order for UX reasons.
   203  		rootConf := c.module.Config()
   204  		names := make([]string, len(rootConf.Variables))
   205  		m := make(map[string]*config.Variable)
   206  		for i, v := range rootConf.Variables {
   207  			names[i] = v.Name
   208  			m[v.Name] = v
   209  		}
   210  		sort.Strings(names)
   211  		for _, n := range names {
   212  			// If we only care about unset variables, then if the variable
   213  			// is set, continue on.
   214  			if mode&InputModeVarUnset != 0 {
   215  				if _, ok := c.variables[n]; ok {
   216  					continue
   217  				}
   218  			}
   219  
   220  			var valueType config.VariableType
   221  
   222  			v := m[n]
   223  			switch valueType = v.Type(); valueType {
   224  			case config.VariableTypeUnknown:
   225  				continue
   226  			case config.VariableTypeMap:
   227  				// OK
   228  			case config.VariableTypeList:
   229  				// OK
   230  			case config.VariableTypeString:
   231  				// OK
   232  			default:
   233  				panic(fmt.Sprintf("Unknown variable type: %#v", v.Type()))
   234  			}
   235  
   236  			// If the variable is not already set, and the variable defines a
   237  			// default, use that for the value.
   238  			if _, ok := c.variables[n]; !ok {
   239  				if v.Default != nil {
   240  					c.variables[n] = v.Default.(string)
   241  					continue
   242  				}
   243  			}
   244  
   245  			// this should only happen during tests
   246  			if c.uiInput == nil {
   247  				log.Println("[WARN] Content.uiInput is nil")
   248  				continue
   249  			}
   250  
   251  			// Ask the user for a value for this variable
   252  			var value string
   253  			retry := 0
   254  			for {
   255  				var err error
   256  				value, err = c.uiInput.Input(&InputOpts{
   257  					Id:          fmt.Sprintf("var.%s", n),
   258  					Query:       fmt.Sprintf("var.%s", n),
   259  					Description: v.Description,
   260  				})
   261  				if err != nil {
   262  					return fmt.Errorf(
   263  						"Error asking for %s: %s", n, err)
   264  				}
   265  
   266  				if value == "" && v.Required() {
   267  					// Redo if it is required, but abort if we keep getting
   268  					// blank entries
   269  					if retry > 2 {
   270  						return fmt.Errorf("missing required value for %q", n)
   271  					}
   272  					retry++
   273  					continue
   274  				}
   275  
   276  				break
   277  			}
   278  
   279  			// no value provided, so don't set the variable at all
   280  			if value == "" {
   281  				continue
   282  			}
   283  
   284  			decoded, err := parseVariableAsHCL(n, value, valueType)
   285  			if err != nil {
   286  				return err
   287  			}
   288  
   289  			if decoded != nil {
   290  				c.variables[n] = decoded
   291  			}
   292  		}
   293  	}
   294  
   295  	if mode&InputModeProvider != 0 {
   296  		// Build the graph
   297  		graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   298  		if err != nil {
   299  			return err
   300  		}
   301  
   302  		// Do the walk
   303  		if _, err := c.walk(graph, walkInput); err != nil {
   304  			return err
   305  		}
   306  	}
   307  
   308  	return nil
   309  }
   310  
   311  // Apply applies the changes represented by this context and returns
   312  // the resulting state.
   313  //
   314  // In addition to returning the resulting state, this context is updated
   315  // with the latest state.
   316  func (c *Context) Apply() (*State, error) {
   317  	v := c.acquireRun()
   318  	defer c.releaseRun(v)
   319  
   320  	// Copy our own state
   321  	c.state = c.state.DeepCopy()
   322  
   323  	// Build the graph
   324  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	// Do the walk
   330  	var walker *ContextGraphWalker
   331  	if c.destroy {
   332  		walker, err = c.walk(graph, walkDestroy)
   333  	} else {
   334  		walker, err = c.walk(graph, walkApply)
   335  	}
   336  
   337  	if len(walker.ValidationErrors) > 0 {
   338  		err = multierror.Append(err, walker.ValidationErrors...)
   339  	}
   340  
   341  	// Clean out any unused things
   342  	c.state.prune()
   343  
   344  	return c.state, err
   345  }
   346  
   347  // Plan generates an execution plan for the given context.
   348  //
   349  // The execution plan encapsulates the context and can be stored
   350  // in order to reinstantiate a context later for Apply.
   351  //
   352  // Plan also updates the diff of this context to be the diff generated
   353  // by the plan, so Apply can be called after.
   354  func (c *Context) Plan() (*Plan, error) {
   355  	v := c.acquireRun()
   356  	defer c.releaseRun(v)
   357  
   358  	p := &Plan{
   359  		Module:  c.module,
   360  		Vars:    c.variables,
   361  		State:   c.state,
   362  		Targets: c.targets,
   363  	}
   364  
   365  	var operation walkOperation
   366  	if c.destroy {
   367  		operation = walkPlanDestroy
   368  	} else {
   369  		// Set our state to be something temporary. We do this so that
   370  		// the plan can update a fake state so that variables work, then
   371  		// we replace it back with our old state.
   372  		old := c.state
   373  		if old == nil {
   374  			c.state = &State{}
   375  			c.state.init()
   376  		} else {
   377  			c.state = old.DeepCopy()
   378  		}
   379  		defer func() {
   380  			c.state = old
   381  		}()
   382  
   383  		operation = walkPlan
   384  	}
   385  
   386  	// Setup our diff
   387  	c.diffLock.Lock()
   388  	c.diff = new(Diff)
   389  	c.diff.init()
   390  	c.diffLock.Unlock()
   391  
   392  	// Build the graph
   393  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   394  	if err != nil {
   395  		return nil, err
   396  	}
   397  
   398  	// Do the walk
   399  	walker, err := c.walk(graph, operation)
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  	p.Diff = c.diff
   404  
   405  	// Now that we have a diff, we can build the exact graph that Apply will use
   406  	// and catch any possible cycles during the Plan phase.
   407  	if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil {
   408  		return nil, err
   409  	}
   410  	var errs error
   411  	if len(walker.ValidationErrors) > 0 {
   412  		errs = multierror.Append(errs, walker.ValidationErrors...)
   413  	}
   414  	return p, errs
   415  }
   416  
   417  // Refresh goes through all the resources in the state and refreshes them
   418  // to their latest state. This will update the state that this context
   419  // works with, along with returning it.
   420  //
   421  // Even in the case an error is returned, the state will be returned and
   422  // will potentially be partially updated.
   423  func (c *Context) Refresh() (*State, error) {
   424  	v := c.acquireRun()
   425  	defer c.releaseRun(v)
   426  
   427  	// Copy our own state
   428  	c.state = c.state.DeepCopy()
   429  
   430  	// Build the graph
   431  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   432  	if err != nil {
   433  		return nil, err
   434  	}
   435  
   436  	// Do the walk
   437  	if _, err := c.walk(graph, walkRefresh); err != nil {
   438  		return nil, err
   439  	}
   440  
   441  	// Clean out any unused things
   442  	c.state.prune()
   443  
   444  	return c.state, nil
   445  }
   446  
   447  // Stop stops the running task.
   448  //
   449  // Stop will block until the task completes.
   450  func (c *Context) Stop() {
   451  	c.l.Lock()
   452  	ch := c.runCh
   453  
   454  	// If we aren't running, then just return
   455  	if ch == nil {
   456  		c.l.Unlock()
   457  		return
   458  	}
   459  
   460  	// Tell the hook we want to stop
   461  	c.sh.Stop()
   462  
   463  	// Wait for us to stop
   464  	c.l.Unlock()
   465  	<-ch
   466  }
   467  
   468  // Validate validates the configuration and returns any warnings or errors.
   469  func (c *Context) Validate() ([]string, []error) {
   470  	v := c.acquireRun()
   471  	defer c.releaseRun(v)
   472  
   473  	var errs error
   474  
   475  	// Validate the configuration itself
   476  	if err := c.module.Validate(); err != nil {
   477  		errs = multierror.Append(errs, err)
   478  	}
   479  
   480  	// This only needs to be done for the root module, since inter-module
   481  	// variables are validated in the module tree.
   482  	if config := c.module.Config(); config != nil {
   483  		// Validate the user variables
   484  		if err := smcUserVariables(config, c.variables); len(err) > 0 {
   485  			errs = multierror.Append(errs, err...)
   486  		}
   487  	}
   488  
   489  	// If we have errors at this point, the graphing has no chance,
   490  	// so just bail early.
   491  	if errs != nil {
   492  		return nil, []error{errs}
   493  	}
   494  
   495  	// Build the graph so we can walk it and run Validate on nodes.
   496  	// We also validate the graph generated here, but this graph doesn't
   497  	// necessarily match the graph that Plan will generate, so we'll validate the
   498  	// graph again later after Planning.
   499  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   500  	if err != nil {
   501  		return nil, []error{err}
   502  	}
   503  
   504  	// Walk
   505  	walker, err := c.walk(graph, walkValidate)
   506  	if err != nil {
   507  		return nil, multierror.Append(errs, err).Errors
   508  	}
   509  
   510  	// Return the result
   511  	rerrs := multierror.Append(errs, walker.ValidationErrors...)
   512  	return walker.ValidationWarnings, rerrs.Errors
   513  }
   514  
   515  // Module returns the module tree associated with this context.
   516  func (c *Context) Module() *module.Tree {
   517  	return c.module
   518  }
   519  
   520  // Variables will return the mapping of variables that were defined
   521  // for this Context. If Input was called, this mapping may be different
   522  // than what was given.
   523  func (c *Context) Variables() map[string]interface{} {
   524  	return c.variables
   525  }
   526  
   527  // SetVariable sets a variable after a context has already been built.
   528  func (c *Context) SetVariable(k string, v interface{}) {
   529  	c.variables[k] = v
   530  }
   531  
   532  func (c *Context) acquireRun() chan<- struct{} {
   533  	c.l.Lock()
   534  	defer c.l.Unlock()
   535  
   536  	// Wait for no channel to exist
   537  	for c.runCh != nil {
   538  		c.l.Unlock()
   539  		ch := c.runCh
   540  		<-ch
   541  		c.l.Lock()
   542  	}
   543  
   544  	ch := make(chan struct{})
   545  	c.runCh = ch
   546  	return ch
   547  }
   548  
   549  func (c *Context) releaseRun(ch chan<- struct{}) {
   550  	c.l.Lock()
   551  	defer c.l.Unlock()
   552  
   553  	close(ch)
   554  	c.runCh = nil
   555  	c.sh.Reset()
   556  }
   557  
   558  func (c *Context) walk(
   559  	graph *Graph, operation walkOperation) (*ContextGraphWalker, error) {
   560  	// Walk the graph
   561  	log.Printf("[DEBUG] Starting graph walk: %s", operation.String())
   562  	walker := &ContextGraphWalker{Context: c, Operation: operation}
   563  	return walker, graph.Walk(walker)
   564  }
   565  
   566  // parseVariableAsHCL parses the value of a single variable as would have been specified
   567  // on the command line via -var or in an environment variable named TF_VAR_x, where x is
   568  // the name of the variable. In order to get around the restriction of HCL requiring a
   569  // top level object, we prepend a sentinel key, decode the user-specified value as its
   570  // value and pull the value back out of the resulting map.
   571  func parseVariableAsHCL(name string, input string, targetType config.VariableType) (interface{}, error) {
   572  	// expecting a string so don't decode anything, just strip quotes
   573  	if targetType == config.VariableTypeString {
   574  		return strings.Trim(input, `"`), nil
   575  	}
   576  
   577  	// return empty types
   578  	if strings.TrimSpace(input) == "" {
   579  		switch targetType {
   580  		case config.VariableTypeList:
   581  			return []interface{}{}, nil
   582  		case config.VariableTypeMap:
   583  			return make(map[string]interface{}), nil
   584  		}
   585  	}
   586  
   587  	const sentinelValue = "SENTINEL_TERRAFORM_VAR_OVERRIDE_KEY"
   588  	inputWithSentinal := fmt.Sprintf("%s = %s", sentinelValue, input)
   589  
   590  	var decoded map[string]interface{}
   591  	err := hcl.Decode(&decoded, inputWithSentinal)
   592  	if err != nil {
   593  		return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL: %s", name, input, err)
   594  	}
   595  
   596  	if len(decoded) != 1 {
   597  		return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. Only one value may be specified.", name, input)
   598  	}
   599  
   600  	parsedValue, ok := decoded[sentinelValue]
   601  	if !ok {
   602  		return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input)
   603  	}
   604  
   605  	switch targetType {
   606  	case config.VariableTypeList:
   607  		return parsedValue, nil
   608  	case config.VariableTypeMap:
   609  		if list, ok := parsedValue.([]map[string]interface{}); ok {
   610  			return list[0], nil
   611  		}
   612  
   613  		return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input)
   614  	default:
   615  		panic(fmt.Errorf("unknown type %s", targetType.Printable()))
   616  	}
   617  }