github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/config/interpolate.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  // We really need to replace this with a real parser.
    11  var funcRegexp *regexp.Regexp = regexp.MustCompile(
    12  	`(?i)([a-z0-9_]+)\(\s*(?:([.a-z0-9_]+)\s*,\s*)*([.a-z0-9_]+)\s*\)`)
    13  
    14  // Interpolation is something that can be contained in a "${}" in a
    15  // configuration value.
    16  //
    17  // Interpolations might be simple variable references, or it might be
    18  // function calls, or even nested function calls.
    19  type Interpolation interface {
    20  	Interpolate(map[string]string) (string, error)
    21  	Variables() map[string]InterpolatedVariable
    22  }
    23  
    24  // InterpolationFunc is the function signature for implementing
    25  // callable functions in Terraform configurations.
    26  type InterpolationFunc func(map[string]string, ...string) (string, error)
    27  
    28  // An InterpolatedVariable is a variable reference within an interpolation.
    29  //
    30  // Implementations of this interface represents various sources where
    31  // variables can come from: user variables, resources, etc.
    32  type InterpolatedVariable interface {
    33  	FullKey() string
    34  }
    35  
    36  // FunctionInterpolation is an Interpolation that executes a function
    37  // with some variable number of arguments to generate a value.
    38  type FunctionInterpolation struct {
    39  	Func InterpolationFunc
    40  	Args []Interpolation
    41  }
    42  
    43  // LiteralInterpolation implements Interpolation for literals. Ex:
    44  // ${"foo"} will equal "foo".
    45  type LiteralInterpolation struct {
    46  	Literal string
    47  }
    48  
    49  // VariableInterpolation implements Interpolation for simple variable
    50  // interpolation. Ex: "${var.foo}" or "${aws_instance.foo.bar}"
    51  type VariableInterpolation struct {
    52  	Variable InterpolatedVariable
    53  }
    54  
    55  // CountVariable is a variable for referencing information about
    56  // the count.
    57  type CountVariable struct {
    58  	Type CountValueType
    59  	key  string
    60  }
    61  
    62  // CountValueType is the type of the count variable that is referenced.
    63  type CountValueType byte
    64  
    65  const (
    66  	CountValueInvalid CountValueType = iota
    67  	CountValueIndex
    68  )
    69  
    70  // A ModuleVariable is a variable that is referencing the output
    71  // of a module, such as "${module.foo.bar}"
    72  type ModuleVariable struct {
    73  	Name  string
    74  	Field string
    75  	key   string
    76  }
    77  
    78  // A PathVariable is a variable that references path information about the
    79  // module.
    80  type PathVariable struct {
    81  	Type PathValueType
    82  	key  string
    83  }
    84  
    85  type PathValueType byte
    86  
    87  const (
    88  	PathValueInvalid PathValueType = iota
    89  	PathValueCwd
    90  	PathValueModule
    91  	PathValueRoot
    92  )
    93  
    94  // A ResourceVariable is a variable that is referencing the field
    95  // of a resource, such as "${aws_instance.foo.ami}"
    96  type ResourceVariable struct {
    97  	Type  string // Resource type, i.e. "aws_instance"
    98  	Name  string // Resource name
    99  	Field string // Resource field
   100  
   101  	Multi bool // True if multi-variable: aws_instance.foo.*.id
   102  	Index int  // Index for multi-variable: aws_instance.foo.1.id == 1
   103  
   104  	key string
   105  }
   106  
   107  // A UserVariable is a variable that is referencing a user variable
   108  // that is inputted from outside the configuration. This looks like
   109  // "${var.foo}"
   110  type UserVariable struct {
   111  	Name string
   112  	Elem string
   113  
   114  	key string
   115  }
   116  
   117  func NewInterpolatedVariable(v string) (InterpolatedVariable, error) {
   118  	if strings.HasPrefix(v, "count.") {
   119  		return NewCountVariable(v)
   120  	} else if strings.HasPrefix(v, "path.") {
   121  		return NewPathVariable(v)
   122  	} else if strings.HasPrefix(v, "var.") {
   123  		return NewUserVariable(v)
   124  	} else if strings.HasPrefix(v, "module.") {
   125  		return NewModuleVariable(v)
   126  	} else {
   127  		return NewResourceVariable(v)
   128  	}
   129  }
   130  
   131  func (i *FunctionInterpolation) Interpolate(
   132  	vs map[string]string) (string, error) {
   133  	args := make([]string, len(i.Args))
   134  	for idx, a := range i.Args {
   135  		v, err := a.Interpolate(vs)
   136  		if err != nil {
   137  			return "", err
   138  		}
   139  
   140  		args[idx] = v
   141  	}
   142  
   143  	return i.Func(vs, args...)
   144  }
   145  
   146  func (i *FunctionInterpolation) GoString() string {
   147  	return fmt.Sprintf("*%#v", *i)
   148  }
   149  
   150  func (i *FunctionInterpolation) Variables() map[string]InterpolatedVariable {
   151  	result := make(map[string]InterpolatedVariable)
   152  	for _, a := range i.Args {
   153  		for k, v := range a.Variables() {
   154  			result[k] = v
   155  		}
   156  	}
   157  
   158  	return result
   159  }
   160  
   161  func (i *LiteralInterpolation) Interpolate(
   162  	map[string]string) (string, error) {
   163  	return i.Literal, nil
   164  }
   165  
   166  func (i *LiteralInterpolation) Variables() map[string]InterpolatedVariable {
   167  	return nil
   168  }
   169  
   170  func (i *VariableInterpolation) Interpolate(
   171  	vs map[string]string) (string, error) {
   172  	v, ok := vs[i.Variable.FullKey()]
   173  	if !ok {
   174  		return "", fmt.Errorf(
   175  			"%s: value for variable not found",
   176  			i.Variable.FullKey())
   177  	}
   178  
   179  	return v, nil
   180  }
   181  
   182  func (i *VariableInterpolation) GoString() string {
   183  	return fmt.Sprintf("*%#v", *i)
   184  }
   185  
   186  func (i *VariableInterpolation) Variables() map[string]InterpolatedVariable {
   187  	return map[string]InterpolatedVariable{i.Variable.FullKey(): i.Variable}
   188  }
   189  
   190  func NewCountVariable(key string) (*CountVariable, error) {
   191  	var fieldType CountValueType
   192  	parts := strings.SplitN(key, ".", 2)
   193  	switch parts[1] {
   194  	case "index":
   195  		fieldType = CountValueIndex
   196  	}
   197  
   198  	return &CountVariable{
   199  		Type: fieldType,
   200  		key:  key,
   201  	}, nil
   202  }
   203  
   204  func (c *CountVariable) FullKey() string {
   205  	return c.key
   206  }
   207  
   208  func NewModuleVariable(key string) (*ModuleVariable, error) {
   209  	parts := strings.SplitN(key, ".", 3)
   210  	if len(parts) < 3 {
   211  		return nil, fmt.Errorf(
   212  			"%s: module variables must be three parts: module.name.attr",
   213  			key)
   214  	}
   215  
   216  	return &ModuleVariable{
   217  		Name:  parts[1],
   218  		Field: parts[2],
   219  		key:   key,
   220  	}, nil
   221  }
   222  
   223  func (v *ModuleVariable) FullKey() string {
   224  	return v.key
   225  }
   226  
   227  func NewPathVariable(key string) (*PathVariable, error) {
   228  	var fieldType PathValueType
   229  	parts := strings.SplitN(key, ".", 2)
   230  	switch parts[1] {
   231  	case "cwd":
   232  		fieldType = PathValueCwd
   233  	case "module":
   234  		fieldType = PathValueModule
   235  	case "root":
   236  		fieldType = PathValueRoot
   237  	}
   238  
   239  	return &PathVariable{
   240  		Type: fieldType,
   241  		key:  key,
   242  	}, nil
   243  }
   244  
   245  func (v *PathVariable) FullKey() string {
   246  	return v.key
   247  }
   248  
   249  func NewResourceVariable(key string) (*ResourceVariable, error) {
   250  	parts := strings.SplitN(key, ".", 3)
   251  	if len(parts) < 3 {
   252  		return nil, fmt.Errorf(
   253  			"%s: resource variables must be three parts: type.name.attr",
   254  			key)
   255  	}
   256  
   257  	field := parts[2]
   258  	multi := false
   259  	var index int
   260  
   261  	if idx := strings.Index(field, "."); idx != -1 {
   262  		indexStr := field[:idx]
   263  		multi = indexStr == "*"
   264  		index = -1
   265  
   266  		if !multi {
   267  			indexInt, err := strconv.ParseInt(indexStr, 0, 0)
   268  			if err == nil {
   269  				multi = true
   270  				index = int(indexInt)
   271  			}
   272  		}
   273  
   274  		if multi {
   275  			field = field[idx+1:]
   276  		}
   277  	}
   278  
   279  	return &ResourceVariable{
   280  		Type:  parts[0],
   281  		Name:  parts[1],
   282  		Field: field,
   283  		Multi: multi,
   284  		Index: index,
   285  		key:   key,
   286  	}, nil
   287  }
   288  
   289  func (v *ResourceVariable) ResourceId() string {
   290  	return fmt.Sprintf("%s.%s", v.Type, v.Name)
   291  }
   292  
   293  func (v *ResourceVariable) FullKey() string {
   294  	return v.key
   295  }
   296  
   297  func NewUserVariable(key string) (*UserVariable, error) {
   298  	name := key[len("var."):]
   299  	elem := ""
   300  	if idx := strings.Index(name, "."); idx > -1 {
   301  		elem = name[idx+1:]
   302  		name = name[:idx]
   303  	}
   304  
   305  	return &UserVariable{
   306  		key: key,
   307  
   308  		Name: name,
   309  		Elem: elem,
   310  	}, nil
   311  }
   312  
   313  func (v *UserVariable) FullKey() string {
   314  	return v.key
   315  }
   316  
   317  func (v *UserVariable) GoString() string {
   318  	return fmt.Sprintf("*%#v", *v)
   319  }