mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-04 13:17:43 -06:00
53cafc542b
This idea of a "state manager" was previously modelled via the confusingly-named state.State interface, which we've been calling a "state manager" only in some local variable names in situations where there were also *terraform.State variables. As part of reworking our state models to make room for the new type system, we also need to change what was previously the state.StateReader interface. Since we've found the previous organization confusing anyway, here we just copy all of those interfaces over into statemgr where we can make the relationship to states.State hopefully a little clearer. This is not yet a complete move of the functionality from "state", since we're not yet ready to break existing callers. In a future commit we'll turn the interfaces in the old "state" package into aliases of the interfaces in this package, and update all the implementers of what will by then be statemgr.Reader to use *states.State instead of *terraform.State. This also includes an adaptation of what was previously state.LocalState into statemgr.FileSystem, using the new state serialization functionality from package statefile instead of the old terraform.ReadState and terraform.WriteState.
103 lines
1.9 KiB
Go
103 lines
1.9 KiB
Go
package statemgr
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/terraform/helper/logging"
|
|
)
|
|
|
|
func TestNewLockInfo(t *testing.T) {
|
|
info1 := NewLockInfo()
|
|
info2 := NewLockInfo()
|
|
|
|
if info1.ID == "" {
|
|
t.Fatal("LockInfo missing ID")
|
|
}
|
|
|
|
if info1.Version == "" {
|
|
t.Fatal("LockInfo missing version")
|
|
}
|
|
|
|
if info1.Created.IsZero() {
|
|
t.Fatal("LockInfo missing Created")
|
|
}
|
|
|
|
if info1.ID == info2.ID {
|
|
t.Fatal("multiple LockInfo with identical IDs")
|
|
}
|
|
|
|
// test the JSON output is valid
|
|
newInfo := &LockInfo{}
|
|
err := json.Unmarshal(info1.Marshal(), newInfo)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestLockWithContext(t *testing.T) {
|
|
s := NewFullFake(nil, TestFullInitialState())
|
|
|
|
id, err := s.Lock(NewLockInfo())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// use a cancelled context for an immediate timeout
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
|
|
info := NewLockInfo()
|
|
info.Info = "lock with context"
|
|
_, err = LockWithContext(ctx, s, info)
|
|
if err == nil {
|
|
t.Fatal("lock should have failed immediately")
|
|
}
|
|
|
|
// block until LockwithContext has made a first attempt
|
|
attempted := make(chan struct{})
|
|
postLockHook = func() {
|
|
close(attempted)
|
|
postLockHook = nil
|
|
}
|
|
|
|
// unlock the state during LockWithContext
|
|
unlocked := make(chan struct{})
|
|
go func() {
|
|
defer close(unlocked)
|
|
<-attempted
|
|
if err := s.Unlock(id); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
|
|
id, err = LockWithContext(ctx, s, info)
|
|
if err != nil {
|
|
t.Fatal("lock should have completed within 2s:", err)
|
|
}
|
|
|
|
// ensure the goruotine completes
|
|
<-unlocked
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
flag.Parse()
|
|
if testing.Verbose() {
|
|
// if we're verbose, use the logging requested by TF_LOG
|
|
logging.SetOutput()
|
|
} else {
|
|
// otherwise silence all logs
|
|
log.SetOutput(ioutil.Discard)
|
|
}
|
|
os.Exit(m.Run())
|
|
}
|