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

     1  package pg
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/backend"
     7  	"github.com/hashicorp/terraform/state"
     8  	"github.com/hashicorp/terraform/state/remote"
     9  	"github.com/hashicorp/terraform/states"
    10  )
    11  
    12  func (b *Backend) Workspaces() ([]string, error) {
    13  	query := `SELECT name FROM %s.%s ORDER BY name`
    14  	rows, err := b.db.Query(fmt.Sprintf(query, b.schemaName, statesTableName))
    15  	if err != nil {
    16  		return nil, err
    17  	}
    18  	defer rows.Close()
    19  
    20  	var result []string
    21  
    22  	for rows.Next() {
    23  		var name string
    24  		if err := rows.Scan(&name); err != nil {
    25  			return nil, err
    26  		}
    27  		result = append(result, name)
    28  	}
    29  	if err := rows.Err(); err != nil {
    30  		return nil, err
    31  	}
    32  
    33  	return result, nil
    34  }
    35  
    36  func (b *Backend) DeleteWorkspace(name string) error {
    37  	if name == backend.DefaultStateName || name == "" {
    38  		return fmt.Errorf("can't delete default state")
    39  	}
    40  
    41  	query := `DELETE FROM %s.%s WHERE name = $1`
    42  	_, err := b.db.Exec(fmt.Sprintf(query, b.schemaName, statesTableName), name)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	return nil
    48  }
    49  
    50  func (b *Backend) StateMgr(name string) (state.State, error) {
    51  	// Build the state client
    52  	var stateMgr state.State = &remote.State{
    53  		Client: &RemoteClient{
    54  			Client:     b.db,
    55  			Name:       name,
    56  			SchemaName: b.schemaName,
    57  		},
    58  	}
    59  
    60  	// Check to see if this state already exists.
    61  	// If the state doesn't exist, we have to assume this
    62  	// is a normal create operation, and take the lock at that point.
    63  	existing, err := b.Workspaces()
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	exists := false
    69  	for _, s := range existing {
    70  		if s == name {
    71  			exists = true
    72  			break
    73  		}
    74  	}
    75  
    76  	// Grab a lock, we use this to write an empty state if one doesn't
    77  	// exist already. We have to write an empty state as a sentinel value
    78  	// so Workspaces() knows it exists.
    79  	if !exists {
    80  		lockInfo := state.NewLockInfo()
    81  		lockInfo.Operation = "init"
    82  		lockId, err := stateMgr.Lock(lockInfo)
    83  		if err != nil {
    84  			return nil, fmt.Errorf("failed to lock state in Postgres: %s", err)
    85  		}
    86  
    87  		// Local helper function so we can call it multiple places
    88  		lockUnlock := func(parent error) error {
    89  			if err := stateMgr.Unlock(lockId); err != nil {
    90  				return fmt.Errorf(`error unlocking Postgres state: %s`, err)
    91  			}
    92  			return parent
    93  		}
    94  
    95  		if v := stateMgr.State(); v == nil {
    96  			if err := stateMgr.WriteState(states.NewState()); err != nil {
    97  				err = lockUnlock(err)
    98  				return nil, err
    99  			}
   100  			if err := stateMgr.PersistState(); err != nil {
   101  				err = lockUnlock(err)
   102  				return nil, err
   103  			}
   104  		}
   105  
   106  		// Unlock, the state should now be initialized
   107  		if err := lockUnlock(nil); err != nil {
   108  			return nil, err
   109  		}
   110  	}
   111  
   112  	return stateMgr, nil
   113  }
   114  
   115  func (b *Backend) StateMgrWithoutCheckVersion(name string) (state.State, error) {
   116  	return b.StateMgr(name)
   117  }