config: add lookup function back

This commit is contained in:
Mitchell Hashimoto 2015-01-13 12:06:04 -08:00
parent 49fe0d5c7f
commit 4af4c9e16c
3 changed files with 124 additions and 132 deletions

View File

@ -15,9 +15,8 @@ var Funcs map[string]lang.Function
func init() { func init() {
Funcs = map[string]lang.Function{ Funcs = map[string]lang.Function{
"file": interpolationFuncFile(), "file": interpolationFuncFile(),
"join": interpolationFuncJoin(), "join": interpolationFuncJoin(),
//"lookup": interpolationFuncLookup(),
"element": interpolationFuncElement(), "element": interpolationFuncElement(),
} }
} }
@ -59,22 +58,22 @@ func interpolationFuncJoin() lang.Function {
// interpolationFuncLookup implements the "lookup" function that allows // interpolationFuncLookup implements the "lookup" function that allows
// dynamic lookups of map types within a Terraform configuration. // dynamic lookups of map types within a Terraform configuration.
func interpolationFuncLookup( func interpolationFuncLookup(vs map[string]string) lang.Function {
vs map[string]string, args ...string) (string, error) { return lang.Function{
if len(args) != 2 { ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
return "", fmt.Errorf( ReturnType: ast.TypeString,
"lookup expects 2 arguments, got %d", len(args)) 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))
}
k := fmt.Sprintf("var.%s", strings.Join(args, ".")) return v, nil
v, ok := vs[k] },
if !ok {
return "", fmt.Errorf(
"lookup in '%s' failed to find '%s'",
args[0], args[1])
} }
return v, nil
} }
// interpolationFuncElement implements the "element" function that allows // interpolationFuncElement implements the "element" function that allows

View File

