github.com/serbaut/terraform@v0.6.12-0.20160607213102-ac2d195cc560/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  	"errors"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"net"
    14  	"regexp"
    15  	"sort"
    16  	"strconv"
    17  	"strings"
    18  
    19  	"github.com/apparentlymart/go-cidr/cidr"
    20  	"github.com/hashicorp/go-uuid"
    21  	"github.com/hashicorp/hil"
    22  	"github.com/hashicorp/hil/ast"
    23  	"github.com/mitchellh/go-homedir"
    24  )
    25  
    26  // stringSliceToVariableValue converts a string slice into the value
    27  // required to be returned from interpolation functions which return
    28  // TypeList.
    29  func stringSliceToVariableValue(values []string) []ast.Variable {
    30  	output := make([]ast.Variable, len(values))
    31  	for index, value := range values {
    32  		output[index] = ast.Variable{
    33  			Type:  ast.TypeString,
    34  			Value: value,
    35  		}
    36  	}
    37  	return output
    38  }
    39  
    40  func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) {
    41  	output := make([]string, len(values))
    42  	for index, value := range values {
    43  		if value.Type != ast.TypeString {
    44  			return []string{}, fmt.Errorf("list has non-string element (%T)", value.Type.String())
    45  		}
    46  		output[index] = value.Value.(string)
    47  	}
    48  	return output, nil
    49  }
    50  
    51  // Funcs is the mapping of built-in functions for configuration.
    52  func Funcs() map[string]ast.Function {
    53  	return map[string]ast.Function{
    54  		"base64decode": interpolationFuncBase64Decode(),
    55  		"base64encode": interpolationFuncBase64Encode(),
    56  		"base64sha256": interpolationFuncBase64Sha256(),
    57  		"cidrhost":     interpolationFuncCidrHost(),
    58  		"cidrnetmask":  interpolationFuncCidrNetmask(),
    59  		"cidrsubnet":   interpolationFuncCidrSubnet(),
    60  		"coalesce":     interpolationFuncCoalesce(),
    61  		"compact":      interpolationFuncCompact(),
    62  		"concat":       interpolationFuncConcat(),
    63  		"element":      interpolationFuncElement(),
    64  		"file":         interpolationFuncFile(),
    65  		"format":       interpolationFuncFormat(),
    66  		"formatlist":   interpolationFuncFormatList(),
    67  		"index":        interpolationFuncIndex(),
    68  		"join":         interpolationFuncJoin(),
    69  		"jsonencode":   interpolationFuncJSONEncode(),
    70  		"length":       interpolationFuncLength(),
    71  		"lower":        interpolationFuncLower(),
    72  		"md5":          interpolationFuncMd5(),
    73  		"uuid":         interpolationFuncUUID(),
    74  		"replace":      interpolationFuncReplace(),
    75  		"sha1":         interpolationFuncSha1(),
    76  		"sha256":       interpolationFuncSha256(),
    77  		"signum":       interpolationFuncSignum(),
    78  		"split":        interpolationFuncSplit(),
    79  		"trimspace":    interpolationFuncTrimSpace(),
    80  		"upper":        interpolationFuncUpper(),
    81  	}
    82  }
    83  
    84  // interpolationFuncCompact strips a list of multi-variable values
    85  // (e.g. as returned by "split") of any empty strings.
    86  func interpolationFuncCompact() ast.Function {
    87  	return ast.Function{
    88  		ArgTypes:   []ast.Type{ast.TypeList},
    89  		ReturnType: ast.TypeList,
    90  		Variadic:   false,
    91  		Callback: func(args []interface{}) (interface{}, error) {
    92  			inputList := args[0].([]ast.Variable)
    93  
    94  			var outputList []string
    95  			for _, val := range inputList {
    96  				if strVal, ok := val.Value.(string); ok {
    97  					if strVal == "" {
    98  						continue
    99  					}
   100  
   101  					outputList = append(outputList, strVal)
   102  				}
   103  			}
   104  			return stringSliceToVariableValue(outputList), nil
   105  		},
   106  	}
   107  }
   108  
   109  // interpolationFuncCidrHost implements the "cidrhost" function that
   110  // fills in the host part of a CIDR range address to create a single
   111  // host address
   112  func interpolationFuncCidrHost() ast.Function {
   113  	return ast.Function{
   114  		ArgTypes: []ast.Type{
   115  			ast.TypeString, // starting CIDR mask
   116  			ast.TypeInt,    // host number to insert
   117  		},
   118  		ReturnType: ast.TypeString,
   119  		Variadic:   false,
   120  		Callback: func(args []interface{}) (interface{}, error) {
   121  			hostNum := args[1].(int)
   122  			_, network, err := net.ParseCIDR(args[0].(string))
   123  			if err != nil {
   124  				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
   125  			}
   126  
   127  			ip, err := cidr.Host(network, hostNum)
   128  			if err != nil {
   129  				return nil, err
   130  			}
   131  
   132  			return ip.String(), nil
   133  		},
   134  	}
   135  }
   136  
   137  // interpolationFuncCidrNetmask implements the "cidrnetmask" function
   138  // that returns the subnet mask in IP address notation.
   139  func interpolationFuncCidrNetmask() ast.Function {
   140  	return ast.Function{
   141  		ArgTypes: []ast.Type{
   142  			ast.TypeString, // CIDR mask
   143  		},
   144  		ReturnType: ast.TypeString,
   145  		Variadic:   false,
   146  		Callback: func(args []interface{}) (interface{}, error) {
   147  			_, network, err := net.ParseCIDR(args[0].(string))
   148  			if err != nil {
   149  				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
   150  			}
   151  
   152  			return net.IP(network.Mask).String(), nil
   153  		},
   154  	}
   155  }
   156  
   157  // interpolationFuncCidrSubnet implements the "cidrsubnet" function that
   158  // adds an additional subnet of the given length onto an existing
   159  // IP block expressed in CIDR notation.
   160  func interpolationFuncCidrSubnet() ast.Function {
   161  	return ast.Function{
   162  		ArgTypes: []ast.Type{
   163  			ast.TypeString, // starting CIDR mask
   164  			ast.TypeInt,    // number of bits to extend the prefix
   165  			ast.TypeInt,    // network number to append to the prefix
   166  		},
   167  		ReturnType: ast.TypeString,
   168  		Variadic:   false,
   169  		Callback: func(args []interface{}) (interface{}, error) {
   170  			extraBits := args[1].(int)
   171  			subnetNum := args[2].(int)
   172  			_, network, err := net.ParseCIDR(args[0].(string))
   173  			if err != nil {
   174  				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
   175  			}
   176  
   177  			// For portability with 32-bit systems where the subnet number
   178  			// will be a 32-bit int, we only allow extension of 32 bits in
   179  			// one call even if we're running on a 64-bit machine.
   180  			// (Of course, this is significant only for IPv6.)
   181  			if extraBits > 32 {
   182  				return nil, fmt.Errorf("may not extend prefix by more than 32 bits")
   183  			}
   184  
   185  			newNetwork, err := cidr.Subnet(network, extraBits, subnetNum)
   186  			if err != nil {
   187  				return nil, err
   188  			}
   189  
   190  			return newNetwork.String(), nil
   191  		},
   192  	}
   193  }
   194  
   195  // interpolationFuncCoalesce implements the "coalesce" function that
   196  // returns the first non null / empty string from the provided input
   197  func interpolationFuncCoalesce() ast.Function {
   198  	return ast.Function{
   199  		ArgTypes:     []ast.Type{ast.TypeString},
   200  		ReturnType:   ast.TypeString,
   201  		Variadic:     true,
   202  		VariadicType: ast.TypeString,
   203  		Callback: func(args []interface{}) (interface{}, error) {
   204  			if len(args) < 2 {
   205  				return nil, fmt.Errorf("must provide at least two arguments")
   206  			}
   207  			for _, arg := range args {
   208  				argument := arg.(string)
   209  
   210  				if argument != "" {
   211  					return argument, nil
   212  				}
   213  			}
   214  			return "", nil
   215  		},
   216  	}
   217  }
   218  
   219  // interpolationFuncConcat implements the "concat" function that
   220  // concatenates multiple strings. This isn't actually necessary anymore
   221  // since our language supports string concat natively, but for backwards
   222  // compat we do this.
   223  func interpolationFuncConcat() ast.Function {
   224  	return ast.Function{
   225  		ArgTypes:     []ast.Type{ast.TypeAny},
   226  		ReturnType:   ast.TypeList,
   227  		Variadic:     true,
   228  		VariadicType: ast.TypeAny,
   229  		Callback: func(args []interface{}) (interface{}, error) {
   230  			var finalListElements []string
   231  
   232  			for _, arg := range args {
   233  				// Append strings for backward compatibility
   234  				if argument, ok := arg.(string); ok {
   235  					finalListElements = append(finalListElements, argument)
   236  					continue
   237  				}
   238  
   239  				// Otherwise variables
   240  				if argument, ok := arg.([]ast.Variable); ok {
   241  					for _, element := range argument {
   242  						finalListElements = append(finalListElements, element.Value.(string))
   243  					}
   244  					continue
   245  				}
   246  
   247  				return nil, fmt.Errorf("arguments to concat() must be a string or list")
   248  			}
   249  
   250  			return stringSliceToVariableValue(finalListElements), nil
   251  		},
   252  	}
   253  }
   254  
   255  // interpolationFuncFile implements the "file" function that allows
   256  // loading contents from a file.
   257  func interpolationFuncFile() ast.Function {
   258  	return ast.Function{
   259  		ArgTypes:   []ast.Type{ast.TypeString},
   260  		ReturnType: ast.TypeString,
   261  		Callback: func(args []interface{}) (interface{}, error) {
   262  			path, err := homedir.Expand(args[0].(string))
   263  			if err != nil {
   264  				return "", err
   265  			}
   266  			data, err := ioutil.ReadFile(path)
   267  			if err != nil {
   268  				return "", err
   269  			}
   270  
   271  			return string(data), nil
   272  		},
   273  	}
   274  }
   275  
   276  // interpolationFuncFormat implements the "format" function that does
   277  // string formatting.
   278  func interpolationFuncFormat() ast.Function {
   279  	return ast.Function{
   280  		ArgTypes:     []ast.Type{ast.TypeString},
   281  		Variadic:     true,
   282  		VariadicType: ast.TypeAny,
   283  		ReturnType:   ast.TypeString,
   284  		Callback: func(args []interface{}) (interface{}, error) {
   285  			format := args[0].(string)
   286  			return fmt.Sprintf(format, args[1:]...), nil
   287  		},
   288  	}
   289  }
   290  
   291  // interpolationFuncFormatList implements the "formatlist" function that does
   292  // string formatting on lists.
   293  func interpolationFuncFormatList() ast.Function {
   294  	return ast.Function{
   295  		ArgTypes:     []ast.Type{ast.TypeAny},
   296  		Variadic:     true,
   297  		VariadicType: ast.TypeAny,
   298  		ReturnType:   ast.TypeList,
   299  		Callback: func(args []interface{}) (interface{}, error) {
   300  			// Make a copy of the variadic part of args
   301  			// to avoid modifying the original.
   302  			varargs := make([]interface{}, len(args)-1)
   303  			copy(varargs, args[1:])
   304  
   305  			// Convert arguments that are lists into slices.
   306  			// Confirm along the way that all lists have the same length (n).
   307  			var n int
   308  			for i := 1; i < len(args); i++ {
   309  				s, ok := args[i].([]ast.Variable)
   310  				if !ok {
   311  					continue
   312  				}
   313  
   314  				parts, err := listVariableValueToStringSlice(s)
   315  				if err != nil {
   316  					return nil, err
   317  				}
   318  
   319  				// otherwise the list is sent down to be indexed
   320  				varargs[i-1] = parts
   321  
   322  				// Check length
   323  				if n == 0 {
   324  					// first list we've seen
   325  					n = len(parts)
   326  					continue
   327  				}
   328  				if n != len(parts) {
   329  					return nil, fmt.Errorf("format: mismatched list lengths: %d != %d", n, len(parts))
   330  				}
   331  			}
   332  
   333  			if n == 0 {
   334  				return nil, errors.New("no lists in arguments to formatlist")
   335  			}
   336  
   337  			// Do the formatting.
   338  			format := args[0].(string)
   339  
   340  			// Generate a list of formatted strings.
   341  			list := make([]string, n)
   342  			fmtargs := make([]interface{}, len(varargs))
   343  			for i := 0; i < n; i++ {
   344  				for j, arg := range varargs {
   345  					switch arg := arg.(type) {
   346  					default:
   347  						fmtargs[j] = arg
   348  					case []string:
   349  						fmtargs[j] = arg[i]
   350  					}
   351  				}
   352  				list[i] = fmt.Sprintf(format, fmtargs...)
   353  			}
   354  			return stringSliceToVariableValue(list), nil
   355  		},
   356  	}
   357  }
   358  
   359  // interpolationFuncIndex implements the "index" function that allows one to
   360  // find the index of a specific element in a list
   361  func interpolationFuncIndex() ast.Function {
   362  	return ast.Function{
   363  		ArgTypes:   []ast.Type{ast.TypeList, ast.TypeString},
   364  		ReturnType: ast.TypeInt,
   365  		Callback: func(args []interface{}) (interface{}, error) {
   366  			haystack := args[0].([]ast.Variable)
   367  			needle := args[1].(string)
   368  			for index, element := range haystack {
   369  				if needle == element.Value {
   370  					return index, nil
   371  				}
   372  			}
   373  			return nil, fmt.Errorf("Could not find '%s' in '%s'", needle, haystack)
   374  		},
   375  	}
   376  }
   377  
   378  // interpolationFuncJoin implements the "join" function that allows
   379  // multi-variable values to be joined by some character.
   380  func interpolationFuncJoin() ast.Function {
   381  	return ast.Function{
   382  		ArgTypes:     []ast.Type{ast.TypeString},
   383  		Variadic:     true,
   384  		VariadicType: ast.TypeList,
   385  		ReturnType:   ast.TypeString,
   386  		Callback: func(args []interface{}) (interface{}, error) {
   387  			var list []string
   388  
   389  			if len(args) < 2 {
   390  				return nil, fmt.Errorf("not enough arguments to join()")
   391  			}
   392  
   393  			for _, arg := range args[1:] {
   394  				if parts, ok := arg.(ast.Variable); ok {
   395  					for _, part := range parts.Value.([]ast.Variable) {
   396  						list = append(list, part.Value.(string))
   397  					}
   398  				}
   399  				if parts, ok := arg.([]ast.Variable); ok {
   400  					for _, part := range parts {
   401  						list = append(list, part.Value.(string))
   402  					}
   403  				}
   404  			}
   405  
   406  			return strings.Join(list, args[0].(string)), nil
   407  		},
   408  	}
   409  }
   410  
   411  // interpolationFuncJSONEncode implements the "jsonencode" function that encodes
   412  // a string, list, or map as its JSON representation. For now, values in the
   413  // list or map may only be strings.
   414  func interpolationFuncJSONEncode() ast.Function {
   415  	return ast.Function{
   416  		ArgTypes:   []ast.Type{ast.TypeAny},
   417  		ReturnType: ast.TypeString,
   418  		Callback: func(args []interface{}) (interface{}, error) {
   419  			var toEncode interface{}
   420  
   421  			switch typedArg := args[0].(type) {
   422  			case string:
   423  				toEncode = typedArg
   424  
   425  			case []ast.Variable:
   426  				// We preallocate the list here. Note that it's important that in
   427  				// the length 0 case, we have an empty list rather than nil, as
   428  				// they encode differently.
   429  				// XXX It would be nice to support arbitrarily nested data here. Is
   430  				// there an inverse of hil.InterfaceToVariable?
   431  				strings := make([]string, len(typedArg))
   432  
   433  				for i, v := range typedArg {
   434  					if v.Type != ast.TypeString {
   435  						return "", fmt.Errorf("list elements must be strings")
   436  					}
   437  					strings[i] = v.Value.(string)
   438  				}
   439  				toEncode = strings
   440  
   441  			case map[string]ast.Variable:
   442  				// XXX It would be nice to support arbitrarily nested data here. Is
   443  				// there an inverse of hil.InterfaceToVariable?
   444  				stringMap := make(map[string]string)
   445  				for k, v := range typedArg {
   446  					if v.Type != ast.TypeString {
   447  						return "", fmt.Errorf("map values must be strings")
   448  					}
   449  					stringMap[k] = v.Value.(string)
   450  				}
   451  				toEncode = stringMap
   452  
   453  			default:
   454  				return "", fmt.Errorf("unknown type for JSON encoding: %T", args[0])
   455  			}
   456  
   457  			jEnc, err := json.Marshal(toEncode)
   458  			if err != nil {
   459  				return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
   460  			}
   461  			return string(jEnc), nil
   462  		},
   463  	}
   464  }
   465  
   466  // interpolationFuncReplace implements the "replace" function that does
   467  // string replacement.
   468  func interpolationFuncReplace() ast.Function {
   469  	return ast.Function{
   470  		ArgTypes:   []ast.Type{ast.TypeString, ast.TypeString, ast.TypeString},
   471  		ReturnType: ast.TypeString,
   472  		Callback: func(args []interface{}) (interface{}, error) {
   473  			s := args[0].(string)
   474  			search := args[1].(string)
   475  			replace := args[2].(string)
   476  
   477  			// We search/replace using a regexp if the string is surrounded
   478  			// in forward slashes.
   479  			if len(search) > 1 && search[0] == '/' && search[len(search)-1] == '/' {
   480  				re, err := regexp.Compile(search[1 : len(search)-1])
   481  				if err != nil {
   482  					return nil, err
   483  				}
   484  
   485  				return re.ReplaceAllString(s, replace), nil
   486  			}
   487  
   488  			return strings.Replace(s, search, replace, -1), nil
   489  		},
   490  	}
   491  }
   492  
   493  func interpolationFuncLength() ast.Function {
   494  	return ast.Function{
   495  		ArgTypes:   []ast.Type{ast.TypeAny},
   496  		ReturnType: ast.TypeInt,
   497  		Variadic:   false,
   498  		Callback: func(args []interface{}) (interface{}, error) {
   499  			subject := args[0]
   500  
   501  			switch typedSubject := subject.(type) {
   502  			case string:
   503  				return len(typedSubject), nil
   504  			case []ast.Variable:
   505  				return len(typedSubject), nil
   506  			}
   507  
   508  			return 0, fmt.Errorf("arguments to length() must be a string or list")
   509  		},
   510  	}
   511  }
   512  
   513  func interpolationFuncSignum() ast.Function {
   514  	return ast.Function{
   515  		ArgTypes:   []ast.Type{ast.TypeInt},
   516  		ReturnType: ast.TypeInt,
   517  		Variadic:   false,
   518  		Callback: func(args []interface{}) (interface{}, error) {
   519  			num := args[0].(int)
   520  			switch {
   521  			case num < 0:
   522  				return -1, nil
   523  			case num > 0:
   524  				return +1, nil
   525  			default:
   526  				return 0, nil
   527  			}
   528  		},
   529  	}
   530  }
   531  
   532  // interpolationFuncSplit implements the "split" function that allows
   533  // strings to split into multi-variable values
   534  func interpolationFuncSplit() ast.Function {
   535  	return ast.Function{
   536  		ArgTypes:   []ast.Type{ast.TypeString, ast.TypeString},
   537  		ReturnType: ast.TypeList,
   538  		Callback: func(args []interface{}) (interface{}, error) {
   539  			sep := args[0].(string)
   540  			s := args[1].(string)
   541  			elements := strings.Split(s, sep)
   542  			return stringSliceToVariableValue(elements), nil
   543  		},
   544  	}
   545  }
   546  
   547  // interpolationFuncLookup implements the "lookup" function that allows
   548  // dynamic lookups of map types within a Terraform configuration.
   549  func interpolationFuncLookup(vs map[string]ast.Variable) ast.Function {
   550  	return ast.Function{
   551  		ArgTypes:     []ast.Type{ast.TypeMap, ast.TypeString},
   552  		ReturnType:   ast.TypeString,
   553  		Variadic:     true,
   554  		VariadicType: ast.TypeString,
   555  		Callback: func(args []interface{}) (interface{}, error) {
   556  			defaultValue := ""
   557  			defaultValueSet := false
   558  			if len(args) > 2 {
   559  				defaultValue = args[2].(string)
   560  				defaultValueSet = true
   561  			}
   562  			if len(args) > 3 {
   563  				return "", fmt.Errorf("lookup() takes no more than three arguments")
   564  			}
   565  			index := args[1].(string)
   566  			mapVar := args[0].(map[string]ast.Variable)
   567  
   568  			v, ok := mapVar[index]
   569  			if !ok {
   570  				if defaultValueSet {
   571  					return defaultValue, nil
   572  				} else {
   573  					return "", fmt.Errorf(
   574  						"lookup failed to find '%s'",
   575  						args[1].(string))
   576  				}
   577  			}
   578  			if v.Type != ast.TypeString {
   579  				return "", fmt.Errorf(
   580  					"lookup for '%s' has bad type %s",
   581  					args[1].(string), v.Type)
   582  			}
   583  
   584  			return v.Value.(string), nil
   585  		},
   586  	}
   587  }
   588  
   589  // interpolationFuncElement implements the "element" function that allows
   590  // a specific index to be looked up in a multi-variable value. Note that this will
   591  // wrap if the index is larger than the number of elements in the multi-variable value.
   592  func interpolationFuncElement() ast.Function {
   593  	return ast.Function{
   594  		ArgTypes:   []ast.Type{ast.TypeList, ast.TypeString},
   595  		ReturnType: ast.TypeString,
   596  		Callback: func(args []interface{}) (interface{}, error) {
   597  			list := args[0].([]ast.Variable)
   598  
   599  			index, err := strconv.Atoi(args[1].(string))
   600  			if err != nil || index < 0 {
   601  				return "", fmt.Errorf(
   602  					"invalid number for index, got %s", args[1])
   603  			}
   604  
   605  			resolvedIndex := index % len(list)
   606  
   607  			v := list[resolvedIndex].Value
   608  			return v, nil
   609  		},
   610  	}
   611  }
   612  
   613  // interpolationFuncKeys implements the "keys" function that yields a list of
   614  // keys of map types within a Terraform configuration.
   615  func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function {
   616  	return ast.Function{
   617  		ArgTypes:   []ast.Type{ast.TypeMap},
   618  		ReturnType: ast.TypeList,
   619  		Callback: func(args []interface{}) (interface{}, error) {
   620  			mapVar := args[0].(map[string]ast.Variable)
   621  			keys := make([]string, 0)
   622  
   623  			for k, _ := range mapVar {
   624  				keys = append(keys, k)
   625  			}
   626  
   627  			sort.Strings(keys)
   628  
   629  			//Keys are guaranteed to be strings
   630  			return stringSliceToVariableValue(keys), nil
   631  		},
   632  	}
   633  }
   634  
   635  // interpolationFuncValues implements the "values" function that yields a list of
   636  // keys of map types within a Terraform configuration.
   637  func interpolationFuncValues(vs map[string]ast.Variable) ast.Function {
   638  	return ast.Function{
   639  		ArgTypes:   []ast.Type{ast.TypeMap},
   640  		ReturnType: ast.TypeList,
   641  		Callback: func(args []interface{}) (interface{}, error) {
   642  			mapVar := args[0].(map[string]ast.Variable)
   643  			keys := make([]string, 0)
   644  
   645  			for k, _ := range mapVar {
   646  				keys = append(keys, k)
   647  			}
   648  
   649  			sort.Strings(keys)
   650  
   651  			values := make([]string, len(keys))
   652  			for index, key := range keys {
   653  				if value, ok := mapVar[key].Value.(string); ok {
   654  					values[index] = value
   655  				} else {
   656  					return "", fmt.Errorf("values(): %q has element with bad type %s",
   657  						key, mapVar[key].Type)
   658  				}
   659  			}
   660  
   661  			variable, err := hil.InterfaceToVariable(values)
   662  			if err != nil {
   663  				return nil, err
   664  			}
   665  
   666  			return variable.Value, nil
   667  		},
   668  	}
   669  }
   670  
   671  // interpolationFuncBase64Encode implements the "base64encode" function that
   672  // allows Base64 encoding.
   673  func interpolationFuncBase64Encode() ast.Function {
   674  	return ast.Function{
   675  		ArgTypes:   []ast.Type{ast.TypeString},
   676  		ReturnType: ast.TypeString,
   677  		Callback: func(args []interface{}) (interface{}, error) {
   678  			s := args[0].(string)
   679  			return base64.StdEncoding.EncodeToString([]byte(s)), nil
   680  		},
   681  	}
   682  }
   683  
   684  // interpolationFuncBase64Decode implements the "base64decode" function that
   685  // allows Base64 decoding.
   686  func interpolationFuncBase64Decode() ast.Function {
   687  	return ast.Function{
   688  		ArgTypes:   []ast.Type{ast.TypeString},
   689  		ReturnType: ast.TypeString,
   690  		Callback: func(args []interface{}) (interface{}, error) {
   691  			s := args[0].(string)
   692  			sDec, err := base64.StdEncoding.DecodeString(s)
   693  			if err != nil {
   694  				return "", fmt.Errorf("failed to decode base64 data '%s'", s)
   695  			}
   696  			return string(sDec), nil
   697  		},
   698  	}
   699  }
   700  
   701  // interpolationFuncLower implements the "lower" function that does
   702  // string lower casing.
   703  func interpolationFuncLower() ast.Function {
   704  	return ast.Function{
   705  		ArgTypes:   []ast.Type{ast.TypeString},
   706  		ReturnType: ast.TypeString,
   707  		Callback: func(args []interface{}) (interface{}, error) {
   708  			toLower := args[0].(string)
   709  			return strings.ToLower(toLower), nil
   710  		},
   711  	}
   712  }
   713  
   714  func interpolationFuncMd5() ast.Function {
   715  	return ast.Function{
   716  		ArgTypes:   []ast.Type{ast.TypeString},
   717  		ReturnType: ast.TypeString,
   718  		Callback: func(args []interface{}) (interface{}, error) {
   719  			s := args[0].(string)
   720  			h := md5.New()
   721  			h.Write([]byte(s))
   722  			hash := hex.EncodeToString(h.Sum(nil))
   723  			return hash, nil
   724  		},
   725  	}
   726  }
   727  
   728  // interpolationFuncUpper implements the "upper" function that does
   729  // string upper casing.
   730  func interpolationFuncUpper() ast.Function {
   731  	return ast.Function{
   732  		ArgTypes:   []ast.Type{ast.TypeString},
   733  		ReturnType: ast.TypeString,
   734  		Callback: func(args []interface{}) (interface{}, error) {
   735  			toUpper := args[0].(string)
   736  			return strings.ToUpper(toUpper), nil
   737  		},
   738  	}
   739  }
   740  
   741  func interpolationFuncSha1() ast.Function {
   742  	return ast.Function{
   743  		ArgTypes:   []ast.Type{ast.TypeString},
   744  		ReturnType: ast.TypeString,
   745  		Callback: func(args []interface{}) (interface{}, error) {
   746  			s := args[0].(string)
   747  			h := sha1.New()
   748  			h.Write([]byte(s))
   749  			hash := hex.EncodeToString(h.Sum(nil))
   750  			return hash, nil
   751  		},
   752  	}
   753  }
   754  
   755  // hexadecimal representation of sha256 sum
   756  func interpolationFuncSha256() ast.Function {
   757  	return ast.Function{
   758  		ArgTypes:   []ast.Type{ast.TypeString},
   759  		ReturnType: ast.TypeString,
   760  		Callback: func(args []interface{}) (interface{}, error) {
   761  			s := args[0].(string)
   762  			h := sha256.New()
   763  			h.Write([]byte(s))
   764  			hash := hex.EncodeToString(h.Sum(nil))
   765  			return hash, nil
   766  		},
   767  	}
   768  }
   769  
   770  func interpolationFuncTrimSpace() ast.Function {
   771  	return ast.Function{
   772  		ArgTypes:   []ast.Type{ast.TypeString},
   773  		ReturnType: ast.TypeString,
   774  		Callback: func(args []interface{}) (interface{}, error) {
   775  			trimSpace := args[0].(string)
   776  			return strings.TrimSpace(trimSpace), nil
   777  		},
   778  	}
   779  }
   780  
   781  func interpolationFuncBase64Sha256() ast.Function {
   782  	return ast.Function{
   783  		ArgTypes:   []ast.Type{ast.TypeString},
   784  		ReturnType: ast.TypeString,
   785  		Callback: func(args []interface{}) (interface{}, error) {
   786  			s := args[0].(string)
   787  			h := sha256.New()
   788  			h.Write([]byte(s))
   789  			shaSum := h.Sum(nil)
   790  			encoded := base64.StdEncoding.EncodeToString(shaSum[:])
   791  			return encoded, nil
   792  		},
   793  	}
   794  }
   795  
   796  func interpolationFuncUUID() ast.Function {
   797  	return ast.Function{
   798  		ArgTypes:   []ast.Type{},
   799  		ReturnType: ast.TypeString,
   800  		Callback: func(args []interface{}) (interface{}, error) {
   801  			return uuid.GenerateUUID()
   802  		},
   803  	}
   804  }