zk-server 1.0.1 → 1.1.0

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.
data/bin/zk-server ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../../lib/zk-server', __FILE__)
4
+
5
+ ZK::Server::Command.new.run
6
+
7
+ # vim:ft=ruby
@@ -7,10 +7,10 @@ module ZK
7
7
  include Logging
8
8
 
9
9
  def_delegators :config,
10
- :base_dir, :data_dir, :log4j_props_path, :log_dir, :command_args,
11
- :tick_time, :snap_count, :force_sync, :zoo_cfg_hash, :client_port,
12
- :max_client_cnxns, :stdio_redirect_path, :zoo_cfg_path
13
-
10
+ :base_dir, :data_dir, :log4j_props_path, :command_args,
11
+ :client_port, :stdio_redirect_path, :zoo_cfg_path,
12
+ :zoo_myid_path, :myid
13
+
14
14
  # the {Config} object that will be used to configure this Process
15
15
  attr_accessor :config
16
16
 
@@ -18,7 +18,7 @@ module ZK
18
18
  @child_startup_timeout = opts.delete(:child_startup_timeout) || 6
19
19
 
20
20
  @run_called = false
21
- @config = Config.new(opts)
21
+ @config = opts[:config] || Config.new(opts)
22
22
 
23
23
  @mutex = Monitor.new
24
24
  @exit_cond = @mutex.new_cond
@@ -69,6 +69,7 @@ module ZK
69
69
  def create_files!
70
70
  mkdir_p base_dir
71
71
  mkdir_p data_dir
72
+ write_myid!
72
73
  write_zoo_cfg!
73
74
  write_log4j_properties!
74
75
  mkdir_p(File.dirname(stdio_redirect_path))
@@ -80,21 +81,15 @@ module ZK
80
81
  end
81
82
  end
82
83
 
84
+ def write_myid!
85
+ File.open(zoo_myid_path, 'w') do |io|
86
+ io.puts myid
87
+ end
88
+ end
89
+
83
90
  def write_zoo_cfg!
84
91
  File.open(zoo_cfg_path, 'w') do |fp|
85
- fp.puts <<-EOS
86
- tickTime=#{tick_time}
87
- dataDir=#{data_dir}
88
- clientPort=#{client_port}
89
- maxClientCnxns=#{max_client_cnxns}
90
- EOS
91
-
92
- fp.puts("forceSync=#{force_sync}") if force_sync
93
- fp.puts("snapCount=#{snap_count}") if snap_count
94
- zoo_cfg_hash.each do |k,v|
95
- fp.puts("#{k}=#{v}")
96
- end
97
-
92
+ fp.puts(config.to_config_file_str)
98
93
  fp.fsync
99
94
  end
100
95
  end
