etcdv3 0.9.0 → 0.11.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Etcdv3::Connection do
4
4
 
5
5
  describe '#initialize - without metadata' do
6
- subject { Etcdv3::Connection.new('http://localhost:2379', 10) }
6
+ subject { Etcdv3::Connection.new('http://localhost:2379', 10, nil) }
7
7
 
8
8
  it { is_expected.to have_attributes(endpoint: URI('http://localhost:2379')) }
9
9
  it { is_expected.to have_attributes(credentials: :this_channel_is_insecure) }
@@ -22,7 +22,7 @@ describe Etcdv3::Connection do
22
22
  end
23
23
 
24
24
  describe '#initialize - with metadata' do
25
- subject { Etcdv3::Connection.new('http://localhost:2379', 10, token: 'token123') }
25
+ subject { Etcdv3::Connection.new('http://localhost:2379', 10, nil, token: 'token123') }
26
26
 
27
27
  [:kv, :maintenance, :lease, :watch, :auth].each do |handler|
28
28
  let(:handler_stub) { subject.handlers[handler].instance_variable_get(:@stub) }
@@ -37,7 +37,7 @@ describe Etcdv3::Connection do
37
37
  end
38
38
 
39
39
  describe '#refresh_metadata' do
40
- subject { Etcdv3::Connection.new('http://localhost:2379', token: 'token123') }
40
+ subject { Etcdv3::Connection.new('http://localhost:2379', nil, token: 'token123') }
41
41
  before { subject.refresh_metadata(token: 'newtoken') }
42
42
  [:kv, :maintenance, :lease, :watch, :auth].each do |handler|
43
43
  let(:handler_metadata) { subject.handlers[handler].instance_variable_get(:@metadata) }
@@ -3,9 +3,9 @@ require 'spec_helper'
3
3
  describe Etcdv3::ConnectionWrapper do
4
4
  let(:conn) { local_connection }
5
5
  let(:endpoints) { ['http://localhost:2379', 'http://localhost:2389'] }
6
+ subject { Etcdv3::ConnectionWrapper.new(10, *endpoints, nil, true) }
6
7
 
7
8
  describe '#initialize' do
8
- subject { Etcdv3::ConnectionWrapper.new(10, *endpoints) }
9
9
  it { is_expected.to have_attributes(user: nil, password: nil, token: nil) }
10
10
  it 'sets hostnames in correct order' do
11
11
  expect(subject.endpoints.map(&:hostname)).to eq(['localhost:2379', 'localhost:2389'])
@@ -16,7 +16,6 @@ describe Etcdv3::ConnectionWrapper do
16
16
  end
17
17
 
18
18
  describe "#rotate_connection_endpoint" do
19
- subject { Etcdv3::ConnectionWrapper.new(10, *endpoints) }
20
19
  before do
21
20
  subject.rotate_connection_endpoint
22
21
  end
@@ -29,11 +28,30 @@ describe Etcdv3::ConnectionWrapper do
29
28
  end
30
29
 
31
30
  describe "Failover Simulation" do
32
- let(:modified_conn) { local_connection("http://localhost:2369, http://localhost:2379") }
31
+ let(:allow_reconnect) { true }
32
+ let(:modified_conn) {
33
+ local_connection(
34
+ "http://localhost:2369, http://localhost:2379",
35
+ allow_reconnect: allow_reconnect
36
+ )
37
+ }
38
+ subject { modified_conn.get('boom') }
39
+
33
40
  context 'without auth' do
34
41
  # Set primary endpoint to a non-existing etcd endpoint
35
- subject { modified_conn.get('boom') }
36
- it { is_expected.to be_an_instance_of(Etcdserverpb::RangeResponse) }
42
+ context 'with reconnect' do
43
+ it { is_expected.to be_an_instance_of(Etcdserverpb::RangeResponse) }
44
+ end
45
+ context 'without reconnect' do
46
+ let(:allow_reconnect) { false }
47
+ it { expect { subject }.to raise_error(GRPC::Unavailable) }
48
+ end
49
+ context 'without reconnect with single endpoint' do
50
+ let(:modified_conn) {
51
+ local_connection("http://localhost:2369",allow_reconnect: false)
52
+ }
53
+ it { expect { subject }.to raise_error(GRPC::Unavailable) }
54
+ end
37
55
  end
