github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/backend/remote-state/etcdv3/backend_state.go (about) 1 package etcd 2 3 import ( 4 "context" 5 "fmt" 6 "sort" 7 "strings" 8 9 etcdv3 "github.com/coreos/etcd/clientv3" 10 11 "github.com/hashicorp/terraform/backend" 12 "github.com/hashicorp/terraform/state" 13 "github.com/hashicorp/terraform/state/remote" 14 "github.com/hashicorp/terraform/states" 15 ) 16 17 func (b *Backend) Workspaces() ([]string, error) { 18 res, err := b.client.Get(context.TODO(), b.prefix, etcdv3.WithPrefix(), etcdv3.WithKeysOnly()) 19 if err != nil { 20 return nil, err 21 } 22 23 result := make([]string, 1, len(res.Kvs)+1) 24 result[0] = backend.DefaultStateName 25 for _, kv := range res.Kvs { 26 result = append(result, strings.TrimPrefix(string(kv.Key), b.prefix)) 27 } 28 sort.Strings(result[1:]) 29 30 return result, nil 31 } 32 33 func (b *Backend) DeleteWorkspace(name string) error { 34 if name == backend.DefaultStateName || name == "" { 35 return fmt.Errorf("Can't delete default state.") 36 } 37 38 key := b.determineKey(name) 39 40 _, err := b.client.Delete(context.TODO(), key) 41 return err 42 } 43 44 func (b *Backend) StateMgr(name string) (state.State, error) { 45 return b.stateMgr(name, true) 46 } 47 48 func (b *Backend) StateMgrWithoutCheckVersion(name string) (state.State, error) { 49 return b.stateMgr(name, false) 50 } 51 52 func (b *Backend) stateMgr(name string, checkVersion bool) (state.State, error) { 53 var stateMgr state.State = &remote.State{ 54 Client: &RemoteClient{ 55 Client: b.client, 56 DoLock: b.lock, 57 Key: b.determineKey(name), 58 }, 59 } 60 61 if !b.lock { 62 stateMgr = &state.LockDisabled{Inner: stateMgr} 63 } 64 65 lockInfo := state.NewLockInfo() 66 lockInfo.Operation = "init" 67 lockId, err := stateMgr.Lock(lockInfo) 68 if err != nil { 69 return nil, fmt.Errorf("Failed to lock state in etcd: %s.", err) 70 } 71 72 lockUnlock := func(parent error) error { 73 if err := stateMgr.Unlock(lockId); err != nil { 74 return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err) 75 } 76 return parent 77 } 78 79 if checkVersion { 80 if err := stateMgr.RefreshState(); err != nil { 81 err = lockUnlock(err) 82 return nil, err 83 } 84 } else { 85 if err := stateMgr.RefreshStateWithoutCheckVersion(); err != nil { 86 err = lockUnlock(err) 87 return nil, err 88 } 89 } 90 91 if v := stateMgr.State(); v == nil { 92 if err := stateMgr.WriteState(states.NewState()); err != nil { 93 err = lockUnlock(err) 94 return nil, err 95 } 96 if err := stateMgr.PersistState(); err != nil { 97 err = lockUnlock(err) 98 return nil, err 99 } 100 } 101 102 if err := lockUnlock(nil); err != nil { 103 return nil, err 104 } 105 106 return stateMgr, nil 107 } 108 109 func (b *Backend) determineKey(name string) string { 110 return b.prefix + name 111 } 112 113 const errStateUnlock = ` 114 Error unlocking etcd state. Lock ID: %s 115 116 Error: %s 117 118 You may have to force-unlock this state in order to use it again. 119 `