Merge pull request #7551 from hashicorp/b-computed-map-values
core: Allow use of computed values in maps
This commit is contained in:
commit
2944e2ace8
|
@ -54,6 +54,9 @@ type interpolationWalkerContextFunc func(reflectwalk.Location, ast.Node)
|
|||
|
||||
func (w *interpolationWalker) Enter(loc reflectwalk.Location) error {
|
||||
w.loc = loc
|
||||
if loc == reflectwalk.WalkLoc {
|
||||
w.sliceIndex = -1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -72,6 +75,7 @@ func (w *interpolationWalker) Exit(loc reflectwalk.Location) error {
|
|||
w.cs = w.cs[:len(w.cs)-1]
|
||||
case reflectwalk.SliceElem:
|
||||
w.csKey = w.csKey[:len(w.csKey)-1]
|
||||
w.sliceIndex = -1
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -85,7 +89,13 @@ func (w *interpolationWalker) Map(m reflect.Value) error {
|
|||
func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error {
|
||||
w.csData = k
|
||||
w.csKey = append(w.csKey, k)
|
||||
w.key = append(w.key, k.String())
|
||||
|
||||
if w.sliceIndex != -1 {
|
||||
w.key = append(w.key, fmt.Sprintf("%d.%s", w.sliceIndex, k.String()))
|
||||
} else {
|
||||
w.key = append(w.key, k.String())
|
||||
}
|
||||
|
||||
w.lastValue = v
|
||||
return nil
|
||||
}
|
||||
|
@ -164,6 +174,7 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error {
|
|||
} else if replaceVal == UnknownVariableValue {
|
||||
remove = true
|
||||
}
|
||||
|
||||
if remove {
|
||||
w.removeCurrent()
|
||||
return nil
|
||||
|
|
|
@ -2325,3 +2325,43 @@ func TestContext2Plan_moduleMapLiteral(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Plan_computedValueInMap(t *testing.T) {
|
||||
m := testModule(t, "plan-computed-value-in-map")
|
||||
p := testProvider("aws")
|
||||
p.DiffFn = func(info *InstanceInfo, state *InstanceState, c *ResourceConfig) (*InstanceDiff, error) {
|
||||
switch info.Type {
|
||||
case "aws_computed_source":
|
||||
return &InstanceDiff{
|
||||
Attributes: map[string]*ResourceAttrDiff{
|
||||
"computed_read_only": &ResourceAttrDiff{
|
||||
NewComputed: true,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return testDiffFn(info, state, c)
|
||||
}
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
if _, err := ctx.Plan(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
plan, err := ctx.Plan()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(plan.String())
|
||||
expected := strings.TrimSpace(testTerraformPlanComputedValueInMap)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad:\n%s\n\nexpected\n\n%s", actual, expected)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
|
@ -143,15 +144,60 @@ func (n *EvalVariableBlock) Eval(ctx EvalContext) (interface{}, error) {
|
|||
|
||||
return nil, fmt.Errorf("Variable value for %s is not a string, list or map type", k)
|
||||
}
|
||||
for k, _ := range rc.Raw {
|
||||
if _, ok := n.VariableValues[k]; !ok {
|
||||
n.VariableValues[k] = config.UnknownVariableValue
|
||||
|
||||
for _, path := range rc.ComputedKeys {
|
||||
log.Printf("[DEBUG] Setting Unknown Variable Value for computed key: %s", path)
|
||||
err := n.setUnknownVariableValueForPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *EvalVariableBlock) setUnknownVariableValueForPath(path string) error {
|
||||
pathComponents := strings.Split(path, ".")
|
||||
|
||||
if len(pathComponents) < 1 {
|
||||
return fmt.Errorf("No path comoponents in %s", path)
|
||||
}
|
||||
|
||||
if len(pathComponents) == 1 {
|
||||
// Special case the "top level" since we know the type
|
||||
if _, ok := n.VariableValues[pathComponents[0]]; !ok {
|
||||
n.VariableValues[pathComponents[0]] = config.UnknownVariableValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise find the correct point in the tree and then set to unknown
|
||||
var current interface{} = n.VariableValues[pathComponents[0]]
|
||||
for i := 1; i < len(pathComponents); i++ {
|
||||
switch current.(type) {
|
||||
case []interface{}, []map[string]interface{}:
|
||||
tCurrent := current.([]interface{})
|
||||
index, err := strconv.Atoi(pathComponents[i])
|
||||
if err != nil {
|
||||
return fmt.Errorf("Cannot convert %s to slice index in path %s",
|
||||
pathComponents[i], path)
|
||||
}
|
||||
current = tCurrent[index]
|
||||
case map[string]interface{}:
|
||||
tCurrent := current.(map[string]interface{})
|
||||
if val, hasVal := tCurrent[pathComponents[i]]; hasVal {
|
||||
current = val
|
||||
continue
|
||||
}
|
||||
|
||||
tCurrent[pathComponents[i]] = config.UnknownVariableValue
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EvalCoerceMapVariable is an EvalNode implementation that recognizes a
|
||||
// specific ambiguous HCL parsing situation and resolves it. In HCL parsing, a
|
||||
// bare map literal is indistinguishable from a list of maps w/ one element.
|
||||
|
|
|
@ -1355,3 +1355,19 @@ aws_instance.foo:
|
|||
ID = bar
|
||||
ami = ami-abcd1234
|
||||
`
|
||||
|
||||
const testTerraformPlanComputedValueInMap = `
|
||||
DIFF:
|
||||
|
||||
CREATE: aws_computed_source.intermediates
|
||||
computed_read_only: "" => "<computed>"
|
||||
|
||||
module.test_mod:
|
||||
CREATE: aws_instance.inner2
|
||||
looked_up: "" => "<computed>"
|
||||
type: "" => "aws_instance"
|
||||
|
||||
STATE:
|
||||
|
||||
<no state>
|
||||
`
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
resource "aws_computed_source" "intermediates" {}
|
||||
|
||||
module "test_mod" {
|
||||
source = "./mod"
|
||||
|
||||
services {
|
||||
"exists" = "true"
|
||||
"elb" = "${aws_computed_source.intermediates.computed_read_only}"
|
||||
}
|
||||
|
||||
services {
|
||||
"otherexists" = " true"
|
||||
"elb" = "${aws_computed_source.intermediates.computed_read_only}"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
variable "services" {
|
||||
type = "list"
|
||||
}
|
||||
|
||||
resource "aws_instance" "inner2" {
|
||||
looked_up = "${lookup(var.services[0], "elb")}"
|
||||
}
|
||||
|
Loading…
Reference in New Issue