From 87fe6cbecd8a49ec76aafa2f62a6b39dfbc66047 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sat, 16 Mar 2019 17:49:16 -0700 Subject: [PATCH] plans/objchange: Don't panic when prior state contains nested map blocks We were using the wrong cty operation to access map members, causing a panic whenever a prior value was present for a resource type with a nested block backed by a map value. --- plans/objchange/objchange.go | 2 +- plans/objchange/plan_valid.go | 2 +- plans/objchange/plan_valid_test.go | 39 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/plans/objchange/objchange.go b/plans/objchange/objchange.go index ed0f4ba5ee..18a8828467 100644 --- a/plans/objchange/objchange.go +++ b/plans/objchange/objchange.go @@ -177,7 +177,7 @@ func proposedNewObject(schema *configschema.Block, prior, config cty.Value) cty. atys := configV.Type().AttributeTypes() for name := range atys { configEV := configV.GetAttr(name) - if !priorV.Type().HasAttribute(name) { + if !priorV.IsKnown() || priorV.IsNull() || !priorV.Type().HasAttribute(name) { // If there is no corresponding prior element then // we just take the config value as-is. newVals[name] = configEV diff --git a/plans/objchange/plan_valid.go b/plans/objchange/plan_valid.go index 308c47f59d..dd7d7ce217 100644 --- a/plans/objchange/plan_valid.go +++ b/plans/objchange/plan_valid.go @@ -182,7 +182,7 @@ func assertPlanValid(schema *configschema.Block, priorState, config, plannedStat configEV := configV.Index(idx) priorEV := cty.NullVal(blockS.ImpliedType()) if !priorV.IsNull() && priorV.HasIndex(idx).True() { - priorEV = priorV.GetAttr(k) + priorEV = priorV.Index(idx) } moreErrs := assertPlanValid(&blockS.Block, priorEV, configEV, plannedEV, path) errs = append(errs, moreErrs...) diff --git a/plans/objchange/plan_valid_test.go b/plans/objchange/plan_valid_test.go index ff01881b5a..cc41d694a3 100644 --- a/plans/objchange/plan_valid_test.go +++ b/plans/objchange/plan_valid_test.go @@ -312,6 +312,45 @@ func TestAssertPlanValid(t *testing.T) { }), nil, }, + "nested map, normal update": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "b": { + Nesting: configschema.NestingMap, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "c": { + Type: cty.String, + Optional: true, + }, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "b": cty.MapVal(map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "c": cty.StringVal("hello"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.MapVal(map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "c": cty.StringVal("howdy"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.MapVal(map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "c": cty.StringVal("howdy"), + }), + }), + }), + nil, + }, // Nested block collections are never null "nested list, null in plan": {