zk-server 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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