github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/config/interpolate_funcs.go (about)

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