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