38
56
  context 'with auth' do
39
57
  before do
@@ -50,12 +68,19 @@ describe Etcdv3::ConnectionWrapper do
50
68
  modified_conn.auth_disable
51
69
  modified_conn.user_delete('root')
52
70
  end
53
- subject { modified_conn.get('boom') }
54
- it { is_expected.to be_an_instance_of(Etcdserverpb::RangeResponse) }
71
+ context 'with reconnect' do
72
+ it { is_expected.to be_an_instance_of(Etcdserverpb::RangeResponse) }
73
+ end
74
+ context 'without reconnect' do
75
+ let(:allow_reconnect) { false }
76
+ it { expect { subject }.to raise_error(GRPC::Unavailable) }
77
+ end
55
78
  end
56
79
  end
57
80
 
58
81
  describe "GRPC::Unauthenticated recovery" do
82
+ let(:allow_reconnect) { true }
83
+ let(:conn) { local_connection(allow_reconnect: allow_reconnect) }
59
84
  let(:wrapper) { conn.send(:conn) }
60
85
  let(:connection) { wrapper.connection }
61
86
  before do
@@ -71,6 +96,12 @@ describe Etcdv3::ConnectionWrapper do
71
96
  conn.user_delete('root')
72
97
  end
73
98
  subject { conn.user_get('root') }
74
- it { is_expected.to be_an_instance_of(Etcdserverpb::AuthUserGetResponse) }
99
+ context 'with reconnect' do
100
+ it { is_expected.to be_an_instance_of(Etcdserverpb::AuthUserGetResponse) }
101
+ end
102
+ context 'without reconnect' do
103
+ let(:allow_reconnect) { false }
104
+ it { expect { subject }.to raise_error(GRPC::Unauthenticated) }
105
+ end
75
106
  end
76
107
  end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe Etcdv3::KV do
