terraform/state/state.go

118 lines
2.8 KiB
Go

package state
import (
"context"
"fmt"
"math/rand"
"os"
"os/user"
"time"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/terraform/states/statemgr"
"github.com/hashicorp/terraform/version"
)
var rngSource *rand.Rand
func init() {
rngSource = rand.New(rand.NewSource(time.Now().UnixNano()))
}
// State is a deprecated alias for statemgr.Full
type State = statemgr.Full
// StateReader is a deprecated alias for statemgr.Reader
type StateReader = statemgr.Reader
// StateWriter is a deprecated alias for statemgr.Writer
type StateWriter = statemgr.Writer
// StateRefresher is a deprecated alias for statemgr.Refresher
type StateRefresher = statemgr.Refresher
// StatePersister is a deprecated alias for statemgr.Persister
type StatePersister = statemgr.Persister
// Locker is a deprecated alias for statemgr.Locker
type Locker = statemgr.Locker
// test hook to verify that LockWithContext has attempted a lock
var postLockHook func()
// Lock the state, using the provided context for timeout and cancellation.
// This backs off slightly to an upper limit.
func LockWithContext(ctx context.Context, s State, info *LockInfo) (string, error) {
delay := time.Second
maxDelay := 16 * time.Second
for {
id, err := s.Lock(info)
if err == nil {
return id, nil
}
le, ok := err.(*LockError)
if !ok {
// not a lock error, so we can't retry
return "", err
}
if le == nil || le.Info == nil || le.Info.ID == "" {
// If we dont' have a complete LockError, there's something wrong with the lock
return "", err
}
if postLockHook != nil {
postLockHook()
}
// there's an existing lock, wait and try again
select {
case <-ctx.Done():
// return the last lock error with the info
return "", err
case <-time.After(delay):
if delay < maxDelay {
delay *= 2
}
}
}
}
// Generate a LockInfo structure, populating the required fields.
func NewLockInfo() *LockInfo {
// this doesn't need to be cryptographically secure, just unique.
// Using math/rand alleviates the need to check handle the read error.
// Use a uuid format to match other IDs used throughout Terraform.
buf := make([]byte, 16)
rngSource.Read(buf)
id, err := uuid.FormatUUID(buf)
if err != nil {
// this of course shouldn't happen
panic(err)
}
// don't error out on user and hostname, as we don't require them
userName := ""
if userInfo, err := user.Current(); err == nil {
userName = userInfo.Username
}
host, _ := os.Hostname()
info := &LockInfo{
ID: id,
Who: fmt.Sprintf("%s@%s", userName, host),
Version: version.Version,
Created: time.Now().UTC(),
}
return info
}
// LockInfo is a deprecated lias for statemgr.LockInfo
type LockInfo = statemgr.LockInfo
// LockError is a deprecated alias for statemgr.LockError
type LockError = statemgr.LockError