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