terraform/config/interpolate_funcs.go

193 lines
5.5 KiB
Go
Raw Normal View History

2014-07-21 21:56:03 +02:00
package config
2014-07-21 22:12:43 +02:00
import (
2015-01-13 21:47:54 +01:00
"bytes"
2014-07-21 22:12:43 +02:00
"fmt"
"io/ioutil"
2015-03-02 18:37:40 +01:00
"regexp"
"strconv"
2014-07-21 22:12:43 +02:00
"strings"
"github.com/hashicorp/terraform/config/lang/ast"
"github.com/mitchellh/go-homedir"
2014-07-21 22:12:43 +02:00
)
2014-07-21 21:56:03 +02:00
// Funcs is the mapping of built-in functions for configuration.
2015-01-15 07:01:42 +01:00
var Funcs map[string]ast.Function
2014-07-21 21:56:03 +02:00
func init() {
2015-01-15 07:01:42 +01:00
Funcs = map[string]ast.Function{
2015-01-13 21:06:04 +01:00
"file": interpolationFuncFile(),
2015-03-02 19:26:06 +01:00
"format": interpolationFuncFormat(),
2015-01-13 21:06:04 +01:00
"join": interpolationFuncJoin(),
"element": interpolationFuncElement(),
2015-03-02 18:37:40 +01:00
"replace": interpolationFuncReplace(),
2015-01-28 22:59:16 +01:00
"split": interpolationFuncSplit(),
2015-03-02 18:37:40 +01:00
// Concat is a little useless now since we supported embeddded
// interpolations but we keep it around for backwards compat reasons.
"concat": interpolationFuncConcat(),
2014-07-21 21:56:03 +02:00
}
}
2015-01-13 21:47:54 +01:00
// interpolationFuncConcat implements the "concat" function that
// concatenates multiple strings. This isn't actually necessary anymore
// since our language supports string concat natively, but for backwards
// compat we do this.
2015-01-15 07:01:42 +01:00
func interpolationFuncConcat() ast.Function {
return ast.Function{
2015-01-13 21:47:54 +01:00
ArgTypes: []ast.Type{ast.TypeString},
ReturnType: ast.TypeString,
Variadic: true,
VariadicType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
var b bytes.Buffer
for _, v := range args {
b.WriteString(v.(string))
}
return b.String(), nil
},
}
}
// interpolationFuncFile implements the "file" function that allows
// loading contents from a file.
2015-01-15 07:01:42 +01:00
func interpolationFuncFile() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeString},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
path, err := homedir.Expand(args[0].(string))
if err != nil {
return "", err
}
data, err := ioutil.ReadFile(path)
if err != nil {
return "", err
}
return string(data), nil
},
}
}
2015-03-02 19:26:06 +01:00
// interpolationFuncFormat implements the "replace" function that does
// string replacement.
func interpolationFuncFormat() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeString},
Variadic: true,
VariadicType: ast.TypeAny,
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
format := args[0].(string)
return fmt.Sprintf(format, args[1:]...), nil
},
}
}
2014-10-10 06:22:35 +02:00
// interpolationFuncJoin implements the "join" function that allows
// multi-variable values to be joined by some character.
2015-01-15 07:01:42 +01:00
func interpolationFuncJoin() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
var list []string
for _, arg := range args[1:] {
parts := strings.Split(arg.(string), InterpSplitDelim)
list = append(list, parts...)
}
return strings.Join(list, args[0].(string)), nil
},
2014-10-10 06:22:35 +02:00
}
}
2015-03-02 18:37:40 +01:00
// interpolationFuncReplace implements the "replace" function that does
// string replacement.
func interpolationFuncReplace() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString, ast.TypeString},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
s := args[0].(string)
search := args[1].(string)
replace := args[2].(string)
// We search/replace using a regexp if the string is surrounded
// in forward slashes.
if len(search) > 1 && search[0] == '/' && search[len(search)-1] == '/' {
2015-03-02 18:37:40 +01:00
re, err := regexp.Compile(search[1 : len(search)-1])
if err != nil {
return nil, err
}
return re.ReplaceAllString(s, replace), nil
}
return strings.Replace(s, search, replace, -1), nil
},
}
}
2015-01-28 22:59:16 +01:00
// interpolationFuncSplit implements the "split" function that allows
// strings to split into multi-variable values
func interpolationFuncSplit() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
return strings.Replace(args[1].(string), args[0].(string), InterpSplitDelim, -1), nil
},
}
}
2014-07-21 21:56:03 +02:00
// interpolationFuncLookup implements the "lookup" function that allows
// dynamic lookups of map types within a Terraform configuration.
2015-01-15 07:01:42 +01:00
func interpolationFuncLookup(vs map[string]ast.Variable) ast.Function {
return ast.Function{
2015-01-13 21:06:04 +01:00
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
k := fmt.Sprintf("var.%s.%s", args[0].(string), args[1].(string))
v, ok := vs[k]
if !ok {
return "", fmt.Errorf(
"lookup in '%s' failed to find '%s'",
args[0].(string), args[1].(string))
}
2015-01-14 19:40:43 +01:00
if v.Type != ast.TypeString {
return "", fmt.Errorf(
"lookup in '%s' for '%s' has bad type %s",
args[0].(string), args[1].(string), v.Type)
}
2014-07-21 22:12:43 +02:00
2015-01-14 19:40:43 +01:00
return v.Value.(string), nil
2015-01-13 21:06:04 +01:00
},
2014-07-21 22:12:43 +02:00
}
2014-07-21 21:56:03 +02:00
}
// interpolationFuncElement implements the "element" function that allows
// a specific index to be looked up in a multi-variable value. Note that this will
// wrap if the index is larger than the number of elements in the multi-variable value.
2015-01-15 07:01:42 +01:00
func interpolationFuncElement() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
list := strings.Split(args[0].(string), InterpSplitDelim)
index, err := strconv.Atoi(args[1].(string))
if err != nil {
return "", fmt.Errorf(
"invalid number for index, got %s", args[1])
}
v := list[index%len(list)]
return v, nil
},
}
}