diff --git a/command/format/diff.go b/command/format/diff.go index 260c031de..c58661663 100644 --- a/command/format/diff.go +++ b/command/format/diff.go @@ -856,7 +856,7 @@ func ctyGetAttrMaybeNull(val cty.Value, name string) cty.Value { } func ctyCollectionValues(val cty.Value) []cty.Value { - if !val.IsKnown() { + if !val.IsKnown() || val.IsNull() { return nil } diff --git a/helper/schema/resource.go b/helper/schema/resource.go index 7d5a44aa7..a26dfc9f8 100644 --- a/helper/schema/resource.go +++ b/helper/schema/resource.go @@ -320,6 +320,11 @@ func (r *Resource) simpleDiff( return instanceDiff, err } + if instanceDiff == nil { + log.Printf("[DEBUG] Instance Diff is nil in SimpleDiff()") + return nil, err + } + // Make sure the old value is set in each of the instance diffs. // This was done by the RequiresNew logic in the full legacy Diff. for k, attr := range instanceDiff.Attributes { @@ -331,14 +336,9 @@ func (r *Resource) simpleDiff( } } - if instanceDiff != nil { - if err := t.DiffEncode(instanceDiff); err != nil { - log.Printf("[ERR] Error encoding timeout to instance diff: %s", err) - } - } else { - log.Printf("[DEBUG] Instance Diff is nil in Diff()") + if err := t.DiffEncode(instanceDiff); err != nil { + log.Printf("[ERR] Error encoding timeout to instance diff: %s", err) } - return instanceDiff, err } diff --git a/plans/objchange/compatible.go b/plans/objchange/compatible.go index 83b717554..8ac80ab05 100644 --- a/plans/objchange/compatible.go +++ b/plans/objchange/compatible.go @@ -64,7 +64,7 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu return errs } case configschema.NestingList, configschema.NestingMap, configschema.NestingSet: - if plannedV.IsKnown() && plannedV.LengthInt() == 1 { + if plannedV.IsKnown() && !plannedV.IsNull() && plannedV.LengthInt() == 1 { elemVs := plannedV.AsValueSlice() if allLeafValuesUnknown(elemVs[0]) { return errs @@ -84,7 +84,7 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu // whether there are dynamically-typed attributes inside. However, // both support a similar-enough API that we can treat them the // same for our purposes here. - if !plannedV.IsKnown() { + if !plannedV.IsKnown() || plannedV.IsNull() || actualV.IsNull() { continue } @@ -129,7 +129,7 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu } } } else { - if !plannedV.IsKnown() { + if !plannedV.IsKnown() || plannedV.IsNull() || actualV.IsNull() { continue } plannedL := plannedV.LengthInt() @@ -153,7 +153,7 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu // content is also their key, and so we have no way to correlate // them. Because of this, we simply verify that we still have the // same number of elements. - if !plannedV.IsKnown() { + if !plannedV.IsKnown() || plannedV.IsNull() || actualV.IsNull() { continue } plannedL := plannedV.LengthInt() @@ -251,7 +251,7 @@ func assertValueCompatible(planned, actual cty.Value, path cty.Path) []error { // so we can't correlate them properly. However, we will at least check // to ensure that the number of elements is consistent, along with // the general type-match checks we ran earlier in this function. - if planned.IsKnown() { + if planned.IsKnown() && !planned.IsNull() && !actual.IsNull() { plannedL := planned.LengthInt() actualL := actual.LengthInt() if plannedL < actualL { diff --git a/plans/objchange/objchange.go b/plans/objchange/objchange.go index 5e68a780e..33e82dd52 100644 --- a/plans/objchange/objchange.go +++ b/plans/objchange/objchange.go @@ -92,8 +92,12 @@ func ProposedNewObject(schema *configschema.Block, prior, config cty.Value) cty. case configschema.NestingList: // Nested blocks are correlated by index. - if l := configV.LengthInt(); l > 0 { - newVals := make([]cty.Value, 0, l) + configVLen := 0 + if configV.IsKnown() && !configV.IsNull() { + configVLen = configV.LengthInt() + } + if configVLen > 0 { + newVals := make([]cty.Value, 0, configVLen) for it := configV.ElementIterator(); it.Next(); { idx, configEV := it.Element() if priorV.IsKnown() && (priorV.IsNull() || !priorV.HasIndex(idx).True()) { @@ -130,8 +134,12 @@ func ProposedNewObject(schema *configschema.Block, prior, config cty.Value) cty. // dynamically-typed attributes. if configV.Type().IsObjectType() { // Nested blocks are correlated by key. - if l := configV.LengthInt(); l > 0 { - newVals := make(map[string]cty.Value, l) + configVLen := 0 + if configV.IsKnown() && !configV.IsNull() { + configVLen = configV.LengthInt() + } + if configVLen > 0 { + newVals := make(map[string]cty.Value, configVLen) atys := configV.Type().AttributeTypes() for name := range atys { configEV := configV.GetAttr(name) @@ -154,8 +162,12 @@ func ProposedNewObject(schema *configschema.Block, prior, config cty.Value) cty. newV = cty.EmptyObjectVal } } else { - if l := configV.LengthInt(); l > 0 { - newVals := make(map[string]cty.Value, l) + configVLen := 0 + if configV.IsKnown() && !configV.IsNull() { + configVLen = configV.LengthInt() + } + if configVLen > 0 { + newVals := make(map[string]cty.Value, configVLen) for it := configV.ElementIterator(); it.Next(); { idx, configEV := it.Element() k := idx.AsString() @@ -190,9 +202,13 @@ func ProposedNewObject(schema *configschema.Block, prior, config cty.Value) cty. if priorV.IsKnown() && !priorV.IsNull() { cmpVals = setElementCompareValues(&blockType.Block, priorV, false) } - if l := configV.LengthInt(); l > 0 { + configVLen := 0 + if configV.IsKnown() && !configV.IsNull() { + configVLen = configV.LengthInt() + } + if configVLen > 0 { used := make([]bool, len(cmpVals)) // track used elements in case multiple have the same compare value - newVals := make([]cty.Value, 0, l) + newVals := make([]cty.Value, 0, configVLen) for it := configV.ElementIterator(); it.Next(); { _, configEV := it.Element() var priorEV cty.Value