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