github.com/ctrox/terraform@v0.11.12-beta1/backend/remote-state/consul/backend_state.go (about) 1 package consul 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/terraform/backend" 8 "github.com/hashicorp/terraform/state" 9 "github.com/hashicorp/terraform/state/remote" 10 "github.com/hashicorp/terraform/terraform" 11 ) 12 13 const ( 14 keyEnvPrefix = "-env:" 15 ) 16 17 func (b *Backend) States() ([]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) DeleteState(name string) 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) State(name string) (state.State, 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 state.State = &remote.State{ 75 Client: &RemoteClient{ 76 Client: b.client, 77 Path: path, 78 GZip: gzip, 79 lockState: b.lock, 80 }, 81 } 82 83 // If we're not locking, disable it 84 if !b.lock { 85 stateMgr = &state.LockDisabled{Inner: stateMgr} 86 } 87 88 // the default state always exists 89 if name == backend.DefaultStateName { 90 return stateMgr, nil 91 } 92 93 // Grab a lock, we use this to write an empty state if one doesn't 94 // exist already. We have to write an empty state as a sentinel value 95 // so States() knows it exists. 96 lockInfo := state.NewLockInfo() 97 lockInfo.Operation = "init" 98 lockId, err := stateMgr.Lock(lockInfo) 99 if err != nil { 100 return nil, fmt.Errorf("failed to lock state in Consul: %s", err) 101 } 102 103 // Local helper function so we can call it multiple places 104 lockUnlock := func(parent error) error { 105 if err := stateMgr.Unlock(lockId); err != nil { 106 return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err) 107 } 108 109 return parent 110 } 111 112 // Grab the value 113 if err := stateMgr.RefreshState(); err != nil { 114 err = lockUnlock(err) 115 return nil, err 116 } 117 118 // If we have no state, we have to create an empty state 119 if v := stateMgr.State(); v == nil { 120 if err := stateMgr.WriteState(terraform.NewState()); err != nil { 121 err = lockUnlock(err) 122 return nil, err 123 } 124 if err := stateMgr.PersistState(); err != nil { 125 err = lockUnlock(err) 126 return nil, err 127 } 128 } 129 130 // Unlock, the state should now be initialized 131 if err := lockUnlock(nil); err != nil { 132 return nil, err 133 } 134 135 return stateMgr, nil 136 } 137 138 func (b *Backend) path(name string) string { 139 path := b.configData.Get("path").(string) 140 if name != backend.DefaultStateName { 141 path += fmt.Sprintf("%s%s", keyEnvPrefix, name) 142 } 143 144 return path 145 } 146 147 const errStateUnlock = ` 148 Error unlocking Consul state. Lock ID: %s 149 150 Error: %s 151 152 You may have to force-unlock this state in order to use it again. 153 The Consul backend acquires a lock during initialization to ensure 154 the minimum required key/values are prepared. 155 `