diff --git a/config/hcl2shim/values_equiv.go b/config/hcl2shim/values_equiv.go index ab437de80..92f0213d7 100644 --- a/config/hcl2shim/values_equiv.go +++ b/config/hcl2shim/values_equiv.go @@ -60,6 +60,8 @@ func ValuesSDKEquivalent(a, b cty.Value) bool { return valuesSDKEquivalentMappings(a, b) case aTy.IsObjectType() && bTy.IsObjectType(): return valuesSDKEquivalentMappings(a, b) + case aTy == cty.Number && bTy == cty.Number: + return valuesSDKEquivalentNumbers(a, b) default: // We've now covered all the interesting cases, so anything that falls // down here cannot be equivalent. @@ -175,3 +177,38 @@ func valuesSDKEquivalentMappings(a, b cty.Value) bool { } return true } + +// valuesSDKEquivalentNumbers decides equivalence for two number values based +// on the fact that the SDK uses int and float64 representations while +// cty (and thus Terraform Core) uses big.Float, and so we expect to lose +// precision in the round-trip. +// +// This does _not_ attempt to allow for an epsilon difference that may be +// caused by accumulated innacuracy in a float calculation, under the +// expectation that providers generally do not actually do compuations on +// floats and instead just pass string representations of them on verbatim +// to remote APIs. A remote API _itself_ may introduce inaccuracy, but that's +// a problem for the provider itself to deal with, based on its knowledge of +// the remote system, e.g. using DiffSuppressFunc. +func valuesSDKEquivalentNumbers(a, b cty.Value) bool { + if a.RawEquals(b) { + return true // easy + } + + af := a.AsBigFloat() + bf := b.AsBigFloat() + + if af.IsInt() != bf.IsInt() { + return false + } + if af.IsInt() && bf.IsInt() { + return false // a.RawEquals(b) test above is good enough for integers + } + + // The SDK supports only int and float64, so if it's not an integer + // we know that only a float64-level of precision can possibly be + // significant. + af64, _ := af.Float64() + bf64, _ := bf.Float64() + return af64 == bf64 +} diff --git a/config/hcl2shim/values_equiv_test.go b/config/hcl2shim/values_equiv_test.go index 443eb6b7d..aff01a9e3 100644 --- a/config/hcl2shim/values_equiv_test.go +++ b/config/hcl2shim/values_equiv_test.go @@ -2,12 +2,19 @@ package hcl2shim import ( "fmt" + "math/big" "testing" "github.com/zclconf/go-cty/cty" ) func TestValuesSDKEquivalent(t *testing.T) { + piBig, _, err := big.ParseFloat("3.14159265358979323846264338327950288419716939937510582097494459", 10, 512, big.ToZero) + if err != nil { + t.Fatal(err) + } + pi64, _ := piBig.Float64() + tests := []struct { A, B cty.Value Want bool @@ -55,6 +62,21 @@ func TestValuesSDKEquivalent(t *testing.T) { cty.Zero, true, }, + { + cty.NumberVal(piBig), + cty.Zero, + false, + }, + { + cty.NumberFloatVal(pi64), + cty.Zero, + false, + }, + { + cty.NumberFloatVal(pi64), + cty.NumberVal(piBig), + true, + }, // Bools {