4
+ let(:stub) { local_namespace_stub(Etcdv3::Namespace::KV, 1, '/namespace/') }
5
+ let(:lease_stub) { local_stub(Etcdv3::Lease, 1) }
6
+
7
+ it_should_behave_like "a method with a GRPC timeout", described_class, :get, :range, "key"
8
+ it_should_behave_like "a method with a GRPC timeout", described_class, :del, :delete_range, "key"
9
+ it_should_behave_like "a method with a GRPC timeout", described_class, :put, :put, "key", "val"
10
+
11
+ it "should timeout transactions" do
12
+ stub = local_namespace_stub(Etcdv3::Namespace::KV, 0, '/namespace/')
13
+ expect { stub.transaction(Proc.new { nil }) }.to raise_error(GRPC::DeadlineExceeded)
14
+ end
15
+
16
+ describe '#put' do
17
+ context 'without lease' do
18
+ subject { stub.put('test', 'test') }
19
+ it { is_expected.to be_an_instance_of(Etcdserverpb::PutResponse) }
20
+ end
21
+
22
+ context 'with lease' do
23
+ let(:lease_id) { lease_stub.lease_grant(1)['ID'] }
24
+ subject { stub.put('lease', 'test', lease: lease_id) }
25
+ it { is_expected.to be_an_instance_of(Etcdserverpb::PutResponse) }
26
+ end
27
+ end
28
+
29
+ describe '#get' do
30
+ subject { stub.get('test') }
31
+ it { is_expected.to be_an_instance_of(Etcdserverpb::RangeResponse) }
32
+ end
33
+
34
+ describe '#del' do
35
+ context 'del without range' do
36
+ subject { stub.del('test') }
37
+ it { is_expected.to be_an_instance_of(Etcdserverpb::DeleteRangeResponse) }
38
+ end
39
+ context 'del with range' do
40
+ subject { stub.del('test', range_end: 'testtt') }
41
+ it { is_expected.to be_an_instance_of(Etcdserverpb::DeleteRangeResponse) }
42
+ end
43
+ end
44
+
45
+ describe '#transaction' do
46
+ context 'put' do
47
+ let!(:block) do
48
+ Proc.new do |txn|
49
+ txn.compare = [ txn.value('txn', :equal, 'value') ]
50
+ txn.success = [ txn.put('txn-test', 'success') ]
51
+ txn.failure = [ txn.put('txn-test', 'failed') ]
52
+ end
53
+ end
54
+ subject { stub.transaction(block) }
55
+ it { is_expected.to be_an_instance_of(Etcdserverpb::TxnResponse) }
56
+ end
57
+
58
+ context 'del' do
59
+ let!(:block) do
60
+ Proc.new do |txn|
61
+ txn.compare = [ txn.value('txn', :equal, 'value') ]
62
+ txn.success = [ txn.del('txn-one') ]
63
+ txn.failure = [ txn.del('txn-two') ]
64
+ end
65
+ end
66
+ subject { stub.transaction(block) }
67
+ it { is_expected.to be_an_instance_of(Etcdserverpb::TxnResponse) }
68
+ end
69
+ end
70
+
71
+ context 'get' do
72
+ let!(:block) do
73
+ Proc.new do |txn|
74
+ txn.compare = [ txn.value('txn', :equal, 'value') ]
75
+ txn.success = [ txn.get('txn-success') ]
76
+ txn.failure = [ txn.get('txn-failure') ]
77
+ end
78
+ end
79
+ subject { stub.transaction(block) }
80
+ it { is_expected.to be_an_instance_of(Etcdserverpb::TxnResponse) }
81
+ end
82
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ # Locking is not implemented in etcd v3.1.X
4
+ unless $instance.version < Gem::Version.new("3.2.0")
5
+ describe Etcdv3::Lock do
6
+ let(:stub) { local_namespace_stub(Etcdv3::Namespace::Lock, 1, '/namespace/') }
7
+ let(:lease_stub) { local_stub(Etcdv3::Lease, 1) }
8
+
9
+ it_should_behave_like "a method with a GRPC timeout", described_class, :unlock, :unlock, 'foo'
10
+ # it_should_behave_like "a method with a GRPC timeout", described_class, :lock, :lock, 'foo'
11
+
12
+ describe '#lock' do
13
+ let(:lease_id) { lease_stub.lease_grant(10)['ID'] }
14
+ subject { stub.lock('foo', lease_id) }
15
+ it { is_expected.to be_an_instance_of(V3lockpb::LockResponse) }
16
+ end
17
+
18
+ describe '#unlock' do
19
+ subject { stub.unlock('foo') }
20
+ it { is_expected.to be_an_instance_of(V3lockpb::UnlockResponse) }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'securerandom'
3
+
4
+
5
+ describe Etcdv3::Namespace::Watch do
6
+ let(:stub) { local_namespace_stub(Etcdv3::Namespace::Watch, 5, '/namespace/') }
7
+ let(:kv_stub_no_ns) { local_stub(Etcdv3::KV, 1) }
8
+ let(:kv_stub) { local_namespace_stub(Etcdv3::Namespace::KV, 1, '/namespace/') }
9
+
10
+ context 'watch' do
11
+ it 'should return an event' do
12
+ resp = nil
13
+ thr = Thread.new do |thr|
14
+ resp = stub.watch("foo", nil, 1, nil)
15
+ end
16
+ sleep 2
17
+ kv_stub.put("foo", "works")
18
+ thr.join
19
+ expect(resp).to be_an_instance_of(Google::Protobuf::RepeatedField)
20
+ expect(resp.last.kv.key).to eq('foo')
21
+ end
22
+
23
+ it 'should return event when non-namespace client writes to key' do
24
+ resp = nil
25
+ thr = Thread.new do |thr|
26
+ resp = stub.watch("foobar", nil, 1, nil)
27
+ end
28
+ sleep 2
29
+ kv_stub_no_ns.put("/namespace/foobar", "works")
30
+ thr.join
31
+ expect(resp).to be_an_instance_of(Google::Protobuf::RepeatedField)
32
+ expect(resp.last.kv.key).to eq('foobar')
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'securerandom'
3
+
4
+ # Locking is not implemented in etcd v3.1.X
5
+ unless $instance.version < Gem::Version.new("3.2.0")
6
+ describe Etcdv3::Watch do
7
+ let(:stub) { local_stub(Etcdv3::Watch, 5) }
8
+ let(:kv_stub) { local_stub(Etcdv3::KV, 1) }
9
+
10
+ context 'watch' do
11
+ it 'should return an event' do
12
+ resp = nil
13
+ thr = Thread.new do |thr|
14
+ resp = stub.watch("foo", nil, 1, nil)
15
+ end
16
+ sleep 2
17
+ kv_stub.put("foo", "works")
18
+ thr.join
19
+ expect(resp).to be_an_instance_of(Google::Protobuf::RepeatedField)
20
+ expect(resp.last.kv.key).to eq('foo')
21
+ end
22
+ end
23
+ end
24
+ end
data/spec/etcdv3_spec.rb CHANGED
@@ -181,6 +181,20 @@ describe Etcdv3 do
181
181
  end
