From b6fff854a6e6dac2ebc99438acc05f052d9743cc Mon Sep 17 00:00:00 2001 From: James Nugent Date: Fri, 8 Jul 2016 10:11:25 +0100 Subject: [PATCH] core: Set all unknown keys to UnknownVariableValue As part of evaluating a variable block, there is a pass made on unknown keys setting them to the config.DefaultVariableValue sentinal value. Previously this only took into account one level of nesting and assumed all values were strings. This commit now traverses the unknown keys via lists and maps and sets unknown map keys surgically. Fixes #7241. --- terraform/eval_variable.go | 52 +++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/terraform/eval_variable.go b/terraform/eval_variable.go index ce6064706..47bd2ea2b 100644 --- a/terraform/eval_variable.go +++ b/terraform/eval_variable.go @@ -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.