diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index c67e3b733c..8f19d3b95f 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -15,9 +15,8 @@ var Funcs map[string]lang.Function func init() { Funcs = map[string]lang.Function{ - "file": interpolationFuncFile(), - "join": interpolationFuncJoin(), - //"lookup": interpolationFuncLookup(), + "file": interpolationFuncFile(), + "join": interpolationFuncJoin(), "element": interpolationFuncElement(), } } @@ -59,22 +58,22 @@ func interpolationFuncJoin() lang.Function { // interpolationFuncLookup implements the "lookup" function that allows // dynamic lookups of map types within a Terraform configuration. -func interpolationFuncLookup( - vs map[string]string, args ...string) (string, error) { - if len(args) != 2 { - return "", fmt.Errorf( - "lookup expects 2 arguments, got %d", len(args)) - } +func interpolationFuncLookup(vs map[string]string) lang.Function { + return lang.Function{ + ArgTypes: []ast.Type{ast.TypeString, ast.TypeString}, + ReturnType: ast.TypeString, + 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, ".")) - v, ok := vs[k] - if !ok { - return "", fmt.Errorf( - "lookup in '%s' failed to find '%s'", - args[0], args[1]) + return v, nil + }, } - - return v, nil } // interpolationFuncElement implements the "element" function that allows diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index 3a9a86fe14..9ea2ac1cb7 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -20,163 +20,149 @@ func TestInterpolateFuncFile(t *testing.T) { tf.Close() defer os.Remove(path) - testFunction(t, []testFunctionCase{ - { - fmt.Sprintf(`${file("%s")}`, path), - "foo", - false, - }, + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ + { + fmt.Sprintf(`${file("%s")}`, path), + "foo", + false, + }, - // Invalid path - { - `${file("/i/dont/exist")}`, - nil, - true, - }, + // Invalid path + { + `${file("/i/dont/exist")}`, + nil, + true, + }, - // Too many args - { - `${file("foo", "bar")}`, - nil, - true, + // Too many args + { + `${file("foo", "bar")}`, + nil, + true, + }, }, }) } func TestInterpolateFuncJoin(t *testing.T) { - testFunction(t, []testFunctionCase{ - { - `${join(",")}`, - nil, - true, - }, - - { - `${join(",", "foo")}`, - "foo", - false, - }, - - /* - TODO + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ { - `${join(",", "foo", "bar")}`, - "foo,bar", + `${join(",")}`, + nil, + true, + }, + + { + `${join(",", "foo")}`, + "foo", false, }, - */ - { - fmt.Sprintf(`${join(".", "%s")}`, - fmt.Sprintf( - "foo%sbar%sbaz", - InterpSplitDelim, - InterpSplitDelim)), - "foo.bar.baz", - false, + /* + TODO + { + `${join(",", "foo", "bar")}`, + "foo,bar", + false, + }, + */ + + { + fmt.Sprintf(`${join(".", "%s")}`, + fmt.Sprintf( + "foo%sbar%sbaz", + InterpSplitDelim, + InterpSplitDelim)), + "foo.bar.baz", + false, + }, }, }) } -/* func TestInterpolateFuncLookup(t *testing.T) { - testFunction(t, []testFunctionCase{ - cases := []struct { - M map[string]string - Args []string - Result string - Error bool - }{ - { - map[string]string{ - "var.foo.bar": "baz", + testFunction(t, testFunctionConfig{ + Vars: map[string]string{"var.foo.bar": "baz"}, + Cases: []testFunctionCase{ + { + `${lookup("foo", "bar")}`, + "baz", + false, }, - []string{"foo", "bar"}, - "baz", - false, - }, - // Invalid key - { - map[string]string{ - "var.foo.bar": "baz", + // Invalid key + { + `${lookup("foo", "baz")}`, + nil, + true, }, - []string{"foo", "baz"}, - "", - true, - }, - // Too many args - { - map[string]string{ - "var.foo.bar": "baz", + // Too many args + { + `${lookup("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) { - testFunction(t, []testFunctionCase{ - { - fmt.Sprintf(`${element("%s", "1")}`, - "foo"+InterpSplitDelim+"baz"), - "baz", - false, - }, + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ + { + fmt.Sprintf(`${element("%s", "1")}`, + "foo"+InterpSplitDelim+"baz"), + "baz", + false, + }, - { - `${element("foo", "0")}`, - "foo", - false, - }, + { + `${element("foo", "0")}`, + "foo", + false, + }, - // Invalid index should wrap vs. out-of-bounds - { - fmt.Sprintf(`${element("%s", "2")}`, - "foo"+InterpSplitDelim+"baz"), - "foo", - false, - }, + // Invalid index should wrap vs. out-of-bounds + { + fmt.Sprintf(`${element("%s", "2")}`, + "foo"+InterpSplitDelim+"baz"), + "foo", + false, + }, - // Too many args - { - fmt.Sprintf(`${element("%s", "0", "2")}`, - "foo"+InterpSplitDelim+"baz"), - nil, - true, + // Too many args + { + fmt.Sprintf(`${element("%s", "0", "2")}`, + "foo"+InterpSplitDelim+"baz"), + nil, + true, + }, }, }) } +type testFunctionConfig struct { + Cases []testFunctionCase + Vars map[string]string +} + type testFunctionCase struct { Input string Result interface{} Error bool } -func testFunction(t *testing.T, cases []testFunctionCase) { - for i, tc := range cases { +func testFunction(t *testing.T, config testFunctionConfig) { + for i, tc := range config.Cases { ast, err := lang.Parse(tc.Input) if err != nil { t.Fatalf("%d: err: %s", i, err) } - engine := langEngine(nil) + engine := langEngine(config.Vars) out, _, err := engine.Execute(ast) if (err != nil) != tc.Error { t.Fatalf("%d: err: %s", i, err) diff --git a/config/raw_config.go b/config/raw_config.go index 37867c638a..0e19391849 100644 --- a/config/raw_config.go +++ b/config/raw_config.go @@ -208,10 +208,17 @@ func langEngine(vs map[string]string) *lang.Engine { for k, v := range vs { 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{ GlobalScope: &lang.Scope{ VarMap: varMap, - FuncMap: Funcs, + FuncMap: funcMap, }, } }