config: if any var is computed, the entire interpolation is computed
This commit is contained in:
parent
2feaebdca5
commit
5e25dc54a7
|
@ -152,12 +152,12 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error {
|
|||
if w.loc == reflectwalk.SliceElem {
|
||||
parts := strings.Split(replaceVal, InterpSplitDelim)
|
||||
for _, p := range parts {
|
||||
if strings.Contains(p, UnknownVariableValue) {
|
||||
if p == UnknownVariableValue {
|
||||
remove = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if strings.Contains(replaceVal, UnknownVariableValue) {
|
||||
} else if replaceVal == UnknownVariableValue {
|
||||
remove = true
|
||||
}
|
||||
if remove {
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/config/lang"
|
||||
"github.com/hashicorp/terraform/config/lang/ast"
|
||||
"github.com/mitchellh/reflectwalk"
|
||||
)
|
||||
|
@ -125,7 +124,7 @@ func TestInterpolationWalker_replace(t *testing.T) {
|
|||
"foo": "hello, ${var.foo}",
|
||||
},
|
||||
Output: map[string]interface{}{
|
||||
"foo": "hello, bar",
|
||||
"foo": "bar",
|
||||
},
|
||||
Value: "bar",
|
||||
},
|
||||
|
@ -171,38 +170,11 @@ func TestInterpolationWalker_replace(t *testing.T) {
|
|||
Output: map[string]interface{}{},
|
||||
Value: UnknownVariableValue + InterpSplitDelim + "baz",
|
||||
},
|
||||
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"foo": []interface{}{
|
||||
"${var.foo}/32",
|
||||
"bing",
|
||||
},
|
||||
},
|
||||
Output: map[string]interface{}{},
|
||||
Value: UnknownVariableValue,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
config := &lang.EvalConfig{
|
||||
GlobalScope: &ast.BasicScope{
|
||||
VarMap: map[string]ast.Variable{
|
||||
"var.foo": ast.Variable{
|
||||
Value: tc.Value,
|
||||
Type: ast.TypeString,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
fn := func(root ast.Node) (string, error) {
|
||||
value, _, err := lang.Eval(root, config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return value.(string), nil
|
||||
fn := func(ast.Node) (string, error) {
|
||||
return tc.Value, nil
|
||||
}
|
||||
|
||||
w := &interpolationWalker{F: fn, Replace: true}
|
||||
|
|
|
@ -83,6 +83,29 @@ func (r *RawConfig) Config() map[string]interface{} {
|
|||
func (r *RawConfig) Interpolate(vs map[string]ast.Variable) error {
|
||||
config := langEvalConfig(vs)
|
||||
return r.interpolate(func(root ast.Node) (string, 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.
|
||||
out, _, err := lang.Eval(root, config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -238,6 +238,39 @@ func TestRawConfig_unknown(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRawConfig_unknownPartial(t *testing.T) {
|
||||
raw := map[string]interface{}{
|
||||
"foo": "${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.TypeString,
|
||||
},
|
||||
}
|
||||
if err := rc.Interpolate(vars); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
actual := rc.Config()
|
||||
expected := map[string]interface{}{}
|
||||
|
||||
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 TestRawConfigValue(t *testing.T) {
|
||||
raw := map[string]interface{}{
|
||||
"foo": "${var.bar}",
|
||||
|
|
Loading…
Reference in New Issue