Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP]: Add failing test for missing computed map entries #9634

Merged
merged 12 commits into from
Nov 9, 2016
Merged
4 changes: 4 additions & 0 deletions config/interpolate.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ func (v *ModuleVariable) FullKey() string {
return v.key
}

func (v *ModuleVariable) GoString() string {
return fmt.Sprintf("*%#v", *v)
}

func NewPathVariable(key string) (*PathVariable, error) {
var fieldType PathValueType
parts := strings.SplitN(key, ".", 2)
Expand Down
16 changes: 16 additions & 0 deletions config/interpolate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,22 @@ func TestDetectVariables(t *testing.T) {
},
},
},

{
`foo ${module.foo.output["key"]}`,
[]InterpolatedVariable{
&ModuleVariable{
Name: "foo",
Field: "output",
key: "module.foo.output",
},
&ModuleVariable{
Name: "foo",
Field: "output",
key: "module.foo.output",
},
},
},
}

for _, tc := range cases {
Expand Down
24 changes: 1 addition & 23 deletions config/interpolate_walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,7 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error {
}

if remove {
w.removeCurrent()
return nil
w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, "."))
}

resultVal := reflect.ValueOf(replaceVal)
Expand Down Expand Up @@ -209,27 +208,6 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error {
return nil
}

func (w *interpolationWalker) removeCurrent() {
// Append the key to the unknown keys
w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, "."))

for i := 1; i <= len(w.cs); i++ {
c := w.cs[len(w.cs)-i]
switch c.Kind() {
case reflect.Map:
// Zero value so that we delete the map key
var val reflect.Value

// Get the key and delete it
k := w.csData.(reflect.Value)
c.SetMapIndex(k, val)
return
}
}

panic("No container found for removeCurrent")
}

func (w *interpolationWalker) replaceCurrent(v reflect.Value) {
c := w.cs[len(w.cs)-2]
switch c.Kind() {
Expand Down
10 changes: 8 additions & 2 deletions config/interpolate_walk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,14 @@ func TestInterpolationWalker_replace(t *testing.T) {
"bing",
},
},
Output: map[string]interface{}{},
Value: []interface{}{UnknownVariableValue, "baz"},
Output: map[string]interface{}{
"foo": []interface{}{
UnknownVariableValue,
"baz",
"bing",
},
},
Value: []interface{}{UnknownVariableValue, "baz"},
},
}

Expand Down
21 changes: 0 additions & 21 deletions config/raw_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,27 +127,6 @@ func (r *RawConfig) Interpolate(vs map[string]ast.Variable) error {

config := langEvalConfig(vs)
return r.interpolate(func(root ast.Node) (interface{}, error) {
// We detect the variables again and check if the value of any
// of the variables is the computed value. If it is, then we
// treat this entire value as computed.
//
// We have to do this here before the `lang.Eval` because
// if any of the variables it depends on are computed, then
// the interpolation can fail at runtime for other reasons. Example:
// `${count.index+1}`: in a world where `count.index` is computed,
// this would fail a type check since the computed placeholder is
// a string, but realistically the whole value is just computed.
vars, err := DetectVariables(root)
if err != nil {
return "", err
}
for _, v := range vars {
varVal, ok := vs[v.FullKey()]
if ok && varVal.Value == UnknownVariableValue {
return UnknownVariableValue, nil
}
}

// None of the variables we need are computed, meaning we should
// be able to properly evaluate.
result, err := hil.Eval(root, config)
Expand Down
46 changes: 41 additions & 5 deletions config/raw_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ func TestRawConfig_merge(t *testing.T) {
},
"var.baz": ast.Variable{
Value: UnknownVariableValue,
Type: ast.TypeString,
Type: ast.TypeUnknown,
},
}
if err := rc2.Interpolate(vars); err != nil {
Expand All @@ -216,6 +216,7 @@ func TestRawConfig_merge(t *testing.T) {
expected := map[string]interface{}{
"foo": "foovalue",
"bar": "barvalue",
"baz": UnknownVariableValue,
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
Expand Down Expand Up @@ -250,15 +251,15 @@ func TestRawConfig_unknown(t *testing.T) {
vars := map[string]ast.Variable{
"var.bar": ast.Variable{
Value: UnknownVariableValue,
Type: ast.TypeString,
Type: ast.TypeUnknown,
},
}
if err := rc.Interpolate(vars); err != nil {
t.Fatalf("err: %s", err)
}

actual := rc.Config()
expected := map[string]interface{}{}
expected := map[string]interface{}{"foo": UnknownVariableValue}

if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
Expand All @@ -283,15 +284,50 @@ func TestRawConfig_unknownPartial(t *testing.T) {
vars := map[string]ast.Variable{
"var.bar": ast.Variable{
Value: UnknownVariableValue,
Type: ast.TypeString,
Type: ast.TypeUnknown,
},
}
if err := rc.Interpolate(vars); err != nil {
t.Fatalf("err: %s", err)
}

actual := rc.Config()
expected := map[string]interface{}{"foo": UnknownVariableValue}

if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}

expectedKeys := []string{"foo"}
if !reflect.DeepEqual(rc.UnknownKeys(), expectedKeys) {
t.Fatalf("bad: %#v", rc.UnknownKeys())
}
}

func TestRawConfig_unknownPartialList(t *testing.T) {
raw := map[string]interface{}{
"foo": []interface{}{
"${var.bar}/32",
},
}

rc, err := NewRawConfig(raw)
if err != nil {
t.Fatalf("err: %s", err)
}

vars := map[string]ast.Variable{
"var.bar": ast.Variable{
Value: UnknownVariableValue,
Type: ast.TypeUnknown,
},
}
if err := rc.Interpolate(vars); err != nil {
t.Fatalf("err: %s", err)
}

actual := rc.Config()
expected := map[string]interface{}{}
expected := map[string]interface{}{"foo": []interface{}{UnknownVariableValue}}

if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
Expand Down
3 changes: 2 additions & 1 deletion helper/diff/resource_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package diff
import (
"strings"

"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/flatmap"
"github.com/hashicorp/terraform/terraform"
)
Expand Down Expand Up @@ -94,7 +95,7 @@ func (b *ResourceBuilder) Diff(

// If this key is in the cleaned config, then use that value
// because it'll have its variables properly interpolated
if cleanV, ok := flatConfig[k]; ok {
if cleanV, ok := flatConfig[k]; ok && cleanV != config.UnknownVariableValue {
v = cleanV
originalV = v

Expand Down
4 changes: 2 additions & 2 deletions helper/schema/field_reader_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ func TestConfigFieldReader_ComputedSet(t *testing.T) {
}, map[string]ast.Variable{
"var.foo": ast.Variable{
Value: config.UnknownVariableValue,
Type: ast.TypeString,
Type: ast.TypeUnknown,
},
}),
false,
Expand All @@ -362,7 +362,7 @@ func TestConfigFieldReader_ComputedSet(t *testing.T) {
}, map[string]ast.Variable{
"var.foo": ast.Variable{
Value: config.UnknownVariableValue,
Type: ast.TypeString,
Type: ast.TypeUnknown,
},
}),
false,
Expand Down
Loading