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