2018-10-10 23:42:57 +02:00
|
|
|
package pg
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2021-05-17 17:42:17 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/backend"
|
2021-05-17 21:43:35 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
|
|
"github.com/hashicorp/terraform/internal/states/remote"
|
|
|
|
"github.com/hashicorp/terraform/internal/states/statemgr"
|
2018-10-10 23:42:57 +02:00
|
|
|
)
|
|
|
|
|
2018-11-14 02:09:43 +01:00
|
|
|
func (b *Backend) Workspaces() ([]string, error) {
|
2020-09-29 23:18:34 +02:00
|
|
|
query := `SELECT name FROM %s.%s WHERE name != 'default' ORDER BY name`
|
2018-10-10 23:42:57 +02:00
|
|
|
rows, err := b.db.Query(fmt.Sprintf(query, b.schemaName, statesTableName))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
2020-09-29 23:18:34 +02:00
|
|
|
result := []string{
|
|
|
|
backend.DefaultStateName,
|
|
|
|
}
|
2018-10-10 23:42:57 +02:00
|
|
|
|
|
|
|
for rows.Next() {
|
|
|
|
var name string
|
|
|
|
if err := rows.Scan(&name); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
result = append(result, name)
|
|
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2018-11-14 02:09:43 +01:00
|
|
|
func (b *Backend) DeleteWorkspace(name string) error {
|
2018-10-10 23:42:57 +02:00
|
|
|
if name == backend.DefaultStateName || name == "" {
|
|
|
|
return fmt.Errorf("can't delete default state")
|
|
|
|
}
|
|
|
|
|
|
|
|
query := `DELETE FROM %s.%s WHERE name = $1`
|
|
|
|
_, err := b.db.Exec(fmt.Sprintf(query, b.schemaName, statesTableName), name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-08-11 17:43:01 +02:00
|
|
|
func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
|
2018-10-10 23:42:57 +02:00
|
|
|
// Build the state client
|
2020-08-11 17:43:01 +02:00
|
|
|
var stateMgr statemgr.Full = &remote.State{
|
2018-10-10 23:42:57 +02:00
|
|
|
Client: &RemoteClient{
|
|
|
|
Client: b.db,
|
|
|
|
Name: name,
|
|
|
|
SchemaName: b.schemaName,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check to see if this state already exists.
|
2019-02-26 01:30:30 +01:00
|
|
|
// If the state doesn't exist, we have to assume this
|
2018-10-10 23:42:57 +02:00
|
|
|
// is a normal create operation, and take the lock at that point.
|
2018-11-14 02:09:43 +01:00
|
|
|
existing, err := b.Workspaces()
|
2018-10-10 23:42:57 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
exists := false
|
|
|
|
for _, s := range existing {
|
|
|
|
if s == name {
|
|
|
|
exists = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Grab a lock, we use this to write an empty state if one doesn't
|
|
|
|
// exist already. We have to write an empty state as a sentinel value
|
2018-11-14 02:09:43 +01:00
|
|
|
// so Workspaces() knows it exists.
|
2018-10-10 23:42:57 +02:00
|
|
|
if !exists {
|
2020-08-11 17:43:01 +02:00
|
|
|
lockInfo := statemgr.NewLockInfo()
|
2018-10-10 23:42:57 +02:00
|
|
|
lockInfo.Operation = "init"
|
|
|
|
lockId, err := stateMgr.Lock(lockInfo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to lock state in Postgres: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Local helper function so we can call it multiple places
|
|
|
|
lockUnlock := func(parent error) error {
|
|
|
|
if err := stateMgr.Unlock(lockId); err != nil {
|
2019-02-26 01:05:53 +01:00
|
|
|
return fmt.Errorf(`error unlocking Postgres state: %s`, err)
|
2018-10-10 23:42:57 +02:00
|
|
|
}
|
|
|
|
return parent
|
|
|
|
}
|
|
|
|
|
|
|
|
if v := stateMgr.State(); v == nil {
|
2018-11-14 02:09:43 +01:00
|
|
|
if err := stateMgr.WriteState(states.NewState()); err != nil {
|
2018-10-10 23:42:57 +02:00
|
|
|
err = lockUnlock(err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := stateMgr.PersistState(); err != nil {
|
|
|
|
err = lockUnlock(err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unlock, the state should now be initialized
|
|
|
|
if err := lockUnlock(nil); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return stateMgr, nil
|
|
|
|
}
|