Merge pull request #2198 from hashicorp/f-keys-values

core: keys() and values() funcs for map variables
This commit is contained in:
Paul Hinze 2015-06-02 17:23:24 -05:00
commit b5a23837bd
3 changed files with 171 additions and 0 deletions

View File

@ -6,6 +6,7 @@ import (
"fmt"
"io/ioutil"
"regexp"
"sort"
"strconv"
"strings"
@ -278,3 +279,73 @@ func interpolationFuncElement() ast.Function {
},
}
}
// interpolationFuncKeys implements the "keys" function that yields a list of
// keys of map types within a Terraform configuration.
func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeString},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
// Prefix must include ending dot to be a map
prefix := fmt.Sprintf("var.%s.", args[0].(string))
keys := make([]string, 0, len(vs))
for k, _ := range vs {
if !strings.HasPrefix(k, prefix) {
continue
}
keys = append(keys, k[len(prefix):])
}
if len(keys) <= 0 {
return "", fmt.Errorf(
"failed to find map '%s'",
args[0].(string))
}
sort.Strings(keys)
return strings.Join(keys, InterpSplitDelim), nil
},
}
}
// interpolationFuncValues implements the "values" function that yields a list of
// keys of map types within a Terraform configuration.
func interpolationFuncValues(vs map[string]ast.Variable) ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeString},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
// Prefix must include ending dot to be a map
prefix := fmt.Sprintf("var.%s.", args[0].(string))
keys := make([]string, 0, len(vs))
for k, _ := range vs {
if !strings.HasPrefix(k, prefix) {
continue
}
keys = append(keys, k)
}
if len(keys) <= 0 {
return "", fmt.Errorf(
"failed to find map '%s'",
args[0].(string))
}
sort.Strings(keys)
vals := make([]string, 0, len(keys))
for _, k := range keys {
v := vs[k]
if v.Type != ast.TypeString {
return "", fmt.Errorf("values(): %q has bad type %s", k, v.Type)
}
vals = append(vals, vs[k].Value.(string))
}
return strings.Join(vals, InterpSplitDelim), nil
},
}
}

View File

@ -384,6 +384,104 @@ func TestInterpolateFuncLookup(t *testing.T) {
})
}
func TestInterpolateFuncKeys(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.foo.bar": ast.Variable{
Value: "baz",
Type: ast.TypeString,
},
"var.foo.qux": ast.Variable{
Value: "quack",
Type: ast.TypeString,
},
"var.str": ast.Variable{
Value: "astring",
Type: ast.TypeString,
},
},
Cases: []testFunctionCase{
{
`${keys("foo")}`,
fmt.Sprintf(
"bar%squx",
InterpSplitDelim),
false,
},
// Invalid key
{
`${keys("not")}`,
nil,
true,
},
// Too many args
{
`${keys("foo", "bar")}`,
nil,
true,
},
// Not a map
{
`${keys("str")}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncValues(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.foo.bar": ast.Variable{
Value: "quack",
Type: ast.TypeString,
},
"var.foo.qux": ast.Variable{
Value: "baz",
Type: ast.TypeString,
},
"var.str": ast.Variable{
Value: "astring",
Type: ast.TypeString,
},
},
Cases: []testFunctionCase{
{
`${values("foo")}`,
fmt.Sprintf(
"quack%sbaz",
InterpSplitDelim),
false,
},
// Invalid key
{
`${values("not")}`,
nil,
true,
},
// Too many args
{
`${values("foo", "bar")}`,
nil,
true,
},
// Not a map
{
`${values("str")}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncElement(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{

View File

@ -304,6 +304,8 @@ func langEvalConfig(vs map[string]ast.Variable) *lang.EvalConfig {
funcMap[k] = v
}
funcMap["lookup"] = interpolationFuncLookup(vs)
funcMap["keys"] = interpolationFuncKeys(vs)
funcMap["values"] = interpolationFuncValues(vs)
return &lang.EvalConfig{
GlobalScope: &ast.BasicScope{