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 }