Skip to content

Commit

Permalink
Merge pull request #2198 from hashicorp/f-keys-values
Browse files Browse the repository at this point in the history
core: keys() and values() funcs for map variables
  • Loading branch information
phinze committed Jun 2, 2015
2 parents fdd19a5 + b781c6c commit b5a2383
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 0 deletions.
71 changes: 71 additions & 0 deletions config/interpolate_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io/ioutil"
"regexp"
"sort"
"strconv"
"strings"

Expand Down Expand Up @@ -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
},
}
}
98 changes: 98 additions & 0 deletions config/interpolate_funcs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
2 changes: 2 additions & 0 deletions config/raw_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down

0 comments on commit b5a2383

Please sign in to comment.