mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-25 16:31:10 -06:00
34a01c92d6
Instances of the same AbsResource may share the same Dependencies, which could point to the same backing array of values. Since address values are not pointers, and not meant to be shared, we must copy the value before sorting the slice in-place. Because individual instances of the same resource may be encoded to state concurrently, failure to copy the slice first can result in a data race.
84 lines
2.3 KiB
Go
84 lines
2.3 KiB
Go
package states
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func TestResourceInstanceObject_encode(t *testing.T) {
|
|
value := cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.True,
|
|
})
|
|
// The in-memory order of resource dependencies is random, since they're an
|
|
// unordered set.
|
|
depsOne := []addrs.ConfigResource{
|
|
addrs.RootModule.Resource(addrs.ManagedResourceMode, "test", "honk"),
|
|
addrs.RootModule.Child("child").Resource(addrs.ManagedResourceMode, "test", "flub"),
|
|
addrs.RootModule.Resource(addrs.ManagedResourceMode, "test", "boop"),
|
|
}
|
|
depsTwo := []addrs.ConfigResource{
|
|
addrs.RootModule.Child("child").Resource(addrs.ManagedResourceMode, "test", "flub"),
|
|
addrs.RootModule.Resource(addrs.ManagedResourceMode, "test", "boop"),
|
|
addrs.RootModule.Resource(addrs.ManagedResourceMode, "test", "honk"),
|
|
}
|
|
|
|
// multiple instances may have been assigned the same deps slice
|
|
objs := []*ResourceInstanceObject{
|
|
&ResourceInstanceObject{
|
|
Value: value,
|
|
Status: ObjectPlanned,
|
|
Dependencies: depsOne,
|
|
},
|
|
&ResourceInstanceObject{
|
|
Value: value,
|
|
Status: ObjectPlanned,
|
|
Dependencies: depsTwo,
|
|
},
|
|
&ResourceInstanceObject{
|
|
Value: value,
|
|
Status: ObjectPlanned,
|
|
Dependencies: depsOne,
|
|
},
|
|
&ResourceInstanceObject{
|
|
Value: value,
|
|
Status: ObjectPlanned,
|
|
Dependencies: depsOne,
|
|
},
|
|
}
|
|
|
|
var encoded []*ResourceInstanceObjectSrc
|
|
|
|
// Encoding can happen concurrently, so we need to make sure the shared
|
|
// Dependencies are safely handled
|
|
var wg sync.WaitGroup
|
|
var mu sync.Mutex
|
|
|
|
for _, obj := range objs {
|
|
obj := obj
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
rios, err := obj.Encode(value.Type(), 0)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %s", err)
|
|
}
|
|
mu.Lock()
|
|
encoded = append(encoded, rios)
|
|
mu.Unlock()
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
// However, identical sets of dependencies should always be written to state
|
|
// in an identical order, so we don't do meaningless state updates on refresh.
|
|
for i := 0; i < len(encoded)-1; i++ {
|
|
if diff := cmp.Diff(encoded[i].Dependencies, encoded[i+1].Dependencies); diff != "" {
|
|
t.Errorf("identical dependencies got encoded in different orders:\n%s", diff)
|
|
}
|
|
}
|
|
}
|