github.com/hobbeswalsh/terraform@v0.3.7-0.20150619183303-ad17cf55a0fa/terraform/interpolate.go (about)

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