@@ -0,0 +1,96 @@
1
+ module ZK
2
+ module Server
3
+ # Cluster simplifies the case when you want to test a 3+ node cluster
4
+ # You give the base_dir, and number of nodes, and it will construct a
5
+ # cluster for you. It's less configurable than the SubProcess class, but
6
+ # that's the price of convenience!
7
+ class Cluster
8
+ FOLLOWER_PORT_OFFSET = 100
9
+ LEADER_PORT_OFFSET = 200
10
+ DEFAULT_BASE_PORT = 21811
11
+
12
+ attr_accessor :base_dir
13
+
14
+ # defaults to 21811, used as the lowest port number for the cluster,
15
+ # all others will be offsets of this number
16
+ attr_accessor :base_port
17
+
18
+ # access to the SubProcess instances that make up this cluster
19
+ attr_reader :processes
20
+
21
+ # how many nodes in the cluster
22
+ attr_reader :num_members
23
+
24
+ def initialize(num_members, opts={})
25
+ @num_members = num_members
26
+ @base_dir = Config.default_base_dir
27
+ @base_port = DEFAULT_BASE_PORT
28
+ @processes = nil
29
+ @running = false
30
+ opts.each { |k,v| __send__(:"#{k}=", v) }
31
+ end
32
+
33
+ def running?
34
+ !!@running
35
+ end
36
+
37
+ def run
38
+ return if running?
39
+ @running = true
40
+
41
+ processes.each { |p| p.run }
42
+ rescue Exception
43
+ processes.each { |p| p.shutdown }
44
+ raise
45
+ end
46
+
47
+ def shutdown
48
+ return unless running?
49
+ @running = false
50
+
51
+ pary, @processes = @processes, nil
52
+
53
+ pary.each(&:shutdown)
54
+ pary
55
+ end
56
+
57
+ def clobber!
58
+ processes.each(&:clobber!)
59
+ end
60
+
61
+ def all_running?
62
+ processes.all?(&:running?)
63
+ end
64
+
65
+ def ping_all?
66
+ processes.all?(&:ping?)
67
+ end
68
+
69
+ def processes
70
+ @processes ||= [].tap do |ary|
71
+ num_members.times do |idx|
72
+ ary << SubProcess.new.tap do |sp|
73
+ c = sp.config
74
+
75
+ c.myid = idx
76
+ c.base_dir = File.join(@base_dir, "server-#{idx}")
77
+ c.client_port = base_port + idx
78
+ c.zoo_cfg_hash = server_hash
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ # terrible name, the list of 'servers' lines in the config
85
+ #
86
+ def server_hash
87
+ @server_hash ||= {}.tap do |h|
88
+ num_members.times do |idx|
89
+ h["server.#{idx}"] = "127.0.0.1:#{base_port + FOLLOWER_PORT_OFFSET + idx}:#{base_port + LEADER_PORT_OFFSET + idx}"
90
+ end
91
+ end
92
+ end
93
+
94
+ end # Cluster
95
+ end # Server
96
+ end # ZK
@@ -0,0 +1,69 @@
1
+ require 'thread'
2
+
3
+ module ZK
4
+ module Server
5
+ class Command
6
+ include Logging
7
+
8
+ def initialize
9
+ @mutex = Mutex.new
10
+ @cond = ConditionVariable.new
11
+ @shutdown_requested = false
12
+ end
13
+
14
+ def spawn_shutdown_handler
15
+ @shutdown_thread ||= Thread.new do
16
+ @mutex.synchronize do
17
+ @cond.wait until @shutdown_requested
18
+ logger.debug { "shutdown thread awakened! shutting down server!" }
19
+ @server.shutdown if @server
20
+ end
21
+ end
22
+ end
23
+
24
+ def run
25
+ opts = Slop.parse(:help => true, :strict => true) do
26
+ banner "zk-server [opts] runs a ZooKeeper server in the foreground"
27
+ on :d, :base_dir=, "defaults to #{ZK::Server::Config.default_base_dir}"
28
+ on :force_sync, 'force fsync on every snapshot'
29
+ on :skip_acl, 'skip acl checks'
30
+ on :p, :port=, 'port to listen on', :as => :integer
31
+ on :jvm_flags=, 'additional JVM flags to pass'
32
+ on :snap_count=, 'how often to take a snapshot, default 100_000', :as => :integer
33
+ end
34
+
35
+ return if opts.help?
36
+
37
+ hash = opts.to_hash
38
+
39
+ hash.delete(:help)
40
+ hash.reject! { |k,v| v.nil? }
41
+
42
+ config = ZK::Server::Config.new(hash)
43
+
44
+ spawn_shutdown_handler
45
+
46
+
47
+ %w[HUP INT].each do |sig|
48
+ trap(sig) do
49
+ @mutex.synchronize do
50
+ $stderr.puts "trapped #{sig}, shutting down"
51
+ @shutdown_requested = true
52
+ @cond.broadcast
53
+ end
54
+ end
55
+ end
56
+
57
+ @server = ZK::Server.new(:config => config)
58
+ @server.run
59
+
60
+ unless @server.join
61
+ $stderr.puts "server exited with status #{@server.status.inspect}"
62
+ st = @server.status
63
+ exit st.exited? ? st.exitstatus : 42
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+
@@ -1,10 +1,15 @@
1
1
  module ZK
2
2
  module Server
3
+ # Note that this supports all of the 3.3.5 options, but __will not__ do any
4
+ # sanity checking for you. All options specifyable here (especially those that
5
+ # require directories to be created outside of {#base_dir}) may not be handled
6
+ # properly by {Base} and subclasses.
7
+ #
3
8
  class Config
4
9
  DEFAULT_JVM_FLAGS = %w[
5
- -server
10
+ -server
6
11
  -Xmx256m
7
- -Dzookeeper.serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
12
+ -Dzookeeper.serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
8
13
  ].freeze