182
182
  end
183
183
 
184
+ describe '#watch' do
185
+ let!(:foo) { conn.put('foo', 'bar') }
186
+ subject { conn.watch('foo', start_revision: 1) }
187
+ it { is_expected.to_not be_nil }
188
+ it "raises a GRPC::DeadlineExceeded exception when it takes too long" do
189
+ expect do
190
+ conn.watch('foo', timeout: 0)
191
+ end.to raise_exception(GRPC::DeadlineExceeded)
192
+ end
193
+ it "accepts a timeout" do
194
+ expect{ conn.watch('foo', start_revision: 1, timeout: 10) }.to_not raise_exception
195
+ end
196
+ end
197
+
184
198
  describe '#lease_keep_alive_once' do
185
199
  let!(:lease_id) { conn.lease_grant(2)['ID'] }
186
200
  subject { conn.lease_keep_alive_once(lease_id) }
@@ -279,7 +293,7 @@ describe Etcdv3 do
279
293
  describe '#role_grant_permission' do
280
294
  before { conn.role_add('grant') }
281
295
  after { conn.role_delete('grant') }
282
- subject { conn.role_grant_permission('grant', :readwrite, 'a', {range_end: 'Z'}) }
296
+ subject { conn.role_grant_permission('grant', :readwrite, 'a', **{range_end: 'Z'}) }
283
297
  it { is_expected.to_not be_nil }
284
298
  it_should_behave_like "Etcdv3 instance using a timeout", :role_grant_permission, 'grant', :readwrite, 'a'
285
299
  end
@@ -507,5 +521,261 @@ describe Etcdv3 do
507
521
  end
508
522
  end
509
523
  end
