Improve stability on 32-bit architectures (#1154)

Signed-off-by: James Humphries <james@james-humphries.co.uk>
This commit is contained in:
James Humphries 2024-01-23 15:17:53 +00:00 committed by GitHub
parent 444b059d28
commit 92a055b60f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 70 additions and 69 deletions

View File

@ -25,6 +25,7 @@ BUG FIXES:
* Don't check for version conflicts when doing a force-unlock ([#1123](https://github.com/opentofu/opentofu/pull/1123))
* Fix Global Schema Cache not working in provider acceptance tests ([#1054](https://github.com/opentofu/opentofu/pull/1054))
* Fix `tofu show` and `tofu state show` not working with state files referencing Terraform registry providers in some instances ([#1141](https://github.com/opentofu/opentofu/pull/1141))
* Improved stability on 32-bit architectures ([#1154](https://github.com/opentofu/opentofu/pull/1154))
## Previous Releases

View File

@ -10,19 +10,26 @@ import (
)
// String hashes a string to a unique hashcode.
//
// crc32 returns a uint32, but for our use we need
// a non negative integer. Here we cast to an integer
// and invert it if the result is negative.
// Returns a non-negative integer representing the hashcode of the string.
func String(s string) int {
v := int(crc32.ChecksumIEEE([]byte(s)))
if v >= 0 {
return v
// crc32 returns an uint32, so we need to massage it into an int.
crc := crc32.ChecksumIEEE([]byte(s))
// We need to first squash the result to 32 bits, embracing the overflow
// to ensure that there is no difference between 32 and 64-bit
// platforms.
squashed := int32(crc)
// convert into a generic int that is sized as per the architecture
systemSized := int(squashed)
// If the integer is negative, we return the absolute value of the
// integer. This is because we want to return a non-negative integer
if systemSized >= 0 {
return systemSized
}
if -v >= 0 {
return -v
if -systemSized >= 0 {
return -systemSized
}
// v == MinInt
// systemSized == MinInt
return 0
}

View File

@ -410,7 +410,7 @@ func TestConfigFieldReader_ComputedSet(t *testing.T) {
[]string{"strSet"},
FieldReadResult{
Value: map[string]interface{}{
"2356372769": "foo",
"1938594527": "foo",
},
Exists: true,
Computed: false,

View File

@ -5,7 +5,6 @@ package schema
import (
"fmt"
"math"
"os"
"reflect"
"testing"
@ -1587,6 +1586,7 @@ func TestResourceDataSet(t *testing.T) {
var testNilPtr *string
cases := []struct {
TestName string
Schema map[string]*Schema
State *tofu.InstanceState
Diff *tofu.InstanceDiff
@ -1600,8 +1600,8 @@ func TestResourceDataSet(t *testing.T) {
// compared to GetValue
GetPreProcess func(interface{}) interface{}
}{
// #0: Basic good
{
TestName: "Basic good",
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
@ -1621,9 +1621,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone",
GetValue: "foo",
},
// #1: Basic int
{
TestName: "Basic int",
Schema: map[string]*Schema{
"port": &Schema{
Type: TypeInt,
@ -1643,9 +1642,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "port",
GetValue: 80,
},
// #2: Basic bool
{
TestName: "Basic bool, true",
Schema: map[string]*Schema{
"vpc": &Schema{
Type: TypeBool,
@ -1663,9 +1661,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "vpc",
GetValue: true,
},
// #3
{
TestName: "Basic bool, false",
Schema: map[string]*Schema{
"vpc": &Schema{
Type: TypeBool,
@ -1683,9 +1680,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "vpc",
GetValue: false,
},
// #4: Invalid type
{
TestName: "Invalid type",
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
@ -1706,9 +1702,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone",
GetValue: "",
},
// #5: List of primitives, set list
{
TestName: "List of primitives, set list",
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeList,
@ -1727,9 +1722,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ports",
GetValue: []interface{}{1, 2, 5},
},
// #6: List of primitives, set list with error
{
TestName: "List of primitives, set list with error",
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeList,
@ -1749,9 +1743,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ports",
GetValue: []interface{}{},
},
// #7: Set a list of maps
{
TestName: "Set a list of maps",
Schema: map[string]*Schema{
"config_vars": &Schema{
Type: TypeList,
@ -1788,9 +1781,8 @@ func TestResourceDataSet(t *testing.T) {
},
},
},
// #8: Set, with list
{
TestName: "Set, with list",
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeSet,
@ -1818,9 +1810,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ports",
GetValue: []interface{}{100, 125},
},
// #9: Set, with Set
{
TestName: " Set, with Set",
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeSet,
@ -1853,9 +1844,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ports",
GetValue: []interface{}{1, 2},
},
// #10: Set single item
{
TestName: "Set single item",
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeSet,
@ -1883,9 +1873,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ports",
GetValue: []interface{}{100, 80},
},
// #11: Set with nested set
{
TestName: "Set with nested set",
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeSet,
@ -1951,9 +1940,8 @@ func TestResourceDataSet(t *testing.T) {
return v
},
},
// #12: List of floats, set list
{
TestName: "List of floats, set list",
Schema: map[string]*Schema{
"ratios": &Schema{
Type: TypeList,
@ -1972,16 +1960,20 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ratios",
GetValue: []interface{}{1.0, 2.2, 5.5},
},
// #12: Set of floats, set list
{
TestName: "Set of floats, set list",
Schema: map[string]*Schema{
"ratios": &Schema{
Type: TypeSet,
Computed: true,
Elem: &Schema{Type: TypeFloat},
Set: func(a interface{}) int {
return int(math.Float64bits(a.(float64)))
// Because we want to be safe on a 32-bit and 64-bit system,
// we can just set a "scale factor" here that's always larger than the number of
// decimal places we expect to see., and then multiply by that to cast to int
// otherwise we could get clashes in unique ids
scaleFactor := 100000
return int(a.(float64) * float64(scaleFactor))
},
},
},
@ -1996,9 +1988,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ratios",
GetValue: []interface{}{1.0, 2.2, 5.5},
},
// #13: Basic pointer
{
TestName: "Basic pointer",
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
@ -2018,9 +2009,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone",
GetValue: "foo",
},
// #14: Basic nil value
{
TestName: "Basic nil value",
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
@ -2040,9 +2030,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone",
GetValue: "",
},
// #15: Basic nil pointer
{
TestName: "Basic nil pointer",
Schema: map[string]*Schema{
"availability_zone": &Schema{
Type: TypeString,
@ -2062,9 +2051,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone",
GetValue: "",
},
// #16: Set in a list
{
TestName: "Set in a list",
Schema: map[string]*Schema{
"ports": &Schema{
Type: TypeList,
@ -2131,29 +2119,31 @@ func TestResourceDataSet(t *testing.T) {
os.Setenv(PanicOnErr, "")
defer os.Setenv(PanicOnErr, oldEnv)
for i, tc := range cases {
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
if err != nil {
t.Fatalf("err: %s", err)
}
for _, tc := range cases {
t.Run(tc.TestName, func(t *testing.T) {
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
if err != nil {
t.Fatalf("err: %s", err)
}
err = d.Set(tc.Key, tc.Value)
if err != nil != tc.Err {
t.Fatalf("%d err: %s", i, err)
}
err = d.Set(tc.Key, tc.Value)
if err != nil != tc.Err {
t.Fatalf("unexpected err: %s", err)
}
v := d.Get(tc.GetKey)
if s, ok := v.(*Set); ok {
v = s.List()
}
v := d.Get(tc.GetKey)
if s, ok := v.(*Set); ok {
v = s.List()
}
if tc.GetPreProcess != nil {
v = tc.GetPreProcess(v)
}
if tc.GetPreProcess != nil {
v = tc.GetPreProcess(v)
}
if !reflect.DeepEqual(v, tc.GetValue) {
t.Fatalf("Get Bad: %d\n\n%#v", i, v)
}
if !reflect.DeepEqual(v, tc.GetValue) {
t.Fatalf("Got unexpected value\nactual: %#v\nexpected:%#v", v, tc.GetValue)
}
})
}
}

View File

@ -129,7 +129,7 @@ func testDiffCases(t *testing.T, oldPrefix string, oldOffset int, computed bool)
NewComputed: true,
}
} else {
result["foo.2800005064"] = &tofu.ResourceAttrDiff{
result["foo.1494962232"] = &tofu.ResourceAttrDiff{
Old: "",
New: "qux",
}

View File

@ -100,7 +100,10 @@ func upgradeAttributesV2ToV3(instanceState *InstanceState) error {
// First, detect "obvious" maps - which have non-numeric keys (mostly).
hasNonNumericKeys := false
for _, key := range actualKeysMatching {
if _, err := strconv.Atoi(key); err != nil {
// Ensure that we attempt to parse the key using 64 bits, this is because the state
// could've been generated on a 64-bit system, and we need to be able to
// convert this on both a 32-bit and 64-bit arch.
if _, err := strconv.ParseInt(key, 10, 64); err != nil {
hasNonNumericKeys = true
}
}