github.com/articulate/terraform@v0.6.13-0.20160303003731-8d31c93862de/config/interpolate_funcs.go (about)

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