524
+
525
+ describe "namespace" do
526
+
527
+ describe '#get' do
528
+ let(:get_conn) { local_connection_with_namespace("/namespace-get/") }
529
+
530
+ before do
531
+ conn.put('/apples/', 'app')
532
+ conn.put('/namespace-get/apple', 'apple')
533
+ conn.put('/namespace-get/apples', 'apples')
534
+ conn.put('/namespace-get/appless', 'appless')
535
+ end
536
+
537
+ it 'returns key w/o namespace' do
538
+ expect(get_conn.get("apple").kvs.last.value).to eq('apple')
539
+ end
540
+
541
+ it 'returns keys w/o namespace' do
542
+ expect(get_conn.get("apple", range_end: 'applf').kvs.size).to eq(3)
543
+ end
544
+
545
+ it 'returns all keys under namespace' do
546
+ expect(get_conn.get("", range_end: "\0").kvs.size).to eq(3)
547
+ end
548
+ end
549
+
550
+ describe '#put' do
551
+ let(:put_conn) { local_connection_with_namespace("/namespace-put/") }
552
+
553
+ before do
554
+ put_conn.put('apple_put', 'test')
555
+ end
556
+ it 'returns key with namespace' do
557
+ expect(conn.get("/namespace-put/apple_put").kvs.last.value).to eq('test')
558
+ end
559
+ end
560
+
561
+ describe '#del' do
562
+ let(:del_conn) { local_connection_with_namespace("/del-test/") }
563
+
564
+ context 'zero-byte' do
565
+ before do
566
+ del_conn.put('test', "key")
567
+ del_conn.put('test2', "key2")
568
+ conn.put('wall', 'zzzz')
569
+ conn.put('walzz', 'adsfas')
570
+ end
571
+
572
+ it 'deleting all keys should be scoped to namespace' do
573
+ resp = del_conn.del("", range_end: "\0")
574
+ expect(resp.deleted).to eq(2)
575
+ expect(conn.get("wall").kvs.last.value).to eq('zzzz')
576
+ end
577
+ end
578
+
579
+ context 'no range' do
580
+ before { del_conn.put('test', 'value') }
581
+ subject { del_conn.del('test') }
582
+ it { is_expected.to_not be_nil }
583
+ end
584
+
585
+ context 'ranged del' do
586
+ before do
587
+ del_conn.put('test', 'value')
588
+ del_conn.put('testt', 'value')
589
+ end
590
+ subject { del_conn.del('test', range_end: 'testtt') }
591
+ it { is_expected.to_not be_nil }
592
+ end
593
+ end
594
+
595
+ describe '#transaction' do
596
+ let(:trans_conn) { local_connection_with_namespace("/namespace/") }
597
+
598
+ describe 'txn.value' do
599
+ before { trans_conn.put('txn', 'value') }
600
+ after { trans_conn.del('txn') }
601
+ context 'success' do
602
+ subject! do
603
+ trans_conn.transaction do |txn|
604
+ txn.compare = [ txn.value('txn', :equal, 'value') ]
605
+ txn.success = [ txn.put('txn-test', 'success') ]
606
+ txn.failure = [ txn.put('txn-test', 'failed') ]
607
+ end
608
+ end
609
+ it 'sets correct key' do
610
+ expect(trans_conn.get('txn-test').kvs.first.value).to eq('success')
611
+ expect(conn.get("/namespace/txn-test").kvs.first.value).to eq('success')
612
+ end
613
+ it "raises a GRPC::DeadlineExceeded exception when it takes too long" do
614
+ expect do
615
+ trans_conn.transaction(timeout: 0) do |txn|
616
+ txn.compare = [ txn.value('txn', :equal, 'value') ]
617
+ txn.success = [ txn.put('txn-test', 'success') ]
618
+ txn.failure = [ txn.put('txn-test', 'failed') ]
619
+ end
620
+ end.to raise_exception(GRPC::DeadlineExceeded)
621
+ end
622
+ it "accepts a timeout" do
623
+ expect do
624
+ trans_conn.transaction(timeout: 1) do |txn|
625
+ txn.compare = [ txn.value('txn', :equal, 'value') ]
626
+ txn.success = [ txn.put('txn-test', 'success') ]
627
+ txn.failure = [ txn.put('txn-test', 'failed') ]
628
+ end
629
+ end.to_not raise_exception
630
+ end
631
+ end
632
+ context "success, value with lease" do
633
+ let!(:lease_id) { trans_conn.lease_grant(2)['ID'] }
634
+ subject! do
635
+ trans_conn.transaction do |txn|
636
+ txn.compare = [ txn.value('txn', :equal, 'value') ]
637
+ txn.success = [ txn.put('txn-test', 'success', lease_id) ]
638
+ txn.failure = [ txn.put('txn-test', 'failed', lease_id) ]
639
+ end
640
+ end
641
+ it 'sets correct key, with a lease' do
642
+ expect(trans_conn.get('txn-test').kvs.first.value).to eq('success')
643
+ expect(trans_conn.get('txn-test').kvs.first.lease).to eq(lease_id)
644
+ end
645
+ end
646
+ context 'failure' do
647
+ subject! do
648
+ trans_conn.transaction do |txn|
649
+ txn.compare = [ txn.value('txn', :equal, 'notright') ]
650
+ txn.success = [ txn.put('txn-test', 'success') ]
651
+ txn.failure = [ txn.put('txn-test', 'failed') ]
652
+ end
653
+ end
654
+ it 'sets correct key' do
655
+ expect(trans_conn.get('txn-test').kvs.first.value).to eq('failed')
656
+ end
657
+ end
658
+ end
659
+
660
+ describe 'txn.create_revision' do
661
+ before { trans_conn.put('txn', 'value') }
662
+ after { trans_conn.del('txn') }
663
+ context 'success' do
664
+ subject! do
665
+ trans_conn.transaction do |txn|
666
+ txn.compare = [ txn.create_revision('txn', :greater, 1) ]
667
+ txn.success = [ txn.put('txn-test', 'success') ]
668
+ txn.failure = [ txn.put('txn-test', 'failed') ]
669
+ end
670
+ end
671
+ it 'sets correct key' do
672
+ expect(trans_conn.get('txn-test').kvs.first.value).to eq('success')
673
+ end
674
+ end
675
+ context 'failure' do
676
+ subject! do
677
+ trans_conn.transaction do |txn|
678
+ txn.compare = [ txn.create_revision('txn', :equal, 1) ]
679
+ txn.success = [ txn.put('txn-test', 'success') ]
680
+ txn.failure = [ txn.put('txn-test', 'failed') ]
681
+ end
682
+ end
683
+ it 'sets correct key' do
684
+ expect(trans_conn.get('txn-test').kvs.first.value).to eq('failed')
685
+ expect(conn.get('/namespace/txn-test').kvs.first.value).to eq('failed')
686
+ end
687
+ end
688
+ end
689
+
690
+ describe 'txn.mod_revision' do
691
+ before { trans_conn.put('txn', 'value') }
692
+ after { trans_conn.del('txn') }
693
+ context 'success' do
694
+ subject! do
695
+ trans_conn.transaction do |txn|
696
+ txn.compare = [ txn.mod_revision('txn', :less, 1000) ]
697
+ txn.success = [ txn.put('txn-test', 'success') ]
698
+ txn.failure = [ txn.put('txn-test', 'failed') ]
699
+ end
700
+ end
701
+ it 'sets correct key' do
702
+ expect(trans_conn.get('txn-test').kvs.first.value).to eq('success')
703
+ expect(conn.get('/namespace/txn-test').kvs.first.value).to eq('success')
704
+ end
705
+ end
706
+ context 'failure' do
707
+ subject! do
708
+ trans_conn.transaction do |txn|
709
+ txn.compare = [ txn.mod_revision('txn', :greater, 1000) ]
710
+ txn.success = [ txn.put('txn-test', 'success') ]
711
+ txn.failure = [ txn.put('txn-test', 'failed') ]
712
+ end
713
+ end
714
+ it 'sets correct key' do
715
+ expect(trans_conn.get('txn-test').kvs.first.value).to eq('failed')
716
+ expect(conn.get('/namespace/txn-test').kvs.first.value).to eq('failed')
717
+
718
+ end
719
+ end
720
+ end
721
+
722
+ describe 'txn.version' do
723
+ before { trans_conn.put('txn-version', 'value') }
724
+ after { trans_conn.del('txn-version') }
725
+ context 'success' do
726
+ subject! do
727
+ trans_conn.transaction do |txn|
728
+ txn.compare = [ txn.version('txn-version', :equal, 1) ]
729
+ txn.success = [ txn.put('txn-test', 'success') ]
730
+ txn.failure = [ txn.put('txn-test', 'failed') ]
731
+ end
732
+ end
733
+ it 'sets correct key' do
734
+ expect(trans_conn.get('txn-test').kvs.first.value).to eq('success')
735
+ expect(conn.get('/namespace/txn-test').kvs.first.value).to eq('success')
736
+ end
737
+ end
738
+ context 'failure' do
739
+ subject! do
740
+ trans_conn.transaction do |txn|
741
+ txn.compare = [ txn.version('txn', :equal, 100)]
742
+ txn.success = [ txn.put('txn-test', 'success') ]
743
+ txn.failure = [ txn.put('txn-test', 'failed') ]
744
+ end
745
+ end
746
+ it 'sets correct key' do
747
+ expect(trans_conn.get('txn-test').kvs.first.value).to eq('failed')
748
+ expect(conn.get('/namespace/txn-test').kvs.first.value).to eq('failed')
749
+ end
750
+ end
751
+ end
752
+ end
753
+
754
+ # Locking is not implemented in etcd v3.1.X
755
+ unless $instance.version < Gem::Version.new("3.2.0")
756
+ describe "locking" do
757
+ let(:ns_conn) { local_connection_with_namespace("/namespace/") }
758
+
759
+ describe '#lock' do
760
+ let(:lease_id) { lease_stub.lease_grant(10)['ID'] }
761
+ subject { ns_conn.lock('mylocklock', lease_id) }
762
+ it 'should lock key under specified namespace' do
763
+ expect(conn.get("/namespace/#{subject.key}").kvs).to_not be_empty
764
+ end
765
+ end
766
+
767
+ describe '#with_lock' do
768
+ let(:lease_id) { lease_stub.lease_grant(10)['ID'] }
769
+ let(:lease_id_2) { lease_stub.lease_grant(15)['ID'] }
770
+ it 'enforces lock' do
771
+ ns_conn.with_lock('mylock', lease_id) do
772
+ expect { ns_conn.lock('mylock', lease_id_2, timeout: 0.1) }
773
+ .to raise_error(GRPC::DeadlineExceeded)
774
+ end
775
+ end
776
+ end
777
+ end
778
+ end
779
+ end
510
780
  end
