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