github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/kvstore/etcd_test.go (about) 1 // Copyright 2016-2020 Authors of Cilium 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 // +build !privileged_tests 16 17 package kvstore 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "io" 24 "io/ioutil" 25 "path" 26 "testing" 27 "time" 28 29 "github.com/cilium/cilium/pkg/checker" 30 31 etcdAPI "go.etcd.io/etcd/clientv3" 32 . "gopkg.in/check.v1" 33 ) 34 35 type EtcdSuite struct { 36 BaseTests 37 } 38 39 var _ = Suite(&EtcdSuite{}) 40 41 func (e *EtcdSuite) SetUpTest(c *C) { 42 SetupDummy("etcd") 43 } 44 45 func (e *EtcdSuite) TearDownTest(c *C) { 46 Client().Close() 47 } 48 49 type MaintenanceMocker struct { 50 OnAlarmList func(ctx context.Context) (*etcdAPI.AlarmResponse, error) 51 OnAlarmDisarm func(ctx context.Context, m *etcdAPI.AlarmMember) (*etcdAPI.AlarmResponse, error) 52 OnDefragment func(ctx context.Context, endpoint string) (*etcdAPI.DefragmentResponse, error) 53 OnStatus func(ctx context.Context, endpoint string) (*etcdAPI.StatusResponse, error) 54 OnSnapshot func(ctx context.Context) (io.ReadCloser, error) 55 OnHashKV func(ctx context.Context, endpoint string, rev int64) (*etcdAPI.HashKVResponse, error) 56 OnMoveLeader func(ctx context.Context, transfereeID uint64) (*etcdAPI.MoveLeaderResponse, error) 57 } 58 59 func (m MaintenanceMocker) AlarmList(ctx context.Context) (*etcdAPI.AlarmResponse, error) { 60 if m.OnAlarmList != nil { 61 return m.OnAlarmList(ctx) 62 } 63 return nil, fmt.Errorf("Method AlarmList should not have been called") 64 } 65 66 func (m MaintenanceMocker) AlarmDisarm(ctx context.Context, am *etcdAPI.AlarmMember) (*etcdAPI.AlarmResponse, error) { 67 if m.OnAlarmDisarm != nil { 68 return m.OnAlarmDisarm(ctx, am) 69 } 70 return nil, fmt.Errorf("Method AlarmDisarm should not have been called") 71 } 72 73 func (m MaintenanceMocker) Defragment(ctx context.Context, endpoint string) (*etcdAPI.DefragmentResponse, error) { 74 if m.OnDefragment != nil { 75 return m.OnDefragment(ctx, endpoint) 76 } 77 return nil, fmt.Errorf("Method Defragment should not have been called") 78 } 79 80 func (m MaintenanceMocker) Status(ctx context.Context, endpoint string) (*etcdAPI.StatusResponse, error) { 81 if m.OnStatus != nil { 82 return m.OnStatus(ctx, endpoint) 83 } 84 return nil, fmt.Errorf("Method Status should not have been called") 85 } 86 87 func (m MaintenanceMocker) Snapshot(ctx context.Context) (io.ReadCloser, error) { 88 if m.OnSnapshot != nil { 89 return m.OnSnapshot(ctx) 90 } 91 return nil, fmt.Errorf("Method Snapshot should not have been called") 92 } 93 94 func (m MaintenanceMocker) HashKV(ctx context.Context, endpoint string, rev int64) (*etcdAPI.HashKVResponse, error) { 95 if m.OnSnapshot != nil { 96 return m.OnHashKV(ctx, endpoint, rev) 97 } 98 return nil, fmt.Errorf("Method HashKV should not have been called") 99 } 100 101 func (m MaintenanceMocker) MoveLeader(ctx context.Context, transfereeID uint64) (*etcdAPI.MoveLeaderResponse, error) { 102 if m.OnSnapshot != nil { 103 return m.OnMoveLeader(ctx, transfereeID) 104 } 105 return nil, fmt.Errorf("Method MoveLeader should not have been called") 106 } 107 108 func (s *EtcdSuite) TestHint(c *C) { 109 var err error 110 111 c.Assert(Hint(err), IsNil) 112 113 err = errors.New("foo bar") 114 c.Assert(Hint(err), ErrorMatches, "foo bar") 115 116 err = fmt.Errorf("ayy lmao") 117 c.Assert(Hint(err), ErrorMatches, "ayy lmao") 118 119 err = context.DeadlineExceeded 120 c.Assert(Hint(err), ErrorMatches, "etcd client timeout exceeded") 121 122 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond) 123 defer cancel() 124 125 <-ctx.Done() 126 err = ctx.Err() 127 128 c.Assert(Hint(err), ErrorMatches, "etcd client timeout exceeded") 129 } 130 131 func (s *EtcdSuite) TestETCDVersionCheck(c *C) { 132 badVersionStr := "3.0.0" 133 goodVersion := "3.1.0" 134 mm := MaintenanceMocker{ 135 OnStatus: func(ctx context.Context, endpoint string) (*etcdAPI.StatusResponse, error) { 136 switch endpoint { 137 case "http://127.0.0.1:4004": 138 return &etcdAPI.StatusResponse{ 139 Version: badVersionStr, 140 }, nil 141 default: 142 return &etcdAPI.StatusResponse{ 143 Version: goodVersion, 144 }, nil 145 } 146 }, 147 } 148 // Check a good version 149 v, err := getEPVersion(mm, "http://127.0.0.1:4003", time.Second) 150 c.Assert(err, IsNil) 151 c.Assert(v.String(), Equals, goodVersion) 152 153 // Check a bad version 154 v, err = getEPVersion(mm, "http://127.0.0.1:4004", time.Second) 155 c.Assert(err, IsNil) 156 c.Assert(v.String(), Equals, badVersionStr) 157 158 // CheckMinVersion all good 159 cfg := etcdAPI.Config{} 160 cfg.Endpoints = []string{"http://127.0.0.1:4003", "http://127.0.0.1:4005"} 161 cli, err := etcdAPI.New(cfg) 162 c.Assert(err, IsNil) 163 cli.Maintenance = mm 164 client := etcdClient{ 165 client: cli, 166 } 167 168 // short timeout for tests 169 versionCheckTimeout = time.Second 170 171 c.Assert(client.checkMinVersion(), IsNil) 172 173 // One endpoint has a bad version and should fail 174 cfg.Endpoints = []string{"http://127.0.0.1:4003", "http://127.0.0.1:4004", "http://127.0.0.1:4005"} 175 cli, err = etcdAPI.New(cfg) 176 c.Assert(err, IsNil) 177 cli.Maintenance = mm 178 client = etcdClient{ 179 client: cli, 180 } 181 182 c.Assert(client.checkMinVersion(), Not(IsNil)) 183 } 184 185 type EtcdHelpersSuite struct{} 186 187 var _ = Suite(&EtcdHelpersSuite{}) 188 189 func (s *EtcdHelpersSuite) TestIsEtcdOperator(c *C) { 190 temp := c.MkDir() 191 etcdConfigByte := []byte(`--- 192 endpoints: 193 - https://cilium-etcd-client.kube-system.svc:2379 194 `) 195 etcdTempFile := path.Join(temp, "etcd-config.yaml") 196 err := ioutil.WriteFile(etcdTempFile, etcdConfigByte, 0600) 197 c.Assert(err, IsNil) 198 type args struct { 199 backend string 200 opts map[string]string 201 k8sNamespace string 202 } 203 tests := []struct { 204 args args 205 wantSvcName string 206 wantBool bool 207 }{ 208 { 209 args: args{ 210 backend: consulName, 211 }, 212 // it is not etcd 213 wantBool: false, 214 }, 215 { 216 args: args{ 217 backend: EtcdBackendName, 218 }, 219 // misses configuration 220 wantBool: false, 221 }, 222 { 223 args: args{ 224 backend: EtcdBackendName, 225 opts: map[string]string{ 226 "etcd.address": "http://cilium-etcd-client.kube-system.svc", 227 }, 228 k8sNamespace: "kube-system", 229 }, 230 wantSvcName: "http://cilium-etcd-client.kube-system.svc", 231 // everything valid 232 wantBool: true, 233 }, 234 { 235 args: args{ 236 backend: EtcdBackendName, 237 opts: map[string]string{ 238 "etcd.address": "cilium-etcd-client.kube-system.svc", 239 }, 240 k8sNamespace: "kube-system", 241 }, 242 // domain name misses protocol 243 wantBool: false, 244 }, 245 { 246 args: args{ 247 opts: map[string]string{ 248 "etcd.address": "cilium-etcd-client.kube-system.svc", 249 }, 250 k8sNamespace: "kube-system", 251 }, 252 // backend not specified 253 wantBool: false, 254 }, 255 { 256 args: args{ 257 backend: EtcdBackendName, 258 opts: map[string]string{ 259 "etcd.config": etcdTempFile, 260 }, 261 k8sNamespace: "kube-system", 262 }, 263 wantSvcName: "https://cilium-etcd-client.kube-system.svc:2379", 264 // config file with everything setup 265 wantBool: true, 266 }, 267 { 268 args: args{ 269 backend: EtcdBackendName, 270 opts: map[string]string{ 271 "etcd.address": "foo-bar.kube-system.svc", 272 "etcd.operator": "true", 273 }, 274 k8sNamespace: "kube-system", 275 }, 276 wantSvcName: "foo-bar.kube-system.svc", 277 wantBool: true, 278 }, 279 { 280 args: args{ 281 backend: EtcdBackendName, 282 opts: map[string]string{ 283 "etcd.address": "foo-bar.kube-system.svc", 284 "etcd.operator": "false", 285 }, 286 k8sNamespace: "kube-system", 287 }, 288 wantBool: false, 289 }, 290 { 291 args: args{ 292 backend: EtcdBackendName, 293 opts: map[string]string{ 294 "etcd.address": "foo-bar.kube-system.svc", 295 }, 296 k8sNamespace: "kube-system", 297 }, 298 wantBool: false, 299 }, 300 { 301 args: args{ 302 backend: EtcdBackendName, 303 opts: map[string]string{ 304 "etcd.address": "foo-bar.kube-system.svc", 305 "etcd.operator": "foo-bar", 306 }, 307 k8sNamespace: "kube-system", 308 }, 309 wantBool: false, 310 }, 311 { 312 args: args{ 313 backend: EtcdBackendName, 314 opts: map[string]string{ 315 "etcd.address": "https://cilium-etcd-client.kube-system.svc", 316 "etcd.operator": "foo-bar", 317 }, 318 k8sNamespace: "kube-system", 319 }, 320 wantSvcName: "https://cilium-etcd-client.kube-system.svc", 321 wantBool: true, 322 }, 323 { 324 args: args{ 325 backend: EtcdBackendName, 326 opts: map[string]string{ 327 "etcd.config": etcdTempFile, 328 "etcd.operator": "foo-bar", 329 }, 330 k8sNamespace: "kube-system", 331 }, 332 wantSvcName: "https://cilium-etcd-client.kube-system.svc:2379", 333 // config file with everything setup 334 wantBool: true, 335 }, 336 } 337 for i, tt := range tests { 338 gotSvcName, gotBool := IsEtcdOperator(tt.args.backend, tt.args.opts, tt.args.k8sNamespace) 339 c.Assert(gotBool, Equals, tt.wantBool, Commentf("Test %d", i)) 340 c.Assert(gotSvcName, Equals, tt.wantSvcName, Commentf("Test %d", i)) 341 } 342 } 343 344 type EtcdLockedSuite struct { 345 etcdClient *etcdAPI.Client 346 } 347 348 var _ = Suite(&EtcdLockedSuite{}) 349 350 func (e *EtcdLockedSuite) SetUpSuite(c *C) { 351 SetupDummy("etcd") 352 353 // setup client 354 cfg := etcdAPI.Config{} 355 cfg.Endpoints = []string{etcdDummyAddress} 356 cfg.DialTimeout = 0 357 cli, err := etcdAPI.New(cfg) 358 c.Assert(err, IsNil) 359 e.etcdClient = cli 360 } 361 362 func (e *EtcdLockedSuite) TearDownSuite(c *C) { 363 err := e.etcdClient.Close() 364 c.Assert(err, IsNil) 365 Client().Close() 366 } 367 368 func (e *EtcdLockedSuite) TestGetIfLocked(c *C) { 369 randomPath := c.MkDir() 370 type args struct { 371 key string 372 lock KVLocker 373 } 374 type wanted struct { 375 err error 376 value []byte 377 } 378 tests := []struct { 379 name string 380 setupArgs func() args 381 setupWanted func() wanted 382 cleanup func(args args) error 383 }{ 384 { 385 name: "getting locked path", 386 setupArgs: func() args { 387 key := randomPath + "foo" 388 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 389 c.Assert(err, IsNil) 390 _, err = e.etcdClient.Put(context.Background(), key, "bar") 391 c.Assert(err, IsNil) 392 393 return args{ 394 key: key, 395 lock: kvlocker, 396 } 397 }, 398 setupWanted: func() wanted { 399 return wanted{ 400 err: nil, 401 value: []byte("bar"), 402 } 403 }, 404 cleanup: func(args args) error { 405 _, err := e.etcdClient.Delete(context.Background(), args.key) 406 if err != nil { 407 return err 408 } 409 return args.lock.Unlock() 410 }, 411 }, 412 { 413 name: "getting locked path with no value", 414 setupArgs: func() args { 415 key := randomPath + "foo" 416 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 417 c.Assert(err, IsNil) 418 _, err = e.etcdClient.Delete(context.Background(), key) 419 c.Assert(err, IsNil) 420 421 return args{ 422 key: key, 423 lock: kvlocker, 424 } 425 }, 426 setupWanted: func() wanted { 427 return wanted{ 428 err: nil, 429 value: nil, 430 } 431 }, 432 cleanup: func(args args) error { 433 return args.lock.Unlock() 434 }, 435 }, 436 { 437 name: "getting locked path where lock was lost", 438 setupArgs: func() args { 439 key := randomPath + "foo" 440 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 441 c.Assert(err, IsNil) 442 err = kvlocker.Unlock() 443 c.Assert(err, IsNil) 444 445 _, err = e.etcdClient.Put(context.Background(), key, "bar") 446 c.Assert(err, IsNil) 447 448 return args{ 449 key: key, 450 lock: kvlocker, 451 } 452 }, 453 setupWanted: func() wanted { 454 return wanted{ 455 err: ErrLockLeaseExpired, 456 value: nil, 457 } 458 }, 459 cleanup: func(args args) error { 460 _, err := e.etcdClient.Delete(context.Background(), args.key) 461 return err 462 }, 463 }, 464 } 465 for _, tt := range tests { 466 c.Log(tt.name) 467 args := tt.setupArgs() 468 want := tt.setupWanted() 469 value, err := Client().GetIfLocked(args.key, args.lock) 470 c.Assert(err, Equals, want.err) 471 c.Assert(value, checker.DeepEquals, want.value) 472 err = tt.cleanup(args) 473 c.Assert(err, IsNil) 474 } 475 } 476 477 func (e *EtcdLockedSuite) TestGetPrefixIfLocked(c *C) { 478 randomPath := c.MkDir() 479 type args struct { 480 key string 481 lock KVLocker 482 } 483 type wanted struct { 484 err error 485 key string 486 value []byte 487 } 488 tests := []struct { 489 name string 490 setupArgs func() args 491 setupWanted func() wanted 492 cleanup func(args args) error 493 }{ 494 { 495 name: "getting locked prefix path", 496 setupArgs: func() args { 497 key := randomPath + "foo" 498 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 499 c.Assert(err, IsNil) 500 _, err = e.etcdClient.Put(context.Background(), key, "bar") 501 c.Assert(err, IsNil) 502 503 return args{ 504 key: key, 505 lock: kvlocker, 506 } 507 }, 508 setupWanted: func() wanted { 509 return wanted{ 510 err: nil, 511 key: randomPath + "foo", 512 value: []byte("bar"), 513 } 514 }, 515 cleanup: func(args args) error { 516 _, err := e.etcdClient.Delete(context.Background(), args.key) 517 if err != nil { 518 return err 519 } 520 return args.lock.Unlock() 521 }, 522 }, 523 { 524 name: "getting locked prefix path with no value", 525 setupArgs: func() args { 526 key := randomPath + "foo" 527 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 528 c.Assert(err, IsNil) 529 530 return args{ 531 key: key, 532 lock: kvlocker, 533 } 534 }, 535 setupWanted: func() wanted { 536 return wanted{ 537 err: nil, 538 value: nil, 539 } 540 }, 541 cleanup: func(args args) error { 542 return args.lock.Unlock() 543 }, 544 }, 545 { 546 name: "getting locked prefix path where lock was lost", 547 setupArgs: func() args { 548 key := randomPath + "foo" 549 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 550 c.Assert(err, IsNil) 551 err = kvlocker.Unlock() 552 c.Assert(err, IsNil) 553 _, err = e.etcdClient.Put(context.Background(), key, "bar") 554 c.Assert(err, IsNil) 555 556 return args{ 557 key: key, 558 lock: kvlocker, 559 } 560 }, 561 setupWanted: func() wanted { 562 return wanted{ 563 err: ErrLockLeaseExpired, 564 value: nil, 565 } 566 }, 567 cleanup: func(args args) error { 568 _, err := e.etcdClient.Delete(context.Background(), args.key) 569 return err 570 }, 571 }, 572 } 573 for _, tt := range tests { 574 c.Log(tt.name) 575 args := tt.setupArgs() 576 want := tt.setupWanted() 577 k, value, err := Client().GetPrefixIfLocked(context.Background(), args.key, args.lock) 578 c.Assert(err, Equals, want.err) 579 c.Assert(k, Equals, want.key) 580 c.Assert(value, checker.DeepEquals, want.value) 581 err = tt.cleanup(args) 582 c.Assert(err, IsNil) 583 } 584 } 585 586 func (e *EtcdLockedSuite) TestDeleteIfLocked(c *C) { 587 randomPath := c.MkDir() 588 type args struct { 589 key string 590 lock KVLocker 591 } 592 type wanted struct { 593 err error 594 } 595 tests := []struct { 596 name string 597 setupArgs func() args 598 setupWanted func() wanted 599 cleanup func(args args) error 600 }{ 601 { 602 name: "deleting locked path", 603 setupArgs: func() args { 604 key := randomPath + "foo" 605 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 606 c.Assert(err, IsNil) 607 _, err = e.etcdClient.Put(context.Background(), key, "bar") 608 c.Assert(err, IsNil) 609 610 return args{ 611 key: key, 612 lock: kvlocker, 613 } 614 }, 615 setupWanted: func() wanted { 616 return wanted{ 617 err: nil, 618 } 619 }, 620 cleanup: func(args args) error { 621 key := randomPath + "foo" 622 // verify that key was actually deleted 623 gr, err := e.etcdClient.Get(context.Background(), key) 624 c.Assert(err, IsNil) 625 c.Assert(gr.Count, Equals, int64(0)) 626 627 return args.lock.Unlock() 628 }, 629 }, 630 { 631 name: "deleting locked path with no value", 632 setupArgs: func() args { 633 key := randomPath + "foo" 634 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 635 c.Assert(err, IsNil) 636 637 _, err = e.etcdClient.Delete(context.Background(), key) 638 c.Assert(err, IsNil) 639 640 return args{ 641 key: key, 642 lock: kvlocker, 643 } 644 }, 645 setupWanted: func() wanted { 646 return wanted{ 647 err: nil, 648 } 649 }, 650 cleanup: func(args args) error { 651 key := randomPath + "foo" 652 // verify that key was actually deleted (this should not matter 653 // as the key was never in the kvstore but still) 654 gr, err := e.etcdClient.Get(context.Background(), key) 655 c.Assert(err, IsNil) 656 c.Assert(gr.Count, Equals, int64(0)) 657 658 return args.lock.Unlock() 659 }, 660 }, 661 { 662 name: "deleting locked path where lock was lost", 663 setupArgs: func() args { 664 key := randomPath + "foo" 665 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 666 c.Assert(err, IsNil) 667 _, err = e.etcdClient.Put(context.Background(), key, "bar") 668 c.Assert(err, IsNil) 669 err = kvlocker.Unlock() 670 c.Assert(err, IsNil) 671 672 return args{ 673 key: key, 674 lock: kvlocker, 675 } 676 }, 677 setupWanted: func() wanted { 678 return wanted{ 679 err: ErrLockLeaseExpired, 680 } 681 }, 682 cleanup: func(args args) error { 683 key := randomPath + "foo" 684 // If the lock was lost it means the value still exists 685 value, err := e.etcdClient.Get(context.Background(), key) 686 c.Assert(err, IsNil) 687 c.Assert(value.Count, Equals, int64(1)) 688 c.Assert(value.Kvs[0].Value, checker.DeepEquals, []byte("bar")) 689 return nil 690 }, 691 }, 692 } 693 for _, tt := range tests { 694 c.Log(tt.name) 695 args := tt.setupArgs() 696 want := tt.setupWanted() 697 err := Client().DeleteIfLocked(args.key, args.lock) 698 c.Assert(err, Equals, want.err) 699 err = tt.cleanup(args) 700 c.Assert(err, IsNil) 701 } 702 } 703 704 func (e *EtcdLockedSuite) TestUpdateIfLocked(c *C) { 705 randomPath := c.MkDir() 706 type args struct { 707 key string 708 lock KVLocker 709 newValue []byte 710 lease bool 711 } 712 type wanted struct { 713 err error 714 } 715 tests := []struct { 716 name string 717 setupArgs func() args 718 setupWanted func() wanted 719 cleanup func(args args) error 720 }{ 721 { 722 name: "update locked path without lease", 723 setupArgs: func() args { 724 key := randomPath + "foo" 725 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 726 c.Assert(err, IsNil) 727 _, err = e.etcdClient.Put(context.Background(), key, "bar") 728 c.Assert(err, IsNil) 729 730 return args{ 731 key: key, 732 lock: kvlocker, 733 newValue: []byte("newbar"), 734 } 735 }, 736 setupWanted: func() wanted { 737 return wanted{ 738 err: nil, 739 } 740 }, 741 cleanup: func(args args) error { 742 key := randomPath + "foo" 743 // verify that key was actually updated 744 gr, err := e.etcdClient.Get(context.Background(), key) 745 c.Assert(err, IsNil) 746 c.Assert(gr.Count, Equals, int64(1)) 747 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("newbar")) 748 749 return args.lock.Unlock() 750 }, 751 }, 752 { 753 name: "update locked path with no value without lease", 754 setupArgs: func() args { 755 key := randomPath + "foo" 756 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 757 c.Assert(err, IsNil) 758 759 _, err = e.etcdClient.Delete(context.Background(), key) 760 c.Assert(err, IsNil) 761 762 return args{ 763 key: key, 764 lock: kvlocker, 765 newValue: []byte("newbar"), 766 } 767 }, 768 setupWanted: func() wanted { 769 return wanted{ 770 err: nil, 771 } 772 }, 773 cleanup: func(args args) error { 774 key := randomPath + "foo" 775 // a key that was updated with no value will create a new value 776 gr, err := e.etcdClient.Get(context.Background(), key) 777 c.Assert(err, IsNil) 778 c.Assert(gr.Count, Equals, int64(1)) 779 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("newbar")) 780 781 return args.lock.Unlock() 782 }, 783 }, 784 { 785 name: "update locked path where lock was lost without lease", 786 setupArgs: func() args { 787 key := randomPath + "foo" 788 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 789 c.Assert(err, IsNil) 790 _, err = e.etcdClient.Put(context.Background(), key, "bar") 791 c.Assert(err, IsNil) 792 err = kvlocker.Unlock() 793 c.Assert(err, IsNil) 794 795 return args{ 796 key: key, 797 lock: kvlocker, 798 } 799 }, 800 setupWanted: func() wanted { 801 return wanted{ 802 err: ErrLockLeaseExpired, 803 } 804 }, 805 cleanup: func(args args) error { 806 key := randomPath + "foo" 807 // verify that key was actually updated 808 gr, err := e.etcdClient.Get(context.Background(), key) 809 c.Assert(err, IsNil) 810 c.Assert(gr.Count, Equals, int64(1)) 811 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("bar")) 812 return nil 813 }, 814 }, 815 { 816 name: "update locked path with lease", 817 setupArgs: func() args { 818 key := randomPath + "foo" 819 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 820 c.Assert(err, IsNil) 821 _, err = e.etcdClient.Put(context.Background(), key, "bar") 822 c.Assert(err, IsNil) 823 824 return args{ 825 key: key, 826 lock: kvlocker, 827 newValue: []byte("newbar"), 828 lease: true, 829 } 830 }, 831 setupWanted: func() wanted { 832 return wanted{ 833 err: nil, 834 } 835 }, 836 cleanup: func(args args) error { 837 key := randomPath + "foo" 838 // verify that key was actually updated 839 gr, err := e.etcdClient.Get(context.Background(), key) 840 c.Assert(err, IsNil) 841 c.Assert(gr.Count, Equals, int64(1)) 842 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("newbar")) 843 844 return args.lock.Unlock() 845 }, 846 }, 847 { 848 name: "update locked path with no value with lease", 849 setupArgs: func() args { 850 key := randomPath + "foo" 851 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 852 c.Assert(err, IsNil) 853 854 _, err = e.etcdClient.Delete(context.Background(), key) 855 c.Assert(err, IsNil) 856 857 return args{ 858 key: key, 859 lock: kvlocker, 860 newValue: []byte("newbar"), 861 lease: true, 862 } 863 }, 864 setupWanted: func() wanted { 865 return wanted{ 866 err: nil, 867 } 868 }, 869 cleanup: func(args args) error { 870 key := randomPath + "foo" 871 // a key that was updated with no value will create a new value 872 gr, err := e.etcdClient.Get(context.Background(), key) 873 c.Assert(err, IsNil) 874 c.Assert(gr.Count, Equals, int64(1)) 875 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("newbar")) 876 877 return args.lock.Unlock() 878 }, 879 }, 880 { 881 name: "update locked path where lock was lost with lease", 882 setupArgs: func() args { 883 key := randomPath + "foo" 884 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 885 c.Assert(err, IsNil) 886 _, err = e.etcdClient.Put(context.Background(), key, "bar") 887 c.Assert(err, IsNil) 888 err = kvlocker.Unlock() 889 c.Assert(err, IsNil) 890 891 return args{ 892 key: key, 893 lock: kvlocker, 894 lease: true, 895 } 896 }, 897 setupWanted: func() wanted { 898 return wanted{ 899 err: ErrLockLeaseExpired, 900 } 901 }, 902 cleanup: func(args args) error { 903 key := randomPath + "foo" 904 // verify that key was actually updated 905 gr, err := e.etcdClient.Get(context.Background(), key) 906 c.Assert(err, IsNil) 907 c.Assert(gr.Count, Equals, int64(1)) 908 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("bar")) 909 return nil 910 }, 911 }, 912 } 913 for _, tt := range tests { 914 c.Log(tt.name) 915 args := tt.setupArgs() 916 want := tt.setupWanted() 917 err := Client().UpdateIfLocked(context.Background(), args.key, args.newValue, args.lease, args.lock) 918 c.Assert(err, Equals, want.err) 919 err = tt.cleanup(args) 920 c.Assert(err, IsNil) 921 } 922 } 923 924 func (e *EtcdLockedSuite) TestUpdateIfDifferentIfLocked(c *C) { 925 randomPath := c.MkDir() 926 type args struct { 927 key string 928 lock KVLocker 929 newValue []byte 930 lease bool 931 } 932 type wanted struct { 933 err error 934 updated bool 935 } 936 tests := []struct { 937 name string 938 setupArgs func() args 939 setupWanted func() wanted 940 cleanup func(args args) error 941 }{ 942 { 943 name: "update locked path without lease", 944 setupArgs: func() args { 945 key := randomPath + "foo" 946 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 947 c.Assert(err, IsNil) 948 _, err = e.etcdClient.Put(context.Background(), key, "bar") 949 c.Assert(err, IsNil) 950 951 return args{ 952 key: key, 953 lock: kvlocker, 954 newValue: []byte("newbar"), 955 } 956 }, 957 setupWanted: func() wanted { 958 return wanted{ 959 err: nil, 960 updated: true, 961 } 962 }, 963 cleanup: func(args args) error { 964 key := randomPath + "foo" 965 // verify that key was actually updated 966 gr, err := e.etcdClient.Get(context.Background(), key) 967 c.Assert(err, IsNil) 968 c.Assert(gr.Count, Equals, int64(1)) 969 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("newbar")) 970 _, err = e.etcdClient.Delete(context.Background(), key) 971 c.Assert(err, IsNil) 972 return args.lock.Unlock() 973 }, 974 }, 975 { 976 name: "update locked path without lease and with same value", 977 setupArgs: func() args { 978 key := randomPath + "foo" 979 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 980 c.Assert(err, IsNil) 981 _, err = e.etcdClient.Put(context.Background(), key, "bar") 982 c.Assert(err, IsNil) 983 984 return args{ 985 key: key, 986 lock: kvlocker, 987 newValue: []byte("bar"), 988 } 989 }, 990 setupWanted: func() wanted { 991 return wanted{ 992 err: nil, 993 } 994 }, 995 cleanup: func(args args) error { 996 key := randomPath + "foo" 997 // verify that key was actually updated 998 gr, err := e.etcdClient.Get(context.Background(), key) 999 c.Assert(err, IsNil) 1000 c.Assert(gr.Count, Equals, int64(1)) 1001 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("bar")) 1002 1003 return args.lock.Unlock() 1004 }, 1005 }, 1006 { 1007 name: "update locked path with no value without lease", 1008 setupArgs: func() args { 1009 key := randomPath + "foo" 1010 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1011 c.Assert(err, IsNil) 1012 1013 _, err = e.etcdClient.Delete(context.Background(), key) 1014 c.Assert(err, IsNil) 1015 1016 return args{ 1017 key: key, 1018 lock: kvlocker, 1019 newValue: []byte("newbar"), 1020 } 1021 }, 1022 setupWanted: func() wanted { 1023 return wanted{ 1024 err: nil, 1025 updated: true, 1026 } 1027 }, 1028 cleanup: func(args args) error { 1029 key := randomPath + "foo" 1030 // a key that was updated with no value will create a new value 1031 gr, err := e.etcdClient.Get(context.Background(), key) 1032 c.Assert(err, IsNil) 1033 c.Assert(gr.Count, Equals, int64(1)) 1034 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("newbar")) 1035 _, err = e.etcdClient.Delete(context.Background(), key) 1036 c.Assert(err, IsNil) 1037 return args.lock.Unlock() 1038 }, 1039 }, 1040 { 1041 name: "update locked path where lock was lost without lease", 1042 setupArgs: func() args { 1043 key := randomPath + "foo" 1044 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1045 c.Assert(err, IsNil) 1046 _, err = e.etcdClient.Put(context.Background(), key, "bar") 1047 c.Assert(err, IsNil) 1048 err = kvlocker.Unlock() 1049 c.Assert(err, IsNil) 1050 1051 return args{ 1052 key: key, 1053 newValue: []byte("baz"), 1054 lock: kvlocker, 1055 } 1056 }, 1057 setupWanted: func() wanted { 1058 return wanted{ 1059 err: ErrLockLeaseExpired, 1060 } 1061 }, 1062 cleanup: func(args args) error { 1063 key := randomPath + "foo" 1064 // verify that key was actually updated 1065 gr, err := e.etcdClient.Get(context.Background(), key) 1066 c.Assert(err, IsNil) 1067 c.Assert(gr.Count, Equals, int64(1)) 1068 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("bar")) 1069 _, err = e.etcdClient.Delete(context.Background(), key) 1070 c.Assert(err, IsNil) 1071 return nil 1072 }, 1073 }, 1074 { 1075 name: "update locked path with lease", 1076 setupArgs: func() args { 1077 key := randomPath + "foo" 1078 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1079 c.Assert(err, IsNil) 1080 _, err = e.etcdClient.Put(context.Background(), key, "bar") 1081 c.Assert(err, IsNil) 1082 1083 return args{ 1084 key: key, 1085 lock: kvlocker, 1086 newValue: []byte("newbar"), 1087 lease: true, 1088 } 1089 }, 1090 setupWanted: func() wanted { 1091 return wanted{ 1092 err: nil, 1093 updated: true, 1094 } 1095 }, 1096 cleanup: func(args args) error { 1097 key := randomPath + "foo" 1098 // verify that key was actually updated 1099 gr, err := e.etcdClient.Get(context.Background(), key) 1100 c.Assert(err, IsNil) 1101 c.Assert(gr.Count, Equals, int64(1)) 1102 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("newbar")) 1103 _, err = e.etcdClient.Delete(context.Background(), key) 1104 c.Assert(err, IsNil) 1105 return args.lock.Unlock() 1106 }, 1107 }, 1108 { 1109 name: "update locked path with no value with lease", 1110 setupArgs: func() args { 1111 key := randomPath + "foo" 1112 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1113 c.Assert(err, IsNil) 1114 1115 _, err = e.etcdClient.Delete(context.Background(), key) 1116 c.Assert(err, IsNil) 1117 1118 return args{ 1119 key: key, 1120 lock: kvlocker, 1121 newValue: []byte("newbar"), 1122 lease: true, 1123 } 1124 }, 1125 setupWanted: func() wanted { 1126 return wanted{ 1127 err: nil, 1128 updated: true, 1129 } 1130 }, 1131 cleanup: func(args args) error { 1132 key := randomPath + "foo" 1133 // a key that was updated with no value will create a new value 1134 gr, err := e.etcdClient.Get(context.Background(), key) 1135 c.Assert(err, IsNil) 1136 c.Assert(gr.Count, Equals, int64(1)) 1137 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("newbar")) 1138 1139 _, err = e.etcdClient.Delete(context.Background(), key) 1140 c.Assert(err, IsNil) 1141 1142 return args.lock.Unlock() 1143 }, 1144 }, 1145 { 1146 name: "update locked path with lease and with same value", 1147 setupArgs: func() args { 1148 key := randomPath + "foo" 1149 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1150 c.Assert(err, IsNil) 1151 created, err := Client().CreateOnly(context.Background(), key, []byte("bar"), true) 1152 c.Assert(err, IsNil) 1153 c.Assert(created, Equals, true) 1154 1155 return args{ 1156 key: key, 1157 lock: kvlocker, 1158 newValue: []byte("bar"), 1159 lease: true, 1160 } 1161 }, 1162 setupWanted: func() wanted { 1163 return wanted{ 1164 err: nil, 1165 } 1166 }, 1167 cleanup: func(args args) error { 1168 key := randomPath + "foo" 1169 // verify that key was actually updated 1170 gr, err := e.etcdClient.Get(context.Background(), key) 1171 c.Assert(err, IsNil) 1172 c.Assert(gr.Count, Equals, int64(1)) 1173 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("bar")) 1174 _, err = e.etcdClient.Delete(context.Background(), key) 1175 c.Assert(err, IsNil) 1176 return args.lock.Unlock() 1177 }, 1178 }, 1179 { 1180 name: "update locked path where lock was lost with lease", 1181 setupArgs: func() args { 1182 key := randomPath + "foo" 1183 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1184 c.Assert(err, IsNil) 1185 _, err = e.etcdClient.Put(context.Background(), key, "bar") 1186 c.Assert(err, IsNil) 1187 err = kvlocker.Unlock() 1188 c.Assert(err, IsNil) 1189 1190 return args{ 1191 key: key, 1192 lock: kvlocker, 1193 lease: true, 1194 } 1195 }, 1196 setupWanted: func() wanted { 1197 return wanted{ 1198 err: ErrLockLeaseExpired, 1199 } 1200 }, 1201 cleanup: func(args args) error { 1202 key := randomPath + "foo" 1203 // verify that key was actually updated 1204 gr, err := e.etcdClient.Get(context.Background(), key) 1205 c.Assert(err, IsNil) 1206 c.Assert(gr.Count, Equals, int64(1)) 1207 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("bar")) 1208 return nil 1209 }, 1210 }, 1211 } 1212 for _, tt := range tests { 1213 c.Log(tt.name) 1214 args := tt.setupArgs() 1215 want := tt.setupWanted() 1216 updated, err := Client().UpdateIfDifferentIfLocked(context.Background(), args.key, args.newValue, args.lease, args.lock) 1217 c.Assert(err, Equals, want.err) 1218 c.Assert(updated, Equals, want.updated) 1219 err = tt.cleanup(args) 1220 c.Assert(err, IsNil) 1221 } 1222 } 1223 1224 func (e *EtcdLockedSuite) TestCreateOnlyIfLocked(c *C) { 1225 randomPath := c.MkDir() 1226 type args struct { 1227 key string 1228 lock KVLocker 1229 newValue []byte 1230 lease bool 1231 } 1232 type wanted struct { 1233 err error 1234 created bool 1235 } 1236 tests := []struct { 1237 name string 1238 setupArgs func() args 1239 setupWanted func() wanted 1240 cleanup func(args args) error 1241 }{ 1242 { 1243 name: "create only locked path without lease", 1244 setupArgs: func() args { 1245 key := randomPath + "foo" 1246 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1247 c.Assert(err, IsNil) 1248 1249 _, err = e.etcdClient.Delete(context.Background(), key) 1250 c.Assert(err, IsNil) 1251 1252 return args{ 1253 key: key, 1254 lock: kvlocker, 1255 newValue: []byte("newbar"), 1256 } 1257 }, 1258 setupWanted: func() wanted { 1259 return wanted{ 1260 err: nil, 1261 created: true, 1262 } 1263 }, 1264 cleanup: func(args args) error { 1265 key := randomPath + "foo" 1266 // verify that key was actually created 1267 gr, err := e.etcdClient.Get(context.Background(), key) 1268 c.Assert(err, IsNil) 1269 c.Assert(gr.Count, Equals, int64(1)) 1270 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("newbar")) 1271 1272 return args.lock.Unlock() 1273 }, 1274 }, 1275 { 1276 name: "create only locked path with an existing value without lease", 1277 setupArgs: func() args { 1278 key := randomPath + "foo" 1279 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1280 c.Assert(err, IsNil) 1281 1282 _, err = e.etcdClient.Put(context.Background(), key, "bar") 1283 c.Assert(err, IsNil) 1284 1285 return args{ 1286 key: key, 1287 lock: kvlocker, 1288 newValue: []byte("newbar"), 1289 } 1290 }, 1291 setupWanted: func() wanted { 1292 return wanted{ 1293 err: nil, 1294 } 1295 }, 1296 cleanup: func(args args) error { 1297 key := randomPath + "foo" 1298 // the key should not have been created and therefore the old 1299 // value is still there 1300 gr, err := e.etcdClient.Get(context.Background(), key) 1301 c.Assert(err, IsNil) 1302 c.Assert(gr.Count, Equals, int64(1)) 1303 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("bar")) 1304 1305 return args.lock.Unlock() 1306 }, 1307 }, 1308 { 1309 name: "create only locked path where lock was lost without lease", 1310 setupArgs: func() args { 1311 key := randomPath + "foo" 1312 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1313 c.Assert(err, IsNil) 1314 _, err = e.etcdClient.Delete(context.Background(), key) 1315 c.Assert(err, IsNil) 1316 err = kvlocker.Unlock() 1317 c.Assert(err, IsNil) 1318 1319 return args{ 1320 key: key, 1321 lock: kvlocker, 1322 newValue: []byte("bar"), 1323 } 1324 }, 1325 setupWanted: func() wanted { 1326 return wanted{ 1327 err: ErrLockLeaseExpired, 1328 } 1329 }, 1330 cleanup: func(args args) error { 1331 key := randomPath + "foo" 1332 // verify that key was not created 1333 gr, err := e.etcdClient.Get(context.Background(), key) 1334 c.Assert(err, IsNil) 1335 c.Assert(gr.Count, Equals, int64(0)) 1336 return nil 1337 }, 1338 }, 1339 { 1340 name: "create only locked path with lease", 1341 setupArgs: func() args { 1342 key := randomPath + "foo" 1343 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1344 c.Assert(err, IsNil) 1345 1346 _, err = e.etcdClient.Delete(context.Background(), key) 1347 c.Assert(err, IsNil) 1348 1349 return args{ 1350 key: key, 1351 lock: kvlocker, 1352 newValue: []byte("newbar"), 1353 lease: true, 1354 } 1355 }, 1356 setupWanted: func() wanted { 1357 return wanted{ 1358 err: nil, 1359 created: true, 1360 } 1361 }, 1362 cleanup: func(args args) error { 1363 key := randomPath + "foo" 1364 // verify that key was actually created 1365 gr, err := e.etcdClient.Get(context.Background(), key) 1366 c.Assert(err, IsNil) 1367 c.Assert(gr.Count, Equals, int64(1)) 1368 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("newbar")) 1369 1370 return args.lock.Unlock() 1371 }, 1372 }, 1373 { 1374 name: "create only locked path with an existing value with lease", 1375 setupArgs: func() args { 1376 key := randomPath + "foo" 1377 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1378 c.Assert(err, IsNil) 1379 1380 _, err = e.etcdClient.Put(context.Background(), key, "bar") 1381 c.Assert(err, IsNil) 1382 1383 return args{ 1384 key: key, 1385 lock: kvlocker, 1386 newValue: []byte("newbar"), 1387 lease: true, 1388 } 1389 }, 1390 setupWanted: func() wanted { 1391 return wanted{ 1392 err: nil, 1393 } 1394 }, 1395 cleanup: func(args args) error { 1396 key := randomPath + "foo" 1397 // the key should not have been created and therefore the old 1398 // value is still there 1399 gr, err := e.etcdClient.Get(context.Background(), key) 1400 c.Assert(err, IsNil) 1401 c.Assert(gr.Count, Equals, int64(1)) 1402 c.Assert(gr.Kvs[0].Value, checker.DeepEquals, []byte("bar")) 1403 1404 return args.lock.Unlock() 1405 }, 1406 }, 1407 { 1408 name: "create only locked path where lock was lost with lease", 1409 setupArgs: func() args { 1410 key := randomPath + "foo" 1411 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1412 c.Assert(err, IsNil) 1413 _, err = e.etcdClient.Delete(context.Background(), key) 1414 c.Assert(err, IsNil) 1415 err = kvlocker.Unlock() 1416 c.Assert(err, IsNil) 1417 1418 return args{ 1419 key: key, 1420 lock: kvlocker, 1421 newValue: []byte("bar"), 1422 lease: true, 1423 } 1424 }, 1425 setupWanted: func() wanted { 1426 return wanted{ 1427 err: ErrLockLeaseExpired, 1428 } 1429 }, 1430 cleanup: func(args args) error { 1431 key := randomPath + "foo" 1432 // verify that key was not created 1433 gr, err := e.etcdClient.Get(context.Background(), key) 1434 c.Assert(err, IsNil) 1435 c.Assert(gr.Count, Equals, int64(0)) 1436 return nil 1437 }, 1438 }, 1439 } 1440 for _, tt := range tests { 1441 c.Log(tt.name) 1442 args := tt.setupArgs() 1443 want := tt.setupWanted() 1444 created, err := Client().CreateOnlyIfLocked(context.Background(), args.key, args.newValue, args.lease, args.lock) 1445 c.Assert(err, Equals, want.err) 1446 c.Assert(created, Equals, want.created) 1447 err = tt.cleanup(args) 1448 c.Assert(err, IsNil) 1449 } 1450 } 1451 1452 func (e *EtcdLockedSuite) TestListPrefixIfLocked(c *C) { 1453 randomPath := c.MkDir() 1454 type args struct { 1455 key string 1456 lock KVLocker 1457 } 1458 type wanted struct { 1459 err error 1460 kvPairs KeyValuePairs 1461 } 1462 tests := []struct { 1463 name string 1464 setupArgs func() args 1465 setupWanted func() wanted 1466 cleanup func(args args) error 1467 }{ 1468 { 1469 name: "list prefix locked", 1470 setupArgs: func() args { 1471 key := randomPath + "foo" 1472 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1473 c.Assert(err, IsNil) 1474 _, err = e.etcdClient.Put(context.Background(), key, "bar") 1475 c.Assert(err, IsNil) 1476 _, err = e.etcdClient.Put(context.Background(), key+"1", "bar1") 1477 c.Assert(err, IsNil) 1478 1479 return args{ 1480 key: key, 1481 lock: kvlocker, 1482 } 1483 }, 1484 setupWanted: func() wanted { 1485 key := randomPath + "foo" 1486 return wanted{ 1487 err: nil, 1488 kvPairs: KeyValuePairs{ 1489 key: Value{ 1490 Data: []byte("bar"), 1491 }, 1492 key + "1": Value{ 1493 Data: []byte("bar1"), 1494 }, 1495 }, 1496 } 1497 }, 1498 cleanup: func(args args) error { 1499 _, err := e.etcdClient.Delete(context.Background(), args.key, etcdAPI.WithPrefix()) 1500 if err != nil { 1501 return err 1502 } 1503 return args.lock.Unlock() 1504 }, 1505 }, 1506 { 1507 name: "list prefix locked with no values", 1508 setupArgs: func() args { 1509 key := randomPath + "foo" 1510 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1511 c.Assert(err, IsNil) 1512 _, err = e.etcdClient.Delete(context.Background(), key, etcdAPI.WithPrefix()) 1513 c.Assert(err, IsNil) 1514 1515 return args{ 1516 key: key, 1517 lock: kvlocker, 1518 } 1519 }, 1520 setupWanted: func() wanted { 1521 return wanted{ 1522 err: nil, 1523 } 1524 }, 1525 cleanup: func(args args) error { 1526 return args.lock.Unlock() 1527 }, 1528 }, 1529 { 1530 name: "list prefix locked where lock was lost", 1531 setupArgs: func() args { 1532 key := randomPath + "foo" 1533 kvlocker, err := Client().LockPath(context.Background(), "locks/"+key+"/.lock") 1534 c.Assert(err, IsNil) 1535 _, err = e.etcdClient.Put(context.Background(), key, "bar") 1536 c.Assert(err, IsNil) 1537 _, err = e.etcdClient.Put(context.Background(), key+"1", "bar1") 1538 c.Assert(err, IsNil) 1539 err = kvlocker.Unlock() 1540 c.Assert(err, IsNil) 1541 1542 return args{ 1543 key: key, 1544 lock: kvlocker, 1545 } 1546 }, 1547 setupWanted: func() wanted { 1548 return wanted{ 1549 err: ErrLockLeaseExpired, 1550 } 1551 }, 1552 cleanup: func(args args) error { 1553 _, err := e.etcdClient.Delete(context.Background(), args.key) 1554 return err 1555 }, 1556 }, 1557 } 1558 for _, tt := range tests { 1559 c.Log(tt.name) 1560 args := tt.setupArgs() 1561 want := tt.setupWanted() 1562 kvPairs, err := Client().ListPrefixIfLocked(args.key, args.lock) 1563 c.Assert(err, Equals, want.err) 1564 for k, v := range kvPairs { 1565 // We don't compare revision of the value because we can't predict 1566 // its value. 1567 v1, ok := want.kvPairs[k] 1568 c.Assert(ok, Equals, true) 1569 c.Assert(v.Data, checker.DeepEquals, v1.Data) 1570 } 1571 err = tt.cleanup(args) 1572 c.Assert(err, IsNil) 1573 } 1574 } 1575 1576 func TestGetSvcNamespace(t *testing.T) { 1577 type args struct { 1578 address string 1579 } 1580 tests := []struct { 1581 name string 1582 args args 1583 wantSvcName string 1584 wantNamespace string 1585 wantErr bool 1586 }{ 1587 { 1588 name: "test-1", 1589 args: args{ 1590 address: "http://foo.bar.something", 1591 }, 1592 wantSvcName: "foo", 1593 wantNamespace: "bar", 1594 wantErr: false, 1595 }, 1596 { 1597 name: "test-2", 1598 args: args{ 1599 address: "http://foo.bar", 1600 }, 1601 wantSvcName: "foo", 1602 wantNamespace: "bar", 1603 wantErr: false, 1604 }, 1605 { 1606 name: "test-3", 1607 args: args{ 1608 address: "http://foo", 1609 }, 1610 wantErr: true, 1611 }, 1612 { 1613 name: "test-4", 1614 args: args{ 1615 address: "http://foo.bar:5679/", 1616 }, 1617 wantSvcName: "foo", 1618 wantNamespace: "bar", 1619 wantErr: false, 1620 }, 1621 { 1622 name: "test-5", 1623 args: args{ 1624 address: "http://foo:2379", 1625 }, 1626 wantErr: true, 1627 }, 1628 } 1629 for _, tt := range tests { 1630 t.Run(tt.name, func(t *testing.T) { 1631 got, got1, err := SplitK8sServiceURL(tt.args.address) 1632 if (err != nil) != tt.wantErr { 1633 t.Errorf("SplitK8sServiceURL() error = %v, wantErr %v", err, tt.wantErr) 1634 return 1635 } 1636 if got != tt.wantSvcName { 1637 t.Errorf("SplitK8sServiceURL() got = %v, want %v", got, tt.wantSvcName) 1638 } 1639 if got1 != tt.wantNamespace { 1640 t.Errorf("SplitK8sServiceURL() got1 = %v, want %v", got1, tt.wantNamespace) 1641 } 1642 }) 1643 } 1644 }