511
781
  end
@@ -5,18 +5,26 @@ module Helpers
5
5
  Etcdv3.new(endpoints: "http://#{local_url}", user: user, password: password)
6
6
  end
7
7
 
8
- def local_connection(endpoints="http://#{local_url}")
9
- Etcdv3.new(endpoints: endpoints)
8
+ def local_connection(endpoints="http://#{local_url}", allow_reconnect: true)
9
+ Etcdv3.new(endpoints: endpoints, allow_reconnect: allow_reconnect)
10
10
  end
11
11
 
12
12
  def local_connection_with_timeout(timeout)
13
13
  Etcdv3.new(endpoints: "http://#{local_url}", command_timeout: timeout)
14
14
  end
15
15
 
16
+ def local_connection_with_namespace(namespace)
17
+ Etcdv3.new(endpoints: "http://#{local_url}", namespace: namespace)
18
+ end
19
+
16
20
  def local_stub(interface, timeout=nil)
17
21
  interface.new(local_url, :this_channel_is_insecure, timeout, {})
18
22
  end
19
23
 
24
+ def local_namespace_stub(interface, timeout=nil, namespace)
25
+ interface.new(local_url, :this_channel_is_insecure, timeout, namespace, {})
26
+ end
27
+
20
28
  def local_url
21
29
  "127.0.0.1:#{port}"
