From 48c8afaa11bc77f4f7965a92d513631b0019bc61 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 7 Oct 2016 15:00:50 -0400 Subject: [PATCH] Check for multi-values maps in output too A map value read from a config file will be the default `[]map[string]interface{}` type decoded from HCL. Since this type can't be applied to a variable, it's likely that it was a simple map. If there's a single map value, we can pull that out of the slice during Eval. --- terraform/eval_output.go | 13 ++++++++ terraform/eval_output_test.go | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 terraform/eval_output_test.go diff --git a/terraform/eval_output.go b/terraform/eval_output.go index bee4f1084..cf61781e5 100644 --- a/terraform/eval_output.go +++ b/terraform/eval_output.go @@ -98,6 +98,19 @@ func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) { Sensitive: n.Sensitive, Value: valueTyped, } + case []map[string]interface{}: + // an HCL map is multi-valued, so if this was read out of a config the + // map may still be in a slice. + if len(valueTyped) == 1 { + mod.Outputs[n.Name] = &OutputState{ + Type: "map", + Sensitive: n.Sensitive, + Value: valueTyped[0], + } + break + } + return nil, fmt.Errorf("output %s type (%T) with %d values not valid for type map", + n.Name, valueTyped, len(valueTyped)) default: return nil, fmt.Errorf("output %s is not a valid type (%T)\n", n.Name, valueTyped) } diff --git a/terraform/eval_output_test.go b/terraform/eval_output_test.go new file mode 100644 index 000000000..f73b127de --- /dev/null +++ b/terraform/eval_output_test.go @@ -0,0 +1,56 @@ +package terraform + +import ( + "sync" + "testing" +) + +func TestEvalWriteMapOutput(t *testing.T) { + ctx := new(MockEvalContext) + ctx.StateState = NewState() + ctx.StateLock = new(sync.RWMutex) + + cases := []struct { + name string + cfg *ResourceConfig + err bool + }{ + { + // Eval should recognize a single map in a slice, and collapse it + // into the map value + "single-map", + &ResourceConfig{ + Config: map[string]interface{}{ + "value": []map[string]interface{}{ + map[string]interface{}{"a": "b"}, + }, + }, + }, + false, + }, + { + // we can't apply a multi-valued map to a variable, so this should error + "multi-map", + &ResourceConfig{ + Config: map[string]interface{}{ + "value": []map[string]interface{}{ + map[string]interface{}{"a": "b"}, + map[string]interface{}{"c": "d"}, + }, + }, + }, + true, + }, + } + + for _, tc := range cases { + evalNode := &EvalWriteOutput{Name: tc.name} + ctx.InterpolateConfigResult = tc.cfg + t.Run(tc.name, func(t *testing.T) { + _, err := evalNode.Eval(ctx) + if err != nil && !tc.err { + t.Fatal(err) + } + }) + } +}