github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/backend/remote-state/consul/backend_state.go (about) 1 package consul 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/terraform/internal/backend" 8 "github.com/hashicorp/terraform/internal/states" 9 "github.com/hashicorp/terraform/internal/states/remote" 10 "github.com/hashicorp/terraform/internal/states/statemgr" 11 ) 12 13 const ( 14 keyEnvPrefix = "-env:" 15 ) 16 17 func (b *Backend) Workspaces() ([]string, error) { 18 // List our raw path 19 prefix := b.configData.Get("path").(string) + keyEnvPrefix 20 keys, _, err := b.client.KV().Keys(prefix, "/", nil) 21 if err != nil { 22 return nil, err 23 } 24 25 // Find the envs, we use a map since we can get duplicates with 26 // path suffixes. 27 envs := map[string]struct{}{} 28 for _, key := range keys { 29 // Consul should ensure this but it doesn't hurt to check again 30 if strings.HasPrefix(key, prefix) { 31 key = strings.TrimPrefix(key, prefix) 32 33 // Ignore anything with a "/" in it since we store the state 34 // directly in a key not a directory. 35 if idx := strings.IndexRune(key, '/'); idx >= 0 { 36 continue 37 } 38 39 envs[key] = struct{}{} 40 } 41 } 42 43 result := make([]string, 1, len(envs)+1) 44 result[0] = backend.DefaultStateName 45 for k, _ := range envs { 46 result = append(result, k) 47 } 48 49 return result, nil 50 } 51 52 func (b *Backend) DeleteWorkspace(name string, _ bool) error { 53 if name == backend.DefaultStateName || name == "" { 54 return fmt.Errorf("can't delete default state") 55 } 56 57 // Determine the path of the data 58 path := b.path(name) 59 60 // Delete it. We just delete it without any locking since 61 // the DeleteState API is documented as such. 62 _, err := b.client.KV().Delete(path, nil) 63 return err 64 } 65 66 func (b *Backend) StateMgr(name string) (statemgr.Full, error) { 67 // Determine the path of the data 68 path := b.path(name) 69 70 // Determine whether to gzip or not 71 gzip := b.configData.Get("gzip").(bool) 72 73 // Build the state client 74 var stateMgr = &remote.State{ 75 Client: &RemoteClient{ 76 Client: b.client, 77 Path: path, 78 GZip: gzip, 79 lockState: b.lock, 80 }, 81 } 82 83 if !b.lock { 84 stateMgr.DisableLocks() 85 } 86 87 // the default state always exists 88 if name == backend.DefaultStateName { 89 return stateMgr, nil 90 } 91 92 // Grab a lock, we use this to write an empty state if one doesn't 93 // exist already. We have to write an empty state as a sentinel value 94 // so States() knows it exists. 95 lockInfo := statemgr.NewLockInfo() 96 lockInfo.Operation = "init" 97 lockId, err := stateMgr.Lock(lockInfo) 98 if err != nil { 99 return nil, fmt.Errorf("failed to lock state in Consul: %s", err) 100 } 101 102 // Local helper function so we can call it multiple places 103 lockUnlock := func(parent error) error { 104 if err := stateMgr.Unlock(lockId); err != nil { 105 return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err) 106 } 107 108 return parent 109 } 110 111 // Grab the value 112 if err := stateMgr.RefreshState(); err != nil { 113 err = lockUnlock(err) 114 return nil, err 115 } 116 117 // If we have no state, we have to create an empty state 118 if v := stateMgr.State(); v == nil { 119 if err := stateMgr.WriteState(states.NewState()); err != nil { 120 err = lockUnlock(err) 121 return nil, err 122 } 123 if err := stateMgr.PersistState(nil); err != nil { 124 err = lockUnlock(err) 125 return nil, err 126 } 127 } 128 129 // Unlock, the state should now be initialized 130 if err := lockUnlock(nil); err != nil { 131 return nil, err 132 } 133 134 return stateMgr, nil 135 } 136 137 func (b *Backend) path(name string) string { 138 path := b.configData.Get("path").(string) 139 if name != backend.DefaultStateName { 140 path += fmt.Sprintf("%s%s", keyEnvPrefix, name) 141 } 142 143 return path 144 } 145 146 const errStateUnlock = ` 147 Error unlocking Consul state. Lock ID: %s 148 149 Error: %s 150 151 You may have to force-unlock this state in order to use it again. 152 The Consul backend acquires a lock during initialization to ensure 153 the minimum required key/values are prepared. 154 `