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