github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/config/interpolate_funcs.go (about)

     1  package config
     2  
     3  import (
     4  	"crypto/md5"
     5  	"crypto/sha1"
     6  	"crypto/sha256"
     7  	"encoding/base64"
     8  	"encoding/hex"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"math"
    13  	"net"
    14  	"regexp"
    15  	"sort"
    16  	"strconv"
    17  	"strings"
    18  
    19  	"github.com/apparentlymart/go-cidr/cidr"
    20  	"github.com/hashicorp/go-uuid"
    21  	"github.com/hashicorp/hil"
    22  	"github.com/hashicorp/hil/ast"
    23  	"github.com/mitchellh/go-homedir"
    24  )
    25  
    26  // stringSliceToVariableValue converts a string slice into the value
    27  // required to be returned from interpolation functions which return
    28  // TypeList.
    29  func stringSliceToVariableValue(values []string) []ast.Variable {
    30  	output := make([]ast.Variable, len(values))
    31  	for index, value := range values {
    32  		output[index] = ast.Variable{
    33  			Type:  ast.TypeString,
    34  			Value: value,
    35  		}
    36  	}
    37  	return output
    38  }
    39  
    40  func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) {
    41  	output := make([]string, len(values))
    42  	for index, value := range values {
    43  		if value.Type != ast.TypeString {
    44  			return []string{}, fmt.Errorf("list has non-string element (%T)", value.Type.String())
    45  		}
    46  		output[index] = value.Value.(string)
    47  	}
    48  	return output, nil
    49  }
    50  
    51  // Funcs is the mapping of built-in functions for configuration.
    52  func Funcs() map[string]ast.Function {
    53  	return map[string]ast.Function{
    54  		"base64decode": interpolationFuncBase64Decode(),
    55  		"base64encode": interpolationFuncBase64Encode(),
    56  		"base64sha256": interpolationFuncBase64Sha256(),
    57  		"ceil":         interpolationFuncCeil(),
    58  		"cidrhost":     interpolationFuncCidrHost(),
    59  		"cidrnetmask":  interpolationFuncCidrNetmask(),
    60  		"cidrsubnet":   interpolationFuncCidrSubnet(),
    61  		"coalesce":     interpolationFuncCoalesce(),
    62  		"compact":      interpolationFuncCompact(),
    63  		"concat":       interpolationFuncConcat(),
    64  		"distinct":     interpolationFuncDistinct(),
    65  		"element":      interpolationFuncElement(),
    66  		"file":         interpolationFuncFile(),
    67  		"floor":        interpolationFuncFloor(),
    68  		"format":       interpolationFuncFormat(),
    69  		"formatlist":   interpolationFuncFormatList(),
    70  		"index":        interpolationFuncIndex(),
    71  		"join":         interpolationFuncJoin(),
    72  		"jsonencode":   interpolationFuncJSONEncode(),
    73  		"length":       interpolationFuncLength(),
    74  		"list":         interpolationFuncList(),
    75  		"lower":        interpolationFuncLower(),
    76  		"map":          interpolationFuncMap(),
    77  		"max":          interpolationFuncMax(),
    78  		"md5":          interpolationFuncMd5(),
    79  		"merge":        interpolationFuncMerge(),
    80  		"min":          interpolationFuncMin(),
    81  		"uuid":         interpolationFuncUUID(),
    82  		"replace":      interpolationFuncReplace(),
    83  		"sha1":         interpolationFuncSha1(),
    84  		"sha256":       interpolationFuncSha256(),
    85  		"signum":       interpolationFuncSignum(),
    86  		"sort":         interpolationFuncSort(),
    87  		"split":        interpolationFuncSplit(),
    88  		"title":        interpolationFuncTitle(),
    89  		"trimspace":    interpolationFuncTrimSpace(),
    90  		"upper":        interpolationFuncUpper(),
    91  		"zipmap":       interpolationFuncZipMap(),
    92  	}
    93  }
    94  
    95  // interpolationFuncList creates a list from the parameters passed
    96  // to it.
    97  func interpolationFuncList() ast.Function {
    98  	return ast.Function{
    99  		ArgTypes:     []ast.Type{},
   100  		ReturnType:   ast.TypeList,
   101  		Variadic:     true,
   102  		VariadicType: ast.TypeAny,
   103  		Callback: func(args []interface{}) (interface{}, error) {
   104  			var outputList []ast.Variable
   105  
   106  			for i, val := range args {
   107  				switch v := val.(type) {
   108  				case string:
   109  					outputList = append(outputList, ast.Variable{Type: ast.TypeString, Value: v})
   110  				case []ast.Variable:
   111  					outputList = append(outputList, ast.Variable{Type: ast.TypeList, Value: v})
   112  				case map[string]ast.Variable:
   113  					outputList = append(outputList, ast.Variable{Type: ast.TypeMap, Value: v})
   114  				default:
   115  					return nil, fmt.Errorf("unexpected type %T for argument %d in list", v, i)
   116  				}
   117  			}
   118  
   119  			// we don't support heterogeneous types, so make sure all types match the first
   120  			if len(outputList) > 0 {
   121  				firstType := outputList[0].Type
   122  				for i, v := range outputList[1:] {
   123  					if v.Type != firstType {
   124  						return nil, fmt.Errorf("unexpected type %s for argument %d in list", v.Type, i+1)
   125  					}
   126  				}
   127  			}
   128  
   129  			return outputList, nil
   130  		},
   131  	}
   132  }
   133  
   134  // interpolationFuncMap creates a map from the parameters passed
   135  // to it.
   136  func interpolationFuncMap() ast.Function {
   137  	return ast.Function{
   138  		ArgTypes:     []ast.Type{},
   139  		ReturnType:   ast.TypeMap,
   140  		Variadic:     true,
   141  		VariadicType: ast.TypeAny,
   142  		Callback: func(args []interface{}) (interface{}, error) {
   143  			outputMap := make(map[string]ast.Variable)
   144  
   145  			if len(args)%2 != 0 {
   146  				return nil, fmt.Errorf("requires an even number of arguments, got %d", len(args))
   147  			}
   148  
   149  			var firstType *ast.Type
   150  			for i := 0; i < len(args); i += 2 {
   151  				key, ok := args[i].(string)
   152  				if !ok {
   153  					return nil, fmt.Errorf("argument %d represents a key, so it must be a string", i+1)
   154  				}
   155  				val := args[i+1]
   156  				variable, err := hil.InterfaceToVariable(val)
   157  				if err != nil {
   158  					return nil, err
   159  				}
   160  				// Enforce map type homogeneity
   161  				if firstType == nil {
   162  					firstType = &variable.Type
   163  				} else if variable.Type != *firstType {
   164  					return nil, fmt.Errorf("all map values must have the same type, got %s then %s", firstType.Printable(), variable.Type.Printable())
   165  				}
   166  				// Check for duplicate keys
   167  				if _, ok := outputMap[key]; ok {
   168  					return nil, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key)
   169  				}
   170  				outputMap[key] = variable
   171  			}
   172  
   173  			return outputMap, nil
   174  		},
   175  	}
   176  }
   177  
   178  // interpolationFuncCompact strips a list of multi-variable values
   179  // (e.g. as returned by "split") of any empty strings.
   180  func interpolationFuncCompact() ast.Function {
   181  	return ast.Function{
   182  		ArgTypes:   []ast.Type{ast.TypeList},
   183  		ReturnType: ast.TypeList,
   184  		Variadic:   false,
   185  		Callback: func(args []interface{}) (interface{}, error) {
   186  			inputList := args[0].([]ast.Variable)
   187  
   188  			var outputList []string
   189  			for _, val := range inputList {
   190  				strVal, ok := val.Value.(string)
   191  				if !ok {
   192  					return nil, fmt.Errorf(
   193  						"compact() may only be used with flat lists, this list contains elements of %s",
   194  						val.Type.Printable())
   195  				}
   196  				if strVal == "" {
   197  					continue
   198  				}
   199  
   200  				outputList = append(outputList, strVal)
   201  			}
   202  			return stringSliceToVariableValue(outputList), nil
   203  		},
   204  	}
   205  }
   206  
   207  // interpolationFuncCidrHost implements the "cidrhost" function that
   208  // fills in the host part of a CIDR range address to create a single
   209  // host address
   210  func interpolationFuncCidrHost() ast.Function {
   211  	return ast.Function{
   212  		ArgTypes: []ast.Type{
   213  			ast.TypeString, // starting CIDR mask
   214  			ast.TypeInt,    // host number to insert
   215  		},
   216  		ReturnType: ast.TypeString,
   217  		Variadic:   false,
   218  		Callback: func(args []interface{}) (interface{}, error) {
   219  			hostNum := args[1].(int)
   220  			_, network, err := net.ParseCIDR(args[0].(string))
   221  			if err != nil {
   222  				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
   223  			}
   224  
   225  			ip, err := cidr.Host(network, hostNum)
   226  			if err != nil {
   227  				return nil, err
   228  			}
   229  
   230  			return ip.String(), nil
   231  		},
   232  	}
   233  }
   234  
   235  // interpolationFuncCidrNetmask implements the "cidrnetmask" function
   236  // that returns the subnet mask in IP address notation.
   237  func interpolationFuncCidrNetmask() ast.Function {
   238  	return ast.Function{
   239  		ArgTypes: []ast.Type{
   240  			ast.TypeString, // CIDR mask
   241  		},
   242  		ReturnType: ast.TypeString,
   243  		Variadic:   false,
   244  		Callback: func(args []interface{}) (interface{}, error) {
   245  			_, network, err := net.ParseCIDR(args[0].(string))
   246  			if err != nil {
   247  				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
   248  			}
   249  
   250  			return net.IP(network.Mask).String(), nil
   251  		},
   252  	}
   253  }
   254  
   255  // interpolationFuncCidrSubnet implements the "cidrsubnet" function that
   256  // adds an additional subnet of the given length onto an existing
   257  // IP block expressed in CIDR notation.
   258  func interpolationFuncCidrSubnet() ast.Function {
   259  	return ast.Function{
   260  		ArgTypes: []ast.Type{
   261  			ast.TypeString, // starting CIDR mask
   262  			ast.TypeInt,    // number of bits to extend the prefix
   263  			ast.TypeInt,    // network number to append to the prefix
   264  		},
   265  		ReturnType: ast.TypeString,
   266  		Variadic:   false,
   267  		Callback: func(args []interface{}) (interface{}, error) {
   268  			extraBits := args[1].(int)
   269  			subnetNum := args[2].(int)
   270  			_, network, err := net.ParseCIDR(args[0].(string))
   271  			if err != nil {
   272  				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
   273  			}
   274  
   275  			// For portability with 32-bit systems where the subnet number
   276  			// will be a 32-bit int, we only allow extension of 32 bits in
   277  			// one call even if we're running on a 64-bit machine.
   278  			// (Of course, this is significant only for IPv6.)
   279  			if extraBits > 32 {
   280  				return nil, fmt.Errorf("may not extend prefix by more than 32 bits")
   281  			}
   282  
   283  			newNetwork, err := cidr.Subnet(network, extraBits, subnetNum)
   284  			if err != nil {
   285  				return nil, err
   286  			}
   287  
   288  			return newNetwork.String(), nil
   289  		},
   290  	}
   291  }
   292  
   293  // interpolationFuncCoalesce implements the "coalesce" function that
   294  // returns the first non null / empty string from the provided input
   295  func interpolationFuncCoalesce() ast.Function {
   296  	return ast.Function{
   297  		ArgTypes:     []ast.Type{ast.TypeString},
   298  		ReturnType:   ast.TypeString,
   299  		Variadic:     true,
   300  		VariadicType: ast.TypeString,
   301  		Callback: func(args []interface{}) (interface{}, error) {
   302  			if len(args) < 2 {
   303  				return nil, fmt.Errorf("must provide at least two arguments")
   304  			}
   305  			for _, arg := range args {
   306  				argument := arg.(string)
   307  
   308  				if argument != "" {
   309  					return argument, nil
   310  				}
   311  			}
   312  			return "", nil
   313  		},
   314  	}
   315  }
   316  
   317  // interpolationFuncConcat implements the "concat" function that concatenates
   318  // multiple lists.
   319  func interpolationFuncConcat() ast.Function {
   320  	return ast.Function{
   321  		ArgTypes:     []ast.Type{ast.TypeList},
   322  		ReturnType:   ast.TypeList,
   323  		Variadic:     true,
   324  		VariadicType: ast.TypeList,
   325  		Callback: func(args []interface{}) (interface{}, error) {
   326  			var outputList []ast.Variable
   327  
   328  			for _, arg := range args {
   329  				for _, v := range arg.([]ast.Variable) {
   330  					switch v.Type {
   331  					case ast.TypeString:
   332  						outputList = append(outputList, v)
   333  					case ast.TypeList:
   334  						outputList = append(outputList, v)
   335  					case ast.TypeMap:
   336  						outputList = append(outputList, v)
   337  					default:
   338  						return nil, fmt.Errorf("concat() does not support lists of %s", v.Type.Printable())
   339  					}
   340  				}
   341  			}
   342  
   343  			// we don't support heterogeneous types, so make sure all types match the first
   344  			if len(outputList) > 0 {
   345  				firstType := outputList[0].Type
   346  				for _, v := range outputList[1:] {
   347  					if v.Type != firstType {
   348  						return nil, fmt.Errorf("unexpected %s in list of %s", v.Type.Printable(), firstType.Printable())
   349  					}
   350  				}
   351  			}
   352  
   353  			return outputList, nil
   354  		},
   355  	}
   356  }
   357  
   358  // interpolationFuncFile implements the "file" function that allows
   359  // loading contents from a file.
   360  func interpolationFuncFile() ast.Function {
   361  	return ast.Function{
   362  		ArgTypes:   []ast.Type{ast.TypeString},
   363  		ReturnType: ast.TypeString,
   364  		Callback: func(args []interface{}) (interface{}, error) {
   365  			path, err := homedir.Expand(args[0].(string))
   366  			if err != nil {
   367  				return "", err
   368  			}
   369  			data, err := ioutil.ReadFile(path)
   370  			if err != nil {
   371  				return "", err
   372  			}
   373  
   374  			return string(data), nil
   375  		},
   376  	}
   377  }
   378  
   379  // interpolationFuncFormat implements the "format" function that does
   380  // string formatting.
   381  func interpolationFuncFormat() ast.Function {
   382  	return ast.Function{
   383  		ArgTypes:     []ast.Type{ast.TypeString},
   384  		Variadic:     true,
   385  		VariadicType: ast.TypeAny,
   386  		ReturnType:   ast.TypeString,
   387  		Callback: func(args []interface{}) (interface{}, error) {
   388  			format := args[0].(string)
   389  			return fmt.Sprintf(format, args[1:]...), nil
   390  		},
   391  	}
   392  }
   393  
   394  // interpolationFuncMax returns the maximum of the numeric arguments
   395  func interpolationFuncMax() ast.Function {
   396  	return ast.Function{
   397  		ArgTypes:     []ast.Type{ast.TypeFloat},
   398  		ReturnType:   ast.TypeFloat,
   399  		Variadic:     true,
   400  		VariadicType: ast.TypeFloat,
   401  		Callback: func(args []interface{}) (interface{}, error) {
   402  			max := args[0].(float64)
   403  
   404  			for i := 1; i < len(args); i++ {
   405  				max = math.Max(max, args[i].(float64))
   406  			}
   407  
   408  			return max, nil
   409  		},
   410  	}
   411  }
   412  
   413  // interpolationFuncMin returns the minimum of the numeric arguments
   414  func interpolationFuncMin() ast.Function {
   415  	return ast.Function{
   416  		ArgTypes:     []ast.Type{ast.TypeFloat},
   417  		ReturnType:   ast.TypeFloat,
   418  		Variadic:     true,
   419  		VariadicType: ast.TypeFloat,
   420  		Callback: func(args []interface{}) (interface{}, error) {
   421  			min := args[0].(float64)
   422  
   423  			for i := 1; i < len(args); i++ {
   424  				min = math.Min(min, args[i].(float64))
   425  			}
   426  
   427  			return min, nil
   428  		},
   429  	}
   430  }
   431  
   432  // interpolationFuncCeil returns the the least integer value greater than or equal to the argument
   433  func interpolationFuncCeil() ast.Function {
   434  	return ast.Function{
   435  		ArgTypes:   []ast.Type{ast.TypeFloat},
   436  		ReturnType: ast.TypeInt,
   437  		Callback: func(args []interface{}) (interface{}, error) {
   438  			return int(math.Ceil(args[0].(float64))), nil
   439  		},
   440  	}
   441  }
   442  
   443  // interpolationFuncFloorreturns returns the greatest integer value less than or equal to the argument
   444  func interpolationFuncFloor() ast.Function {
   445  	return ast.Function{
   446  		ArgTypes:   []ast.Type{ast.TypeFloat},
   447  		ReturnType: ast.TypeInt,
   448  		Callback: func(args []interface{}) (interface{}, error) {
   449  			return int(math.Floor(args[0].(float64))), nil
   450  		},
   451  	}
   452  }
   453  
   454  func interpolationFuncZipMap() ast.Function {
   455  	return ast.Function{
   456  		ArgTypes: []ast.Type{
   457  			ast.TypeList, // Keys
   458  			ast.TypeList, // Values
   459  		},
   460  		ReturnType: ast.TypeMap,
   461  		Callback: func(args []interface{}) (interface{}, error) {
   462  			keys := args[0].([]ast.Variable)
   463  			values := args[1].([]ast.Variable)
   464  
   465  			if len(keys) != len(values) {
   466  				return nil, fmt.Errorf("count of keys (%d) does not match count of values (%d)",
   467  					len(keys), len(values))
   468  			}
   469  
   470  			for i, val := range keys {
   471  				if val.Type != ast.TypeString {
   472  					return nil, fmt.Errorf("keys must be strings. value at position %d is %s",
   473  						i, val.Type.Printable())
   474  				}
   475  			}
   476  
   477  			result := map[string]ast.Variable{}
   478  			for i := 0; i < len(keys); i++ {
   479  				result[keys[i].Value.(string)] = values[i]
   480  			}
   481  
   482  			return result, nil
   483  		},
   484  	}
   485  }
   486  
   487  // interpolationFuncFormatList implements the "formatlist" function that does
   488  // string formatting on lists.
   489  func interpolationFuncFormatList() ast.Function {
   490  	return ast.Function{
   491  		ArgTypes:     []ast.Type{ast.TypeAny},
   492  		Variadic:     true,
   493  		VariadicType: ast.TypeAny,
   494  		ReturnType:   ast.TypeList,
   495  		Callback: func(args []interface{}) (interface{}, error) {
   496  			// Make a copy of the variadic part of args
   497  			// to avoid modifying the original.
   498  			varargs := make([]interface{}, len(args)-1)
   499  			copy(varargs, args[1:])
   500  
   501  			// Verify we have some arguments
   502  			if len(varargs) == 0 {
   503  				return nil, fmt.Errorf("no arguments to formatlist")
   504  			}
   505  
   506  			// Convert arguments that are lists into slices.
   507  			// Confirm along the way that all lists have the same length (n).
   508  			var n int
   509  			listSeen := false
   510  			for i := 1; i < len(args); i++ {
   511  				s, ok := args[i].([]ast.Variable)
   512  				if !ok {
   513  					continue
   514  				}
   515  
   516  				// Mark that we've seen at least one list
   517  				listSeen = true
   518  
   519  				// Convert the ast.Variable to a slice of strings
   520  				parts, err := listVariableValueToStringSlice(s)
   521  				if err != nil {
   522  					return nil, err
   523  				}
   524  
   525  				// otherwise the list is sent down to be indexed
   526  				varargs[i-1] = parts
   527  
   528  				// Check length
   529  				if n == 0 {
   530  					// first list we've seen
   531  					n = len(parts)
   532  					continue
   533  				}
   534  				if n != len(parts) {
   535  					return nil, fmt.Errorf("format: mismatched list lengths: %d != %d", n, len(parts))
   536  				}
   537  			}
   538  
   539  			// If we didn't see a list this is an error because we
   540  			// can't determine the return value length.
   541  			if !listSeen {
   542  				return nil, fmt.Errorf(
   543  					"formatlist requires at least one list argument")
   544  			}
   545  
   546  			// Do the formatting.
   547  			format := args[0].(string)
   548  
   549  			// Generate a list of formatted strings.
   550  			list := make([]string, n)
   551  			fmtargs := make([]interface{}, len(varargs))
   552  			for i := 0; i < n; i++ {
   553  				for j, arg := range varargs {
   554  					switch arg := arg.(type) {
   555  					default:
   556  						fmtargs[j] = arg
   557  					case []string:
   558  						fmtargs[j] = arg[i]
   559  					}
   560  				}
   561  				list[i] = fmt.Sprintf(format, fmtargs...)
   562  			}
   563  			return stringSliceToVariableValue(list), nil
   564  		},
   565  	}
   566  }
   567  
   568  // interpolationFuncIndex implements the "index" function that allows one to
   569  // find the index of a specific element in a list
   570  func interpolationFuncIndex() ast.Function {
   571  	return ast.Function{
   572  		ArgTypes:   []ast.Type{ast.TypeList, ast.TypeString},
   573  		ReturnType: ast.TypeInt,
   574  		Callback: func(args []interface{}) (interface{}, error) {
   575  			haystack := args[0].([]ast.Variable)
   576  			needle := args[1].(string)
   577  			for index, element := range haystack {
   578  				if needle == element.Value {
   579  					return index, nil
   580  				}
   581  			}
   582  			return nil, fmt.Errorf("Could not find '%s' in '%s'", needle, haystack)
   583  		},
   584  	}
   585  }
   586  
   587  // interpolationFuncDistinct implements the "distinct" function that
   588  // removes duplicate elements from a list.
   589  func interpolationFuncDistinct() ast.Function {
   590  	return ast.Function{
   591  		ArgTypes:     []ast.Type{ast.TypeList},
   592  		ReturnType:   ast.TypeList,
   593  		Variadic:     true,
   594  		VariadicType: ast.TypeList,
   595  		Callback: func(args []interface{}) (interface{}, error) {
   596  			var list []string
   597  
   598  			if len(args) != 1 {
   599  				return nil, fmt.Errorf("accepts only one argument.")
   600  			}
   601  
   602  			if argument, ok := args[0].([]ast.Variable); ok {
   603  				for _, element := range argument {
   604  					if element.Type != ast.TypeString {
   605  						return nil, fmt.Errorf(
   606  							"only works for flat lists, this list contains elements of %s",
   607  							element.Type.Printable())
   608  					}
   609  					list = appendIfMissing(list, element.Value.(string))
   610  				}
   611  			}
   612  
   613  			return stringSliceToVariableValue(list), nil
   614  		},
   615  	}
   616  }
   617  
   618  // helper function to add an element to a list, if it does not already exsit
   619  func appendIfMissing(slice []string, element string) []string {
   620  	for _, ele := range slice {
   621  		if ele == element {
   622  			return slice
   623  		}
   624  	}
   625  	return append(slice, element)
   626  }
   627  
   628  // interpolationFuncJoin implements the "join" function that allows
   629  // multi-variable values to be joined by some character.
   630  func interpolationFuncJoin() ast.Function {
   631  	return ast.Function{
   632  		ArgTypes:     []ast.Type{ast.TypeString},
   633  		Variadic:     true,
   634  		VariadicType: ast.TypeList,
   635  		ReturnType:   ast.TypeString,
   636  		Callback: func(args []interface{}) (interface{}, error) {
   637  			var list []string
   638  
   639  			if len(args) < 2 {
   640  				return nil, fmt.Errorf("not enough arguments to join()")
   641  			}
   642  
   643  			for _, arg := range args[1:] {
   644  				for _, part := range arg.([]ast.Variable) {
   645  					if part.Type != ast.TypeString {
   646  						return nil, fmt.Errorf(
   647  							"only works on flat lists, this list contains elements of %s",
   648  							part.Type.Printable())
   649  					}
   650  					list = append(list, part.Value.(string))
   651  				}
   652  			}
   653  
   654  			return strings.Join(list, args[0].(string)), nil
   655  		},
   656  	}
   657  }
   658  
   659  // interpolationFuncJSONEncode implements the "jsonencode" function that encodes
   660  // a string, list, or map as its JSON representation. For now, values in the
   661  // list or map may only be strings.
   662  func interpolationFuncJSONEncode() ast.Function {
   663  	return ast.Function{
   664  		ArgTypes:   []ast.Type{ast.TypeAny},
   665  		ReturnType: ast.TypeString,
   666  		Callback: func(args []interface{}) (interface{}, error) {
   667  			var toEncode interface{}
   668  
   669  			switch typedArg := args[0].(type) {
   670  			case string:
   671  				toEncode = typedArg
   672  
   673  			case []ast.Variable:
   674  				// We preallocate the list here. Note that it's important that in
   675  				// the length 0 case, we have an empty list rather than nil, as
   676  				// they encode differently.
   677  				// XXX It would be nice to support arbitrarily nested data here. Is
   678  				// there an inverse of hil.InterfaceToVariable?
   679  				strings := make([]string, len(typedArg))
   680  
   681  				for i, v := range typedArg {
   682  					if v.Type != ast.TypeString {
   683  						return "", fmt.Errorf("list elements must be strings")
   684  					}
   685  					strings[i] = v.Value.(string)
   686  				}
   687  				toEncode = strings
   688  
   689  			case map[string]ast.Variable:
   690  				// XXX It would be nice to support arbitrarily nested data here. Is
   691  				// there an inverse of hil.InterfaceToVariable?
   692  				stringMap := make(map[string]string)
   693  				for k, v := range typedArg {
   694  					if v.Type != ast.TypeString {
   695  						return "", fmt.Errorf("map values must be strings")
   696  					}
   697  					stringMap[k] = v.Value.(string)
   698  				}
   699  				toEncode = stringMap
   700  
   701  			default:
   702  				return "", fmt.Errorf("unknown type for JSON encoding: %T", args[0])
   703  			}
   704  
   705  			jEnc, err := json.Marshal(toEncode)
   706  			if err != nil {
   707  				return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
   708  			}
   709  			return string(jEnc), nil
   710  		},
   711  	}
   712  }
   713  
   714  // interpolationFuncReplace implements the "replace" function that does
   715  // string replacement.
   716  func interpolationFuncReplace() ast.Function {
   717  	return ast.Function{
   718  		ArgTypes:   []ast.Type{ast.TypeString, ast.TypeString, ast.TypeString},
   719  		ReturnType: ast.TypeString,
   720  		Callback: func(args []interface{}) (interface{}, error) {
   721  			s := args[0].(string)
   722  			search := args[1].(string)
   723  			replace := args[2].(string)
   724  
   725  			// We search/replace using a regexp if the string is surrounded
   726  			// in forward slashes.
   727  			if len(search) > 1 && search[0] == '/' && search[len(search)-1] == '/' {
   728  				re, err := regexp.Compile(search[1 : len(search)-1])
   729  				if err != nil {
   730  					return nil, err
   731  				}
   732  
   733  				return re.ReplaceAllString(s, replace), nil
   734  			}
   735  
   736  			return strings.Replace(s, search, replace, -1), nil
   737  		},
   738  	}
   739  }
   740  
   741  func interpolationFuncLength() ast.Function {
   742  	return ast.Function{
   743  		ArgTypes:   []ast.Type{ast.TypeAny},
   744  		ReturnType: ast.TypeInt,
   745  		Variadic:   false,
   746  		Callback: func(args []interface{}) (interface{}, error) {
   747  			subject := args[0]
   748  
   749  			switch typedSubject := subject.(type) {
   750  			case string:
   751  				return len(typedSubject), nil
   752  			case []ast.Variable:
   753  				return len(typedSubject), nil
   754  			case map[string]ast.Variable:
   755  				return len(typedSubject), nil
   756  			}
   757  
   758  			return 0, fmt.Errorf("arguments to length() must be a string, list, or map")
   759  		},
   760  	}
   761  }
   762  
   763  func interpolationFuncSignum() ast.Function {
   764  	return ast.Function{
   765  		ArgTypes:   []ast.Type{ast.TypeInt},
   766  		ReturnType: ast.TypeInt,
   767  		Variadic:   false,
   768  		Callback: func(args []interface{}) (interface{}, error) {
   769  			num := args[0].(int)
   770  			switch {
   771  			case num < 0:
   772  				return -1, nil
   773  			case num > 0:
   774  				return +1, nil
   775  			default:
   776  				return 0, nil
   777  			}
   778  		},
   779  	}
   780  }
   781  
   782  // interpolationFuncSort sorts a list of a strings lexographically
   783  func interpolationFuncSort() ast.Function {
   784  	return ast.Function{
   785  		ArgTypes:   []ast.Type{ast.TypeList},
   786  		ReturnType: ast.TypeList,
   787  		Variadic:   false,
   788  		Callback: func(args []interface{}) (interface{}, error) {
   789  			inputList := args[0].([]ast.Variable)
   790  
   791  			// Ensure that all the list members are strings and
   792  			// create a string slice from them
   793  			members := make([]string, len(inputList))
   794  			for i, val := range inputList {
   795  				if val.Type != ast.TypeString {
   796  					return nil, fmt.Errorf(
   797  						"sort() may only be used with lists of strings - %s at index %d",
   798  						val.Type.String(), i)
   799  				}
   800  
   801  				members[i] = val.Value.(string)
   802  			}
   803  
   804  			sort.Strings(members)
   805  			return stringSliceToVariableValue(members), nil
   806  		},
   807  	}
   808  }
   809  
   810  // interpolationFuncSplit implements the "split" function that allows
   811  // strings to split into multi-variable values
   812  func interpolationFuncSplit() ast.Function {
   813  	return ast.Function{
   814  		ArgTypes:   []ast.Type{ast.TypeString, ast.TypeString},
   815  		ReturnType: ast.TypeList,
   816  		Callback: func(args []interface{}) (interface{}, error) {
   817  			sep := args[0].(string)
   818  			s := args[1].(string)
   819  			elements := strings.Split(s, sep)
   820  			return stringSliceToVariableValue(elements), nil
   821  		},
   822  	}
   823  }
   824  
   825  // interpolationFuncLookup implements the "lookup" function that allows
   826  // dynamic lookups of map types within a Terraform configuration.
   827  func interpolationFuncLookup(vs map[string]ast.Variable) ast.Function {
   828  	return ast.Function{
   829  		ArgTypes:     []ast.Type{ast.TypeMap, ast.TypeString},
   830  		ReturnType:   ast.TypeString,
   831  		Variadic:     true,
   832  		VariadicType: ast.TypeString,
   833  		Callback: func(args []interface{}) (interface{}, error) {
   834  			defaultValue := ""
   835  			defaultValueSet := false
   836  			if len(args) > 2 {
   837  				defaultValue = args[2].(string)
   838  				defaultValueSet = true
   839  			}
   840  			if len(args) > 3 {
   841  				return "", fmt.Errorf("lookup() takes no more than three arguments")
   842  			}
   843  			index := args[1].(string)
   844  			mapVar := args[0].(map[string]ast.Variable)
   845  
   846  			v, ok := mapVar[index]
   847  			if !ok {
   848  				if defaultValueSet {
   849  					return defaultValue, nil
   850  				} else {
   851  					return "", fmt.Errorf(
   852  						"lookup failed to find '%s'",
   853  						args[1].(string))
   854  				}
   855  			}
   856  			if v.Type != ast.TypeString {
   857  				return nil, fmt.Errorf(
   858  					"lookup() may only be used with flat maps, this map contains elements of %s",
   859  					v.Type.Printable())
   860  			}
   861  
   862  			return v.Value.(string), nil
   863  		},
   864  	}
   865  }
   866  
   867  // interpolationFuncElement implements the "element" function that allows
   868  // a specific index to be looked up in a multi-variable value. Note that this will
   869  // wrap if the index is larger than the number of elements in the multi-variable value.
   870  func interpolationFuncElement() ast.Function {
   871  	return ast.Function{
   872  		ArgTypes:   []ast.Type{ast.TypeList, ast.TypeString},
   873  		ReturnType: ast.TypeString,
   874  		Callback: func(args []interface{}) (interface{}, error) {
   875  			list := args[0].([]ast.Variable)
   876  			if len(list) == 0 {
   877  				return nil, fmt.Errorf("element() may not be used with an empty list")
   878  			}
   879  
   880  			index, err := strconv.Atoi(args[1].(string))
   881  			if err != nil || index < 0 {
   882  				return "", fmt.Errorf(
   883  					"invalid number for index, got %s", args[1])
   884  			}
   885  
   886  			resolvedIndex := index % len(list)
   887  
   888  			v := list[resolvedIndex]
   889  			if v.Type != ast.TypeString {
   890  				return nil, fmt.Errorf(
   891  					"element() may only be used with flat lists, this list contains elements of %s",
   892  					v.Type.Printable())
   893  			}
   894  			return v.Value, nil
   895  		},
   896  	}
   897  }
   898  
   899  // interpolationFuncKeys implements the "keys" function that yields a list of
   900  // keys of map types within a Terraform configuration.
   901  func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function {
   902  	return ast.Function{
   903  		ArgTypes:   []ast.Type{ast.TypeMap},
   904  		ReturnType: ast.TypeList,
   905  		Callback: func(args []interface{}) (interface{}, error) {
   906  			mapVar := args[0].(map[string]ast.Variable)
   907  			keys := make([]string, 0)
   908  
   909  			for k, _ := range mapVar {
   910  				keys = append(keys, k)
   911  			}
   912  
   913  			sort.Strings(keys)
   914  
   915  			// Keys are guaranteed to be strings
   916  			return stringSliceToVariableValue(keys), nil
   917  		},
   918  	}
   919  }
   920  
   921  // interpolationFuncValues implements the "values" function that yields a list of
   922  // keys of map types within a Terraform configuration.
   923  func interpolationFuncValues(vs map[string]ast.Variable) ast.Function {
   924  	return ast.Function{
   925  		ArgTypes:   []ast.Type{ast.TypeMap},
   926  		ReturnType: ast.TypeList,
   927  		Callback: func(args []interface{}) (interface{}, error) {
   928  			mapVar := args[0].(map[string]ast.Variable)
   929  			keys := make([]string, 0)
   930  
   931  			for k, _ := range mapVar {
   932  				keys = append(keys, k)
   933  			}
   934  
   935  			sort.Strings(keys)
   936  
   937  			values := make([]string, len(keys))
   938  			for index, key := range keys {
   939  				if value, ok := mapVar[key].Value.(string); ok {
   940  					values[index] = value
   941  				} else {
   942  					return "", fmt.Errorf("values(): %q has element with bad type %s",
   943  						key, mapVar[key].Type)
   944  				}
   945  			}
   946  
   947  			variable, err := hil.InterfaceToVariable(values)
   948  			if err != nil {
   949  				return nil, err
   950  			}
   951  
   952  			return variable.Value, nil
   953  		},
   954  	}
   955  }
   956  
   957  // interpolationFuncBase64Encode implements the "base64encode" function that
   958  // allows Base64 encoding.
   959  func interpolationFuncBase64Encode() ast.Function {
   960  	return ast.Function{
   961  		ArgTypes:   []ast.Type{ast.TypeString},
   962  		ReturnType: ast.TypeString,
   963  		Callback: func(args []interface{}) (interface{}, error) {
   964  			s := args[0].(string)
   965  			return base64.StdEncoding.EncodeToString([]byte(s)), nil
   966  		},
   967  	}
   968  }
   969  
   970  // interpolationFuncBase64Decode implements the "base64decode" function that
   971  // allows Base64 decoding.
   972  func interpolationFuncBase64Decode() ast.Function {
   973  	return ast.Function{
   974  		ArgTypes:   []ast.Type{ast.TypeString},
   975  		ReturnType: ast.TypeString,
   976  		Callback: func(args []interface{}) (interface{}, error) {
   977  			s := args[0].(string)
   978  			sDec, err := base64.StdEncoding.DecodeString(s)
   979  			if err != nil {
   980  				return "", fmt.Errorf("failed to decode base64 data '%s'", s)
   981  			}
   982  			return string(sDec), nil
   983  		},
   984  	}
   985  }
   986  
   987  // interpolationFuncLower implements the "lower" function that does
   988  // string lower casing.
   989  func interpolationFuncLower() ast.Function {
   990  	return ast.Function{
   991  		ArgTypes:   []ast.Type{ast.TypeString},
   992  		ReturnType: ast.TypeString,
   993  		Callback: func(args []interface{}) (interface{}, error) {
   994  			toLower := args[0].(string)
   995  			return strings.ToLower(toLower), nil
   996  		},
   997  	}
   998  }
   999  
  1000  func interpolationFuncMd5() ast.Function {
  1001  	return ast.Function{
  1002  		ArgTypes:   []ast.Type{ast.TypeString},
  1003  		ReturnType: ast.TypeString,
  1004  		Callback: func(args []interface{}) (interface{}, error) {
  1005  			s := args[0].(string)
  1006  			h := md5.New()
  1007  			h.Write([]byte(s))
  1008  			hash := hex.EncodeToString(h.Sum(nil))
  1009  			return hash, nil
  1010  		},
  1011  	}
  1012  }
  1013  
  1014  func interpolationFuncMerge() ast.Function {
  1015  	return ast.Function{
  1016  		ArgTypes:     []ast.Type{ast.TypeMap},
  1017  		ReturnType:   ast.TypeMap,
  1018  		Variadic:     true,
  1019  		VariadicType: ast.TypeMap,
  1020  		Callback: func(args []interface{}) (interface{}, error) {
  1021  			outputMap := make(map[string]ast.Variable)
  1022  
  1023  			for _, arg := range args {
  1024  				for k, v := range arg.(map[string]ast.Variable) {
  1025  					outputMap[k] = v
  1026  				}
  1027  			}
  1028  
  1029  			return outputMap, nil
  1030  		},
  1031  	}
  1032  }
  1033  
  1034  // interpolationFuncUpper implements the "upper" function that does
  1035  // string upper casing.
  1036  func interpolationFuncUpper() ast.Function {
  1037  	return ast.Function{
  1038  		ArgTypes:   []ast.Type{ast.TypeString},
  1039  		ReturnType: ast.TypeString,
  1040  		Callback: func(args []interface{}) (interface{}, error) {
  1041  			toUpper := args[0].(string)
  1042  			return strings.ToUpper(toUpper), nil
  1043  		},
  1044  	}
  1045  }
  1046  
  1047  func interpolationFuncSha1() ast.Function {
  1048  	return ast.Function{
  1049  		ArgTypes:   []ast.Type{ast.TypeString},
  1050  		ReturnType: ast.TypeString,
  1051  		Callback: func(args []interface{}) (interface{}, error) {
  1052  			s := args[0].(string)
  1053  			h := sha1.New()
  1054  			h.Write([]byte(s))
  1055  			hash := hex.EncodeToString(h.Sum(nil))
  1056  			return hash, nil
  1057  		},
  1058  	}
  1059  }
  1060  
  1061  // hexadecimal representation of sha256 sum
  1062  func interpolationFuncSha256() ast.Function {
  1063  	return ast.Function{
  1064  		ArgTypes:   []ast.Type{ast.TypeString},
  1065  		ReturnType: ast.TypeString,
  1066  		Callback: func(args []interface{}) (interface{}, error) {
  1067  			s := args[0].(string)
  1068  			h := sha256.New()
  1069  			h.Write([]byte(s))
  1070  			hash := hex.EncodeToString(h.Sum(nil))
  1071  			return hash, nil
  1072  		},
  1073  	}
  1074  }
  1075  
  1076  func interpolationFuncTrimSpace() ast.Function {
  1077  	return ast.Function{
  1078  		ArgTypes:   []ast.Type{ast.TypeString},
  1079  		ReturnType: ast.TypeString,
  1080  		Callback: func(args []interface{}) (interface{}, error) {
  1081  			trimSpace := args[0].(string)
  1082  			return strings.TrimSpace(trimSpace), nil
  1083  		},
  1084  	}
  1085  }
  1086  
  1087  func interpolationFuncBase64Sha256() ast.Function {
  1088  	return ast.Function{
  1089  		ArgTypes:   []ast.Type{ast.TypeString},
  1090  		ReturnType: ast.TypeString,
  1091  		Callback: func(args []interface{}) (interface{}, error) {
  1092  			s := args[0].(string)
  1093  			h := sha256.New()
  1094  			h.Write([]byte(s))
  1095  			shaSum := h.Sum(nil)
  1096  			encoded := base64.StdEncoding.EncodeToString(shaSum[:])
  1097  			return encoded, nil
  1098  		},
  1099  	}
  1100  }
  1101  
  1102  func interpolationFuncUUID() ast.Function {
  1103  	return ast.Function{
  1104  		ArgTypes:   []ast.Type{},
  1105  		ReturnType: ast.TypeString,
  1106  		Callback: func(args []interface{}) (interface{}, error) {
  1107  			return uuid.GenerateUUID()
  1108  		},
  1109  	}
  1110  }
  1111  
  1112  // interpolationFuncTitle implements the "title" function that returns a copy of the
  1113  // string in which first characters of all the words are capitalized.
  1114  func interpolationFuncTitle() ast.Function {
  1115  	return ast.Function{
  1116  		ArgTypes:   []ast.Type{ast.TypeString},
  1117  		ReturnType: ast.TypeString,
  1118  		Callback: func(args []interface{}) (interface{}, error) {
  1119  			toTitle := args[0].(string)
  1120  			return strings.Title(toTitle), nil
  1121  		},
  1122  	}
  1123  }