9
14
 
10
15
  # the com.sun.managemnt.jmxremote.port arg will be filled in dynamically
@@ -23,12 +28,32 @@ module ZK
23
28
  # basis for all other path generation. Defaults to `File.join(Dir.getwd, 'zookeeper')`
24
29
  attr_accessor :base_dir
25
30
 
31
+ # defaults to `#{base_dir}/data`. use this to override the default
32
+ #
33
+ # > the location where ZooKeeper will store the in-memory database
34
+ # > snapshots and, unless specified otherwise, the transaction log of
35
+ # > updates to the database.
36
+ #
37
+ # be aware that the {ZK::Server::Base} class will not create any
38
+ # directories but {#base_dir}.
39
+ #
40
+ attr_writer :data_dir
41
+
42
+ # defaults to nil, and zookeeper will just use the default value (being the
43
+ # same as {#data_dir}
44
+ #
45
+ # > This option will direct the machine to write the transaction log to the
46
+ # > dataLogDir rather than the dataDir. This allows a dedicated log device
47
+ # > to be used, and helps avoid competition between logging and snaphots.
48
+ #
49
+ attr_accessor :data_log_dir
50
+
26
51
  # a hash that will be used to provide extra values for the zoo.cfg file.
27
- # keys are written as-is to the file, so they should be camel-cased.
52
+ # keys are written as-is to the file, so they should be camel-cased.
28
53
  #
29
54
  # dataDir will be set relative to {#base_dir} and clientPort will either use
30
55
  # the default of 2181, or can be adjusted by {#client_port=}
31
- #
56
+ #
32
57
  attr_accessor :zoo_cfg_hash
33
58
 
34
59
  # what port should the server listen on for connections? (default 2181)
@@ -42,6 +67,36 @@ module ZK
42
67
  # defaults to 2000
43
68
  attr_accessor :tick_time
44
69
 
70
+ # what cluster id should this zookeeper use for itself? (default 1)
71
+ attr_accessor :myid
72
+
73
+ # default is nil, and will not be specified in the config
74
+ #
75
+ # > Clients can submit requests faster than ZooKeeper can process them,
76
+ # > especially if there are a lot of clients. To prevent ZooKeeper from
77
+ # > running out of memory due to queued requests, ZooKeeper will throttle
78
+ # > clients so that there is no more than globalOutstandingLimit
79
+ # > outstanding requests in the system. The default limit is 1,000.
80
+ #
81
+ attr_accessor :global_outstanding_limit
82
+
83
+ # necessary for cluster nodes (default 5)
84
+ #
85
+ # from the zookeeper admin guide:
86
+ # > Amount of time, in ticks (see tickTime), to allow followers to connect
87
+ # > and sync to a leader. Increased this value as needed, if the amount of data
88
+ # > managed by ZooKeeper is large.
89
+ #
90
+ attr_accessor :init_limit
91
+
92
+ # necessary for cluster nodes (default 2)
93
+ #
94
+ # > Amount of time, in ticks (see tickTime), to allow followers to sync
95
+ # > with ZooKeeper. If followers fall too far behind a leader, they will be
96
+ # > dropped.
97
+ #
98
+ attr_accessor :sync_limit
99
+
45
100
  # from the [admin guide](http://zookeeper.apache.org/doc/r3.3.5/zookeeperAdmin.html)
46
101
  #
47
102
  # > ZooKeeper logs transactions to a transaction log. After snapCount
@@ -50,7 +105,7 @@ module ZK
50
105
  #
51
106
  # For testing, to speed up disk IO, I generally set this to 1_000_000 and
52
107
  # force_sync to false. YMMV, understand what this does before messing with it
53
- # if you care about your data.
108
+ # if you care about your data.
54
109
  #
55
110
  # default: unset
56
111
  attr_accessor :snap_count
@@ -59,9 +114,74 @@ module ZK
59
114
  # an fsync() call for each snapshot write. It is however DANGEROUS if you
60
115
  # care about the data. (I set it to false for running tests)
61
116
  #
117
+ # if true: 'yes', false: 'no', nil not specified in the config
118
+ #
62
119
  # default: no value set
63
120
  attr_accessor :force_sync
64
121
 
