From 5be772d85ae82fd52dbba53c6f053204db70f350 Mon Sep 17 00:00:00 2001 From: Alisdair McDiarmid Date: Thu, 27 Aug 2020 20:29:36 -0400 Subject: [PATCH] terraform: Fix createEmptyBlocks NestingSingle bug When calling createEmptyBlocks, we only intend to process legacy SDK blocks (NestingList or NestingSet), but the function also needs to pass through NestingSingle block types untouched. To do so we must only look up the container's element type after we've established that the block is backed by a container. --- terraform/eval_read_data_plan.go | 4 ++-- terraform/eval_read_data_plan_test.go | 30 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/terraform/eval_read_data_plan.go b/terraform/eval_read_data_plan.go index 78b6c564d..f13381855 100644 --- a/terraform/eval_read_data_plan.go +++ b/terraform/eval_read_data_plan.go @@ -239,8 +239,6 @@ func createEmptyBlocks(schema *configschema.Block, val cty.Value) cty.Value { continue } - ety := block.Type().ElementType() - // helper to build the recursive block values nextBlocks := func() []cty.Value { // this is only called once we know this is a non-null List or Set @@ -259,6 +257,7 @@ func createEmptyBlocks(schema *configschema.Block, val cty.Value) cty.Value { // We are only concerned with block types that can come from the legacy // sdk, which means TypeList or TypeSet. case configschema.NestingList: + ety := block.Type().ElementType() switch { case block.IsNull(): objMap[name] = cty.ListValEmpty(ety) @@ -269,6 +268,7 @@ func createEmptyBlocks(schema *configschema.Block, val cty.Value) cty.Value { } case configschema.NestingSet: + ety := block.Type().ElementType() switch { case block.IsNull(): objMap[name] = cty.SetValEmpty(ety) diff --git a/terraform/eval_read_data_plan_test.go b/terraform/eval_read_data_plan_test.go index 13f2da349..212856d4e 100644 --- a/terraform/eval_read_data_plan_test.go +++ b/terraform/eval_read_data_plan_test.go @@ -86,6 +86,22 @@ func TestReadDataCreateEmptyBlocks(t *testing.T) { }, } + singleSchema := &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "single": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "attr": { + Type: cty.String, + Optional: true, + }, + }, + }, + }, + }, + } + for _, tc := range []struct { name string schema *configschema.Block @@ -330,6 +346,20 @@ func TestReadDataCreateEmptyBlocks(t *testing.T) { }), }), }, + { + "single-block-null", + singleSchema, + cty.ObjectVal(map[string]cty.Value{ + "single": cty.NullVal(cty.Object(map[string]cty.Type{ + "attr": cty.String, + })), + }), + cty.ObjectVal(map[string]cty.Value{ + "single": cty.NullVal(cty.Object(map[string]cty.Type{ + "attr": cty.String, + })), + }), + }, } { t.Run(tc.name, func(t *testing.T) { val := createEmptyBlocks(tc.schema, tc.val)