Merge pull request #7832 from hashicorp/f-map-func
config: Add map() interpolation function
This commit is contained in:
commit
4cdebd7a60
|
@ -71,6 +71,7 @@ func Funcs() map[string]ast.Function {
|
|||
"length": interpolationFuncLength(),
|
||||
"list": interpolationFuncList(),
|
||||
"lower": interpolationFuncLower(),
|
||||
"map": interpolationFuncMap(),
|
||||
"md5": interpolationFuncMd5(),
|
||||
"uuid": interpolationFuncUUID(),
|
||||
"replace": interpolationFuncReplace(),
|
||||
|
@ -123,6 +124,50 @@ func interpolationFuncList() ast.Function {
|
|||
}
|
||||
}
|
||||
|
||||
// interpolationFuncMap creates a map from the parameters passed
|
||||
// to it.
|
||||
func interpolationFuncMap() ast.Function {
|
||||
return ast.Function{
|
||||
ArgTypes: []ast.Type{},
|
||||
ReturnType: ast.TypeMap,
|
||||
Variadic: true,
|
||||
VariadicType: ast.TypeAny,
|
||||
Callback: func(args []interface{}) (interface{}, error) {
|
||||
outputMap := make(map[string]ast.Variable)
|
||||
|
||||
if len(args)%2 != 0 {
|
||||
return nil, fmt.Errorf("requires an even number of arguments, got %d", len(args))
|
||||
}
|
||||
|
||||
var firstType *ast.Type
|
||||
for i := 0; i < len(args); i += 2 {
|
||||
key, ok := args[i].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("argument %d represents a key, so it must be a string", i+1)
|
||||
}
|
||||
val := args[i+1]
|
||||
variable, err := hil.InterfaceToVariable(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Enforce map type homogeneity
|
||||
if firstType == nil {
|
||||
firstType = &variable.Type
|
||||
} else if variable.Type != *firstType {
|
||||
return nil, fmt.Errorf("all map values must have the same type, got %s then %s", firstType.Printable(), variable.Type.Printable())
|
||||
}
|
||||
// Check for duplicate keys
|
||||
if _, ok := outputMap[key]; ok {
|
||||
return nil, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key)
|
||||
}
|
||||
outputMap[key] = variable
|
||||
}
|
||||
|
||||
return outputMap, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// interpolationFuncCompact strips a list of multi-variable values
|
||||
// (e.g. as returned by "split") of any empty strings.
|
||||
func interpolationFuncCompact() ast.Function {
|
||||
|
|
|
@ -113,6 +113,81 @@ func TestInterpolateFuncList(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestInterpolateFuncMap(t *testing.T) {
|
||||
testFunction(t, testFunctionConfig{
|
||||
Cases: []testFunctionCase{
|
||||
// empty input returns empty map
|
||||
{
|
||||
`${map()}`,
|
||||
map[string]interface{}{},
|
||||
false,
|
||||
},
|
||||
|
||||
// odd args is error
|
||||
{
|
||||
`${map("odd")}`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
|
||||
// two args returns map w/ one k/v
|
||||
{
|
||||
`${map("hello", "world")}`,
|
||||
map[string]interface{}{"hello": "world"},
|
||||
false,
|
||||
},
|
||||
|
||||
// four args get two k/v
|
||||
{
|
||||
`${map("hello", "world", "what's", "up?")}`,
|
||||
map[string]interface{}{"hello": "world", "what's": "up?"},
|
||||
false,
|
||||
},
|
||||
|
||||
// map of lists is okay
|
||||
{
|
||||
`${map("hello", list("world"), "what's", list("up?"))}`,
|
||||
map[string]interface{}{
|
||||
"hello": []interface{}{"world"},
|
||||
"what's": []interface{}{"up?"},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
// map of maps is okay
|
||||
{
|
||||
`${map("hello", map("there", "world"), "what's", map("really", "up?"))}`,
|
||||
map[string]interface{}{
|
||||
"hello": map[string]interface{}{"there": "world"},
|
||||
"what's": map[string]interface{}{"really": "up?"},
|
||||
},
|
||||
false,
|
||||
},
|
||||
|
||||
// keys have to be strings
|
||||
{
|
||||
`${map(list("listkey"), "val")}`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
|
||||
// types have to match
|
||||
{
|
||||
`${map("some", "strings", "also", list("lists"))}`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
|
||||
// duplicate keys are an error
|
||||
{
|
||||
`${map("key", "val", "key", "again")}`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInterpolateFuncCompact(t *testing.T) {
|
||||
testFunction(t, testFunctionConfig{
|
||||
Cases: []testFunctionCase{
|
||||
|
|
|
@ -181,6 +181,13 @@ The supported built-in functions are:
|
|||
|
||||
* `lower(string)` - Returns a copy of the string with all Unicode letters mapped to their lower case.
|
||||
|
||||
* `map(key, value, ...)` - Returns a map consisting of the key/value pairs
|
||||
specified as arguments. Every odd argument must be a string key, and every
|
||||
even argument must have the same type as the other values specified.
|
||||
Duplicate keys are not allowed. Examples:
|
||||
* `map("hello", "world")`
|
||||
* `map("us-east", list("a", "b", "c"), "us-west", list("b", "c", "d"))`
|
||||
|
||||
* `md5(string)` - Returns a (conventional) hexadecimal representation of the
|
||||
MD5 hash of the given string.
|
||||
|
||||
|
|
Loading…
Reference in New Issue