122
+ # default: nil
123
+ #
124
+ # > To avoid seeks ZooKeeper allocates space in the transaction log file in
125
+ # > blocks of preAllocSize kilobytes. The default block size is 64M. One
126
+ # > reason for changing the size of the blocks is to reduce the block size
127
+ # > if snapshots are taken more often. (Also, see snapCount).
128
+ attr_accessor :pre_alloc_size
129
+
130
+ # default: nil
131
+ #
132
+ # > the address (ipv4, ipv6 or hostname) to listen for client
133
+ # > connections; that is, the address that clients attempt to connect to.
134
+ # > This is optional, by default we bind in such a way that any connection
135
+ # > to the clientPort for any address/interface/nic on the server will be
136
+ # > accepted.
137
+ #
138
+ attr_accessor :client_port_address
139
+
140
+ # default: nil
141
+ #
142
+ # > the minimum session timeout in milliseconds that the server will allow
143
+ # > the client to negotiate. Defaults to 2 times the tickTime
144
+ #
145
+ attr_accessor :min_session_timeout
146
+
147
+ # default: nil
148
+ #
149
+ # > the maximum session timeout in milliseconds that the server will allow
150
+ # > the client to negotiate. Defaults to 20 times the tickTime.
151
+ attr_accessor :max_session_timeout
152
+
153
+ # default: nil
154
+ #
155
+ # If true, the value 'yes' will be used for this value, false will write 'no'
156
+ # to the config file. The default (nil) is not to specify a value in the config.
157
+ #
158
+ # > Leader accepts client connections. Default value is "yes". The leader
159
+ # > machine coordinates updates. For higher update throughput at thes
160
+ # > slight expense of read throughput the leader can be configured to not
161
+ # > accept clients and focus on coordination. The default to this option is
162
+ # > yes, which means that a leader will accept client connections
163
+ #
164
+ attr_accessor :leader_serves
165
+
166
+ # default: nil
167
+ #
168
+ # by default this is not specified
169
+ #
170
+ # > Sets the timeout value for opening connections for leader election
171
+ # > notifications.
172
+ #
173
+ attr_accessor :cnx_timeout
174
+
175
+ # default: nil
176
+ #
177
+ # if true: 'yes', false: 'no', nil not specified in the config
178
+ #
179
+ # this is listed as a DANGEROUS setting
180
+ #
181
+ # > Skips ACL checks. This results in a boost in throughput, but opens up
182
+ # > full access to the data tree to everyone.
183
+ attr_accessor :skip_acl
184
+
65
185
  # if truthy, will enable jmx (defaults to false)
66
186
  # note that our defualt jmx config has all security and auth turned off
67
187
  # if you want to customize this, then use jvm_flags and set this to false
@@ -75,8 +195,13 @@ module ZK
75
195
  # default is {DEEFAULT_JVM_FLAGS}
76
196
  attr_accessor :jvm_flags
77
197
 
198
+ def self.default_base_dir
199
+ File.join(Dir.getwd, 'zookeeper')
200
+ end
201
+
78
202
  def initialize(opts={})
79
- @base_dir = File.join(Dir.getwd, 'zookeeper')
203
+ $stderr.puts "#{self.class}#initialize #{opts.inspect}"
204
+ @base_dir = self.class.default_base_dir
80
205
  @zoo_cfg_hash = {}
81
206
  @tick_time = 2000
82
207
  @client_port = 2181
@@ -85,6 +210,9 @@ module ZK
85
210
  @jmx_port = 22222
86
211
  @enable_jmx = false
87
212
  @jvm_flags = DEFAULT_JVM_FLAGS.dup
213
+ @myid = 1
214
+ @init_limit = 5
215
+ @sync_limit = 2
88
216
 
89
217
  @max_client_cnxns = 100
90
218
 
@@ -96,6 +224,11 @@ module ZK
96
224
  File.join(base_dir, 'zoo.cfg')
97
225
  end
98
226
 
227
+ # @private
228
+ def zoo_myid_path
229
+ File.join(base_dir, 'data', 'myid')
230
+ end
231
+
99
232
  # @private
100
233
  def log4j_props_path
101
234
  File.join(base_dir, 'log4j.properties')
@@ -113,7 +246,7 @@ module ZK
113
246
 
114
247
  # @private
115
248
  def data_dir
