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)) * 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 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)) * 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 ## Previous Releases

View File

@ -10,19 +10,26 @@ import (
) )
// String hashes a string to a unique hashcode. // String hashes a string to a unique hashcode.
// // Returns a non-negative integer representing the hashcode of the string.
// 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.
func String(s string) int { func String(s string) int {
v := int(crc32.ChecksumIEEE([]byte(s))) // crc32 returns an uint32, so we need to massage it into an int.
if v >= 0 { crc := crc32.ChecksumIEEE([]byte(s))
return v // 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 { if -systemSized >= 0 {
return -v return -systemSized
} }
// v == MinInt // systemSized == MinInt
return 0 return 0
} }

View File

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

View File

@ -5,7 +5,6 @@ package schema
import ( import (
"fmt" "fmt"
"math"
"os" "os"
"reflect" "reflect"
"testing" "testing"
@ -1587,6 +1586,7 @@ func TestResourceDataSet(t *testing.T) {
var testNilPtr *string var testNilPtr *string
cases := []struct { cases := []struct {
TestName string
Schema map[string]*Schema Schema map[string]*Schema
State *tofu.InstanceState State *tofu.InstanceState
Diff *tofu.InstanceDiff Diff *tofu.InstanceDiff
@ -1600,8 +1600,8 @@ func TestResourceDataSet(t *testing.T) {
// compared to GetValue // compared to GetValue
GetPreProcess func(interface{}) interface{} GetPreProcess func(interface{}) interface{}
}{ }{
// #0: Basic good
{ {
TestName: "Basic good",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
Type: TypeString, Type: TypeString,
@ -1621,9 +1621,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone", GetKey: "availability_zone",
GetValue: "foo", GetValue: "foo",
}, },
// #1: Basic int
{ {
TestName: "Basic int",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"port": &Schema{ "port": &Schema{
Type: TypeInt, Type: TypeInt,
@ -1643,9 +1642,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "port", GetKey: "port",
GetValue: 80, GetValue: 80,
}, },
// #2: Basic bool
{ {
TestName: "Basic bool, true",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"vpc": &Schema{ "vpc": &Schema{
Type: TypeBool, Type: TypeBool,
@ -1663,9 +1661,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "vpc", GetKey: "vpc",
GetValue: true, GetValue: true,
}, },
// #3
{ {
TestName: "Basic bool, false",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"vpc": &Schema{ "vpc": &Schema{
Type: TypeBool, Type: TypeBool,
@ -1683,9 +1680,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "vpc", GetKey: "vpc",
GetValue: false, GetValue: false,
}, },
// #4: Invalid type
{ {
TestName: "Invalid type",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
Type: TypeString, Type: TypeString,
@ -1706,9 +1702,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone", GetKey: "availability_zone",
GetValue: "", GetValue: "",
}, },
// #5: List of primitives, set list
{ {
TestName: "List of primitives, set list",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
Type: TypeList, Type: TypeList,
@ -1727,9 +1722,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ports", GetKey: "ports",
GetValue: []interface{}{1, 2, 5}, 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{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
Type: TypeList, Type: TypeList,
@ -1749,9 +1743,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ports", GetKey: "ports",
GetValue: []interface{}{}, GetValue: []interface{}{},
}, },
// #7: Set a list of maps
{ {
TestName: "Set a list of maps",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"config_vars": &Schema{ "config_vars": &Schema{
Type: TypeList, Type: TypeList,
@ -1788,9 +1781,8 @@ func TestResourceDataSet(t *testing.T) {
}, },
}, },
}, },
// #8: Set, with list
{ {
TestName: "Set, with list",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
Type: TypeSet, Type: TypeSet,
@ -1818,9 +1810,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ports", GetKey: "ports",
GetValue: []interface{}{100, 125}, GetValue: []interface{}{100, 125},
}, },
// #9: Set, with Set
{ {
TestName: " Set, with Set",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
Type: TypeSet, Type: TypeSet,
@ -1853,9 +1844,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ports", GetKey: "ports",
GetValue: []interface{}{1, 2}, GetValue: []interface{}{1, 2},
}, },
// #10: Set single item
{ {
TestName: "Set single item",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
Type: TypeSet, Type: TypeSet,
@ -1883,9 +1873,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ports", GetKey: "ports",
GetValue: []interface{}{100, 80}, GetValue: []interface{}{100, 80},
}, },
// #11: Set with nested set
{ {
TestName: "Set with nested set",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
Type: TypeSet, Type: TypeSet,
@ -1951,9 +1940,8 @@ func TestResourceDataSet(t *testing.T) {
return v return v
}, },
}, },
// #12: List of floats, set list
{ {
TestName: "List of floats, set list",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ratios": &Schema{ "ratios": &Schema{
Type: TypeList, Type: TypeList,
@ -1972,16 +1960,20 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "ratios", GetKey: "ratios",
GetValue: []interface{}{1.0, 2.2, 5.5}, GetValue: []interface{}{1.0, 2.2, 5.5},
}, },
// #12: Set of floats, set list
{ {
TestName: "Set of floats, set list",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ratios": &Schema{ "ratios": &Schema{
Type: TypeSet, Type: TypeSet,
Computed: true, Computed: true,
Elem: &Schema{Type: TypeFloat}, Elem: &Schema{Type: TypeFloat},
Set: func(a interface{}) int { 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", GetKey: "ratios",
GetValue: []interface{}{1.0, 2.2, 5.5}, GetValue: []interface{}{1.0, 2.2, 5.5},
}, },
// #13: Basic pointer
{ {
TestName: "Basic pointer",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
Type: TypeString, Type: TypeString,
@ -2018,9 +2009,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone", GetKey: "availability_zone",
GetValue: "foo", GetValue: "foo",
}, },
// #14: Basic nil value
{ {
TestName: "Basic nil value",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
Type: TypeString, Type: TypeString,
@ -2040,9 +2030,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone", GetKey: "availability_zone",
GetValue: "", GetValue: "",
}, },
// #15: Basic nil pointer
{ {
TestName: "Basic nil pointer",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"availability_zone": &Schema{ "availability_zone": &Schema{
Type: TypeString, Type: TypeString,
@ -2062,9 +2051,8 @@ func TestResourceDataSet(t *testing.T) {
GetKey: "availability_zone", GetKey: "availability_zone",
GetValue: "", GetValue: "",
}, },
// #16: Set in a list
{ {
TestName: "Set in a list",
Schema: map[string]*Schema{ Schema: map[string]*Schema{
"ports": &Schema{ "ports": &Schema{
Type: TypeList, Type: TypeList,
@ -2131,29 +2119,31 @@ func TestResourceDataSet(t *testing.T) {
os.Setenv(PanicOnErr, "") os.Setenv(PanicOnErr, "")
defer os.Setenv(PanicOnErr, oldEnv) defer os.Setenv(PanicOnErr, oldEnv)
for i, tc := range cases { for _, tc := range cases {
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) t.Run(tc.TestName, func(t *testing.T) {
if err != nil { d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
t.Fatalf("err: %s", err) if err != nil {
} t.Fatalf("err: %s", err)
}
err = d.Set(tc.Key, tc.Value) err = d.Set(tc.Key, tc.Value)
if err != nil != tc.Err { if err != nil != tc.Err {
t.Fatalf("%d err: %s", i, err) t.Fatalf("unexpected err: %s", err)
} }
v := d.Get(tc.GetKey) v := d.Get(tc.GetKey)
if s, ok := v.(*Set); ok { if s, ok := v.(*Set); ok {
v = s.List() v = s.List()
} }
if tc.GetPreProcess != nil { if tc.GetPreProcess != nil {
v = tc.GetPreProcess(v) v = tc.GetPreProcess(v)
} }
if !reflect.DeepEqual(v, tc.GetValue) { if !reflect.DeepEqual(v, tc.GetValue) {
t.Fatalf("Get Bad: %d\n\n%#v", i, v) 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, NewComputed: true,
} }
} else { } else {
result["foo.2800005064"] = &tofu.ResourceAttrDiff{ result["foo.1494962232"] = &tofu.ResourceAttrDiff{
Old: "", Old: "",
New: "qux", New: "qux",
} }

View File

@ -100,7 +100,10 @@ func upgradeAttributesV2ToV3(instanceState *InstanceState) error {
// First, detect "obvious" maps - which have non-numeric keys (mostly). // First, detect "obvious" maps - which have non-numeric keys (mostly).
hasNonNumericKeys := false hasNonNumericKeys := false
for _, key := range actualKeysMatching { 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 hasNonNumericKeys = true
} }
} }