bumbleworks 0.0.90 → 0.0.91

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 28e8c92dc9de4fb9204a5b2348565a1d47d960b0
4
- data.tar.gz: 6fb4c218cbe093862c9a61b418aa32b2a4594238
3
+ metadata.gz: a41e2e146849e9de9752fcc88353008da72e6c94
4
+ data.tar.gz: 776a60a147c9cbd1512cae7d81519667ec514c2a
5
5
  SHA512:
6
- metadata.gz: abb2292ed00b147314a70f633619c62e77a20e5ccd7bd73c4cb91e2131c8e58b5764dc3a38a4bb840079cc51859618a654b1ef5ae32270ea8f58a7429f5a8fce
7
- data.tar.gz: b5fee4533318cea0829dcb9e691d6ccea143e94157730e89fccae34dbeac3513f1620d39b8f46f876f18230d0361fbb53cd5817c5ed861224b6fc78241019285
6
+ metadata.gz: 5f1d34125e457ae2b08b599d80cddd4e2f51b7ae332636d14c52cfb0fa2dd8f40e92ccc9336c30356d43bf17593338c5018bb5e1cc0d5a0e90ce0a5fd7d8a030
7
+ data.tar.gz: 01636c8cf9af7e4b5f452282f663962ef16afe8520ccb469a5f1deb8730bbcd76f810acb39aae6eaf13e3e436e41ea14f017910a479994c11414abe0c30c94ca
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.5
5
+ - 2.2.0
@@ -1,3 +1,3 @@
1
1
  module Bumbleworks
2
- VERSION = "0.0.90"
2
+ VERSION = "0.0.91"
3
3
  end
@@ -8,12 +8,13 @@ class Bumbleworks::Worker < Ruote::Worker
8
8
 
9
9
  class << self
10
10
  def info
11
- Bumbleworks.dashboard.worker_info || {}
11
+ Bumbleworks::Worker::Info || {}
12
12
  end
13
13
 
14
14
  def shutdown_all(options = {})
15
15
  # First, send all running workers a message to stop
16
16
  change_worker_state('stopped', options)
17
+ ensure
17
18
  # Now ensure that future started workers will be started
18
19
  # in "running" mode instead of automatically stopped
19
20
  change_worker_state('running', options)
@@ -27,10 +28,10 @@ class Bumbleworks::Worker < Ruote::Worker
27
28
  change_worker_state('running', options)
28
29
  end
29
30
 
