github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/state/state.go (about)

     1  package state
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"os/user"
     8  	"time"
     9  
    10  	uuid "github.com/hashicorp/go-uuid"
    11  
    12  	"github.com/hashicorp/terraform/states/statemgr"
    13  	"github.com/hashicorp/terraform/version"
    14  )
    15  
    16  // State is a deprecated alias for statemgr.Full
    17  type State = statemgr.Full
    18  
    19  // StateReader is a deprecated alias for statemgr.Reader
    20  type StateReader = statemgr.Reader
    21  
    22  // StateWriter is a deprecated alias for statemgr.Writer
    23  type StateWriter = statemgr.Writer
    24  
    25  // StateRefresher is a deprecated alias for statemgr.Refresher
    26  type StateRefresher = statemgr.Refresher
    27  
    28  // StatePersister is a deprecated alias for statemgr.Persister
    29  type StatePersister = statemgr.Persister
    30  
    31  // Locker is a deprecated alias for statemgr.Locker
    32  type Locker = statemgr.Locker
    33  
    34  // test hook to verify that LockWithContext has attempted a lock
    35  var postLockHook func()
    36  
    37  // Lock the state, using the provided context for timeout and cancellation.
    38  // This backs off slightly to an upper limit.
    39  func LockWithContext(ctx context.Context, s State, info *LockInfo) (string, error) {
    40  	delay := time.Second
    41  	maxDelay := 16 * time.Second
    42  	for {
    43  		id, err := s.Lock(info)
    44  		if err == nil {
    45  			return id, nil
    46  		}
    47  
    48  		le, ok := err.(*LockError)
    49  		if !ok {
    50  			// not a lock error, so we can't retry
    51  			return "", err
    52  		}
    53  
    54  		if le == nil || le.Info == nil || le.Info.ID == "" {
    55  			// If we dont' have a complete LockError, there's something wrong with the lock
    56  			return "", err
    57  		}
    58  
    59  		if postLockHook != nil {
    60  			postLockHook()
    61  		}
    62  
    63  		// there's an existing lock, wait and try again
    64  		select {
    65  		case <-ctx.Done():
    66  			// return the last lock error with the info
    67  			return "", err
    68  		case <-time.After(delay):
    69  			if delay < maxDelay {
    70  				delay *= 2
    71  			}
    72  		}
    73  	}
    74  }
    75  
    76  // Generate a LockInfo structure, populating the required fields.
    77  func NewLockInfo() *LockInfo {
    78  	id, err := uuid.GenerateUUID()
    79  	if err != nil {
    80  		// this of course shouldn't happen
    81  		panic(err)
    82  	}
    83  
    84  	// don't error out on user and hostname, as we don't require them
    85  	userName := ""
    86  	if userInfo, err := user.Current(); err == nil {
    87  		userName = userInfo.Username
    88  	}
    89  	host, _ := os.Hostname()
    90  
    91  	info := &LockInfo{
    92  		ID:      id,
    93  		Who:     fmt.Sprintf("%s@%s", userName, host),
    94  		Version: version.Version,
    95  		Created: time.Now().UTC(),
    96  	}
    97  	return info
    98  }
    99  
   100  // LockInfo is a deprecated lias for statemgr.LockInfo
   101  type LockInfo = statemgr.LockInfo
   102  
   103  // LockError is a deprecated alias for statemgr.LockError
   104  type LockError = statemgr.LockError