github.com/portworx/kvdb@v0.0.0-20241107215734-a185a966f535/zookeeper/kv_zookeeper_test.go (about) 1 package zookeeper 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "testing" 9 "time" 10 11 "github.com/portworx/kvdb" 12 "github.com/portworx/kvdb/test" 13 "github.com/stretchr/testify/assert" 14 ) 15 16 const ( 17 domain = "pwx/test" 18 dataDir = "/data/zookeeper" 19 ) 20 21 func TestAll(t *testing.T) { 22 test.RunBasic(New, t, Start, Stop) 23 } 24 25 func TestVersion(t *testing.T) { 26 fmt.Println("verify version") 27 version, err := Version("", nil) 28 assert.NoError(t, err, "Unexpected error on Version") 29 assert.Equal(t, kvdb.ZookeeperVersion1, version) 30 } 31 32 func TestZookeeperOps(t *testing.T) { 33 err := Start(true) 34 assert.NoError(t, err, "Unable to start kvdb") 35 // Wait for kvdb to start 36 time.Sleep(5 * time.Second) 37 38 kv, err := New(domain, nil, nil, nil) 39 if err != nil { 40 t.Fatalf(err.Error()) 41 } 42 43 testName(t, kv) 44 testEmptyValue(t, kv) 45 testCreateWithTTL(t, kv) 46 testPutWithTTL(t, kv) 47 testUpdateWithTTL(t, kv) 48 testCreateEphemeral(t) 49 testLockBetweenClientRestarts(t) 50 testLockWithIDBetweenClientRestarts(t) 51 testLockWithTimeoutBetweenClientRestarts(t) 52 53 err = Stop() 54 assert.NoError(t, err, "Unable to stop kvdb") 55 } 56 57 func testName(t *testing.T, kv kvdb.Kvdb) { 58 fmt.Println("verify name") 59 assert.Equal(t, Name, kv.String()) 60 } 61 62 func testEmptyValue(t *testing.T, kv kvdb.Kvdb) { 63 fmt.Println("create with empty value") 64 key := "empty/value" 65 kv.Delete(key) 66 67 _, err := kv.Create(key, []byte(""), 0) 68 assert.Equal(t, kvdb.ErrEmptyValue, err) 69 70 _, err = kv.Put(key, []byte(""), 0) 71 assert.Equal(t, kvdb.ErrEmptyValue, err) 72 73 _, err = kv.Create(key, []byte("test"), 0) 74 assert.NoError(t, err, "Unexpected error on create") 75 76 _, err = kv.Update(key, []byte(""), 0) 77 assert.Equal(t, kvdb.ErrEmptyValue, err) 78 79 // The parent for the key will internally have empty 80 // value and hence should return ErrNotFound error 81 _, err = kv.Get("/empty") 82 assert.Equal(t, kvdb.ErrNotFound, err) 83 84 var str string 85 _, err = kv.GetVal("/empty", &str) 86 assert.Equal(t, kvdb.ErrNotFound, err) 87 } 88 89 func testCreateWithTTL(t *testing.T, kv kvdb.Kvdb) { 90 fmt.Println("create with ttl") 91 key := "create/foottl" 92 kv.Delete(key) 93 94 _, err := kv.Create(key, []byte("barttl"), 10) 95 assert.Equal(t, kvdb.ErrTTLNotSupported, err) 96 } 97 98 func testPutWithTTL(t *testing.T, kv kvdb.Kvdb) { 99 fmt.Println("put with ttl") 100 key := "put/foottl" 101 kv.Delete(key) 102 103 _, err := kv.Put(key, []byte("barttl"), 10) 104 assert.Equal(t, kvdb.ErrTTLNotSupported, err) 105 } 106 107 func testUpdateWithTTL(t *testing.T, kv kvdb.Kvdb) { 108 fmt.Println("update with ttl") 109 key := "update/foottl" 110 kv.Delete(key) 111 112 _, err := kv.Create(key, []byte("bar"), 0) 113 assert.NoError(t, err, "Unexpected error on create") 114 115 _, err = kv.Update(key, []byte("barttl"), 10) 116 assert.Equal(t, kvdb.ErrTTLNotSupported, err) 117 } 118 119 func testCreateEphemeral(t *testing.T) { 120 fmt.Println("create ephemeral node") 121 zk, err := newClient(domain, nil, nil, nil) 122 assert.NoError(t, err) 123 assert.NotNil(t, zk) 124 125 key := "create/ephemeral" 126 value := []byte("val") 127 zk.Delete(key) 128 129 // Should fail for empty value 130 kvp, err := zk.createEphemeral(key, []byte("")) 131 assert.Equal(t, kvdb.ErrEmptyValue, err) 132 133 kvp, err = zk.createEphemeral(key, value) 134 assert.NoError(t, err, "Unexpected error on ephemeral create") 135 136 defer func() { 137 zk.Delete(key) 138 }() 139 assert.Equal(t, kvp.Action, kvdb.KVCreate, 140 "Expected action KVCreate, action %v", kvp.Action) 141 assert.Equal(t, value, kvp.Value) 142 assert.Equal(t, uint64(1), kvp.ModifiedIndex) 143 144 _, err = zk.createEphemeral(key, []byte("val")) 145 assert.Equal(t, kvdb.ErrExist, err) 146 } 147 148 func testLockBetweenClientRestarts(t *testing.T) { 149 fmt.Println("Lock between client restarts") 150 zk, err := newClient(domain, nil, nil, nil) 151 assert.NoError(t, err, "Unable to create a client") 152 assert.NotNil(t, zk) 153 154 zk.SetLockHoldDuration(time.Minute) 155 156 // Lock before restarting client 157 kvPair, err := zk.Lock("lock_key") 158 assert.NoError(t, err, "Unable to take a lock") 159 160 // Stopping client connection 161 zk.closeClient() 162 163 // We don't need to wait for session timeout, as we are doing 164 // a proper session close. In this case, the server will delete 165 // all ephemeral nodes created in that session immediately. 166 167 // Reconnecting the client 168 zk, err = newClient(domain, nil, nil, nil) 169 assert.NoError(t, err, "Unable to reconnect client") 170 171 // Locking again should succeed as the previous client died 172 lockChan := make(chan struct{}) 173 go func() { 174 kvPair, err = zk.Lock("lock_key") 175 lockChan <- struct{}{} 176 }() 177 select { 178 case <-time.After(5 * time.Second): 179 assert.Fail(t, "Unable to take a lock even when previous session was expired") 180 case <-lockChan: 181 } 182 err = zk.Unlock(kvPair) 183 assert.NoError(t, err, "Unable to unlock") 184 } 185 186 func testLockWithIDBetweenClientRestarts(t *testing.T) { 187 fmt.Println("LockWithID between client restarts") 188 zk, err := newClient(domain, nil, nil, nil) 189 assert.NoError(t, err) 190 assert.NotNil(t, zk) 191 192 zk.SetLockHoldDuration(time.Minute) 193 194 // Lock before restarting client 195 kvPair, err := zk.LockWithID("lock_key", "lock_with_id") 196 assert.NoError(t, err, "Unable to take a lock") 197 198 // Stopping client connection 199 zk.closeClient() 200 201 // We don't need to wait for session timeout, as we are doing 202 // a proper session close. In this case, the server will delete 203 // all ephemeral nodes created in that session immediately. 204 205 // Reconnecting the client 206 zk, err = newClient(domain, nil, nil, nil) 207 assert.NoError(t, err, "Unable to reconnect client") 208 209 // Locking again should succeed as the previous client died 210 lockChan := make(chan struct{}) 211 go func() { 212 kvPair, err = zk.LockWithID("lock_key", "lock_with_id") 213 lockChan <- struct{}{} 214 }() 215 select { 216 case <-time.After(5 * time.Second): 217 assert.Fail(t, "Unable to take a lock even when previous session was expired") 218 case <-lockChan: 219 } 220 err = zk.Unlock(kvPair) 221 assert.NoError(t, err, "Unable to unlock") 222 } 223 224 func testLockWithTimeoutBetweenClientRestarts(t *testing.T) { 225 fmt.Println("LockWithTimeout between client restarts") 226 zk, err := newClient(domain, nil, nil, nil) 227 assert.NoError(t, err) 228 assert.NotNil(t, zk) 229 230 zk.SetLockHoldDuration(time.Minute) 231 232 // Lock before restarting client 233 kvPair, err := zk.LockWithTimeout("lock_key", "lock_with_id", 234 10*time.Second, time.Minute) 235 assert.NoError(t, err, "Unable to take a lock") 236 237 // Stopping client connection 238 zk.closeClient() 239 240 // We don't need to wait for session timeout, as we are doing 241 // a proper session close. In this case, the server will delete 242 // all ephemeral nodes created in that session immediately. 243 244 // Reconnecting the client 245 zk, err = newClient(domain, nil, nil, nil) 246 assert.NoError(t, err, "Unable to reconnect client") 247 248 // Locking again should succeed as the previous client died 249 lockChan := make(chan struct{}) 250 go func() { 251 kvPair, err = zk.LockWithTimeout("lock_key", "lock_with_id", 252 10*time.Second, time.Minute) 253 lockChan <- struct{}{} 254 }() 255 select { 256 case <-time.After(5 * time.Second): 257 assert.Fail(t, "Unable to take a lock even when previous session was expired") 258 case <-lockChan: 259 } 260 err = zk.Unlock(kvPair) 261 assert.NoError(t, err, "Unable to unlock") 262 } 263 264 func Start(removeData bool) error { 265 if removeData { 266 err := os.RemoveAll(dataDir) 267 if err != nil { 268 return err 269 } 270 } 271 err := os.MkdirAll(dataDir, 0644) 272 if err != nil { 273 return err 274 } 275 err = ioutil.WriteFile(dataDir+"/myid", []byte("1"), 0644) 276 if err != nil { 277 return err 278 } 279 cmd := exec.Command("/tmp/test-zookeeper/bin/zkServer.sh", "start") 280 err = cmd.Start() 281 time.Sleep(5 * time.Second) 282 return err 283 } 284 285 func Stop() error { 286 cmd := exec.Command("/tmp/test-zookeeper/bin/zkServer.sh", "stop") 287 err := cmd.Start() 288 time.Sleep(5 * time.Second) 289 return err 290 }