mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-11 16:15:33 -06:00
Merge pull request #2198 from hashicorp/f-keys-values
core: keys() and values() funcs for map variables
This commit is contained in:
commit
b5a23837bd
@ -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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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{
|
||||
|
Loading…
Reference in New Issue
Block a user