From d41d58967f6a4abae3190d99cc7e097c25acf356 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sat, 1 Jul 2017 09:23:42 -0700 Subject: [PATCH] config: parsing of local.foo variables for interpolation --- config/interpolate.go | 27 +++++++++++++++++++++++++ config/interpolate_test.go | 41 +++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/config/interpolate.go b/config/interpolate.go index bbb3555418..b18f2d3a11 100644 --- a/config/interpolate.go +++ b/config/interpolate.go @@ -101,6 +101,12 @@ type UserVariable struct { key string } +// A LocalVariable is a variable that references a local value defined within +// the current module, via a "locals" block. This looks like "${local.foo}". +type LocalVariable struct { + Name string +} + func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { if strings.HasPrefix(v, "count.") { return NewCountVariable(v) @@ -112,6 +118,8 @@ func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { return NewTerraformVariable(v) } else if strings.HasPrefix(v, "var.") { return NewUserVariable(v) + } else if strings.HasPrefix(v, "local.") { + return NewLocalVariable(v) } else if strings.HasPrefix(v, "module.") { return NewModuleVariable(v) } else if !strings.ContainsRune(v, '.') { @@ -331,6 +339,25 @@ func (v *UserVariable) GoString() string { return fmt.Sprintf("*%#v", *v) } +func NewLocalVariable(key string) (*LocalVariable, error) { + name := key[len("local."):] + if idx := strings.Index(name, "."); idx > -1 { + return nil, fmt.Errorf("Can't use dot (.) attribute access in local.%s; use square bracket indexing", name) + } + + return &LocalVariable{ + Name: name, + }, nil +} + +func (v *LocalVariable) FullKey() string { + return fmt.Sprintf("local.%s", v.Name) +} + +func (v *LocalVariable) GoString() string { + return fmt.Sprintf("*%#v", *v) +} + // DetectVariables takes an AST root and returns all the interpolated // variables that are detected in the AST tree. func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) { diff --git a/config/interpolate_test.go b/config/interpolate_test.go index 0cdb18b69d..db2acf4d2b 100644 --- a/config/interpolate_test.go +++ b/config/interpolate_test.go @@ -9,10 +9,10 @@ import ( ) func TestNewInterpolatedVariable(t *testing.T) { - cases := []struct { - Input string - Result InterpolatedVariable - Error bool + tests := []struct { + Input string + Want InterpolatedVariable + Error bool }{ { "var.foo", @@ -22,6 +22,18 @@ func TestNewInterpolatedVariable(t *testing.T) { }, false, }, + { + "local.foo", + &LocalVariable{ + Name: "foo", + }, + false, + }, + { + "local.foo.nope", + nil, + true, + }, { "module.foo.bar", &ModuleVariable{ @@ -73,14 +85,19 @@ func TestNewInterpolatedVariable(t *testing.T) { }, } - for i, tc := range cases { - actual, err := NewInterpolatedVariable(tc.Input) - if err != nil != tc.Error { - t.Fatalf("%d. Error: %s", i, err) - } - if !reflect.DeepEqual(actual, tc.Result) { - t.Fatalf("%d bad: %#v", i, actual) - } + for i, test := range tests { + t.Run(test.Input, func(t *testing.T) { + got, err := NewInterpolatedVariable(test.Input) + if err != nil != test.Error { + t.Errorf("%d. Error: %s", i, err) + } + if !test.Error && !reflect.DeepEqual(got, test.Want) { + t.Errorf( + "wrong result\ninput: %s\ngot: %#v\nwant: %#v", + test.Input, got, test.Want, + ) + } + }) } }