zkruby 3.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,141 @@
1
+ module ZooKeeper
2
+
3
+ class Client
4
+
5
+ # Recursive make path
6
+ #
7
+ # This will send parallel creates for ALL nodes up to the root
8
+ # and then ignore any NODE_EXISTS errors.
9
+ #
10
+ # You generally want to call this after receiving a NO_NODE error from a
11
+ # simple {#create}
12
+ #
13
+ # @param [String] path
14
+ # @param [Data::ACL] acl the access control list to apply to any created nodes
15
+ # @return
16
+ # @raise [Error]
17
+ def mkpath(path,acl=ZK::ACL_OPEN_UNSAFE,&callback)
18
+
19
+ return synchronous_call(:mkpath,path,acl) unless block_given?
20
+
21
+ connection_lost = false
22
+
23
+ path_comp = path.split("/")
24
+
25
+ # we only care about the result of the final create op
26
+ last_op = nil
27
+
28
+ (1-path_comp.length..-1).each do |i|
29
+
30
+ sub_path = path_comp[0..i].join("/")
31
+
32
+ op = create(sub_path,"",acl) { if i == -1 then callback.call() else :true end }
33
+
34
+ op.errback do |err|
35
+ if i == -1
36
+ if ZK::Error::NODE_EXISTS === err
37
+ callback.call()
38
+ elsif ZK::Error::CONNECTION_LOST === err || ( ZK::Error::NO_NODE && connection_lost )
39
+ # try again
40
+ mkpath(path,acl)
41
+ callback.call()
42
+ else
43
+ raise err
44
+ end
45
+ elsif ZK::Error::CONNECTION_LOST === err
46
+ connection_lost = true
47
+ :connection_lost
48
+ else
49
+ # we don't care about any other errors, but we will log them
50
+ logger.warn { "Error creating #{sub_path}, #{err}" }
51
+ end
52
+ end
53
+ last_op = op if (i == -1)
54
+ end
55
+
56
+ return WrappedOp.new(last_op)
57
+ end
58
+
59
+ # Recursive delete
60
+ #
61
+ # Although this method itself can be called synchronously all the zk activity
62
+ # is asynchronous, ie all subnodes are removed in parallel
63
+ #
64
+ # Will retry on connection loss, or if some other activity is adding nodes in parallel.
65
+ # If you get a session expiry in the middle of this you will end up with a
66
+ # partially completed recursive delete. In all other circumstances it will eventually
67
+ # complete.
68
+ #
69
+ # @overload rmpath(path,version)
70
+ # @param [String] path this node and all its children will be recursively deleted
71
+ # @param [FixNum] version the expected version to be deleted (-1 to match any version).
72
+ # Only applies to the top level path
73
+ # @return
74
+ # @raise [Error]
75
+ # @overload rmpath(path,version)
76
+ # @return [AsyncOp]
77
+ # @yield [] callback invoked if delete is successful
78
+ def rmpath(path,version = -1, &callback)
79
+
80
+ return synchronous_call(:rmpath,path,version) unless block_given?
81
+
82
+ del_op = delete(path,version) { callback.call() }
83
+
84
+ del_op.errback do |err|
85
+ # we don't leave this method unless we get an exception
86
+ # or have completed and called the callback
87
+ case err
88
+ when ZK::Error::NO_NODE
89
+ when ZK::Error::CONNECTION_LOST
90
+ rmpath(path,version)
91
+ when ZK::Error::NOT_EMPTY
92
+
93
+ stat, child_list = children(path)
94
+
95
+ unless child_list.empty?
96
+ child_ops = {}
97
+ child_list.each do |child|
98
+ child_path = "#{path}/#{child}"
99
+
100
+ rm_op = rmpath(child_path,-1) { :success }
101
+
102
+ rm_op.errback { |err| child_results[child_path] = err }
103
+
104
+ child_ops[child_path] = rm_op
105
+ end
106
+
107
+ # Wait until all our children are done (or error'd)
108
+ child_ops.each { |child_path,op| op.value }
109
+ rmpath(path,version)
110
+ end
111
+ else
112
+ raise err
113
+ end
114
+ callback.call()
115
+ end
116
+
117
+ return WrappedOp.new(del_op)
118
+ end
119
+ end #Client
120
+
121
+ class WrappedOp < AsyncOp
122
+ def initialize(delegate_op)
123
+ @delegate_op = delegate_op
124
+ @errback = nil
125
+ end
126
+
127
+ private
128
+ def wait_value()
129
+ begin
130
+ @delegate_op.value()
131
+ rescue StandardError > err
132
+ @errback ? @errback.call(err) : raise
133
+ end
134
+ end
135
+
136
+ def set_error_handler(errback)
137
+ @errback = errback
138
+ end
139
+ end
140
+
141
+ end #ZooKeeper
@@ -0,0 +1,27 @@
1
+ # A pure ruby implementation of the zk client library
2
+ #
3
+ # It implements the client side of the ZooKeeper TCP protocol directly rather
4
+ # than calling the zk client libraries
5
+ #
6
+ module ZooKeeper
7
+ # Major/Minor numbers track zookeeper itself, final digit is our build number
8
+ VERSION = "3.4.3"
9
+ @bindings = []
10
+ def self.add_binding(binding)
11
+ @bindings << binding unless @bindings.include?(binding)
12
+ end
13
+ end
14
+
15
+ # Shorthand
16
+ ZK=ZooKeeper
17
+
18
+ require 'slf4r'
19
+ require 'zkruby/enum'
20
+ require 'zkruby/bindata'
21
+ require 'jute/zookeeper'
22
+ require 'zkruby/multi'
23
+ require 'zkruby/protocol'
24
+ require 'zkruby/session'
25
+ require 'zkruby/client'
26
+ # Utilities
27
+ require 'zkruby/util'
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ class ZKBTest < BinData::Record
4
+ int8 :testint
5
+ zk_boolean :bool
6
+ end
7
+
8
+ describe ZK::ZKBoolean do
9
+
10
+ it "should behave like 'false' after reading 00" do
11
+ hex = "0000"
12
+ bin = [ hex ].pack("H*")
13
+
14
+ b = ZKBTest.read(bin)
15
+
16
+ b.bool.should == false
17
+ # NOTE!! this is unfortunate consequence of Ruby's view of FALSE
18
+ b.bool.should_not be_false
19
+
20
+ b.to_binary_s.unpack("H*")[0].should == hex
21
+ end
22
+
23
+ it "should behave like 'true' after reading 01" do
24
+ hex = "0001"
25
+ bin = [ hex ].pack("H*")
26
+
27
+ b = ZKBTest.read(bin)
28
+
29
+ b.bool.should == true
30
+ # NOTE!! this is unfortunate consequence of Ruby's view of FALSE
31
+ b.bool.should be_true
32
+
33
+ b.to_binary_s.unpack("H*")[0].should == hex
34
+ end
35
+
36
+ it "should behave like a boolean after creating" do
37
+
38
+ b = ZKBTest.new()
39
+ b.bool= true
40
+ b.bool.should == true
41
+
42
+ b.bool= false
43
+ b.bool.should == false
44
+
45
+ b = ZKBTest.new( :bool => true )
46
+ b.bool.should == true
47
+
48
+ b = ZKBTest.new( :bool => false )
49
+ b.bool.should == false
50
+ end
51
+ end
@@ -0,0 +1,84 @@
1
+ require 'zkruby/enum.rb'
2
+
3
+ class TestEnum
4
+ include Enumeration
5
+
6
+ enum :one, 1
7
+ enum :two, -2
8
+ end
9
+
10
+ class TestError < StandardError
11
+ include Enumeration
12
+ enum :badone, 10
13
+ enum :oops,20
14
+ end
15
+
16
+ describe Enumeration do
17
+ it "should equate to itself" do
18
+ TestEnum::ONE.should == TestEnum::ONE
19
+ end
20
+
21
+ it "should have case equals comparisons to symbols and ints" do
22
+ t = TestEnum.fetch(1)
23
+ t.should === :one
24
+ t.should === 1
25
+ end
26
+
27
+ it "should be useable in a mock" do
28
+ m = mock("watchevent")
29
+ m.should_receive(:test).with(TestEnum::TWO)
30
+ m.test(TestEnum::TWO)
31
+ end
32
+
33
+ describe "errors" do
34
+ # Ruby 1.9 inadvertently dropped requirement that the argument
35
+ # to rescue must be a class or module, but despite its usefulness
36
+ # it is not supposed to be that way and JRuby doesn't work that way
37
+ it "should be an exception class" do
38
+ TestError.get(:badone).should equal (TestError::BADONE)
39
+ TestError.get(:badone).class.should == Class
40
+ TestError::BADONE.should < StandardError
41
+ TestError::BADONE.new().should be_kind_of(TestError)
42
+ TestError::OOPS.should === TestError.get(:oops).exception()
43
+ TestError::OOPS.to_sym.should == :oops
44
+ end
45
+
46
+ it "should be raisable with get and rescuable by a constant" do
47
+ begin
48
+ raise TestError.get(:badone), "mymessage"
49
+ rescue TestError::BADONE => ex
50
+ ex.message.should == "mymessage"
51
+ end
52
+ end
53
+
54
+ it "should be raisable with constant and rescuable with fetch" do
55
+ begin
56
+ raise TestError::OOPS, "a message"
57
+ rescue TestError::fetch(20) => ex
58
+ # do nothing
59
+ ex.message.should == "a message"
60
+ end
61
+ end
62
+
63
+ it "should catch the superclass" do
64
+ begin
65
+ raise TestError::BADONE, "a message"
66
+ rescue TestError
67
+ end
68
+ end
69
+
70
+ it "should be raisable with unknown symbol" do
71
+ begin
72
+ raise TestError.lookup(:unknown)
73
+ rescue TestError => err
74
+ end
75
+ end
76
+
77
+ it "should be raisable with unknown int" do
78
+ begin
79
+ raise TestError.lookup(-132)
80
+ rescue TestError => err
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,50 @@
1
+ require 'server_helper'
2
+ require 'shared/binding'
3
+ require 'zkruby/eventmachine'
4
+
5
+ module EMHelper
6
+ alias :restart_cluster_orig :restart_cluster
7
+ def restart_cluster(delay=0)
8
+ if EM.reactor_running?
9
+ cv = Strand::ConditionVariable.new()
10
+ op = Proc.new do
11
+ begin
12
+ restart_cluster_orig(delay)
13
+ rescue Exception => ex
14
+ logger.error ("Exception restarting cluster #{ex}")
15
+ end
16
+ true
17
+ end
18
+ cb = Proc.new { |result| cv.signal() }
19
+ defer = EM.defer(op,cb)
20
+ cv.wait()
21
+ else
22
+ restart_cluster_orig(delay)
23
+ end
24
+ end
25
+
26
+ def sleep(delay)
27
+ Strand.sleep(delay)
28
+ end
29
+ end
30
+
31
+ describe ZooKeeper::EventMachine::Binding do
32
+
33
+ include Slf4r::Logger
34
+ include EMHelper
35
+
36
+ around(:each) do |example|
37
+ EventMachine.run {
38
+ Strand.new() do
39
+ begin
40
+ example.run
41
+ ensure
42
+ EM::stop
43
+ end
44
+ end
45
+ }
46
+ end
47
+
48
+ it_should_behave_like "a zookeeper client binding"
49
+
50
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ def tohex(s)
4
+ s.unpack("H*")[0]
5
+ end
6
+
7
+ describe ZooKeeper::Proto::MultiRequest do
8
+
9
+ it "an empty multi should match the java output" do
10
+
11
+ java_hex = "ffffffff01ffffffff"
12
+ java_bin = [ java_hex ].pack("H*")
13
+
14
+ read_request = ZooKeeper::Proto::MultiRequest.read(java_bin)
15
+
16
+ read_hex = tohex(read_request.to_binary_s)
17
+
18
+ m = ZooKeeper::Proto::MultiRequest.new()
19
+
20
+ m.requests << { :header => { :_type => -1, :done => true, :err => -1 } }
21
+
22
+ m_hex = tohex(m.to_binary_s)
23
+ m_hex.should == java_hex
24
+ m.should == read_request
25
+ end
26
+
27
+ it "should match the java output with records" do
28
+
29
+ java_hex = "0000000d00ffffffff00000005636865636b000000010000000100ffffffff000000066372656174650000000b6372656174652064617461000000010000001f000000046175746800000000000000000000000200ffffffff0000000664656c657465000000110000000500ffffffff000000077365744461746100000008736574206461746100000013ffffffff01ffffffff"
30
+
31
+ java_bin = [ java_hex ].pack("H*")
32
+
33
+ read_request = ZooKeeper::Proto::MultiRequest.read(java_bin)
34
+
35
+ read_hex = tohex(read_request.to_binary_s)
36
+
37
+ read_hex.should == java_hex
38
+
39
+ m = ZooKeeper::Proto::MultiRequest.new()
40
+
41
+ check = ZK::Proto::CheckVersionRequest.new( :path => "check", :version => 1 )
42
+ m.requests << { :header => { :_type => 13, :done => false, :err => -1 }, :request => check }
43
+
44
+ create = ZK::Proto::CreateRequest.new( :path => "create", :data => "create data", :acl => ZK::CREATOR_ALL_ACL, :flags => 0)
45
+ m.requests << { :header => { :_type => 1, :done => false, :err => -1 }, :request => create }
46
+
47
+ delete = ZK::Proto::DeleteRequest.new( :path => "delete", :version => 17)
48
+ m.requests << { :header => { :_type => 2, :done => false, :err => -1 }, :request => delete }
49
+
50
+
51
+ set = ZK::Proto::SetDataRequest.new( :path => "setData", :data => "set data", :version => 19)
52
+ m.requests << { :header => { :_type => 5, :done => false, :err => -1 }, :request => set }
53
+
54
+ m.requests << { :header => { :_type => -1, :done => true, :err => -1 } }
55
+
56
+ m_hex = tohex(m.to_binary_s)
57
+ m_hex.should == java_hex
58
+ m.should == read_request
59
+ end
60
+
61
+ it "should decode a response" do
62
+
63
+ response_hex = "000000010000000000000000062f6d756c7469ffffffff01ffffffff"
64
+
65
+ response_bin = [ response_hex ].pack("H*")
66
+
67
+ m = ZooKeeper::Proto::MultiResponse.read(response_bin)
68
+ m.responses.size.should == 2
69
+ m.responses[0].done?.should be_false
70
+ m.responses[0].header.err.should == 0
71
+ m.responses[0].header._type.should == 1
72
+ m.responses[0].response.path.should == "/multi"
73
+ m.responses[1].done?.should be_true
74
+
75
+ end
76
+
77
+ it "should decode an error response" do
78
+ response_hex = "ffffffff000000000000000000ffffffff00ffffff9bffffff9bffffffff01ffffffff"
79
+
80
+ response_bin = [ response_hex ].pack("H*")
81
+
82
+ m = ZooKeeper::Proto::MultiResponse.read(response_bin)
83
+ m.responses.size.should == 3
84
+ m.responses[0].header._type.should == -1
85
+ m.responses[0].header.err.should == 0
86
+ m.responses[0].done?.should be_false
87
+ m.responses[0].response.err.should == 0
88
+ m.responses[1].header._type.should == -1
89
+ m.responses[1].header.err.should == -101
90
+ m.responses[1].response.err.should == -101
91
+ m.responses[2].done?.should be_true
92
+ end
93
+ end