@ -20,163 +20,149 @@ func TestInterpolateFuncFile(t *testing.T) {
tf.Close() tf.Close()
defer os.Remove(path) defer os.Remove(path)
testFunction(t, []testFunctionCase{ testFunction(t, testFunctionConfig{
{ Cases: []testFunctionCase{
fmt.Sprintf(`${file("%s")}`, path), {
"foo", fmt.Sprintf(`${file("%s")}`, path),
false, "foo",
}, false,
},
// Invalid path // Invalid path
{ {
`${file("/i/dont/exist")}`, `${file("/i/dont/exist")}`,
nil, nil,
true, true,
}, },
// Too many args // Too many args
{ {
`${file("foo", "bar")}`, `${file("foo", "bar")}`,
nil, nil,
true, true,
},
}, },
}) })
} }
func TestInterpolateFuncJoin(t *testing.T) { func TestInterpolateFuncJoin(t *testing.T) {
testFunction(t, []testFunctionCase{ testFunction(t, testFunctionConfig{
{ Cases: []testFunctionCase{
`${join(",")}`,
nil,
true,
},
{
`${join(",", "foo")}`,
"foo",
false,
},
/*
TODO
{ {
`${join(",", "foo", "bar")}`, `${join(",")}`,
"foo,bar", nil,
true,
},
{
`${join(",", "foo")}`,
"foo",
false, false,
}, },
*/
{ /*
fmt.Sprintf(`${join(".", "%s")}`, TODO
fmt.Sprintf( {
"foo%sbar%sbaz", `${join(",", "foo", "bar")}`,
InterpSplitDelim, "foo,bar",
InterpSplitDelim)), false,
"foo.bar.baz", },
false, */
{
fmt.Sprintf(`${join(".", "%s")}`,
fmt.Sprintf(
"foo%sbar%sbaz",
InterpSplitDelim,
InterpSplitDelim)),
"foo.bar.baz",
false,
},
}, },
}) })
} }
/*
func TestInterpolateFuncLookup(t *testing.T) { func TestInterpolateFuncLookup(t *testing.T) {
testFunction(t, []testFunctionCase{ testFunction(t, testFunctionConfig{
cases := []struct { Vars: map[string]string{"var.foo.bar": "baz"},
M map[string]string Cases: []testFunctionCase{
Args []string {
Result string `${lookup("foo", "bar")}`,
Error bool "baz",
}{ false,
{
map[string]string{
"var.foo.bar": "baz",
}, },
[]string{"foo", "bar"},
"baz",
false,
},
// Invalid key // Invalid key
{ {
map[string]string{ `${lookup("foo", "baz")}`,
"var.foo.bar": "baz", nil,
true,
}, },
[]string{"foo", "baz"},
"",
true,
},
// Too many args // Too many args
{ {
map[string]string{ `${lookup("foo", "bar", "baz")}`,
"var.foo.bar": "baz", nil,
true,
}, },
[]string{"foo", "bar", "baz"},
"",
true,
}, },
} })
for i, tc := range cases {
actual, err := interpolationFuncLookup(tc.M, tc.Args...)
if (err != nil) != tc.Error {
t.Fatalf("%d: err: %s", i, err)
}
if actual != tc.Result {
t.Fatalf("%d: bad: %#v", i, actual)
}
}
} }
*/
func TestInterpolateFuncElement(t *testing.T) { func TestInterpolateFuncElement(t *testing.T) {
testFunction(t, []testFunctionCase{ testFunction(t, testFunctionConfig{
{ Cases: []testFunctionCase{
fmt.Sprintf(`${element("%s", "1")}`, {
"foo"+InterpSplitDelim+"baz"), fmt.Sprintf(`${element("%s", "1")}`,
"baz", "foo"+InterpSplitDelim+"baz"),
false, "baz",
}, false,
},
{ {
`${element("foo", "0")}`, `${element("foo", "0")}`,
"foo", "foo",
false, false,
}, },
// Invalid index should wrap vs. out-of-bounds // Invalid index should wrap vs. out-of-bounds
{ {
fmt.Sprintf(`${element("%s", "2")}`, fmt.Sprintf(`${element("%s", "2")}`,
"foo"+InterpSplitDelim+"baz"), "foo"+InterpSplitDelim+"baz"),
"foo", "foo",
false, false,
}, },
// Too many args // Too many args
{ {
fmt.Sprintf(`${element("%s", "0", "2")}`, fmt.Sprintf(`${element("%s", "0", "2")}`,
"foo"+InterpSplitDelim+"baz"), "foo"+InterpSplitDelim+"baz"),
nil, nil,
true, true,
},
}, },
}) })
} }
type testFunctionConfig struct {
Cases []testFunctionCase
Vars map[string]string
}
type testFunctionCase struct { type testFunctionCase struct {
Input string Input string
Result interface{} Result interface{}
Error bool Error bool
} }
func testFunction(t *testing.T, cases []testFunctionCase) { func testFunction(t *testing.T, config testFunctionConfig) {
for i, tc := range cases { for i, tc := range config.Cases {
ast, err := lang.Parse(tc.Input) ast, err := lang.Parse(tc.Input)
if err != nil { if err != nil {
t.Fatalf("%d: err: %s", i, err) t.Fatalf("%d: err: %s", i, err)
} }
engine := langEngine(nil) engine := langEngine(config.Vars)
out, _, err := engine.Execute(ast) out, _, err := engine.Execute(ast)
if (err != nil) != tc.Error { if (err != nil) != tc.Error {
t.Fatalf("%d: err: %s", i, err) t.Fatalf("%d: err: %s", i, err)

View File

@ -208,10 +208,17 @@ func langEngine(vs map[string]string) *lang.Engine {
for k, v := range vs { for k, v := range vs {
varMap[k] = lang.Variable{Value: v, Type: ast.TypeString} varMap[k] = lang.Variable{Value: v, Type: ast.TypeString}
} }
funcMap := make(map[string]lang.Function)
for k, v := range Funcs {
funcMap[k] = v
}
funcMap["lookup"] = interpolationFuncLookup(vs)
return &lang.Engine{ return &lang.Engine{
GlobalScope: &lang.Scope{ GlobalScope: &lang.Scope{
VarMap: varMap, VarMap: varMap,
FuncMap: Funcs, FuncMap: funcMap,
}, },
} }
} }