github.com/Kevinklinger/open_terraform@v0.11.12-beta1/backend/remote-state/consul/client_test.go (about) 1 package consul 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "sync" 8 "testing" 9 "time" 10 11 "github.com/hashicorp/terraform/backend" 12 "github.com/hashicorp/terraform/state" 13 "github.com/hashicorp/terraform/state/remote" 14 ) 15 16 func TestRemoteClient_impl(t *testing.T) { 17 var _ remote.Client = new(RemoteClient) 18 var _ remote.ClientLocker = new(RemoteClient) 19 } 20 21 func TestRemoteClient(t *testing.T) { 22 // Get the backend 23 b := backend.TestBackendConfig(t, New(), map[string]interface{}{ 24 "address": srv.HTTPAddr, 25 "path": fmt.Sprintf("tf-unit/%s", time.Now().String()), 26 }) 27 28 // Grab the client 29 state, err := b.State(backend.DefaultStateName) 30 if err != nil { 31 t.Fatalf("err: %s", err) 32 } 33 34 // Test 35 remote.TestClient(t, state.(*remote.State).Client) 36 } 37 38 // test the gzip functionality of the client 39 func TestRemoteClient_gzipUpgrade(t *testing.T) { 40 statePath := fmt.Sprintf("tf-unit/%s", time.Now().String()) 41 42 // Get the backend 43 b := backend.TestBackendConfig(t, New(), map[string]interface{}{ 44 "address": srv.HTTPAddr, 45 "path": statePath, 46 }) 47 48 // Grab the client 49 state, err := b.State(backend.DefaultStateName) 50 if err != nil { 51 t.Fatalf("err: %s", err) 52 } 53 54 // Test 55 remote.TestClient(t, state.(*remote.State).Client) 56 57 // create a new backend with gzip 58 b = backend.TestBackendConfig(t, New(), map[string]interface{}{ 59 "address": srv.HTTPAddr, 60 "path": statePath, 61 "gzip": true, 62 }) 63 64 // Grab the client 65 state, err = b.State(backend.DefaultStateName) 66 if err != nil { 67 t.Fatalf("err: %s", err) 68 } 69 70 // Test 71 remote.TestClient(t, state.(*remote.State).Client) 72 } 73 74 func TestConsul_stateLock(t *testing.T) { 75 path := fmt.Sprintf("tf-unit/%s", time.Now().String()) 76 77 // create 2 instances to get 2 remote.Clients 78 sA, err := backend.TestBackendConfig(t, New(), map[string]interface{}{ 79 "address": srv.HTTPAddr, 80 "path": path, 81 }).State(backend.DefaultStateName) 82 if err != nil { 83 t.Fatal(err) 84 } 85 86 sB, err := backend.TestBackendConfig(t, New(), map[string]interface{}{ 87 "address": srv.HTTPAddr, 88 "path": path, 89 }).State(backend.DefaultStateName) 90 if err != nil { 91 t.Fatal(err) 92 } 93 94 remote.TestRemoteLocks(t, sA.(*remote.State).Client, sB.(*remote.State).Client) 95 } 96 97 func TestConsul_destroyLock(t *testing.T) { 98 // Get the backend 99 b := backend.TestBackendConfig(t, New(), map[string]interface{}{ 100 "address": srv.HTTPAddr, 101 "path": fmt.Sprintf("tf-unit/%s", time.Now().String()), 102 }) 103 104 // Grab the client 105 s, err := b.State(backend.DefaultStateName) 106 if err != nil { 107 t.Fatalf("err: %s", err) 108 } 109 110 c := s.(*remote.State).Client.(*RemoteClient) 111 112 info := state.NewLockInfo() 113 id, err := c.Lock(info) 114 if err != nil { 115 t.Fatal(err) 116 } 117 118 lockPath := c.Path + lockSuffix 119 120 if err := c.Unlock(id); err != nil { 121 t.Fatal(err) 122 } 123 124 // get the lock val 125 pair, _, err := c.Client.KV().Get(lockPath, nil) 126 if err != nil { 127 t.Fatal(err) 128 } 129 if pair != nil { 130 t.Fatalf("lock key not cleaned up at: %s", pair.Key) 131 } 132 } 133 134 func TestConsul_lostLock(t *testing.T) { 135 path := fmt.Sprintf("tf-unit/%s", time.Now().String()) 136 137 // create 2 instances to get 2 remote.Clients 138 sA, err := backend.TestBackendConfig(t, New(), map[string]interface{}{ 139 "address": srv.HTTPAddr, 140 "path": path, 141 }).State(backend.DefaultStateName) 142 if err != nil { 143 t.Fatal(err) 144 } 145 146 sB, err := backend.TestBackendConfig(t, New(), map[string]interface{}{ 147 "address": srv.HTTPAddr, 148 "path": path + "-not-used", 149 }).State(backend.DefaultStateName) 150 if err != nil { 151 t.Fatal(err) 152 } 153 154 info := state.NewLockInfo() 155 info.Operation = "test-lost-lock" 156 id, err := sA.Lock(info) 157 if err != nil { 158 t.Fatal(err) 159 } 160 161 reLocked := make(chan struct{}) 162 testLockHook = func() { 163 close(reLocked) 164 testLockHook = nil 165 } 166 167 // now we use the second client to break the lock 168 kv := sB.(*remote.State).Client.(*RemoteClient).Client.KV() 169 _, err = kv.Delete(path+lockSuffix, nil) 170 if err != nil { 171 t.Fatal(err) 172 } 173 174 <-reLocked 175 176 if err := sA.Unlock(id); err != nil { 177 t.Fatal(err) 178 } 179 } 180 181 func TestConsul_lostLockConnection(t *testing.T) { 182 // create an "unreliable" network by closing all the consul client's 183 // network connections 184 conns := &unreliableConns{} 185 origDialFn := dialContext 186 defer func() { 187 dialContext = origDialFn 188 }() 189 dialContext = conns.DialContext 190 191 path := fmt.Sprintf("tf-unit/%s", time.Now().String()) 192 193 b := backend.TestBackendConfig(t, New(), map[string]interface{}{ 194 "address": srv.HTTPAddr, 195 "path": path, 196 }) 197 198 s, err := b.State(backend.DefaultStateName) 199 if err != nil { 200 t.Fatal(err) 201 } 202 203 info := state.NewLockInfo() 204 info.Operation = "test-lost-lock-connection" 205 id, err := s.Lock(info) 206 if err != nil { 207 t.Fatal(err) 208 } 209 210 // kill the connection a few times 211 for i := 0; i < 3; i++ { 212 dialed := conns.dialedDone() 213 // kill any open connections 214 conns.Kill() 215 // wait for a new connection to be dialed, and kill it again 216 <-dialed 217 } 218 219 if err := s.Unlock(id); err != nil { 220 t.Fatal("unlock error:", err) 221 } 222 } 223 224 type unreliableConns struct { 225 sync.Mutex 226 conns []net.Conn 227 dialCallback func() 228 } 229 230 func (u *unreliableConns) DialContext(ctx context.Context, netw, addr string) (net.Conn, error) { 231 u.Lock() 232 defer u.Unlock() 233 234 dialer := &net.Dialer{} 235 conn, err := dialer.DialContext(ctx, netw, addr) 236 if err != nil { 237 return nil, err 238 } 239 240 u.conns = append(u.conns, conn) 241 242 if u.dialCallback != nil { 243 u.dialCallback() 244 } 245 246 return conn, nil 247 } 248 249 func (u *unreliableConns) dialedDone() chan struct{} { 250 u.Lock() 251 defer u.Unlock() 252 dialed := make(chan struct{}) 253 u.dialCallback = func() { 254 defer close(dialed) 255 u.dialCallback = nil 256 } 257 258 return dialed 259 } 260 261 // Kill these with a deadline, just to make sure we don't end up with any EOFs 262 // that get ignored. 263 func (u *unreliableConns) Kill() { 264 u.Lock() 265 defer u.Unlock() 266 267 for _, conn := range u.conns { 268 conn.(*net.TCPConn).SetDeadline(time.Now()) 269 } 270 u.conns = nil 271 }