22
30
  end
@@ -32,13 +32,13 @@ shared_examples_for "Etcdv3 instance using a timeout" do |command, *args|
32
32
  it "raises a GRPC::DeadlineExceeded exception when it takes too long" do
33
33
  expect do
34
34
  test_args = args.dup
35
- test_args.push({timeout: 0})
36
- conn.public_send(command, *test_args)
35
+ test_kwargs = {timeout: 0}
36
+ conn.public_send(command, *test_args, **test_kwargs)
37
37
  end.to raise_exception(GRPC::DeadlineExceeded)
38
38
  end
39
39
  it "accepts a timeout" do
40
40
  test_args = args.dup
41
- test_args.push({timeout: 10})
42
- expect{ conn.public_send(command, *test_args) }.to_not raise_exception
41
+ test_kwargs = {timeout: 10}
42
+ expect{ conn.public_send(command, *test_args, **test_kwargs) }.to_not raise_exception
43
43
  end
44
44
  end
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
2
  $LOAD_PATH.unshift File.expand_path('./helpers', __FILE__)
3
+ $LOAD_PATH.unshift File.expand_path('./namespace', __FILE__)
3
4
 
4
- require 'simplecov'
5
- require 'codecov'
6
- SimpleCov.start
7
- SimpleCov.formatter = SimpleCov::Formatter::Codecov
5
+
6
+ # require 'simplecov'
7
+ # require 'codecov'
8
+ # SimpleCov.start
9
+ # SimpleCov.formatter = SimpleCov::Formatter::Codecov
8
10
 
9
11
  require 'etcdv3'
10
12
  require 'helpers/test_instance'