github.com/ojongerius/terraform@v0.7.1-0.20160811111335-97fcd5f4cc90/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/hcl"
    13  	"github.com/hashicorp/terraform/config"
    14  	"github.com/hashicorp/terraform/config/module"
    15  )
    16  
    17  // InputMode defines what sort of input will be asked for when Input
    18  // is called on Context.
    19  type InputMode byte
    20  
    21  const (
    22  	// InputModeVar asks for all variables
    23  	InputModeVar InputMode = 1 << iota
    24  
    25  	// InputModeVarUnset asks for variables which are not set yet
    26  	InputModeVarUnset
    27  
    28  	// InputModeProvider asks for provider variables
    29  	InputModeProvider
    30  
    31  	// InputModeStd is the standard operating mode and asks for both variables
    32  	// and providers.
    33  	InputModeStd = InputModeVar | InputModeProvider
    34  )
    35  
    36  // ContextOpts are the user-configurable options to create a context with
    37  // NewContext.
    38  type ContextOpts struct {
    39  	Destroy            bool
    40  	Diff               *Diff
    41  	Hooks              []Hook
    42  	Module             *module.Tree
    43  	Parallelism        int
    44  	State              *State
    45  	StateFutureAllowed bool
    46  	Providers          map[string]ResourceProviderFactory
    47  	Provisioners       map[string]ResourceProvisionerFactory
    48  	Targets            []string
    49  	Variables          map[string]interface{}
    50  
    51  	UIInput UIInput
    52  }
    53  
    54  // Context represents all the context that Terraform needs in order to
    55  // perform operations on infrastructure. This structure is built using
    56  // NewContext. See the documentation for that.
    57  //
    58  // Extra functions on Context can be found in context_*.go files.
    59  type Context struct {
    60  	destroy      bool
    61  	diff         *Diff
    62  	diffLock     sync.RWMutex
    63  	hooks        []Hook
    64  	module       *module.Tree
    65  	providers    map[string]ResourceProviderFactory
    66  	provisioners map[string]ResourceProvisionerFactory
    67  	sh           *stopHook
    68  	state        *State
    69  	stateLock    sync.RWMutex
    70  	targets      []string
    71  	uiInput      UIInput
    72  	variables    map[string]interface{}
    73  
    74  	l                   sync.Mutex // Lock acquired during any task
    75  	parallelSem         Semaphore
    76  	providerInputConfig map[string]map[string]interface{}
    77  	runCh               <-chan struct{}
    78  }
    79  
    80  // NewContext creates a new Context structure.
    81  //
    82  // Once a Context is creator, the pointer values within ContextOpts
    83  // should not be mutated in any way, since the pointers are copied, not
    84  // the values themselves.
    85  func NewContext(opts *ContextOpts) (*Context, error) {
    86  	// Copy all the hooks and add our stop hook. We don't append directly
    87  	// to the Config so that we're not modifying that in-place.
    88  	sh := new(stopHook)
    89  	hooks := make([]Hook, len(opts.Hooks)+1)
    90  	copy(hooks, opts.Hooks)
    91  	hooks[len(opts.Hooks)] = sh
    92  
    93  	state := opts.State
    94  	if state == nil {
    95  		state = new(State)
    96  		state.init()
    97  	}
    98  
    99  	// If our state is from the future, then error. Callers can avoid
   100  	// this error by explicitly setting `StateFutureAllowed`.
   101  	if !opts.StateFutureAllowed && state.FromFutureTerraform() {
   102  		return nil, fmt.Errorf(
   103  			"Terraform doesn't allow running any operations against a state\n"+
   104  				"that was written by a future Terraform version. The state is\n"+
   105  				"reporting it is written by Terraform '%s'.\n\n"+
   106  				"Please run at least that version of Terraform to continue.",
   107  			state.TFVersion)
   108  	}
   109  
   110  	// Explicitly reset our state version to our current version so that
   111  	// any operations we do will write out that our latest version
   112  	// has run.
   113  	state.TFVersion = Version
   114  
   115  	// Determine parallelism, default to 10. We do this both to limit
   116  	// CPU pressure but also to have an extra guard against rate throttling
   117  	// from providers.
   118  	par := opts.Parallelism
   119  	if par == 0 {
   120  		par = 10
   121  	}
   122  
   123  	// Set up the variables in the following sequence:
   124  	//    0 - Take default values from the configuration
   125  	//    1 - Take values from TF_VAR_x environment variables
   126  	//    2 - Take values specified in -var flags, overriding values
   127  	//        set by environment variables if necessary. This includes
   128  	//        values taken from -var-file in addition.
   129  	variables := make(map[string]interface{})
   130  
   131  	if opts.Module != nil {
   132  		for _, v := range opts.Module.Config().Variables {
   133  			if v.Default != nil {
   134  				if v.Type() == config.VariableTypeString {
   135  					// v.Default has already been parsed as HCL so there may be
   136  					// some stray ints in there
   137  					switch typedDefault := v.Default.(type) {
   138  					case string:
   139  						if typedDefault == "" {
   140  							continue
   141  						}
   142  						variables[v.Name] = typedDefault
   143  					case int, int64:
   144  						variables[v.Name] = fmt.Sprintf("%d", typedDefault)
   145  					case float32, float64:
   146  						variables[v.Name] = fmt.Sprintf("%f", typedDefault)
   147  					case bool:
   148  						variables[v.Name] = fmt.Sprintf("%t", typedDefault)
   149  					}
   150  				} else {
   151  					variables[v.Name] = v.Default
   152  				}
   153  			}
   154  		}
   155  
   156  		for _, v := range os.Environ() {
   157  			if !strings.HasPrefix(v, VarEnvPrefix) {
   158  				continue
   159  			}
   160  
   161  			// Strip off the prefix and get the value after the first "="
   162  			idx := strings.Index(v, "=")
   163  			k := v[len(VarEnvPrefix):idx]
   164  			v = v[idx+1:]
   165  
   166  			// Override the configuration-default values. Note that *not* finding the variable
   167  			// in configuration is OK, as we don't want to preclude people from having multiple
   168  			// sets of TF_VAR_whatever in their environment even if it is a little weird.
   169  			for _, schema := range opts.Module.Config().Variables {
   170  				if schema.Name == k {
   171  					varType := schema.Type()
   172  					varVal, err := parseVariableAsHCL(k, v, varType)
   173  					if err != nil {
   174  						return nil, err
   175  					}
   176  					switch varType {
   177  					case config.VariableTypeMap:
   178  						if existing, hasMap := variables[k]; !hasMap {
   179  							variables[k] = varVal
   180  						} else {
   181  							if existingMap, ok := existing.(map[string]interface{}); !ok {
   182  								panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k))
   183  							} else {
   184  								switch typedV := varVal.(type) {
   185  								case []map[string]interface{}:
   186  									for newKey, newVal := range typedV[0] {
   187  										existingMap[newKey] = newVal
   188  									}
   189  								case map[string]interface{}:
   190  									for newKey, newVal := range typedV {
   191  										existingMap[newKey] = newVal
   192  									}
   193  								default:
   194  									panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k))
   195  								}
   196  							}
   197  						}
   198  					default:
   199  						variables[k] = varVal
   200  					}
   201  				}
   202  			}
   203  		}
   204  
   205  		for k, v := range opts.Variables {
   206  			for _, schema := range opts.Module.Config().Variables {
   207  				if schema.Name == k {
   208  					switch schema.Type() {
   209  					case config.VariableTypeMap:
   210  						if existing, hasMap := variables[k]; !hasMap {
   211  							variables[k] = v
   212  						} else {
   213  							if existingMap, ok := existing.(map[string]interface{}); !ok {
   214  								panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k))
   215  							} else {
   216  								switch typedV := v.(type) {
   217  								case []map[string]interface{}:
   218  									for newKey, newVal := range typedV[0] {
   219  										existingMap[newKey] = newVal
   220  									}
   221  								case map[string]interface{}:
   222  									for newKey, newVal := range typedV {
   223  										existingMap[newKey] = newVal
   224  									}
   225  								default:
   226  									panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k))
   227  								}
   228  							}
   229  						}
   230  					default:
   231  						variables[k] = v
   232  					}
   233  				}
   234  			}
   235  		}
   236  	}
   237  
   238  	return &Context{
   239  		destroy:      opts.Destroy,
   240  		diff:         opts.Diff,
   241  		hooks:        hooks,
   242  		module:       opts.Module,
   243  		providers:    opts.Providers,
   244  		provisioners: opts.Provisioners,
   245  		state:        state,
   246  		targets:      opts.Targets,
   247  		uiInput:      opts.UIInput,
   248  		variables:    variables,
   249  
   250  		parallelSem:         NewSemaphore(par),
   251  		providerInputConfig: make(map[string]map[string]interface{}),
   252  		sh:                  sh,
   253  	}, nil
   254  }
   255  
   256  type ContextGraphOpts struct {
   257  	Validate bool
   258  	Verbose  bool
   259  }
   260  
   261  // Graph returns the graph for this config.
   262  func (c *Context) Graph(g *ContextGraphOpts) (*Graph, error) {
   263  	return c.graphBuilder(g).Build(RootModulePath)
   264  }
   265  
   266  // GraphBuilder returns the GraphBuilder that will be used to create
   267  // the graphs for this context.
   268  func (c *Context) graphBuilder(g *ContextGraphOpts) GraphBuilder {
   269  	// TODO test
   270  	providers := make([]string, 0, len(c.providers))
   271  	for k, _ := range c.providers {
   272  		providers = append(providers, k)
   273  	}
   274  
   275  	provisioners := make([]string, 0, len(c.provisioners))
   276  	for k, _ := range c.provisioners {
   277  		provisioners = append(provisioners, k)
   278  	}
   279  
   280  	return &BuiltinGraphBuilder{
   281  		Root:         c.module,
   282  		Diff:         c.diff,
   283  		Providers:    providers,
   284  		Provisioners: provisioners,
   285  		State:        c.state,
   286  		Targets:      c.targets,
   287  		Destroy:      c.destroy,
   288  		Validate:     g.Validate,
   289  		Verbose:      g.Verbose,
   290  	}
   291  }
   292  
   293  // Input asks for input to fill variables and provider configurations.
   294  // This modifies the configuration in-place, so asking for Input twice
   295  // may result in different UI output showing different current values.
   296  func (c *Context) Input(mode InputMode) error {
   297  	v := c.acquireRun()
   298  	defer c.releaseRun(v)
   299  
   300  	if mode&InputModeVar != 0 {
   301  		// Walk the variables first for the root module. We walk them in
   302  		// alphabetical order for UX reasons.
   303  		rootConf := c.module.Config()
   304  		names := make([]string, len(rootConf.Variables))
   305  		m := make(map[string]*config.Variable)
   306  		for i, v := range rootConf.Variables {
   307  			names[i] = v.Name
   308  			m[v.Name] = v
   309  		}
   310  		sort.Strings(names)
   311  		for _, n := range names {
   312  			// If we only care about unset variables, then if the variable
   313  			// is set, continue on.
   314  			if mode&InputModeVarUnset != 0 {
   315  				if _, ok := c.variables[n]; ok {
   316  					continue
   317  				}
   318  			}
   319  
   320  			var valueType config.VariableType
   321  
   322  			v := m[n]
   323  			switch valueType = v.Type(); valueType {
   324  			case config.VariableTypeUnknown:
   325  				continue
   326  			case config.VariableTypeMap:
   327  				// OK
   328  			case config.VariableTypeList:
   329  				// OK
   330  			case config.VariableTypeString:
   331  				// OK
   332  			default:
   333  				panic(fmt.Sprintf("Unknown variable type: %#v", v.Type()))
   334  			}
   335  
   336  			// If the variable is not already set, and the variable defines a
   337  			// default, use that for the value.
   338  			if _, ok := c.variables[n]; !ok {
   339  				if v.Default != nil {
   340  					c.variables[n] = v.Default.(string)
   341  					continue
   342  				}
   343  			}
   344  
   345  			// this should only happen during tests
   346  			if c.uiInput == nil {
   347  				log.Println("[WARN] Content.uiInput is nil")
   348  				continue
   349  			}
   350  
   351  			// Ask the user for a value for this variable
   352  			var value string
   353  			retry := 0
   354  			for {
   355  				var err error
   356  				value, err = c.uiInput.Input(&InputOpts{
   357  					Id:          fmt.Sprintf("var.%s", n),
   358  					Query:       fmt.Sprintf("var.%s", n),
   359  					Description: v.Description,
   360  				})
   361  				if err != nil {
   362  					return fmt.Errorf(
   363  						"Error asking for %s: %s", n, err)
   364  				}
   365  
   366  				if value == "" && v.Required() {
   367  					// Redo if it is required, but abort if we keep getting
   368  					// blank entries
   369  					if retry > 2 {
   370  						return fmt.Errorf("missing required value for %q", n)
   371  					}
   372  					retry++
   373  					continue
   374  				}
   375  
   376  				break
   377  			}
   378  
   379  			// no value provided, so don't set the variable at all
   380  			if value == "" {
   381  				continue
   382  			}
   383  
   384  			decoded, err := parseVariableAsHCL(n, value, valueType)
   385  			if err != nil {
   386  				return err
   387  			}
   388  
   389  			if decoded != nil {
   390  				c.variables[n] = decoded
   391  			}
   392  		}
   393  	}
   394  
   395  	if mode&InputModeProvider != 0 {
   396  		// Build the graph
   397  		graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   398  		if err != nil {
   399  			return err
   400  		}
   401  
   402  		// Do the walk
   403  		if _, err := c.walk(graph, walkInput); err != nil {
   404  			return err
   405  		}
   406  	}
   407  
   408  	return nil
   409  }
   410  
   411  // Apply applies the changes represented by this context and returns
   412  // the resulting state.
   413  //
   414  // In addition to returning the resulting state, this context is updated
   415  // with the latest state.
   416  func (c *Context) Apply() (*State, error) {
   417  	v := c.acquireRun()
   418  	defer c.releaseRun(v)
   419  
   420  	// Copy our own state
   421  	c.state = c.state.DeepCopy()
   422  
   423  	// Build the graph
   424  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   425  	if err != nil {
   426  		return nil, err
   427  	}
   428  
   429  	// Do the walk
   430  	var walker *ContextGraphWalker
   431  	if c.destroy {
   432  		walker, err = c.walk(graph, walkDestroy)
   433  	} else {
   434  		walker, err = c.walk(graph, walkApply)
   435  	}
   436  
   437  	if len(walker.ValidationErrors) > 0 {
   438  		err = multierror.Append(err, walker.ValidationErrors...)
   439  	}
   440  
   441  	// Clean out any unused things
   442  	c.state.prune()
   443  
   444  	return c.state, err
   445  }
   446  
   447  // Plan generates an execution plan for the given context.
   448  //
   449  // The execution plan encapsulates the context and can be stored
   450  // in order to reinstantiate a context later for Apply.
   451  //
   452  // Plan also updates the diff of this context to be the diff generated
   453  // by the plan, so Apply can be called after.
   454  func (c *Context) Plan() (*Plan, error) {
   455  	v := c.acquireRun()
   456  	defer c.releaseRun(v)
   457  
   458  	p := &Plan{
   459  		Module:  c.module,
   460  		Vars:    c.variables,
   461  		State:   c.state,
   462  		Targets: c.targets,
   463  	}
   464  
   465  	var operation walkOperation
   466  	if c.destroy {
   467  		operation = walkPlanDestroy
   468  	} else {
   469  		// Set our state to be something temporary. We do this so that
   470  		// the plan can update a fake state so that variables work, then
   471  		// we replace it back with our old state.
   472  		old := c.state
   473  		if old == nil {
   474  			c.state = &State{}
   475  			c.state.init()
   476  		} else {
   477  			c.state = old.DeepCopy()
   478  		}
   479  		defer func() {
   480  			c.state = old
   481  		}()
   482  
   483  		operation = walkPlan
   484  	}
   485  
   486  	// Setup our diff
   487  	c.diffLock.Lock()
   488  	c.diff = new(Diff)
   489  	c.diff.init()
   490  	c.diffLock.Unlock()
   491  
   492  	// Build the graph
   493  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   494  	if err != nil {
   495  		return nil, err
   496  	}
   497  
   498  	// Do the walk
   499  	walker, err := c.walk(graph, operation)
   500  	if err != nil {
   501  		return nil, err
   502  	}
   503  	p.Diff = c.diff
   504  
   505  	// Now that we have a diff, we can build the exact graph that Apply will use
   506  	// and catch any possible cycles during the Plan phase.
   507  	if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil {
   508  		return nil, err
   509  	}
   510  	var errs error
   511  	if len(walker.ValidationErrors) > 0 {
   512  		errs = multierror.Append(errs, walker.ValidationErrors...)
   513  	}
   514  	return p, errs
   515  }
   516  
   517  // Refresh goes through all the resources in the state and refreshes them
   518  // to their latest state. This will update the state that this context
   519  // works with, along with returning it.
   520  //
   521  // Even in the case an error is returned, the state will be returned and
   522  // will potentially be partially updated.
   523  func (c *Context) Refresh() (*State, error) {
   524  	v := c.acquireRun()
   525  	defer c.releaseRun(v)
   526  
   527  	// Copy our own state
   528  	c.state = c.state.DeepCopy()
   529  
   530  	// Build the graph
   531  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   532  	if err != nil {
   533  		return nil, err
   534  	}
   535  
   536  	// Do the walk
   537  	if _, err := c.walk(graph, walkRefresh); err != nil {
   538  		return nil, err
   539  	}
   540  
   541  	// Clean out any unused things
   542  	c.state.prune()
   543  
   544  	return c.state, nil
   545  }
   546  
   547  // Stop stops the running task.
   548  //
   549  // Stop will block until the task completes.
   550  func (c *Context) Stop() {
   551  	c.l.Lock()
   552  	ch := c.runCh
   553  
   554  	// If we aren't running, then just return
   555  	if ch == nil {
   556  		c.l.Unlock()
   557  		return
   558  	}
   559  
   560  	// Tell the hook we want to stop
   561  	c.sh.Stop()
   562  
   563  	// Wait for us to stop
   564  	c.l.Unlock()
   565  	<-ch
   566  }
   567  
   568  // Validate validates the configuration and returns any warnings or errors.
   569  func (c *Context) Validate() ([]string, []error) {
   570  	v := c.acquireRun()
   571  	defer c.releaseRun(v)
   572  
   573  	var errs error
   574  
   575  	// Validate the configuration itself
   576  	if err := c.module.Validate(); err != nil {
   577  		errs = multierror.Append(errs, err)
   578  	}
   579  
   580  	// This only needs to be done for the root module, since inter-module
   581  	// variables are validated in the module tree.
   582  	if config := c.module.Config(); config != nil {
   583  		// Validate the user variables
   584  		if err := smcUserVariables(config, c.variables); len(err) > 0 {
   585  			errs = multierror.Append(errs, err...)
   586  		}
   587  	}
   588  
   589  	// If we have errors at this point, the graphing has no chance,
   590  	// so just bail early.
   591  	if errs != nil {
   592  		return nil, []error{errs}
   593  	}
   594  
   595  	// Build the graph so we can walk it and run Validate on nodes.
   596  	// We also validate the graph generated here, but this graph doesn't
   597  	// necessarily match the graph that Plan will generate, so we'll validate the
   598  	// graph again later after Planning.
   599  	graph, err := c.Graph(&ContextGraphOpts{Validate: true})
   600  	if err != nil {
   601  		return nil, []error{err}
   602  	}
   603  
   604  	// Walk
   605  	walker, err := c.walk(graph, walkValidate)
   606  	if err != nil {
   607  		return nil, multierror.Append(errs, err).Errors
   608  	}
   609  
   610  	// Return the result
   611  	rerrs := multierror.Append(errs, walker.ValidationErrors...)
   612  	return walker.ValidationWarnings, rerrs.Errors
   613  }
   614  
   615  // Module returns the module tree associated with this context.
   616  func (c *Context) Module() *module.Tree {
   617  	return c.module
   618  }
   619  
   620  // Variables will return the mapping of variables that were defined
   621  // for this Context. If Input was called, this mapping may be different
   622  // than what was given.
   623  func (c *Context) Variables() map[string]interface{} {
   624  	return c.variables
   625  }
   626  
   627  // SetVariable sets a variable after a context has already been built.
   628  func (c *Context) SetVariable(k string, v interface{}) {
   629  	c.variables[k] = v
   630  }
   631  
   632  func (c *Context) acquireRun() chan<- struct{} {
   633  	c.l.Lock()
   634  	defer c.l.Unlock()
   635  
   636  	// Wait for no channel to exist
   637  	for c.runCh != nil {
   638  		c.l.Unlock()
   639  		ch := c.runCh
   640  		<-ch
   641  		c.l.Lock()
   642  	}
   643  
   644  	ch := make(chan struct{})
   645  	c.runCh = ch
   646  	return ch
   647  }
   648  
   649  func (c *Context) releaseRun(ch chan<- struct{}) {
   650  	c.l.Lock()
   651  	defer c.l.Unlock()
   652  
   653  	close(ch)
   654  	c.runCh = nil
   655  	c.sh.Reset()
   656  }
   657  
   658  func (c *Context) walk(
   659  	graph *Graph, operation walkOperation) (*ContextGraphWalker, error) {
   660  	// Walk the graph
   661  	log.Printf("[DEBUG] Starting graph walk: %s", operation.String())
   662  	walker := &ContextGraphWalker{Context: c, Operation: operation}
   663  	return walker, graph.Walk(walker)
   664  }
   665  
   666  // parseVariableAsHCL parses the value of a single variable as would have been specified
   667  // on the command line via -var or in an environment variable named TF_VAR_x, where x is
   668  // the name of the variable. In order to get around the restriction of HCL requiring a
   669  // top level object, we prepend a sentinel key, decode the user-specified value as its
   670  // value and pull the value back out of the resulting map.
   671  func parseVariableAsHCL(name string, input string, targetType config.VariableType) (interface{}, error) {
   672  	// expecting a string so don't decode anything, just strip quotes
   673  	if targetType == config.VariableTypeString {
   674  		return strings.Trim(input, `"`), nil
   675  	}
   676  
   677  	// return empty types
   678  	if strings.TrimSpace(input) == "" {
   679  		switch targetType {
   680  		case config.VariableTypeList:
   681  			return []interface{}{}, nil
   682  		case config.VariableTypeMap:
   683  			return make(map[string]interface{}), nil
   684  		}
   685  	}
   686  
   687  	const sentinelValue = "SENTINEL_TERRAFORM_VAR_OVERRIDE_KEY"
   688  	inputWithSentinal := fmt.Sprintf("%s = %s", sentinelValue, input)
   689  
   690  	var decoded map[string]interface{}
   691  	err := hcl.Decode(&decoded, inputWithSentinal)
   692  	if err != nil {
   693  		return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL: %s", name, input, err)
   694  	}
   695  
   696  	if len(decoded) != 1 {
   697  		return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. Only one value may be specified.", name, input)
   698  	}
   699  
   700  	parsedValue, ok := decoded[sentinelValue]
   701  	if !ok {
   702  		return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input)
   703  	}
   704  
   705  	switch targetType {
   706  	case config.VariableTypeList:
   707  		return parsedValue, nil
   708  	case config.VariableTypeMap:
   709  		if list, ok := parsedValue.([]map[string]interface{}); ok {
   710  			return list[0], nil
   711  		}
   712  
   713  		return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input)
   714  	default:
   715  		panic(fmt.Errorf("unknown type %s", targetType.Printable()))
   716  	}
   717  }