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