116
- File.join(base_dir, 'data')
249
+ @data_dir ||= File.join(base_dir, 'data')
117
250
  end
118
251
 
119
252
  # @private
@@ -132,6 +265,40 @@ module ZK
132
265
  cmd += jvm_flags
133
266
  cmd += %W[-cp #{classpath.join(':')} #{ZOO_MAIN} #{zoo_cfg_path}]
134
267
  end
268
+
269
+ # renders this config as a string that can be written to zoo.cfg
270
+ def to_config_file_str
271
+ config = {
272
+ 'dataDir' => data_dir,
273
+ 'skipACL' => skip_acl,
274
+ 'tickTime' => tick_time,
275
+ 'initLimit' => init_limit,
276
+ 'syncLimit' => sync_limit,
277
+ 'forceSync' => force_sync,
278
+ 'snapCount' => snap_count,
279
+ 'clientPort' => client_port,
280
+ 'dataLogDir' => data_log_dir,
281
+ 'preAllocSize' => pre_alloc_size,
282
+ 'leaderServes' => leader_serves,
283
+ 'maxClientCnxns' => max_client_cnxns,
284
+ 'clientPortAddress' => client_port_address,
285
+ 'minSessionTimeout' => min_session_timeout,
286
+ 'maxSessionTimeout' => max_session_timeout,
287
+ 'globalOutstandingLimit' => global_outstanding_limit,
288
+ }
289
+
290
+ config = config.merge(zoo_cfg_hash)
291
+
292
+ config.delete_if { |k,v| v.nil? }
293
+
294
+ %w[leaderServes skipACL forceSync].each do |yorn_key|
295
+ if config.has_key?(yorn_key)
296
+ config[yorn_key] = config[yorn_key] ? 'yes' : 'no'
297
+ end
298
+ end
299
+
300
+ config.sort.map {|kv| kv.join("=") }.join("\n")
301
+ end
135
302
  end
136
303
  end
137
304
  end
@@ -8,7 +8,7 @@ module ZK
8
8
  # to store our data under (configurable).
9
9
  #
10
10
  class SubProcess < Base
11
- attr_reader :exit_status
11
+ attr_reader :status
12
12
 
13
13
  # how long should we wait for the child to start responding to 'ruok'?
14
14
  attr_accessor :child_startup_timeout
@@ -17,14 +17,28 @@ module ZK
17
17
  @exit_watching_thread = nil
18
18
 
19
19
  @pid = nil
20
- @exit_status = nil
20
+ @status = nil
21
21
 
22
22
  super
23
23
  end
24
24
 
25
+ # wait for sub-process to exit
26
+ def join
27
+ @mutex.synchronize do
28
+ logger.debug { "spawned process #{@pid}" }
29
+ @exit_cond.wait_until { @status }
30
+ end
31
+ @status.success?
32
+ end
33
+
25
34
  # true if the process was started and is still running
26
35
  def running?
27
- spawned? and !@exit_status and false|Process.kill(0, @pid)
36
+ spawned? and !@status and alive?
37
+ end
38
+
39
+ def alive?
40
+ return false unless spawned?
41
+ false|kill(0)
28
42
  rescue Errno::ESRCH
29
43
  false
30
44
  end
@@ -37,16 +51,16 @@ module ZK
37
51
  # shutdown the child, wait for it to exit, ensure it is dead
38
52
  def shutdown
39
53
  if @pid
40
- return if @exit_status
54
+ return if @status
41
55
 
42
56
  @mutex.synchronize do
43
57
  %w[HUP TERM KILL].each do |signal|
44
58
  logger.debug { "sending #{signal} to #{@pid}" }
45
59
 
46
- return unless running? # jruby doesn't seem to get @exit_status ?
60
+ return unless running? # jruby doesn't seem to get @status ?
47
61
 
48
62
  begin
49
- Process.kill(signal, @pid)
63
+ kill(signal)
50
64
  rescue Errno::ESRCH
51
65
  return true
52
66
  end
@@ -55,7 +69,13 @@ module ZK
55
69
  end
56
70
  end
57
71
 
58
- logger.debug { "@exit_status: #{@exit_status}" }
72
+ unless @exit_watching_thread.join(3) == @exit_watching_thread
73
+ logger.warn { "exit watching thread didn't exit after 3 sec" }
74
+ end
75
+
76
+ @pid = @exit_watching_thread = nil
77
+
78
+ logger.debug { "@status: #{@status}" }
59
79
  end
60
80
  true
61
81
  end
@@ -65,6 +85,11 @@ module ZK
65
85
  @pid
66
86
  end
67
87
 
88
+ def kill(signal)
89
+ raise "No pid yet!" unless @pid
90
+ Process.kill(signal, @pid)
91
+ end
92
+
68
93
  # start the child, using the {#config}. we create the files necessary,
69
94
  # fork the child, and wait 5s for the child to start responding to pings
70
95
  #
@@ -99,7 +124,7 @@ module ZK
99
124
  protected
100
125
  def spawn_exit_watching_thread
101
126
  @exit_watching_thread ||= Thread.new do
102
- _, @exit_status = Process.wait2(@pid)
127
+ _, @status = Process.wait2(@pid)
103
128
  @mutex.synchronize do
104
129
  @exit_cond.broadcast
105
130
  end
@@ -124,7 +149,9 @@ module ZK
124
149
  @pid ||= (
125
150
  args = command_args()
126
151
  args << { :err => [:child, :out], :out => [stdio_redirect_path, 'w'] }
127
- ::Kernel.spawn({}, *command_args)
152
+ ::Kernel.spawn({}, *command_args).tap do |pid|
153
+ logger.debug { "Spawned process #{pid}" }
154
+ end
128
155
  )
129
156
  end
130
157
 
@@ -1,5 +1,5 @@
1
1
  module ZK
2
2
  module Server
3
- VERSION = "1.0.1" unless defined?(::ZK::Server::VERSION)
3
+ VERSION = "1.1.0" unless defined?(::ZK::Server::VERSION)
4
4
  end
5
5
  end
data/lib/zk-server.rb CHANGED
@@ -9,6 +9,8 @@ require 'socket'
9
9
 
10
10
  Bundler.require
11
11
 
12
+ require 'slop'
13
+
12
14
  # require 'zk'
13
15
 
14
16
  #ZK.logger = Logger.new($stderr).tap { |l| l.level = Logger::DEBUG }
@@ -130,6 +132,8 @@ require 'zk-server/logging'
130
132
  require 'zk-server/config'
131
133
  require 'zk-server/base'
132
134
  require 'zk-server/sub_process'
135
+ require 'zk-server/cluster'
136
+ require 'zk-server/command'
133
137
 
134
138
  if defined?(::JRUBY_VERSION)
135
139
  require 'zk-server/java_embedded'
data/spec/spec_helper.rb CHANGED
@@ -13,5 +13,7 @@ ZK::Server.logger = Logger.new($stderr).tap { |l| l.level = Logger::DEBUG }
13
13
 
14
14
  RSpec.configure do |config|
15
15
  config.mock_with :rspec
16
+ config.include(WaitWatchers)
17
+ config.extend(WaitWatchers)
16
18
  end
17
19
 
@@ -0,0 +1,55 @@
1
+ module WaitWatchers
2
+ class TimeoutError < StandardError; end
3
+
4
+ # method to wait until block passed returns truthy (false will not work) or
5
+ # timeout (default is 2 seconds) is reached raises TiemoutError on timeout
6
+ #
7
+ # @returns the truthy value
8
+ #
9
+ # @example
10
+ #
11
+ # @a = nil
12
+ #
13
+ # th = Thread.new do
14
+ # sleep(1)
15
+ # @a = :fudge
16
+ # end
17
+ #
18
+ # wait_until(2) { @a }.should == :fudge
19
+ #
20
+ def wait_until(timeout=2)
21
+ if ENV['TRAVIS'] and timeout and timeout < 5
22
+ timeout = 5
23
+ end
24
+
25
+ time_to_stop = Time.now + timeout
26
+ while true
27
+ rval = yield
28
+ return rval if rval
29
+ raise TimeoutError, "timeout of #{timeout}s exceeded" if Time.now > time_to_stop
30
+ Thread.pass
31
+ end
32
+ end
33
+
34
+ # inverse of wait_until
35
+ def wait_while(timeout=2)
36
+ if ENV['travis'] and timeout and timeout < 5
37
+ timeout = 5
38
+ end
39
+
40
+ time_to_stop = Time.now + timeout
41
+ while true
42
+ rval = yield
43
+ return rval unless rval
44
+ raise TimeoutError, "timeout of #{timeout}s exceeded" if Time.now > time_to_stop
45
+ Thread.pass
46
+ end
47
+ end
48
+
49
+ def report_realtime(what)
50
+ return yield
51
+ t = Benchmark.realtime { yield }
52
+ $stderr.puts "#{what}: %0.3f" % [t.to_f]
53
+ end
54
+ end
55
+
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe ZK::Server::Cluster do
4
+ let(:tmpdir) { '/tmp/zookeeper' }
5
+
6
+ let(:num_members) { 5 }
7
+
8
+ subject do
9
+ described_class.new(num_members, :base_dir => tmpdir)
10
+ end
11
+
12
+ after do
13
+ subject.shutdown
14
+ # subject.clobber!
15
+ # FileUtils.rm_rf(tmpdir)
16
+ end
17
+
18
+ it %[should spawn a ZK server, ping, and then shutdown properly] do
19
+ pending "cannot run this under JRuby" if defined?(::JRUBY_VERSION)
20
+
21
+ subject.run
22
+
23
+ wait_until { subject.all_running? }
24
+
25
+ subject.should be_ping_all
26
+ subject.should be_running
27
+
28
+ subject.shutdown
29
+
30
+ subject.should_not be_ping_all
31
+ subject.should_not be_running
32
+ end
33
+ end
34
+
data/zk-server.gemspec CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.add_runtime_dependency 'bundler', '~> 1.1.3'
12
12
  s.add_runtime_dependency 'slyphon-log4j', '= 1.2.15'
13
13
  s.add_runtime_dependency 'slyphon-zookeeper_jar', '~> 3.3.5'
14
+ s.add_runtime_dependency 'slop', '~> 3.2.0'
14
15
 
15
16
  s.files = `git ls-files`.split($\)
16
17
  s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zk-server
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 0
9
8
  - 1
10
- version: 1.0.1
9
+ - 0
10
+ version: 1.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jonathan D. Simms
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-05-04 00:00:00 Z
18
+ date: 2012-05-16 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: bundler
@@ -65,11 +65,27 @@ dependencies:
65
65
  version: 3.3.5
66
66
  type: :runtime
67
67
  version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: slop
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ hash: 15
77
+ segments:
78
+ - 3
79
+ - 2
80
+ - 0
81
+ version: 3.2.0
82
+ type: :runtime
83
+ version_requirements: *id004
68
84
  description: runs a standalone zookeeper server
69
85
  email:
70
86
  - slyphon@gmail.com
71
- executables: []
72
-
87
+ executables:
88
+ - zk-server
73
89
  extensions: []
74
90
 
75
91
  extra_rdoc_files: []
@@ -83,8 +99,11 @@ files:
83
99
  - MIT_LICENSE
84
100
  - README.md
85
101
  - Rakefile
102
+ - bin/zk-server
86
103
  - lib/zk-server.rb
87
104
  - lib/zk-server/base.rb
105
+ - lib/zk-server/cluster.rb
106
+ - lib/zk-server/command.rb
88
107
  - lib/zk-server/config.rb
89
108
  - lib/zk-server/java_embedded.rb
90
109
  - lib/zk-server/log4j.properties
@@ -92,6 +111,8 @@ files:
92
111
  - lib/zk-server/sub_process.rb
93
112
  - lib/zk-server/version.rb
94
113
  - spec/spec_helper.rb
114
+ - spec/support/wait_watchers.rb
115
+ - spec/zk-server/cluster_spec.rb
95
116
  - spec/zk-server/java_embedded_spec.rb
96
117
  - spec/zk-server/process_spec.rb
97
118
  - zk-server.gemspec
@@ -124,11 +145,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
145
  requirements: []
125
146
 
126
147
  rubyforge_project:
127
- rubygems_version: 1.8.15
148
+ rubygems_version: 1.8.24
128
149
  signing_key:
129
150
  specification_version: 3
130
151
  summary: runs a standalone zookeeper server
131
152
  test_files:
132
153
  - spec/spec_helper.rb
154
+ - spec/support/wait_watchers.rb
155
+ - spec/zk-server/cluster_spec.rb
133
156
  - spec/zk-server/java_embedded_spec.rb
134
157
  - spec/zk-server/process_spec.rb