github.com/ricardclau/terraform@v0.6.17-0.20160519222547-283e3ae6b5a9/terraform/interpolate.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"regexp"
     8  	"sort"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/hashicorp/hil"
    13  	"github.com/hashicorp/hil/ast"
    14  	"github.com/hashicorp/terraform/config"
    15  	"github.com/hashicorp/terraform/config/module"
    16  )
    17  
    18  const (
    19  	// VarEnvPrefix is the prefix of variables that are read from
    20  	// the environment to set variables here.
    21  	VarEnvPrefix = "TF_VAR_"
    22  )
    23  
    24  // Interpolater is the structure responsible for determining the values
    25  // for interpolations such as `aws_instance.foo.bar`.
    26  type Interpolater struct {
    27  	Operation          walkOperation
    28  	Module             *module.Tree
    29  	State              *State
    30  	StateLock          *sync.RWMutex
    31  	VariableValues     map[string]interface{}
    32  	VariableValuesLock *sync.Mutex
    33  }
    34  
    35  // InterpolationScope is the current scope of execution. This is required
    36  // since some variables which are interpolated are dependent on what we're
    37  // operating on and where we are.
    38  type InterpolationScope struct {
    39  	Path     []string
    40  	Resource *Resource
    41  }
    42  
    43  // Values returns the values for all the variables in the given map.
    44  func (i *Interpolater) Values(
    45  	scope *InterpolationScope,
    46  	vars map[string]config.InterpolatedVariable) (map[string]ast.Variable, error) {
    47  	result := make(map[string]ast.Variable, len(vars))
    48  
    49  	// Copy the default variables
    50  	if i.Module != nil && scope != nil {
    51  		mod := i.Module
    52  		if len(scope.Path) > 1 {
    53  			mod = i.Module.Child(scope.Path[1:])
    54  		}
    55  		for _, v := range mod.Config().Variables {
    56  			// Set default variables
    57  			if v.Default == nil {
    58  				continue
    59  			}
    60  
    61  			n := fmt.Sprintf("var.%s", v.Name)
    62  			variable, err := hil.InterfaceToVariable(v.Default)
    63  			if err != nil {
    64  				return nil, fmt.Errorf("invalid default map value for %s: %v", v.Name, v.Default)
    65  			}
    66  
    67  			result[n] = variable
    68  		}
    69  	}
    70  
    71  	for n, rawV := range vars {
    72  		var err error
    73  		switch v := rawV.(type) {
    74  		case *config.CountVariable:
    75  			err = i.valueCountVar(scope, n, v, result)
    76  		case *config.ModuleVariable:
    77  			err = i.valueModuleVar(scope, n, v, result)
    78  		case *config.PathVariable:
    79  			err = i.valuePathVar(scope, n, v, result)
    80  		case *config.ResourceVariable:
    81  			err = i.valueResourceVar(scope, n, v, result)
    82  		case *config.SelfVariable:
    83  			err = i.valueSelfVar(scope, n, v, result)
    84  		case *config.SimpleVariable:
    85  			err = i.valueSimpleVar(scope, n, v, result)
    86  		case *config.UserVariable:
    87  			err = i.valueUserVar(scope, n, v, result)
    88  		default:
    89  			err = fmt.Errorf("%s: unknown variable type: %T", n, rawV)
    90  		}
    91  
    92  		if err != nil {
    93  			return nil, err
    94  		}
    95  	}
    96  
    97  	return result, nil
    98  }
    99  
   100  func (i *Interpolater) valueCountVar(
   101  	scope *InterpolationScope,
   102  	n string,
   103  	v *config.CountVariable,
   104  	result map[string]ast.Variable) error {
   105  	switch v.Type {
   106  	case config.CountValueIndex:
   107  		if scope.Resource == nil {
   108  			return fmt.Errorf("%s: count.index is only valid within resources", n)
   109  		}
   110  		result[n] = ast.Variable{
   111  			Value: scope.Resource.CountIndex,
   112  			Type:  ast.TypeInt,
   113  		}
   114  		return nil
   115  	default:
   116  		return fmt.Errorf("%s: unknown count type: %#v", n, v.Type)
   117  	}
   118  }
   119  
   120  func unknownVariable() ast.Variable {
   121  	return ast.Variable{
   122  		Type:  ast.TypeString,
   123  		Value: config.UnknownVariableValue,
   124  	}
   125  }
   126  
   127  func (i *Interpolater) valueModuleVar(
   128  	scope *InterpolationScope,
   129  	n string,
   130  	v *config.ModuleVariable,
   131  	result map[string]ast.Variable) error {
   132  	// If we're computing all dynamic fields, then module vars count
   133  	// and we mark it as computed.
   134  	if i.Operation == walkValidate {
   135  		result[n] = ast.Variable{
   136  			Value: config.UnknownVariableValue,
   137  			Type:  ast.TypeString,
   138  		}
   139  		return nil
   140  	}
   141  
   142  	// Build the path to the child module we want
   143  	path := make([]string, len(scope.Path), len(scope.Path)+1)
   144  	copy(path, scope.Path)
   145  	path = append(path, v.Name)
   146  
   147  	// Grab the lock so that if other interpolations are running or
   148  	// state is being modified, we'll be safe.
   149  	i.StateLock.RLock()
   150  	defer i.StateLock.RUnlock()
   151  
   152  	// Get the module where we're looking for the value
   153  	mod := i.State.ModuleByPath(path)
   154  	if mod == nil {
   155  		// If the module doesn't exist, then we can return an empty string.
   156  		// This happens usually only in Refresh() when we haven't populated
   157  		// a state. During validation, we semantically verify that all
   158  		// modules reference other modules, and graph ordering should
   159  		// ensure that the module is in the state, so if we reach this
   160  		// point otherwise it really is a panic.
   161  		result[n] = unknownVariable()
   162  	} else {
   163  		// Get the value from the outputs
   164  		if outputState, ok := mod.Outputs[v.Field]; ok {
   165  			output, err := hil.InterfaceToVariable(outputState.Value)
   166  			if err != nil {
   167  				return err
   168  			}
   169  			result[n] = output
   170  		} else {
   171  			// Same reasons as the comment above.
   172  			result[n] = unknownVariable()
   173  
   174  		}
   175  	}
   176  
   177  	return nil
   178  }
   179  
   180  func (i *Interpolater) valuePathVar(
   181  	scope *InterpolationScope,
   182  	n string,
   183  	v *config.PathVariable,
   184  	result map[string]ast.Variable) error {
   185  	switch v.Type {
   186  	case config.PathValueCwd:
   187  		wd, err := os.Getwd()
   188  		if err != nil {
   189  			return fmt.Errorf(
   190  				"Couldn't get cwd for var %s: %s",
   191  				v.FullKey(), err)
   192  		}
   193  
   194  		result[n] = ast.Variable{
   195  			Value: wd,
   196  			Type:  ast.TypeString,
   197  		}
   198  	case config.PathValueModule:
   199  		if t := i.Module.Child(scope.Path[1:]); t != nil {
   200  			result[n] = ast.Variable{
   201  				Value: t.Config().Dir,
   202  				Type:  ast.TypeString,
   203  			}
   204  		}
   205  	case config.PathValueRoot:
   206  		result[n] = ast.Variable{
   207  			Value: i.Module.Config().Dir,
   208  			Type:  ast.TypeString,
   209  		}
   210  	default:
   211  		return fmt.Errorf("%s: unknown path type: %#v", n, v.Type)
   212  	}
   213  
   214  	return nil
   215  
   216  }
   217  
   218  func (i *Interpolater) valueResourceVar(
   219  	scope *InterpolationScope,
   220  	n string,
   221  	v *config.ResourceVariable,
   222  	result map[string]ast.Variable) error {
   223  	// If we're computing all dynamic fields, then module vars count
   224  	// and we mark it as computed.
   225  	if i.Operation == walkValidate {
   226  		result[n] = ast.Variable{
   227  			Value: config.UnknownVariableValue,
   228  			Type:  ast.TypeString,
   229  		}
   230  		return nil
   231  	}
   232  
   233  	var variable *ast.Variable
   234  	var err error
   235  
   236  	if v.Multi && v.Index == -1 {
   237  		variable, err = i.computeResourceMultiVariable(scope, v)
   238  	} else {
   239  		variable, err = i.computeResourceVariable(scope, v)
   240  	}
   241  
   242  	if err != nil {
   243  		return err
   244  	}
   245  
   246  	if variable == nil {
   247  		// During the input walk we tolerate missing variables because
   248  		// we haven't yet had a chance to refresh state, so dynamic data may
   249  		// not yet be complete.
   250  		// If it truly is missing, we'll catch it on a later walk.
   251  		// This applies only to graph nodes that interpolate during the
   252  		// config walk, e.g. providers.
   253  		if i.Operation == walkInput {
   254  			result[n] = ast.Variable{
   255  				Value: config.UnknownVariableValue,
   256  				Type:  ast.TypeString,
   257  			}
   258  			return nil
   259  		}
   260  
   261  		return fmt.Errorf("variable %q is nil, but no error was reported", v.Name)
   262  	}
   263  
   264  	result[n] = *variable
   265  	return nil
   266  }
   267  
   268  func (i *Interpolater) valueSelfVar(
   269  	scope *InterpolationScope,
   270  	n string,
   271  	v *config.SelfVariable,
   272  	result map[string]ast.Variable) error {
   273  	if scope == nil || scope.Resource == nil {
   274  		return fmt.Errorf(
   275  			"%s: invalid scope, self variables are only valid on resources", n)
   276  	}
   277  	rv, err := config.NewResourceVariable(fmt.Sprintf(
   278  		"%s.%s.%d.%s",
   279  		scope.Resource.Type,
   280  		scope.Resource.Name,
   281  		scope.Resource.CountIndex,
   282  		v.Field))
   283  	if err != nil {
   284  		return err
   285  	}
   286  
   287  	return i.valueResourceVar(scope, n, rv, result)
   288  }
   289  
   290  func (i *Interpolater) valueSimpleVar(
   291  	scope *InterpolationScope,
   292  	n string,
   293  	v *config.SimpleVariable,
   294  	result map[string]ast.Variable) error {
   295  	// SimpleVars are never handled by Terraform's interpolator
   296  	result[n] = ast.Variable{
   297  		Value: config.UnknownVariableValue,
   298  		Type:  ast.TypeString,
   299  	}
   300  	return nil
   301  }
   302  
   303  func (i *Interpolater) valueUserVar(
   304  	scope *InterpolationScope,
   305  	n string,
   306  	v *config.UserVariable,
   307  	result map[string]ast.Variable) error {
   308  	i.VariableValuesLock.Lock()
   309  	defer i.VariableValuesLock.Unlock()
   310  	val, ok := i.VariableValues[v.Name]
   311  	if ok {
   312  		varValue, err := hil.InterfaceToVariable(val)
   313  		if err != nil {
   314  			return fmt.Errorf("cannot convert %s value %q to an ast.Variable for interpolation: %s",
   315  				v.Name, val, err)
   316  		}
   317  		result[n] = varValue
   318  		return nil
   319  	}
   320  
   321  	if _, ok := result[n]; !ok && i.Operation == walkValidate {
   322  		result[n] = unknownVariable()
   323  		return nil
   324  	}
   325  
   326  	// Look up if we have any variables with this prefix because
   327  	// those are map overrides. Include those.
   328  	for k, val := range i.VariableValues {
   329  		if strings.HasPrefix(k, v.Name+".") {
   330  			keyComponents := strings.Split(k, ".")
   331  			overrideKey := keyComponents[len(keyComponents)-1]
   332  
   333  			mapInterface, ok := result["var."+v.Name]
   334  			if !ok {
   335  				return fmt.Errorf("override for non-existent variable: %s", v.Name)
   336  			}
   337  
   338  			mapVariable := mapInterface.Value.(map[string]ast.Variable)
   339  
   340  			varValue, err := hil.InterfaceToVariable(val)
   341  			if err != nil {
   342  				return fmt.Errorf("cannot convert %s value %q to an ast.Variable for interpolation: %s",
   343  					v.Name, val, err)
   344  			}
   345  			mapVariable[overrideKey] = varValue
   346  		}
   347  	}
   348  
   349  	return nil
   350  }
   351  
   352  func (i *Interpolater) computeResourceVariable(
   353  	scope *InterpolationScope,
   354  	v *config.ResourceVariable) (*ast.Variable, error) {
   355  	id := v.ResourceId()
   356  	if v.Multi {
   357  		id = fmt.Sprintf("%s.%d", id, v.Index)
   358  	}
   359  
   360  	i.StateLock.RLock()
   361  	defer i.StateLock.RUnlock()
   362  
   363  	unknownVariable := unknownVariable()
   364  
   365  	// Get the information about this resource variable, and verify
   366  	// that it exists and such.
   367  	module, _, err := i.resourceVariableInfo(scope, v)
   368  	if err != nil {
   369  		return nil, err
   370  	}
   371  
   372  	// If we have no module in the state yet or count, return empty
   373  	if module == nil || len(module.Resources) == 0 {
   374  		return nil, nil
   375  	}
   376  
   377  	// Get the resource out from the state. We know the state exists
   378  	// at this point and if there is a state, we expect there to be a
   379  	// resource with the given name.
   380  	r, ok := module.Resources[id]
   381  	if !ok && v.Multi && v.Index == 0 {
   382  		r, ok = module.Resources[v.ResourceId()]
   383  	}
   384  	if !ok {
   385  		r = nil
   386  	}
   387  	if r == nil {
   388  		goto MISSING
   389  	}
   390  
   391  	if r.Primary == nil {
   392  		goto MISSING
   393  	}
   394  
   395  	if attr, ok := r.Primary.Attributes[v.Field]; ok {
   396  		return &ast.Variable{Type: ast.TypeString, Value: attr}, nil
   397  	}
   398  
   399  	// computed list or map attribute
   400  	if _, ok := r.Primary.Attributes[v.Field+".#"]; ok {
   401  		variable, err := i.interpolateComplexTypeAttribute(v.Field, r.Primary.Attributes)
   402  		return &variable, err
   403  	}
   404  
   405  	// At apply time, we can't do the "maybe has it" check below
   406  	// that we need for plans since parent elements might be computed.
   407  	// Therefore, it is an error and we're missing the key.
   408  	//
   409  	// TODO: test by creating a state and configuration that is referencing
   410  	// a non-existent variable "foo.bar" where the state only has "foo"
   411  	// and verify plan works, but apply doesn't.
   412  	if i.Operation == walkApply || i.Operation == walkDestroy {
   413  		goto MISSING
   414  	}
   415  
   416  	// We didn't find the exact field, so lets separate the dots
   417  	// and see if anything along the way is a computed set. i.e. if
   418  	// we have "foo.0.bar" as the field, check to see if "foo" is
   419  	// a computed list. If so, then the whole thing is computed.
   420  	if parts := strings.Split(v.Field, "."); len(parts) > 1 {
   421  		for i := 1; i < len(parts); i++ {
   422  			// Lists and sets make this
   423  			key := fmt.Sprintf("%s.#", strings.Join(parts[:i], "."))
   424  			if attr, ok := r.Primary.Attributes[key]; ok {
   425  				return &ast.Variable{Type: ast.TypeString, Value: attr}, nil
   426  			}
   427  
   428  			// Maps make this
   429  			key = fmt.Sprintf("%s", strings.Join(parts[:i], "."))
   430  			if attr, ok := r.Primary.Attributes[key]; ok {
   431  				return &ast.Variable{Type: ast.TypeString, Value: attr}, nil
   432  			}
   433  		}
   434  	}
   435  
   436  MISSING:
   437  	// Validation for missing interpolations should happen at a higher
   438  	// semantic level. If we reached this point and don't have variables,
   439  	// just return the computed value.
   440  	if scope == nil && scope.Resource == nil {
   441  		return &unknownVariable, nil
   442  	}
   443  
   444  	// If the operation is refresh, it isn't an error for a value to
   445  	// be unknown. Instead, we return that the value is computed so
   446  	// that the graph can continue to refresh other nodes. It doesn't
   447  	// matter because the config isn't interpolated anyways.
   448  	//
   449  	// For a Destroy, we're also fine with computed values, since our goal is
   450  	// only to get destroy nodes for existing resources.
   451  	//
   452  	// For an input walk, computed values are okay to return because we're only
   453  	// looking for missing variables to prompt the user for.
   454  	if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput {
   455  		return &unknownVariable, nil
   456  	}
   457  
   458  	return nil, fmt.Errorf(
   459  		"Resource '%s' does not have attribute '%s' "+
   460  			"for variable '%s'",
   461  		id,
   462  		v.Field,
   463  		v.FullKey())
   464  }
   465  
   466  func (i *Interpolater) computeResourceMultiVariable(
   467  	scope *InterpolationScope,
   468  	v *config.ResourceVariable) (*ast.Variable, error) {
   469  	i.StateLock.RLock()
   470  	defer i.StateLock.RUnlock()
   471  
   472  	unknownVariable := unknownVariable()
   473  
   474  	// Get the information about this resource variable, and verify
   475  	// that it exists and such.
   476  	module, cr, err := i.resourceVariableInfo(scope, v)
   477  	if err != nil {
   478  		return nil, err
   479  	}
   480  
   481  	// Get the count so we know how many to iterate over
   482  	count, err := cr.Count()
   483  	if err != nil {
   484  		return nil, fmt.Errorf(
   485  			"Error reading %s count: %s",
   486  			v.ResourceId(),
   487  			err)
   488  	}
   489  
   490  	// If we have no module in the state yet or count, return empty
   491  	if module == nil || len(module.Resources) == 0 || count == 0 {
   492  		return &ast.Variable{Type: ast.TypeString, Value: ""}, nil
   493  	}
   494  
   495  	var values []string
   496  	for j := 0; j < count; j++ {
   497  		id := fmt.Sprintf("%s.%d", v.ResourceId(), j)
   498  
   499  		// If we're dealing with only a single resource, then the
   500  		// ID doesn't have a trailing index.
   501  		if count == 1 {
   502  			id = v.ResourceId()
   503  		}
   504  
   505  		r, ok := module.Resources[id]
   506  		if !ok {
   507  			continue
   508  		}
   509  
   510  		if r.Primary == nil {
   511  			continue
   512  		}
   513  
   514  		if singleAttr, ok := r.Primary.Attributes[v.Field]; ok {
   515  			if singleAttr == config.UnknownVariableValue {
   516  				return &unknownVariable, nil
   517  			}
   518  
   519  			values = append(values, singleAttr)
   520  			continue
   521  		}
   522  
   523  		// computed list attribute
   524  		_, ok = r.Primary.Attributes[v.Field+".#"]
   525  		if !ok {
   526  			continue
   527  		}
   528  		multiAttr, err := i.interpolateComplexTypeAttribute(v.Field, r.Primary.Attributes)
   529  		if err != nil {
   530  			return nil, err
   531  		}
   532  
   533  		if multiAttr == unknownVariable {
   534  			return &ast.Variable{Type: ast.TypeString, Value: ""}, nil
   535  		}
   536  
   537  		for _, element := range multiAttr.Value.([]ast.Variable) {
   538  			strVal := element.Value.(string)
   539  			if strVal == config.UnknownVariableValue {
   540  				return &unknownVariable, nil
   541  			}
   542  
   543  			values = append(values, strVal)
   544  		}
   545  	}
   546  
   547  	if len(values) == 0 {
   548  		// If the operation is refresh, it isn't an error for a value to
   549  		// be unknown. Instead, we return that the value is computed so
   550  		// that the graph can continue to refresh other nodes. It doesn't
   551  		// matter because the config isn't interpolated anyways.
   552  		//
   553  		// For a Destroy, we're also fine with computed values, since our goal is
   554  		// only to get destroy nodes for existing resources.
   555  		//
   556  		// For an input walk, computed values are okay to return because we're only
   557  		// looking for missing variables to prompt the user for.
   558  		if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput {
   559  			return &unknownVariable, nil
   560  		}
   561  
   562  		return nil, fmt.Errorf(
   563  			"Resource '%s' does not have attribute '%s' "+
   564  				"for variable '%s'",
   565  			v.ResourceId(),
   566  			v.Field,
   567  			v.FullKey())
   568  	}
   569  
   570  	variable, err := hil.InterfaceToVariable(values)
   571  	return &variable, err
   572  }
   573  
   574  func (i *Interpolater) interpolateComplexTypeAttribute(
   575  	resourceID string,
   576  	attributes map[string]string) (ast.Variable, error) {
   577  
   578  	attr := attributes[resourceID+".#"]
   579  	log.Printf("[DEBUG] Interpolating computed complex type attribute %s (%s)",
   580  		resourceID, attr)
   581  
   582  	// In Terraform's internal dotted representation of list-like attributes, the
   583  	// ".#" count field is marked as unknown to indicate "this whole list is
   584  	// unknown". We must honor that meaning here so computed references can be
   585  	// treated properly during the plan phase.
   586  	if attr == config.UnknownVariableValue {
   587  		return unknownVariable(), nil
   588  	}
   589  
   590  	// At this stage we don't know whether the item is a list or a map, so we
   591  	// examine the keys to see whether they are all numeric.
   592  	var numericKeys []string
   593  	var allKeys []string
   594  	numberedListKey := regexp.MustCompile("^" + resourceID + "\\.[0-9]+$")
   595  	otherListKey := regexp.MustCompile("^" + resourceID + "\\.([^#]+)$")
   596  	for id, _ := range attributes {
   597  		if numberedListKey.MatchString(id) {
   598  			numericKeys = append(numericKeys, id)
   599  		}
   600  		if submatches := otherListKey.FindAllStringSubmatch(id, -1); len(submatches) > 0 {
   601  			allKeys = append(allKeys, submatches[0][1])
   602  		}
   603  	}
   604  
   605  	if len(numericKeys) == len(allKeys) {
   606  		// This is a list
   607  		var members []string
   608  		for _, key := range numericKeys {
   609  			members = append(members, attributes[key])
   610  		}
   611  		sort.Strings(members)
   612  		return hil.InterfaceToVariable(members)
   613  	} else {
   614  		// This is a map
   615  		members := make(map[string]interface{})
   616  		for _, key := range allKeys {
   617  			members[key] = attributes[resourceID+"."+key]
   618  		}
   619  		return hil.InterfaceToVariable(members)
   620  	}
   621  }
   622  
   623  func (i *Interpolater) resourceVariableInfo(
   624  	scope *InterpolationScope,
   625  	v *config.ResourceVariable) (*ModuleState, *config.Resource, error) {
   626  	// Get the module tree that contains our current path. This is
   627  	// either the current module (path is empty) or a child.
   628  	modTree := i.Module
   629  	if len(scope.Path) > 1 {
   630  		modTree = i.Module.Child(scope.Path[1:])
   631  	}
   632  
   633  	// Get the resource from the configuration so we can verify
   634  	// that the resource is in the configuration and so we can access
   635  	// the configuration if we need to.
   636  	var cr *config.Resource
   637  	for _, r := range modTree.Config().Resources {
   638  		if r.Id() == v.ResourceId() {
   639  			cr = r
   640  			break
   641  		}
   642  	}
   643  	if cr == nil {
   644  		return nil, nil, fmt.Errorf(
   645  			"Resource '%s' not found for variable '%s'",
   646  			v.ResourceId(),
   647  			v.FullKey())
   648  	}
   649  
   650  	// Get the relevant module
   651  	module := i.State.ModuleByPath(scope.Path)
   652  	return module, cr, nil
   653  }