30
- def worker_states
31
+ def active_worker_states
31
32
  info.inject({}) { |hsh, info|
32
- id, state = info[0], info[1]['state']
33
- if state && state != 'stopped'
33
+ id, state = info.id, info.state
34
+ unless info.state.nil? || info.in_stopped_state?
34
35
  hsh[id] = state
35
36
  end
36
37
  hsh
@@ -41,21 +42,20 @@ class Bumbleworks::Worker < Ruote::Worker
41
42
  with_worker_state_enabled do
42
43
  Bumbleworks.dashboard.worker_state = new_state
43
44
  Bumbleworks::Support.wait_until(options) do
44
- worker_states.values.all? { |state| state == new_state }
45
+ active_worker_states.values.all? { |state| state == new_state }
45
46
  end
46
47
  end
47
48
  return true
48
49
  rescue Bumbleworks::Support::WaitTimeout
49
- raise WorkerStateNotChanged, "Worker states: #{worker_states.inspect}"
50
+ raise WorkerStateNotChanged, "Worker states: #{active_worker_states.inspect}"
50
51
  end
51
52
 
52
53
  def refresh_worker_info(options = {})
53
54
  with_worker_state_enabled do
54
- Bumbleworks::Support.wait_until(options) do
55
- info.all? { |id, worker_info|
56
- worker_info['state'] == 'stopped' ||
57
- Time.parse(worker_info['put_at']) > Time.now - 1
58
- }
55
+ info.each do |worker_info|
56
+ if !worker_info.in_stopped_state? && worker_info.stalling?
57
+ worker_info.record_new_state("stalled")
58
+ end
59
59
  end
60
60
  end
61
61
  end
@@ -65,6 +65,22 @@ class Bumbleworks::Worker < Ruote::Worker
65
65
  yield
66
66
  Bumbleworks.dashboard.context['worker_state_enabled'] = false
67
67
  end
68
+
69
+ def control_document
70
+ doc = Bumbleworks.dashboard.storage.get('variables', 'worker_control') || {}
71
+ doc['type'] = 'variables'
72
+ doc['_id'] = 'worker_control'
73
+ doc['workers'] ||= {}
74
+ doc
75
+ end
76
+
77
+ def info_document
78
+ doc = Bumbleworks.dashboard.storage.get('variables', 'workers') || {}
79
+ doc['type'] = 'variables'
80
+ doc['_id'] = 'workers'
81
+ doc['workers'] ||= {}
82
+ doc
83
+ end
68
84
  end
69
85
 
70
86
  def initialize(*args, &block)
@@ -83,6 +99,10 @@ class Bumbleworks::Worker < Ruote::Worker
83
99
  end
84
100
  end
85
101
 
102
+ def class_name
103
+ self.class.to_s
104
+ end
105
+
86
106
  def save_info
87
107
  @info.save if @info
88
108
  end
@@ -92,10 +112,23 @@ class Bumbleworks::Worker < Ruote::Worker
92
112
  save_info
93
113
  end
94
114
 
115
+ def worker_control_variable
116
+ self.class.control_document["workers"][id]
117
+ end
118
+
119
+ def desired_state
120
+ control_hash = worker_control_variable ||
121
+ @storage.get("variables", "worker") ||
122
+ { "state" => "running" }
123
+ control_hash["state"]
124
+ end
125
+
95
126
  def determine_state
96
- if @context['worker_state_enabled']
97
- super
98
- save_info
127
+ @state_mutex.synchronize do
128
+ if @state != "stopped" && @context["worker_state_enabled"]
129
+ @state = desired_state
130
+ save_info
131
+ end
99
132
  end
100
133
  end
101
134
 
@@ -3,18 +3,27 @@ require_relative "proxy"
3
3
  class Bumbleworks::Worker < Ruote::Worker
4
4
  class Info < Ruote::Worker::Info
5
5
  attr_reader :worker
6
+ extend Forwardable
7
+ extend Enumerable
8
+
9
+ def_delegators :worker,
10
+ :id, :pid, :name, :state, :ip, :hostname, :system, :launched_at
6
11
 
7
12
  class << self
8
13
  def raw_hash
9
14
  Bumbleworks.dashboard.worker_info || {}
10
15
  end
11
16
 
12
- def all
13
- raw_hash.map { |k, v|
14
- from_hash(v.merge('id' => k))
17
+ def each
18
+ raw_hash.each { |k, v|
19
+ yield from_hash(v)
15
20
  }
16
21
  end
17
22
 
23
+ def all
24
+ to_a
25
+ end
26
+
18
27
  def where(options)
19
28
  filter_proc = proc { |worker|
20
29
  options.all? { |k, v|
@@ -26,11 +35,11 @@ class Bumbleworks::Worker < Ruote::Worker
26
35
 
27
36
  def filter
28
37
  return [] unless block_given?
29
- all.select { |info| yield info.worker }
38
+ select { |info| yield info.worker }
30
39
  end
31
40
 
32
41
  def [](worker_id)
33
- from_hash(raw_hash[worker_id].merge('id' => worker_id))
42
+ from_hash(raw_hash[worker_id])
34
43
  end
35
44
 
36
45
  def from_hash(hash)
@@ -61,6 +70,13 @@ class Bumbleworks::Worker < Ruote::Worker
61
70
  end
62
71
  end
63
72
 
73
+ def initialize(worker)
74
+ @worker = worker
75
+ @last_save = Time.now - 2 * 60
76
+
77
+ @msgs = [] unless worker.is_a?(Bumbleworks::Worker::Proxy)
78
+ end
79
+
64
80
  def ==(other)
65
81
  other.is_a?(Bumbleworks::Worker::Info) &&
66
82
  other.worker == worker
@@ -70,6 +86,31 @@ class Bumbleworks::Worker < Ruote::Worker
70
86
  self.class.raw_hash[worker.id]
71
87
  end
72
88
 
89
+ def reload
90
+ @worker = Bumbleworks::Worker::Proxy.new(raw_hash)
91
+ end
92
+
93
+ def record_new_state(state)
94
+ worker.state = state
95
+ save
96
+ end
97
+
98
+ def worker_class_name
99
+ worker.class_name
100
+ end
101
+
102
+ def uptime
103
+ if in_stopped_state? && worker.respond_to?(:uptime)
104
+ worker.uptime
105
+ else
106
+ Time.now - worker.launched_at
107
+ end
108
+ end
109
+
110
+ def in_stopped_state?
111
+ worker.state.nil? || ["stopped", "stalled"].include?(worker.state)
112
+ end
113
+
73
114
  def updated_at
74
115
  Time.parse(raw_hash['put_at'])
75
116
  end
@@ -104,39 +145,76 @@ class Bumbleworks::Worker < Ruote::Worker
104
145
  @worker.storage || Bumbleworks.dashboard.storage
105
146
  end
106
147
 
107
- def initialize(worker)
108
- @worker = worker
109
- @last_save = Time.now - 2 * 60
148
+ def shutdown(options = {})
149
+ send_command("stopped", options)
150
+ end
110
151
 
111
- @msgs = [] unless worker.is_a?(Bumbleworks::Worker::Proxy)
152
+ def pause(options = {})
153
+ send_command("paused", options)
154
+ end
155
+
156
+ def unpause(options = {})
157
+ send_command("running", options)
112
158
  end
113
159
 
114
- def worker_info_document
115
- doc = storage.get('variables', 'workers') || {}
160
+ alias_method :run, :unpause
161
+
162
+ def send_command(command, options = {})
163
+ save_control_message(command)
164
+ Bumbleworks::Worker.with_worker_state_enabled do
165
+ Bumbleworks::Support.wait_until(options) do
166
+ raw_hash["state"] == command
167
+ end
168
+ end
169
+ reload
170
+ end
171
+
172
+ def save_control_message(message)
173
+ doc = Bumbleworks::Worker.control_document
174
+ doc["workers"][id] ||= {}
175
+ doc["workers"][id]["state"] = message
176
+ storage.put(doc)
177
+ end
178
+
179
+ def processed_last_minute
180
+ raw_hash["processed_last_minute"]
181
+ end
182
+
183
+ def wait_time_last_minute
184
+ raw_hash["wait_time_last_minute"]
185
+ end
186
+
187
+ def processed_last_hour
188
+ raw_hash["processed_last_hour"]
189
+ end
190
+
191
+ def wait_time_last_hour
192
+ raw_hash["wait_time_last_hour"]
193
+ end
116
194
 
117
- doc['type'] = 'variables'
118
- doc['_id'] = 'workers'
119
- doc['workers'] ||= {}
120
- doc
195
+ def constant_worker_info_hash
196
+ {
197
+ "id" => @worker.id,
198
+ "class" => @worker.class_name,
199
+ "name" => @worker.name,
200
+ "ip" => @worker.ip,
201
+ "hostname" => @worker.hostname,
202
+ "pid" => @worker.pid,
203
+ "system" => @worker.system,
204
+ "launched_at" => @worker.launched_at,
205
+ "state" => @worker.state
206
+ }
121
207
  end
122
208
 
123
209
  def save
124
- doc = worker_info_document
210
+ doc = Bumbleworks::Worker.info_document
125
211
 
126
212
  worker_info_hash = doc['workers'][@worker.id] || {}
127
213
 
214
+ worker_info_hash.merge!(constant_worker_info_hash)
128
215
  worker_info_hash.merge!({
129
- 'worker_id' => @worker.id,
130
- 'class' => @worker.class.to_s,
131
- 'name' => @worker.name,
132
- 'ip' => @worker.ip,
133
- 'hostname' => @worker.hostname,
134
- 'pid' => @worker.pid,
135
- 'system' => @worker.system,
136
216
  'put_at' => Ruote.now_to_utc_s,
137
- 'uptime' => Time.now - @worker.launched_at,
138
- 'launched_at' => @worker.launched_at,
139
- 'state' => @worker.state
217
+ 'uptime' => uptime,
140
218
  })
141
219
 
142
220
  if defined?(@msgs)
@@ -1,11 +1,12 @@
1
1
  class Bumbleworks::Worker < Ruote::Worker
2
2
  class Proxy
3
3
  ProxiedAttributes = [
4
- :id, :pid, :ip, :hostname, :system, :launched_at, :state, :name, :class
4
+ :id, :pid, :ip, :hostname, :system, :launched_at, :state, :name, :class, :uptime
5
5
  ]
6
6
 
7
7
  attr_reader *(ProxiedAttributes - [:launched_at])
8
8
  attr_reader :raw_hash
9
+ attr_writer :state
9
10
 
10
11
  def initialize(attributes)
11
12
  @raw_hash = attributes
@@ -14,8 +15,22 @@ class Bumbleworks::Worker < Ruote::Worker
14
15
  end
15
16
  end
16
17
 
18
+ # Allow storage to revert to the default for this Bumbleworks
19
+ # instance.
20
+ def storage
21
+ nil
22
+ end
23
+
24
+ def class_name
25
+ @class.to_s
26
+ end
27
+
17
28
  def launched_at
18
- Time.parse(@launched_at)
29
+ if @launched_at.is_a?(String)
30
+ Time.parse(@launched_at)
31
+ else
32
+ @launched_at
33
+ end
19
34
  end
20
35
 
21
36
  def ==(other)
@@ -2,19 +2,51 @@ describe Bumbleworks::Worker::Info do
2
2
  let(:context) { Bumbleworks.dashboard.context }
3
3
  let(:proxy) {
4
4
  Bumbleworks::Worker::Proxy.new(
5
- 'class' => :f_class,
6
- 'pid' => :f_pid,
7
- 'name' => :f_name,
8
- 'id' => :f_id,
9
- 'state' => :f_state,
10
- 'ip' => :f_ip,
11
- 'hostname' => :f_hostname,
12
- 'system' => :f_system,
13
- 'launched_at' => :f_launched_at
5
+ "class" => "f_class",
6
+ "pid" => "f_pid",
7
+ "name" => "f_name",
8
+ "id" => "f_id",
9
+ "state" => "f_state",
10
+ "ip" => "f_ip",
11
+ "hostname" => "f_hostname",
12
+ "system" => "f_system",
13
+ "uptime" => "f_uptime",
14
+ "launched_at" => "2010-10-10 10:10:10"
14
15
  )
15
16
  }
16
17
  subject { described_class.new(proxy) }
17
18
 
19
+
20
+ describe "delegation to hash" do
21
+ let(:raw_hash) {
22
+ {
23
+ "processed_last_minute" => 20,
24
+ "wait_time_last_minute" => 300.5,
25
+ "processed_last_hour" => 540,
26
+ "wait_time_last_hour" => 15.6
27
+ }
28
+ }
29
+ before(:each) do
30
+ allow(subject).to receive(:raw_hash).and_return(raw_hash)
31
+ end
32
+
33
+ it { is_expected.to fetch(:processed_last_minute).from(raw_hash) }
34
+ it { is_expected.to fetch(:wait_time_last_minute).from(raw_hash) }
35
+ it { is_expected.to fetch(:processed_last_hour).from(raw_hash) }
36
+ it { is_expected.to fetch(:wait_time_last_hour).from(raw_hash) }
37
+ end
38
+
39
+ describe "delegation to worker/proxy" do
40
+ it { is_expected.to delegate(:id).to(proxy) }
41
+ it { is_expected.to delegate(:pid).to(proxy) }
42
+ it { is_expected.to delegate(:name).to(proxy) }
43
+ it { is_expected.to delegate(:state).to(proxy) }
44
+ it { is_expected.to delegate(:ip).to(proxy) }
45
+ it { is_expected.to delegate(:hostname).to(proxy) }
46
+ it { is_expected.to delegate(:system).to(proxy) }
47
+ it { is_expected.to delegate(:launched_at).to(proxy) }
48
+ end
49
+
18
50
  describe '.raw_hash' do
19
51
  it 'returns Bumbleworks.dashboard.worker_info' do
20
52
  allow(Bumbleworks.dashboard).to receive(:worker_info).and_return(:bontron)
@@ -30,15 +62,16 @@ describe Bumbleworks::Worker::Info do
30
62
  describe '.from_hash' do
31
63
  it 'returns an info object using a proxy worker with given attributes' do
32
64
  hash = {
33
- 'class' => :f_class,
34
- 'pid' => :f_pid,
35
- 'name' => :f_name,
36
- 'id' => :f_id,
37
- 'state' => :f_state,
38
- 'ip' => :f_ip,
39
- 'hostname' => :f_hostname,
40
- 'system' => :f_system,
41
- 'launched_at' => :f_launched_at
65
+ "class" => "f_class",
66
+ "pid" => "f_pid",
67
+ "name" => "f_name",
68
+ "id" => "f_id",
69
+ "state" => "f_state",
70
+ "ip" => "f_ip",
71
+ "hostname" => "f_hostname",
72
+ "system" => "f_system",
73
+ "uptime" => "f_uptime",
74
+ "launched_at" => "2010-10-10 10:10:10"
42
75
  }
43
76
  allow(described_class).to receive(:new).
44
77
  with(proxy).
@@ -60,7 +93,7 @@ describe Bumbleworks::Worker::Info do
60
93
  describe '.[]' do
61
94
  it 'calls from_hash with raw data for given worker id' do
62
95
  allow(described_class).to receive(:from_hash).
63
- with({ 'foo' => 1, 'id' => 'f_id' }).and_return(:foo_info)
96
+ with({ 'foo' => 1 }).and_return(:foo_info)
64
97
  expect(described_class['f_id']).to eq(:foo_info)
65
98
  end
66
99
  end
@@ -68,9 +101,9 @@ describe Bumbleworks::Worker::Info do
68
101
  describe '.all' do
69
102
  it 'converts raw hash into info instances' do
70
103
  allow(described_class).to receive(:from_hash).
71
- with({ 'foo' => 1, 'id' => 'f_id'}).and_return(:foo_info)
104
+ with({ 'foo' => 1 }).and_return(:foo_info)
72
105
  allow(described_class).to receive(:from_hash).
73
- with({ 'bar' => 1, 'id' => 'b_id'}).and_return(:bar_info)
106
+ with({ 'bar' => 1 }).and_return(:bar_info)
74
107
  expect(described_class.all).to match_array([:foo_info, :bar_info])
75
108
  end
76
109
  end
@@ -128,6 +161,26 @@ describe Bumbleworks::Worker::Info do
128
161
  end
129
162
  end
130
163
 
164
+ describe "#worker_class_name" do
165
+ it "returns class_name from worker/proxy" do
166
+ allow(proxy).to receive(:class_name).and_return('barkle rutfut')
167
+ expect(subject.worker_class_name).to eq('barkle rutfut')
168
+ end
169
+ end
170
+
171
+ describe "#uptime" do
172
+ it "returns difference between now and updated_at" do
173
+ frozen_time = Time.now
174
+ allow(Time).to receive(:now).and_return(frozen_time)
175
+ expect(subject.uptime).to eq(frozen_time - subject.launched_at)
176
+ end
177
+
178
+ it "returns previous persisted uptime if stopped" do
179
+ allow(proxy).to receive(:state).and_return("stopped")
180
+ expect(subject.uptime).to eq(proxy.uptime)
181
+ end
182
+ end
183
+
131
184
  describe "#updated_at" do
132
185
  it "returns parsed put_at from raw hash for worker" do
133
186
  allow(subject).to receive(:raw_hash).and_return({
@@ -175,8 +228,8 @@ describe Bumbleworks::Worker::Info do
175
228
  describe "#raw_hash" do
176
229
  it "returns value from worker_info hash at key of worker id" do
177
230
  allow(described_class).to receive(:raw_hash).and_return({
178
- :f_id => :foo_hash,
179
- :b_id => :bar_hash
231
+ "f_id" => :foo_hash,
232
+ "b_id" => :bar_hash
180
233
  })
181
234
  expect(subject.raw_hash).to eq(:foo_hash)
182
235
  end
@@ -194,6 +247,28 @@ describe Bumbleworks::Worker::Info do
194
247
  end
195
248
  end
196
249
 
250
+ describe "#in_stopped_state?" do
251
+ it "returns true if state is stopped" do
252
+ allow(proxy).to receive(:state).and_return("stopped")
253
+ expect(subject).to be_in_stopped_state
254
+ end
255
+
256
+ it "returns true if state is stalled" do
257
+ allow(proxy).to receive(:state).and_return("stalled")
258
+ expect(subject).to be_in_stopped_state
259
+ end
260
+
261
+ it "returns true if state is nil" do
262
+ allow(proxy).to receive(:state).and_return(nil)
263
+ expect(subject).to be_in_stopped_state
264
+ end
265
+
266
+ it "returns false if state is running" do
267
+ allow(proxy).to receive(:state).and_return("running")
268
+ expect(subject).not_to be_in_stopped_state
269
+ end
270
+ end
271
+
197
272
  describe "#stalling?" do
198
273
  it "returns inverse of #responding?" do
199
274
  allow(subject).to receive(:responding?).and_return(true)
@@ -202,4 +277,96 @@ describe Bumbleworks::Worker::Info do
202
277
  expect(subject).to be_stalling
203
278
  end
204
279
  end
280
+
281
+ context "worker control commands" do
282
+ subject { Bumbleworks::Worker::Info.first }
283
+ let(:other_worker) { Bumbleworks::Worker::Info.all[1] }
284
+ before(:each) do
285
+ 2.times { Bumbleworks.start_worker! }
286
+ end
287
+
288
+ describe "#shutdown" do
289
+ it "shuts down just this worker" do
290
+ subject.shutdown
291
+ expect(subject.state).to eq("stopped")
292
+ expect(other_worker.state).to eq("running")
293
+ end
294
+ end
295
+
296
+ describe "#pause" do
297
+ it "pauses just this worker" do
298
+ subject.pause
299
+ expect(subject.state).to eq("paused")
300
+ expect(other_worker.state).to eq("running")
301
+ end
302
+ end
303
+
304
+ describe "#unpause" do
305
+ it "unpauses just this worker" do
306
+ [subject, other_worker].map(&:pause)
307
+ subject.unpause
308
+ expect(subject.state).to eq("running")
309
+ expect(other_worker.state).to eq("paused")
310
+ end
311
+ end
312
+
313
+ describe "#run" do
314
+ it "is an alias for unpause" do
315
+ expect(subject.method(:run)).to eq(subject.method(:unpause))
316
+ end
317
+ end
318
+ end
319
+
320
+ describe "#reload" do
321
+ it "generates a new proxy from the current raw hash" do
322
+ Bumbleworks.start_worker!
323
+ subject = Bumbleworks::Worker::Info.first
324
+ expect(Bumbleworks::Worker::Proxy).to receive(:new).
325
+ with(subject.raw_hash).and_return(:a_worker)
326
+ subject.reload
327
+ expect(subject.worker).to eq(:a_worker)
328
+ end
329
+ end
330
+
331
+ describe "#record_new_state" do
332
+ it "saves new state" do
333
+ expect(subject.worker).to receive(:state=).with("an awesome state").ordered
334
+ expect(subject).to receive(:save).ordered
335
+ subject.record_new_state("an awesome state")
336
+ end
337
+ end
338
+
339
+ describe "#save" do
340
+ before(:each) { Bumbleworks.start_worker! }
341
+ subject { Bumbleworks::Worker::Info.first }
342
+
343
+ it "persists all data unchanged to the engine storage" do
344
+ expected_hash = subject.constant_worker_info_hash
345
+ subject.save
346
+ expect(described_class[subject.id.to_s].constant_worker_info_hash).to eq(expected_hash)
347
+ end
348
+
349
+ it "updates uptime and updated_at even without changed data" do
350
+ uptime, updated_at = subject.uptime, subject.updated_at
351
+ subject.save
352
+ expect(described_class[subject.id.to_s].uptime).not_to eq(uptime)
353
+ expect(described_class[subject.id.to_s].updated_at).not_to eq(updated_at)
354
+ end
355
+
356
+ it "persists changed data" do
357
+ expected_hash = subject.constant_worker_info_hash
358
+ subject.worker.instance_variable_set(:@pid, "12345")
359
+ subject.save
360
+ expect(described_class[subject.id.to_s].id).to eq(expected_hash["id"])
361
+ expect(described_class[subject.id.to_s].pid).to eq("12345")
362
+ end
363
+
364
+ it "does not update uptime if worker is stopped" do
365
+ allow_any_instance_of(Bumbleworks::Worker::Proxy).to receive(:state).
366
+ and_return("stopped")
367
+ uptime = subject.uptime
368
+ subject.save
369
+ expect(described_class[subject.id.to_s].uptime).to eq(uptime)
370
+ end
371
+ end
205
372
  end
@@ -9,14 +9,39 @@ describe Bumbleworks::Worker::Proxy do
9
9
  'ip' => :f_ip,
10
10
  'hostname' => :f_hostname,
11
11
  'system' => :f_system,
12
- 'launched_at' => :f_launched_at
12
+ 'launched_at' => '2010-10-10 10:10:10'
13
13
  )
14
14
  }
15
15
 
16
16
  describe "#launched_at" do
17
+ let(:expected_time) { Time.parse('2010-10-10 10:10:10') }
18
+
17
19
  it "returns initialized launched_at string parsed as Time" do
18
- allow(Time).to receive(:parse).with(:f_launched_at).and_return(:a_time)
19
- expect(subject.launched_at).to eq(:a_time)
20
+ expect(subject.launched_at).to eq(expected_time)
21
+ end
22
+
23
+ it "returns launched_at directly if already Time" do
24
+ subject.instance_variable_set(:@launched_at, expected_time)
25
+ expect(subject.launched_at).to eq(expected_time)
26
+ end
27
+ end
28
+
29
+ describe "#class_name" do
30
+ it "returns class passed in from hash" do
31
+ expect(subject.class_name).to eq("f_class")
32
+ end
33
+ end
34
+
35
+ describe "#storage" do
36
+ it "returns nil to allow default" do
37
+ expect(subject.storage).to be_nil
38
+ end
39
+ end
40
+
41
+ describe "#state=" do
42
+ it "changes the proxy's recorded state" do
43
+ subject.state = "flummoxing"
44
+ expect(subject.state).to eq("flummoxing")
20
45
  end
21
46
  end
22
47
  end
@@ -17,42 +17,45 @@ describe Bumbleworks::Worker do
17
17
 
18
18
  context 'with multiple workers' do
19
19
  let!(:workers) {
20
- 2.times.map { |i|
20
+ 3.times.map { |i|
21
21
  worker = described_class.new(context)
22
22
  worker.run_in_thread
23
23
  worker
24
24
  }
25
25
  }
26
26
 
27
- describe '.worker_states' do
27
+ describe '.active_worker_states' do
28
28
  it 'returns the states of all active workers' do
29
29
  subject.run_in_thread
30
- expect(described_class.worker_states).to eq({
30
+ expect(described_class.active_worker_states).to eq({
31
31
  subject.id => 'running',
32
32
  workers[0].id => 'running',
33
- workers[1].id => 'running'
33
+ workers[1].id => 'running',
34
+ workers[2].id => 'running'
34
35
  })
35
36
  end
36
37
 
37
- it 'does not include stopped or nil states' do
38
+ it 'does not include stopped, stalled, or nil states' do
38
39
  subject.run_in_thread
39
40
  workers[0].shutdown
40
41
  workers[1].instance_variable_set(:@state, nil)
41
42
  workers[1].instance_variable_get(:@info).save
42
- expect(described_class.worker_states).to eq({
43
+ workers[2].instance_variable_set(:@state, "stalled")
44
+ workers[2].instance_variable_get(:@info).save
45
+ expect(described_class.active_worker_states).to eq({
43
46
  subject.id => 'running'
44
47
  })
45
48
  end
46
49
  end
47
50
 
48
51
  describe '.refresh_worker_info' do
49
- it 'times out if info is not updated in time' do
50
- allow(described_class).to receive(:info).and_return({
51
- :dummy_id => { 'put_at' => (Time.now - 60).to_s }
52
- })
53
- expect {
54
- described_class.refresh_worker_info(:timeout => 0.1)
55
- }.to raise_error(Bumbleworks::Support::WaitTimeout)
52
+ it 'times out and marks worker stalled if stalling' do
53
+ info1 = double(:in_stopped_state? => false, :stalling? => true)
54
+ info2 = double(:in_stopped_state? => false, :stalling? => false)
55
+ allow(described_class).to receive(:info).and_return([info1, info2])
56
+ expect(info1).to receive(:record_new_state).with("stalled")
57
+ expect(info2).to receive(:record_new_state).never
58
+ described_class.refresh_worker_info(:timeout => 0.1)
56
59
  end
57
60
 
58
61
  it 'refreshes worker info' do
@@ -61,11 +64,11 @@ describe Bumbleworks::Worker do
61
64
  doc['workers'][subject.id]['put_at'] = (Time.now - 30).to_s
62
65
  subject.storage.put(doc)
63
66
  described_class.refresh_worker_info
64
- expect(Time.parse(subject.info['put_at'])).to be_within(1).of(Time.now)
67
+ expect(subject.info.updated_at).to be_within(1).of(Time.now)
65
68
  end
66
69
 
67
70
  it 'returns without issue if info empty' do
68
- allow(described_class).to receive(:info).and_return({})
71
+ allow(described_class).to receive(:info).and_return([])
69
72
  expect {
70
73
  described_class.refresh_worker_info
71
74
  }.not_to raise_error
@@ -87,12 +90,14 @@ describe Bumbleworks::Worker do
87
90
  }.to raise_error(described_class::WorkerStateNotChanged)
88
91
  end
89
92
 
90
- it 'ignores already stopped workers' do
93
+ it 'ignores already stopped or stalled workers' do
91
94
  described_class.shutdown_all
95
+ workers[2].instance_variable_set(:@state, "stalled")
96
+ workers[2].instance_variable_get(:@info).save
92
97
  subject.run_in_thread
93
98
  described_class.change_worker_state('paused')
94
99
  expect(subject.state).to eq('paused')
95
- expect(workers.map(&:state)).to eq(['stopped', 'stopped'])
100
+ expect(workers.map(&:state)).to eq(['stopped', 'stopped', 'stalled'])
96
101
  end
97
102
 
98
103
  it 'does nothing if no worker info' do
@@ -110,6 +115,13 @@ describe Bumbleworks::Worker do
110
115
  expect(described_class).to receive(:change_worker_state).with('running', {}).ordered
111
116
  described_class.shutdown_all
112
117
  end
118
+
119
+ it 'resets to running even if error occurred when attempting stop' do
120
+ expect(described_class).to receive(:change_worker_state).with('stopped', {}).ordered.
121
+ and_raise('an interruption')
122
+ expect(described_class).to receive(:change_worker_state).with('running', {}).ordered
123
+ expect { described_class.shutdown_all }.to raise_error('an interruption')
124
+ end
113
125
  end
114
126
 
115
127
  describe '.pause_all' do
@@ -138,4 +150,10 @@ describe Bumbleworks::Worker do
138
150
  expect(subject.info).to eq(described_class.info[subject.id])
139
151
  end
140
152
  end
153
+
154
+ describe "#class_name" do
155
+ it "returns string version of class" do
156
+ expect(subject.class_name).to eq(subject.class.to_s)
157
+ end
158
+ end
141
159
  end
@@ -0,0 +1,9 @@
1
+ RSpec::Matchers.define :delegate do |method|
2
+ match do |actual|
3
+ actual.send(method) == @receiving_object.send(method)
4
+ end
5
+
6
+ chain :to do |receiving_object|
7
+ @receiving_object = receiving_object
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ RSpec::Matchers.define :fetch do |method|
2
+ match do |actual|
3
+ actual.send(method) == @receiving_hash.fetch(method.to_s)
4
+ end
5
+
6
+ chain :from do |receiving_hash|
7
+ @receiving_hash = receiving_hash
8
+ end
9
+ end
@@ -1,20 +1,3 @@
1
- shared_examples "an entity holder" do
2
- [:has_entity_fields?, :has_entity?, :entity, :entity_fields, :entity_name].each do |method|
3
- describe "##{method}" do
4
- it 'delegates to entity storage workitem' do
5
- allow(holder.entity_storage_workitem).to receive(method).with(1, 2, 3).and_return(:yay_for_bikes)
6
- expect(holder.send(method, 1, 2, 3)).to eq(:yay_for_bikes)
7
- end
8
- end
9
- end
10
-
11
- describe "#entity_storage_workitem" do
12
- it 'returns the expected workitem' do
13
- expect(holder.entity_storage_workitem).to eq(storage_workitem)
14
- end
15
- end
16
- end
17
-
18
1
  shared_examples "comparable" do
19
2
  describe "#hash" do
20
3
  it 'returns hash of identifier_for_comparison' do
@@ -0,0 +1,16 @@
1
+ shared_examples "an entity holder" do
2
+ [:has_entity_fields?, :has_entity?, :entity, :entity_fields, :entity_name].each do |method|
3
+ describe "##{method}" do
4
+ it 'delegates to entity storage workitem' do
5
+ allow(holder.entity_storage_workitem).to receive(method).with(1, 2, 3).and_return(:yay_for_bikes)
6
+ expect(holder.send(method, 1, 2, 3)).to eq(:yay_for_bikes)
7
+ end
8
+ end
9
+ end
10
+
11
+ describe "#entity_storage_workitem" do
12
+ it 'returns the expected workitem' do
13
+ expect(holder.entity_storage_workitem).to eq(storage_workitem)
14
+ end
15
+ end
16
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bumbleworks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.90
4
+ version: 0.0.91
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maher Hawash
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2015-03-11 00:00:00.000000000 Z
14
+ date: 2015-03-17 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: ruote
@@ -139,6 +139,7 @@ files:
139
139
  - ".gitignore"
140
140
  - ".rspec"
141
141
  - ".ruby-version"
142
+ - ".travis.yml"
142
143
  - ".watchr"
143
144
  - Gemfile
144
145
  - LICENSE.txt
@@ -245,9 +246,12 @@ files:
245
246
  - spec/lib/bumbleworks/workitem_spec.rb
246
247
  - spec/lib/bumbleworks_spec.rb
247
248
  - spec/spec_helper.rb
249
+ - spec/support/matchers/delegation_matcher.rb
250
+ - spec/support/matchers/fetch_matcher.rb
248
251
  - spec/support/path_helpers.rb
249
252
  - spec/support/process_testing_helpers.rb
250
- - spec/support/shared_examples.rb
253
+ - spec/support/shared_examples/comparable.rb
254
+ - spec/support/shared_examples/entity_holder.rb
251
255
  - spec/support/tracer.rb
252
256
  homepage: ''
253
257
  licenses:
@@ -331,7 +335,10 @@ test_files:
331
335
  - spec/lib/bumbleworks/workitem_spec.rb
332
336
  - spec/lib/bumbleworks_spec.rb
333
337
  - spec/spec_helper.rb
338
+ - spec/support/matchers/delegation_matcher.rb
339
+ - spec/support/matchers/fetch_matcher.rb
334
340
  - spec/support/path_helpers.rb
335
341
  - spec/support/process_testing_helpers.rb
336
- - spec/support/shared_examples.rb
342
+ - spec/support/shared_examples/comparable.rb
343
+ - spec/support/shared_examples/entity_holder.rb
337
344
  - spec/support/tracer.rb