go.etcd.io/etcd@v3.3.27+incompatible/store/store_test.go (about) 1 // Copyright 2015 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package store_test 16 17 import ( 18 "testing" 19 "time" 20 21 etcdErr "github.com/coreos/etcd/error" 22 "github.com/coreos/etcd/pkg/testutil" 23 "github.com/coreos/etcd/store" 24 ) 25 26 type StoreCloser interface { 27 store.Store 28 Close() 29 } 30 31 func TestNewStoreWithNamespaces(t *testing.T) { 32 s := newTestStore(t, "/0", "/1") 33 defer s.Close() 34 35 _, err := s.Get("/0", false, false) 36 testutil.AssertNil(t, err) 37 _, err = s.Get("/1", false, false) 38 testutil.AssertNil(t, err) 39 } 40 41 // Ensure that the store can retrieve an existing value. 42 func TestStoreGetValue(t *testing.T) { 43 s := newTestStore(t) 44 defer s.Close() 45 46 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 47 var eidx uint64 = 1 48 e, err := s.Get("/foo", false, false) 49 testutil.AssertNil(t, err) 50 testutil.AssertEqual(t, e.EtcdIndex, eidx) 51 testutil.AssertEqual(t, e.Action, "get") 52 testutil.AssertEqual(t, e.Node.Key, "/foo") 53 testutil.AssertEqual(t, *e.Node.Value, "bar") 54 } 55 56 // Ensure that the store can retrieve a directory in sorted order. 57 func TestStoreGetSorted(t *testing.T) { 58 s := newTestStore(t) 59 defer s.Close() 60 61 s.Create("/foo", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 62 s.Create("/foo/x", false, "0", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 63 s.Create("/foo/z", false, "0", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 64 s.Create("/foo/y", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 65 s.Create("/foo/y/a", false, "0", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 66 s.Create("/foo/y/b", false, "0", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 67 var eidx uint64 = 6 68 e, err := s.Get("/foo", true, true) 69 testutil.AssertNil(t, err) 70 testutil.AssertEqual(t, e.EtcdIndex, eidx) 71 72 var yNodes store.NodeExterns 73 sortedStrings := []string{"/foo/x", "/foo/y", "/foo/z"} 74 for i := range e.Node.Nodes { 75 node := e.Node.Nodes[i] 76 if node.Key != sortedStrings[i] { 77 t.Errorf("expect key = %s, got key = %s", sortedStrings[i], node.Key) 78 } 79 if node.Key == "/foo/y" { 80 yNodes = node.Nodes 81 } 82 } 83 84 sortedStrings = []string{"/foo/y/a", "/foo/y/b"} 85 for i := range yNodes { 86 node := yNodes[i] 87 if node.Key != sortedStrings[i] { 88 t.Errorf("expect key = %s, got key = %s", sortedStrings[i], node.Key) 89 } 90 } 91 } 92 93 func TestSet(t *testing.T) { 94 s := newTestStore(t) 95 defer s.Close() 96 97 // Set /foo="" 98 var eidx uint64 = 1 99 e, err := s.Set("/foo", false, "", store.TTLOptionSet{ExpireTime: store.Permanent}) 100 testutil.AssertNil(t, err) 101 testutil.AssertEqual(t, e.EtcdIndex, eidx) 102 testutil.AssertEqual(t, e.Action, "set") 103 testutil.AssertEqual(t, e.Node.Key, "/foo") 104 testutil.AssertFalse(t, e.Node.Dir) 105 testutil.AssertEqual(t, *e.Node.Value, "") 106 testutil.AssertNil(t, e.Node.Nodes) 107 testutil.AssertNil(t, e.Node.Expiration) 108 testutil.AssertEqual(t, e.Node.TTL, int64(0)) 109 testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(1)) 110 111 // Set /foo="bar" 112 eidx = 2 113 e, err = s.Set("/foo", false, "bar", store.TTLOptionSet{ExpireTime: store.Permanent}) 114 testutil.AssertNil(t, err) 115 testutil.AssertEqual(t, e.EtcdIndex, eidx) 116 testutil.AssertEqual(t, e.Action, "set") 117 testutil.AssertEqual(t, e.Node.Key, "/foo") 118 testutil.AssertFalse(t, e.Node.Dir) 119 testutil.AssertEqual(t, *e.Node.Value, "bar") 120 testutil.AssertNil(t, e.Node.Nodes) 121 testutil.AssertNil(t, e.Node.Expiration) 122 testutil.AssertEqual(t, e.Node.TTL, int64(0)) 123 testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(2)) 124 // check prevNode 125 testutil.AssertNotNil(t, e.PrevNode) 126 testutil.AssertEqual(t, e.PrevNode.Key, "/foo") 127 testutil.AssertEqual(t, *e.PrevNode.Value, "") 128 testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1)) 129 // Set /foo="baz" (for testing prevNode) 130 eidx = 3 131 e, err = s.Set("/foo", false, "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 132 testutil.AssertNil(t, err) 133 testutil.AssertEqual(t, e.EtcdIndex, eidx) 134 testutil.AssertEqual(t, e.Action, "set") 135 testutil.AssertEqual(t, e.Node.Key, "/foo") 136 testutil.AssertFalse(t, e.Node.Dir) 137 testutil.AssertEqual(t, *e.Node.Value, "baz") 138 testutil.AssertNil(t, e.Node.Nodes) 139 testutil.AssertNil(t, e.Node.Expiration) 140 testutil.AssertEqual(t, e.Node.TTL, int64(0)) 141 testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(3)) 142 // check prevNode 143 testutil.AssertNotNil(t, e.PrevNode) 144 testutil.AssertEqual(t, e.PrevNode.Key, "/foo") 145 testutil.AssertEqual(t, *e.PrevNode.Value, "bar") 146 testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(2)) 147 148 // Set /dir as a directory 149 eidx = 4 150 e, err = s.Set("/dir", true, "", store.TTLOptionSet{ExpireTime: store.Permanent}) 151 testutil.AssertNil(t, err) 152 testutil.AssertEqual(t, e.EtcdIndex, eidx) 153 testutil.AssertEqual(t, e.Action, "set") 154 testutil.AssertEqual(t, e.Node.Key, "/dir") 155 testutil.AssertTrue(t, e.Node.Dir) 156 testutil.AssertNil(t, e.Node.Value) 157 testutil.AssertNil(t, e.Node.Nodes) 158 testutil.AssertNil(t, e.Node.Expiration) 159 testutil.AssertEqual(t, e.Node.TTL, int64(0)) 160 testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(4)) 161 } 162 163 // Ensure that the store can create a new key if it doesn't already exist. 164 func TestStoreCreateValue(t *testing.T) { 165 s := newTestStore(t) 166 defer s.Close() 167 168 // Create /foo=bar 169 var eidx uint64 = 1 170 e, err := s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 171 testutil.AssertNil(t, err) 172 testutil.AssertEqual(t, e.EtcdIndex, eidx) 173 testutil.AssertEqual(t, e.Action, "create") 174 testutil.AssertEqual(t, e.Node.Key, "/foo") 175 testutil.AssertFalse(t, e.Node.Dir) 176 testutil.AssertEqual(t, *e.Node.Value, "bar") 177 testutil.AssertNil(t, e.Node.Nodes) 178 testutil.AssertNil(t, e.Node.Expiration) 179 testutil.AssertEqual(t, e.Node.TTL, int64(0)) 180 testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(1)) 181 182 // Create /empty="" 183 eidx = 2 184 e, err = s.Create("/empty", false, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 185 testutil.AssertNil(t, err) 186 testutil.AssertEqual(t, e.EtcdIndex, eidx) 187 testutil.AssertEqual(t, e.Action, "create") 188 testutil.AssertEqual(t, e.Node.Key, "/empty") 189 testutil.AssertFalse(t, e.Node.Dir) 190 testutil.AssertEqual(t, *e.Node.Value, "") 191 testutil.AssertNil(t, e.Node.Nodes) 192 testutil.AssertNil(t, e.Node.Expiration) 193 testutil.AssertEqual(t, e.Node.TTL, int64(0)) 194 testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(2)) 195 196 } 197 198 // Ensure that the store can create a new directory if it doesn't already exist. 199 func TestStoreCreateDirectory(t *testing.T) { 200 s := newTestStore(t) 201 defer s.Close() 202 203 var eidx uint64 = 1 204 e, err := s.Create("/foo", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 205 testutil.AssertNil(t, err) 206 testutil.AssertEqual(t, e.EtcdIndex, eidx) 207 testutil.AssertEqual(t, e.Action, "create") 208 testutil.AssertEqual(t, e.Node.Key, "/foo") 209 testutil.AssertTrue(t, e.Node.Dir) 210 } 211 212 // Ensure that the store fails to create a key if it already exists. 213 func TestStoreCreateFailsIfExists(t *testing.T) { 214 s := newTestStore(t) 215 defer s.Close() 216 217 // create /foo as dir 218 s.Create("/foo", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 219 220 // create /foo as dir again 221 e, _err := s.Create("/foo", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 222 err := _err.(*etcdErr.Error) 223 testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeNodeExist) 224 testutil.AssertEqual(t, err.Message, "Key already exists") 225 testutil.AssertEqual(t, err.Cause, "/foo") 226 testutil.AssertEqual(t, err.Index, uint64(1)) 227 testutil.AssertNil(t, e) 228 } 229 230 // Ensure that the store can update a key if it already exists. 231 func TestStoreUpdateValue(t *testing.T) { 232 s := newTestStore(t) 233 defer s.Close() 234 235 // create /foo=bar 236 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 237 // update /foo="bzr" 238 var eidx uint64 = 2 239 e, err := s.Update("/foo", "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 240 testutil.AssertNil(t, err) 241 testutil.AssertEqual(t, e.EtcdIndex, eidx) 242 testutil.AssertEqual(t, e.Action, "update") 243 testutil.AssertEqual(t, e.Node.Key, "/foo") 244 testutil.AssertFalse(t, e.Node.Dir) 245 testutil.AssertEqual(t, *e.Node.Value, "baz") 246 testutil.AssertEqual(t, e.Node.TTL, int64(0)) 247 testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(2)) 248 // check prevNode 249 testutil.AssertEqual(t, e.PrevNode.Key, "/foo") 250 testutil.AssertEqual(t, *e.PrevNode.Value, "bar") 251 testutil.AssertEqual(t, e.PrevNode.TTL, int64(0)) 252 testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1)) 253 254 e, _ = s.Get("/foo", false, false) 255 testutil.AssertEqual(t, *e.Node.Value, "baz") 256 testutil.AssertEqual(t, e.EtcdIndex, eidx) 257 258 // update /foo="" 259 eidx = 3 260 e, err = s.Update("/foo", "", store.TTLOptionSet{ExpireTime: store.Permanent}) 261 testutil.AssertNil(t, err) 262 testutil.AssertEqual(t, e.EtcdIndex, eidx) 263 testutil.AssertEqual(t, e.Action, "update") 264 testutil.AssertEqual(t, e.Node.Key, "/foo") 265 testutil.AssertFalse(t, e.Node.Dir) 266 testutil.AssertEqual(t, *e.Node.Value, "") 267 testutil.AssertEqual(t, e.Node.TTL, int64(0)) 268 testutil.AssertEqual(t, e.Node.ModifiedIndex, uint64(3)) 269 // check prevNode 270 testutil.AssertEqual(t, e.PrevNode.Key, "/foo") 271 testutil.AssertEqual(t, *e.PrevNode.Value, "baz") 272 testutil.AssertEqual(t, e.PrevNode.TTL, int64(0)) 273 testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(2)) 274 275 e, _ = s.Get("/foo", false, false) 276 testutil.AssertEqual(t, e.EtcdIndex, eidx) 277 testutil.AssertEqual(t, *e.Node.Value, "") 278 } 279 280 // Ensure that the store cannot update a directory. 281 func TestStoreUpdateFailsIfDirectory(t *testing.T) { 282 s := newTestStore(t) 283 defer s.Close() 284 285 s.Create("/foo", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 286 e, _err := s.Update("/foo", "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 287 err := _err.(*etcdErr.Error) 288 testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeNotFile) 289 testutil.AssertEqual(t, err.Message, "Not a file") 290 testutil.AssertEqual(t, err.Cause, "/foo") 291 testutil.AssertNil(t, e) 292 } 293 294 // Ensure that the store can delete a value. 295 func TestStoreDeleteValue(t *testing.T) { 296 s := newTestStore(t) 297 defer s.Close() 298 299 var eidx uint64 = 2 300 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 301 e, err := s.Delete("/foo", false, false) 302 testutil.AssertNil(t, err) 303 testutil.AssertEqual(t, e.EtcdIndex, eidx) 304 testutil.AssertEqual(t, e.Action, "delete") 305 // check prevNode 306 testutil.AssertNotNil(t, e.PrevNode) 307 testutil.AssertEqual(t, e.PrevNode.Key, "/foo") 308 testutil.AssertEqual(t, *e.PrevNode.Value, "bar") 309 } 310 311 // Ensure that the store can delete a directory if recursive is specified. 312 func TestStoreDeleteDirectory(t *testing.T) { 313 s := newTestStore(t) 314 defer s.Close() 315 316 // create directory /foo 317 var eidx uint64 = 2 318 s.Create("/foo", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 319 // delete /foo with dir = true and recursive = false 320 // this should succeed, since the directory is empty 321 e, err := s.Delete("/foo", true, false) 322 testutil.AssertNil(t, err) 323 testutil.AssertEqual(t, e.EtcdIndex, eidx) 324 testutil.AssertEqual(t, e.Action, "delete") 325 // check prevNode 326 testutil.AssertNotNil(t, e.PrevNode) 327 testutil.AssertEqual(t, e.PrevNode.Key, "/foo") 328 testutil.AssertEqual(t, e.PrevNode.Dir, true) 329 330 // create directory /foo and directory /foo/bar 331 _, err = s.Create("/foo/bar", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 332 testutil.AssertNil(t, err) 333 // delete /foo with dir = true and recursive = false 334 // this should fail, since the directory is not empty 335 _, err = s.Delete("/foo", true, false) 336 testutil.AssertNotNil(t, err) 337 338 // delete /foo with dir=false and recursive = true 339 // this should succeed, since recursive implies dir=true 340 // and recursively delete should be able to delete all 341 // items under the given directory 342 e, err = s.Delete("/foo", false, true) 343 testutil.AssertNil(t, err) 344 testutil.AssertEqual(t, e.Action, "delete") 345 346 } 347 348 // Ensure that the store cannot delete a directory if both of recursive 349 // and dir are not specified. 350 func TestStoreDeleteDirectoryFailsIfNonRecursiveAndDir(t *testing.T) { 351 s := newTestStore(t) 352 defer s.Close() 353 354 s.Create("/foo", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 355 e, _err := s.Delete("/foo", false, false) 356 err := _err.(*etcdErr.Error) 357 testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeNotFile) 358 testutil.AssertEqual(t, err.Message, "Not a file") 359 testutil.AssertNil(t, e) 360 } 361 362 func TestRootRdOnly(t *testing.T) { 363 s := newTestStore(t, "/0") 364 defer s.Close() 365 366 for _, tt := range []string{"/", "/0"} { 367 _, err := s.Set(tt, true, "", store.TTLOptionSet{ExpireTime: store.Permanent}) 368 testutil.AssertNotNil(t, err) 369 370 _, err = s.Delete(tt, true, true) 371 testutil.AssertNotNil(t, err) 372 373 _, err = s.Create(tt, true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 374 testutil.AssertNotNil(t, err) 375 376 _, err = s.Update(tt, "", store.TTLOptionSet{ExpireTime: store.Permanent}) 377 testutil.AssertNotNil(t, err) 378 379 _, err = s.CompareAndSwap(tt, "", 0, "", store.TTLOptionSet{ExpireTime: store.Permanent}) 380 testutil.AssertNotNil(t, err) 381 } 382 } 383 384 func TestStoreCompareAndDeletePrevValue(t *testing.T) { 385 s := newTestStore(t) 386 defer s.Close() 387 388 var eidx uint64 = 2 389 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 390 e, err := s.CompareAndDelete("/foo", "bar", 0) 391 testutil.AssertNil(t, err) 392 testutil.AssertEqual(t, e.EtcdIndex, eidx) 393 testutil.AssertEqual(t, e.Action, "compareAndDelete") 394 testutil.AssertEqual(t, e.Node.Key, "/foo") 395 396 // check prevNode 397 testutil.AssertNotNil(t, e.PrevNode) 398 testutil.AssertEqual(t, e.PrevNode.Key, "/foo") 399 testutil.AssertEqual(t, *e.PrevNode.Value, "bar") 400 testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1)) 401 testutil.AssertEqual(t, e.PrevNode.CreatedIndex, uint64(1)) 402 } 403 404 func TestStoreCompareAndDeletePrevValueFailsIfNotMatch(t *testing.T) { 405 s := newTestStore(t) 406 defer s.Close() 407 408 var eidx uint64 = 1 409 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 410 e, _err := s.CompareAndDelete("/foo", "baz", 0) 411 err := _err.(*etcdErr.Error) 412 testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeTestFailed) 413 testutil.AssertEqual(t, err.Message, "Compare failed") 414 testutil.AssertNil(t, e) 415 e, _ = s.Get("/foo", false, false) 416 testutil.AssertEqual(t, e.EtcdIndex, eidx) 417 testutil.AssertEqual(t, *e.Node.Value, "bar") 418 } 419 420 func TestStoreCompareAndDeletePrevIndex(t *testing.T) { 421 s := newTestStore(t) 422 defer s.Close() 423 424 var eidx uint64 = 2 425 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 426 e, err := s.CompareAndDelete("/foo", "", 1) 427 testutil.AssertNil(t, err) 428 testutil.AssertEqual(t, e.EtcdIndex, eidx) 429 testutil.AssertEqual(t, e.Action, "compareAndDelete") 430 // check prevNode 431 testutil.AssertNotNil(t, e.PrevNode) 432 testutil.AssertEqual(t, e.PrevNode.Key, "/foo") 433 testutil.AssertEqual(t, *e.PrevNode.Value, "bar") 434 testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1)) 435 testutil.AssertEqual(t, e.PrevNode.CreatedIndex, uint64(1)) 436 } 437 438 func TestStoreCompareAndDeletePrevIndexFailsIfNotMatch(t *testing.T) { 439 s := newTestStore(t) 440 defer s.Close() 441 442 var eidx uint64 = 1 443 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 444 e, _err := s.CompareAndDelete("/foo", "", 100) 445 testutil.AssertNotNil(t, _err) 446 err := _err.(*etcdErr.Error) 447 testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeTestFailed) 448 testutil.AssertEqual(t, err.Message, "Compare failed") 449 testutil.AssertNil(t, e) 450 e, _ = s.Get("/foo", false, false) 451 testutil.AssertEqual(t, e.EtcdIndex, eidx) 452 testutil.AssertEqual(t, *e.Node.Value, "bar") 453 } 454 455 // Ensure that the store cannot delete a directory. 456 func TestStoreCompareAndDeleteDirectoryFail(t *testing.T) { 457 s := newTestStore(t) 458 defer s.Close() 459 460 s.Create("/foo", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 461 _, _err := s.CompareAndDelete("/foo", "", 0) 462 testutil.AssertNotNil(t, _err) 463 err := _err.(*etcdErr.Error) 464 testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeNotFile) 465 } 466 467 // Ensure that the store can conditionally update a key if it has a previous value. 468 func TestStoreCompareAndSwapPrevValue(t *testing.T) { 469 s := newTestStore(t) 470 defer s.Close() 471 472 var eidx uint64 = 2 473 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 474 e, err := s.CompareAndSwap("/foo", "bar", 0, "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 475 testutil.AssertNil(t, err) 476 testutil.AssertEqual(t, e.EtcdIndex, eidx) 477 testutil.AssertEqual(t, e.Action, "compareAndSwap") 478 testutil.AssertEqual(t, *e.Node.Value, "baz") 479 // check prevNode 480 testutil.AssertNotNil(t, e.PrevNode) 481 testutil.AssertEqual(t, e.PrevNode.Key, "/foo") 482 testutil.AssertEqual(t, *e.PrevNode.Value, "bar") 483 testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1)) 484 testutil.AssertEqual(t, e.PrevNode.CreatedIndex, uint64(1)) 485 486 e, _ = s.Get("/foo", false, false) 487 testutil.AssertEqual(t, *e.Node.Value, "baz") 488 } 489 490 // Ensure that the store cannot conditionally update a key if it has the wrong previous value. 491 func TestStoreCompareAndSwapPrevValueFailsIfNotMatch(t *testing.T) { 492 s := newTestStore(t) 493 defer s.Close() 494 var eidx uint64 = 1 495 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 496 e, _err := s.CompareAndSwap("/foo", "wrong_value", 0, "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 497 err := _err.(*etcdErr.Error) 498 testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeTestFailed) 499 testutil.AssertEqual(t, err.Message, "Compare failed") 500 testutil.AssertNil(t, e) 501 e, _ = s.Get("/foo", false, false) 502 testutil.AssertEqual(t, *e.Node.Value, "bar") 503 testutil.AssertEqual(t, e.EtcdIndex, eidx) 504 } 505 506 // Ensure that the store can conditionally update a key if it has a previous index. 507 func TestStoreCompareAndSwapPrevIndex(t *testing.T) { 508 s := newTestStore(t) 509 defer s.Close() 510 var eidx uint64 = 2 511 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 512 e, err := s.CompareAndSwap("/foo", "", 1, "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 513 testutil.AssertNil(t, err) 514 testutil.AssertEqual(t, e.EtcdIndex, eidx) 515 testutil.AssertEqual(t, e.Action, "compareAndSwap") 516 testutil.AssertEqual(t, *e.Node.Value, "baz") 517 // check prevNode 518 testutil.AssertNotNil(t, e.PrevNode) 519 testutil.AssertEqual(t, e.PrevNode.Key, "/foo") 520 testutil.AssertEqual(t, *e.PrevNode.Value, "bar") 521 testutil.AssertEqual(t, e.PrevNode.ModifiedIndex, uint64(1)) 522 testutil.AssertEqual(t, e.PrevNode.CreatedIndex, uint64(1)) 523 524 e, _ = s.Get("/foo", false, false) 525 testutil.AssertEqual(t, *e.Node.Value, "baz") 526 testutil.AssertEqual(t, e.EtcdIndex, eidx) 527 } 528 529 // Ensure that the store cannot conditionally update a key if it has the wrong previous index. 530 func TestStoreCompareAndSwapPrevIndexFailsIfNotMatch(t *testing.T) { 531 s := newTestStore(t) 532 defer s.Close() 533 var eidx uint64 = 1 534 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 535 e, _err := s.CompareAndSwap("/foo", "", 100, "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 536 err := _err.(*etcdErr.Error) 537 testutil.AssertEqual(t, err.ErrorCode, etcdErr.EcodeTestFailed) 538 testutil.AssertEqual(t, err.Message, "Compare failed") 539 testutil.AssertNil(t, e) 540 e, _ = s.Get("/foo", false, false) 541 testutil.AssertEqual(t, e.EtcdIndex, eidx) 542 testutil.AssertEqual(t, *e.Node.Value, "bar") 543 } 544 545 // Ensure that the store can watch for key creation. 546 func TestStoreWatchCreate(t *testing.T) { 547 s := newTestStore(t) 548 defer s.Close() 549 var eidx uint64 = 0 550 w, _ := s.Watch("/foo", false, false, 0) 551 c := w.EventChan() 552 testutil.AssertEqual(t, w.StartIndex(), eidx) 553 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 554 eidx = 1 555 e := timeoutSelect(t, c) 556 testutil.AssertEqual(t, e.EtcdIndex, eidx) 557 testutil.AssertEqual(t, e.Action, "create") 558 testutil.AssertEqual(t, e.Node.Key, "/foo") 559 select { 560 case e = <-w.EventChan(): 561 testutil.AssertNil(t, e) 562 case <-time.After(100 * time.Millisecond): 563 } 564 } 565 566 // Ensure that the store can watch for recursive key creation. 567 func TestStoreWatchRecursiveCreate(t *testing.T) { 568 s := newTestStore(t) 569 defer s.Close() 570 var eidx uint64 = 0 571 w, err := s.Watch("/foo", true, false, 0) 572 testutil.AssertNil(t, err) 573 testutil.AssertEqual(t, w.StartIndex(), eidx) 574 eidx = 1 575 s.Create("/foo/bar", false, "baz", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 576 e := timeoutSelect(t, w.EventChan()) 577 testutil.AssertEqual(t, e.EtcdIndex, eidx) 578 testutil.AssertEqual(t, e.Action, "create") 579 testutil.AssertEqual(t, e.Node.Key, "/foo/bar") 580 } 581 582 // Ensure that the store can watch for key updates. 583 func TestStoreWatchUpdate(t *testing.T) { 584 s := newTestStore(t) 585 defer s.Close() 586 var eidx uint64 = 1 587 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 588 w, _ := s.Watch("/foo", false, false, 0) 589 testutil.AssertEqual(t, w.StartIndex(), eidx) 590 eidx = 2 591 s.Update("/foo", "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 592 e := timeoutSelect(t, w.EventChan()) 593 testutil.AssertEqual(t, e.EtcdIndex, eidx) 594 testutil.AssertEqual(t, e.Action, "update") 595 testutil.AssertEqual(t, e.Node.Key, "/foo") 596 } 597 598 // Ensure that the store can watch for recursive key updates. 599 func TestStoreWatchRecursiveUpdate(t *testing.T) { 600 s := newTestStore(t) 601 defer s.Close() 602 var eidx uint64 = 1 603 s.Create("/foo/bar", false, "baz", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 604 w, err := s.Watch("/foo", true, false, 0) 605 testutil.AssertNil(t, err) 606 testutil.AssertEqual(t, w.StartIndex(), eidx) 607 eidx = 2 608 s.Update("/foo/bar", "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 609 e := timeoutSelect(t, w.EventChan()) 610 testutil.AssertEqual(t, e.EtcdIndex, eidx) 611 testutil.AssertEqual(t, e.Action, "update") 612 testutil.AssertEqual(t, e.Node.Key, "/foo/bar") 613 } 614 615 // Ensure that the store can watch for key deletions. 616 func TestStoreWatchDelete(t *testing.T) { 617 s := newTestStore(t) 618 defer s.Close() 619 var eidx uint64 = 1 620 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 621 w, _ := s.Watch("/foo", false, false, 0) 622 testutil.AssertEqual(t, w.StartIndex(), eidx) 623 eidx = 2 624 s.Delete("/foo", false, false) 625 e := timeoutSelect(t, w.EventChan()) 626 testutil.AssertEqual(t, e.EtcdIndex, eidx) 627 testutil.AssertEqual(t, e.Action, "delete") 628 testutil.AssertEqual(t, e.Node.Key, "/foo") 629 } 630 631 // Ensure that the store can watch for recursive key deletions. 632 func TestStoreWatchRecursiveDelete(t *testing.T) { 633 s := newTestStore(t) 634 defer s.Close() 635 var eidx uint64 = 1 636 s.Create("/foo/bar", false, "baz", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 637 w, err := s.Watch("/foo", true, false, 0) 638 testutil.AssertNil(t, err) 639 testutil.AssertEqual(t, w.StartIndex(), eidx) 640 eidx = 2 641 s.Delete("/foo/bar", false, false) 642 e := timeoutSelect(t, w.EventChan()) 643 testutil.AssertEqual(t, e.EtcdIndex, eidx) 644 testutil.AssertEqual(t, e.Action, "delete") 645 testutil.AssertEqual(t, e.Node.Key, "/foo/bar") 646 } 647 648 // Ensure that the store can watch for CAS updates. 649 func TestStoreWatchCompareAndSwap(t *testing.T) { 650 s := newTestStore(t) 651 defer s.Close() 652 var eidx uint64 = 1 653 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 654 w, _ := s.Watch("/foo", false, false, 0) 655 testutil.AssertEqual(t, w.StartIndex(), eidx) 656 eidx = 2 657 s.CompareAndSwap("/foo", "bar", 0, "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 658 e := timeoutSelect(t, w.EventChan()) 659 testutil.AssertEqual(t, e.EtcdIndex, eidx) 660 testutil.AssertEqual(t, e.Action, "compareAndSwap") 661 testutil.AssertEqual(t, e.Node.Key, "/foo") 662 } 663 664 // Ensure that the store can watch for recursive CAS updates. 665 func TestStoreWatchRecursiveCompareAndSwap(t *testing.T) { 666 s := newTestStore(t) 667 defer s.Close() 668 var eidx uint64 = 1 669 s.Create("/foo/bar", false, "baz", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 670 w, _ := s.Watch("/foo", true, false, 0) 671 testutil.AssertEqual(t, w.StartIndex(), eidx) 672 eidx = 2 673 s.CompareAndSwap("/foo/bar", "baz", 0, "bat", store.TTLOptionSet{ExpireTime: store.Permanent}) 674 e := timeoutSelect(t, w.EventChan()) 675 testutil.AssertEqual(t, e.EtcdIndex, eidx) 676 testutil.AssertEqual(t, e.Action, "compareAndSwap") 677 testutil.AssertEqual(t, e.Node.Key, "/foo/bar") 678 } 679 680 // Ensure that the store can watch in streaming mode. 681 func TestStoreWatchStream(t *testing.T) { 682 s := newTestStore(t) 683 defer s.Close() 684 var eidx uint64 = 1 685 w, _ := s.Watch("/foo", false, true, 0) 686 // first modification 687 s.Create("/foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 688 e := timeoutSelect(t, w.EventChan()) 689 testutil.AssertEqual(t, e.EtcdIndex, eidx) 690 testutil.AssertEqual(t, e.Action, "create") 691 testutil.AssertEqual(t, e.Node.Key, "/foo") 692 testutil.AssertEqual(t, *e.Node.Value, "bar") 693 select { 694 case e = <-w.EventChan(): 695 testutil.AssertNil(t, e) 696 case <-time.After(100 * time.Millisecond): 697 } 698 // second modification 699 eidx = 2 700 s.Update("/foo", "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 701 e = timeoutSelect(t, w.EventChan()) 702 testutil.AssertEqual(t, e.EtcdIndex, eidx) 703 testutil.AssertEqual(t, e.Action, "update") 704 testutil.AssertEqual(t, e.Node.Key, "/foo") 705 testutil.AssertEqual(t, *e.Node.Value, "baz") 706 select { 707 case e = <-w.EventChan(): 708 testutil.AssertNil(t, e) 709 case <-time.After(100 * time.Millisecond): 710 } 711 } 712 713 // Ensure that the store can watch for hidden keys as long as it's an exact path match. 714 func TestStoreWatchCreateWithHiddenKey(t *testing.T) { 715 s := newTestStore(t) 716 defer s.Close() 717 var eidx uint64 = 1 718 w, _ := s.Watch("/_foo", false, false, 0) 719 s.Create("/_foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 720 e := timeoutSelect(t, w.EventChan()) 721 testutil.AssertEqual(t, e.EtcdIndex, eidx) 722 testutil.AssertEqual(t, e.Action, "create") 723 testutil.AssertEqual(t, e.Node.Key, "/_foo") 724 select { 725 case e = <-w.EventChan(): 726 testutil.AssertNil(t, e) 727 case <-time.After(100 * time.Millisecond): 728 } 729 } 730 731 // Ensure that the store doesn't see hidden key creates without an exact path match in recursive mode. 732 func TestStoreWatchRecursiveCreateWithHiddenKey(t *testing.T) { 733 s := newTestStore(t) 734 defer s.Close() 735 w, _ := s.Watch("/foo", true, false, 0) 736 s.Create("/foo/_bar", false, "baz", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 737 e := nbselect(w.EventChan()) 738 testutil.AssertNil(t, e) 739 w, _ = s.Watch("/foo", true, false, 0) 740 s.Create("/foo/_baz", true, "", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 741 select { 742 case e = <-w.EventChan(): 743 testutil.AssertNil(t, e) 744 case <-time.After(100 * time.Millisecond): 745 } 746 s.Create("/foo/_baz/quux", false, "quux", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 747 select { 748 case e = <-w.EventChan(): 749 testutil.AssertNil(t, e) 750 case <-time.After(100 * time.Millisecond): 751 } 752 } 753 754 // Ensure that the store doesn't see hidden key updates. 755 func TestStoreWatchUpdateWithHiddenKey(t *testing.T) { 756 s := newTestStore(t) 757 defer s.Close() 758 s.Create("/_foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 759 w, _ := s.Watch("/_foo", false, false, 0) 760 s.Update("/_foo", "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 761 e := timeoutSelect(t, w.EventChan()) 762 testutil.AssertEqual(t, e.Action, "update") 763 testutil.AssertEqual(t, e.Node.Key, "/_foo") 764 e = nbselect(w.EventChan()) 765 testutil.AssertNil(t, e) 766 } 767 768 // Ensure that the store doesn't see hidden key updates without an exact path match in recursive mode. 769 func TestStoreWatchRecursiveUpdateWithHiddenKey(t *testing.T) { 770 s := newTestStore(t) 771 defer s.Close() 772 s.Create("/foo/_bar", false, "baz", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 773 w, _ := s.Watch("/foo", true, false, 0) 774 s.Update("/foo/_bar", "baz", store.TTLOptionSet{ExpireTime: store.Permanent}) 775 e := nbselect(w.EventChan()) 776 testutil.AssertNil(t, e) 777 } 778 779 // Ensure that the store can watch for key deletions. 780 func TestStoreWatchDeleteWithHiddenKey(t *testing.T) { 781 s := newTestStore(t) 782 defer s.Close() 783 var eidx uint64 = 2 784 s.Create("/_foo", false, "bar", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 785 w, _ := s.Watch("/_foo", false, false, 0) 786 s.Delete("/_foo", false, false) 787 e := timeoutSelect(t, w.EventChan()) 788 testutil.AssertEqual(t, e.EtcdIndex, eidx) 789 testutil.AssertEqual(t, e.Action, "delete") 790 testutil.AssertEqual(t, e.Node.Key, "/_foo") 791 e = nbselect(w.EventChan()) 792 testutil.AssertNil(t, e) 793 } 794 795 // Ensure that the store doesn't see hidden key deletes without an exact path match in recursive mode. 796 func TestStoreWatchRecursiveDeleteWithHiddenKey(t *testing.T) { 797 s := newTestStore(t) 798 defer s.Close() 799 s.Create("/foo/_bar", false, "baz", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 800 w, _ := s.Watch("/foo", true, false, 0) 801 s.Delete("/foo/_bar", false, false) 802 e := nbselect(w.EventChan()) 803 testutil.AssertNil(t, e) 804 } 805 806 // Ensure that the store does see hidden key creates if watching deeper than a hidden key in recursive mode. 807 func TestStoreWatchRecursiveCreateDeeperThanHiddenKey(t *testing.T) { 808 s := newTestStore(t) 809 defer s.Close() 810 var eidx uint64 = 1 811 w, _ := s.Watch("/_foo/bar", true, false, 0) 812 s.Create("/_foo/bar/baz", false, "baz", false, store.TTLOptionSet{ExpireTime: store.Permanent}) 813 814 e := timeoutSelect(t, w.EventChan()) 815 testutil.AssertNotNil(t, e) 816 testutil.AssertEqual(t, e.EtcdIndex, eidx) 817 testutil.AssertEqual(t, e.Action, "create") 818 testutil.AssertEqual(t, e.Node.Key, "/_foo/bar/baz") 819 } 820 821 // Ensure that slow consumers are handled properly. 822 // 823 // Since Watcher.EventChan() has a buffer of size 100 we can only queue 100 824 // event per watcher. If the consumer cannot consume the event on time and 825 // another event arrives, the channel is closed and event is discarded. 826 // This test ensures that after closing the channel, the store can continue 827 // to operate correctly. 828 func TestStoreWatchSlowConsumer(t *testing.T) { 829 s := newTestStore(t) 830 defer s.Close() 831 s.Watch("/foo", true, true, 0) // stream must be true 832 // Fill watch channel with 100 events 833 for i := 1; i <= 100; i++ { 834 s.Set("/foo", false, string(i), store.TTLOptionSet{ExpireTime: store.Permanent}) // ok 835 } 836 // testutil.AssertEqual(t, s.WatcherHub.count, int64(1)) 837 s.Set("/foo", false, "101", store.TTLOptionSet{ExpireTime: store.Permanent}) // ok 838 // remove watcher 839 // testutil.AssertEqual(t, s.WatcherHub.count, int64(0)) 840 s.Set("/foo", false, "102", store.TTLOptionSet{ExpireTime: store.Permanent}) // must not panic 841 } 842 843 // Performs a non-blocking select on an event channel. 844 func nbselect(c <-chan *store.Event) *store.Event { 845 select { 846 case e := <-c: 847 return e 848 default: 849 return nil 850 } 851 } 852 853 // Performs a non-blocking select on an event channel. 854 func timeoutSelect(t *testing.T, c <-chan *store.Event) *store.Event { 855 select { 856 case e := <-c: 857 return e 858 case <-time.After(time.Second): 859 t.Errorf("timed out waiting on event") 860 return nil 861 } 862 }