right_agent 0.13.5 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/right_agent/actors/agent_manager.rb +1 -32
- data/lib/right_agent/agent.rb +243 -230
- data/lib/right_agent/dispatched_cache.rb +4 -5
- data/lib/right_agent/dispatcher.rb +146 -157
- data/lib/right_agent/pid_file.rb +1 -1
- data/lib/right_agent/platform.rb +14 -14
- data/lib/right_agent/scripts/agent_controller.rb +2 -4
- data/lib/right_agent/sender.rb +214 -223
- data/lib/right_agent/serialize/secure_serializer.rb +2 -2
- data/right_agent.gemspec +3 -3
- data/spec/agent_spec.rb +50 -171
- data/spec/dispatched_cache_spec.rb +13 -19
- data/spec/dispatcher_spec.rb +192 -254
- data/spec/sender_spec.rb +212 -168
- metadata +7 -4
data/spec/dispatcher_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2009-
|
2
|
+
# Copyright (c) 2009-2012 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -75,311 +75,249 @@ class Doomed
|
|
75
75
|
on_exception :doh
|
76
76
|
end
|
77
77
|
|
78
|
-
# Mock the EventMachine deferrer.
|
79
|
-
class EMMock
|
80
|
-
def self.defer(op = nil, callback = nil)
|
81
|
-
callback.call(op.call)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# Mock the EventMachine deferrer but do not do callback.
|
86
|
-
class EMMockNoCallback
|
87
|
-
def self.defer(op = nil, callback = nil)
|
88
|
-
op.call
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
78
|
describe "RightScale::Dispatcher" do
|
93
79
|
|
94
80
|
include FlexMock::ArgumentTypes
|
95
81
|
|
96
82
|
before(:each) do
|
97
|
-
flexmock(RightScale::Log)
|
98
|
-
|
83
|
+
@log = flexmock(RightScale::Log)
|
84
|
+
@log.should_receive(:error).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
85
|
+
@log.should_receive(:info).by_default
|
99
86
|
@now = Time.at(1000000)
|
100
87
|
flexmock(Time).should_receive(:now).and_return(@now).by_default
|
101
|
-
@broker = flexmock("Broker", :subscribe => true, :publish => true).by_default
|
102
88
|
@actor = Foo.new
|
103
89
|
@registry = RightScale::ActorRegistry.new
|
104
90
|
@registry.register(@actor, nil)
|
105
91
|
@agent_id = "rs-agent-1-1"
|
106
|
-
@agent = flexmock("Agent", :identity => @agent_id, :
|
92
|
+
@agent = flexmock("Agent", :identity => @agent_id, :registry => @registry).by_default
|
107
93
|
@cache = RightScale::DispatchedCache.new(@agent_id)
|
108
94
|
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
109
|
-
@dispatcher.em = EMMock
|
110
|
-
@response_queue = RightScale::Dispatcher::RESPONSE_QUEUE
|
111
|
-
@header = flexmock("amqp header")
|
112
|
-
@header.should_receive(:ack).once.by_default
|
113
95
|
end
|
114
96
|
|
115
|
-
|
116
|
-
req = RightScale::Request.new('/foo/bar', 'you', :token => 'token')
|
117
|
-
res = @dispatcher.dispatch(req, @header)
|
118
|
-
res.should(be_kind_of(RightScale::Result))
|
119
|
-
res.token.should == 'token'
|
120
|
-
res.results.should == ['hello', 'you']
|
121
|
-
end
|
97
|
+
context "routable?" do
|
122
98
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
res.should(be_kind_of(RightScale::Result))
|
127
|
-
res.token.should == 'token'
|
128
|
-
res.results.should == ['hello', 'you', req]
|
129
|
-
end
|
99
|
+
it "should return false if actor is not available for routing" do
|
100
|
+
@dispatcher.routable?("foo").should be_true
|
101
|
+
end
|
130
102
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
res.should(be_kind_of(RightScale::Result))
|
135
|
-
res.token.should == req.token
|
136
|
-
res.results.should == ['hello', 'you']
|
137
|
-
end
|
103
|
+
it "should return true if actor is available for routing" do
|
104
|
+
@dispatcher.routable?("bar").should be_false
|
105
|
+
end
|
138
106
|
|
139
|
-
it "should publish result of request to response queue" do
|
140
|
-
req = RightScale::Request.new('/foo', 'you', :token => 'token')
|
141
|
-
req.reply_to = "rs-mapper-1-1"
|
142
|
-
@broker.should_receive(:publish).with(hsh(:name => @response_queue),
|
143
|
-
on {|arg| arg.class == RightScale::Result &&
|
144
|
-
arg.to == "rs-mapper-1-1" &&
|
145
|
-
arg.results == ['hello', 'you']},
|
146
|
-
hsh(:persistent => true, :mandatory => true)).once
|
147
|
-
@dispatcher.dispatch(req, @header)
|
148
107
|
end
|
149
108
|
|
150
|
-
|
151
|
-
@registry.register(Foo.new, 'umbongo')
|
152
|
-
req = RightScale::Request.new('/umbongo/bar', 'you')
|
153
|
-
res = @dispatcher.dispatch(req, @header)
|
154
|
-
res.should(be_kind_of(RightScale::Result))
|
155
|
-
res.token.should == req.token
|
156
|
-
res.results.should == ['hello', 'you']
|
157
|
-
end
|
109
|
+
context "dispatch" do
|
158
110
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
end
|
111
|
+
it "should dispatch a request" do
|
112
|
+
req = RightScale::Request.new('/foo/bar', 'you', :token => 'token')
|
113
|
+
res = @dispatcher.dispatch(req)
|
114
|
+
res.should(be_kind_of(RightScale::Result))
|
115
|
+
res.token.should == 'token'
|
116
|
+
res.results.should == ['hello', 'you']
|
117
|
+
end
|
167
118
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
called_with[0].should == :i_kill_you
|
176
|
-
called_with[1].should == req
|
177
|
-
called_with[2].should be_kind_of(RuntimeError)
|
178
|
-
called_with[2].message.should == 'I kill you!'
|
179
|
-
end
|
119
|
+
it "should dispatch a request with required arity" do
|
120
|
+
req = RightScale::Request.new('/foo/bar2', 'you', :token => 'token')
|
121
|
+
res = @dispatcher.dispatch(req)
|
122
|
+
res.should(be_kind_of(RightScale::Result))
|
123
|
+
res.token.should == 'token'
|
124
|
+
res.results.should == ['hello', 'you', req]
|
125
|
+
end
|
180
126
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
end
|
127
|
+
it "should dispatch a request to the default action" do
|
128
|
+
req = RightScale::Request.new('/foo', 'you', :token => 'token')
|
129
|
+
res = @dispatcher.dispatch(req)
|
130
|
+
res.should(be_kind_of(RightScale::Result))
|
131
|
+
res.token.should == req.token
|
132
|
+
res.results.should == ['hello', 'you']
|
133
|
+
end
|
189
134
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
135
|
+
it "should return nil for successful push" do
|
136
|
+
req = RightScale::Push.new('/foo', 'you', :token => 'token')
|
137
|
+
res = @dispatcher.dispatch(req)
|
138
|
+
res.should be_nil
|
139
|
+
end
|
195
140
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
@dispatcher.dispatch(req, @header).should be_nil
|
205
|
-
end
|
141
|
+
it "should handle custom prefixes" do
|
142
|
+
@registry.register(Foo.new, 'umbongo')
|
143
|
+
req = RightScale::Request.new('/umbongo/bar', 'you')
|
144
|
+
res = @dispatcher.dispatch(req)
|
145
|
+
res.should(be_kind_of(RightScale::Result))
|
146
|
+
res.token.should == req.token
|
147
|
+
res.results.should == ['hello', 'you']
|
148
|
+
end
|
206
149
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
on {|arg| arg.class == RightScale::Result &&
|
212
|
-
arg.to == @response_queue &&
|
213
|
-
arg.results.non_delivery? &&
|
214
|
-
arg.results.content == RightScale::OperationResult::TTL_EXPIRATION},
|
215
|
-
hsh(:persistent => true, :mandatory => true)).once
|
216
|
-
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
217
|
-
@dispatcher.em = EMMock
|
218
|
-
req = RightScale::Request.new('/foo/bar', 'you', {:reply_to => @response_queue, :expires_at => @now.to_i + 8})
|
219
|
-
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
220
|
-
@dispatcher.dispatch(req, @header).should be_nil
|
221
|
-
end
|
150
|
+
it "should raise exception if actor is unknown" do
|
151
|
+
req = RightScale::Request.new('/bad', 'you', :token => 'token')
|
152
|
+
lambda { @dispatcher.dispatch(req) }.should raise_error(RightScale::Dispatcher::InvalidRequestType)
|
153
|
+
end
|
222
154
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
on {|arg| arg.class == RightScale::Result &&
|
228
|
-
arg.to == "rs-mapper-1-1" &&
|
229
|
-
arg.results.error? &&
|
230
|
-
arg.results.content =~ /Could not deliver/},
|
231
|
-
hsh(:persistent => true, :mandatory => true)).once
|
232
|
-
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
233
|
-
@dispatcher.em = EMMock
|
234
|
-
req = RightScale::Request.new('/foo/bar', 'you', {:reply_to => "rs-mapper-1-1", :expires_at => @now.to_i + 8}, [12, 13])
|
235
|
-
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
236
|
-
@dispatcher.dispatch(req, @header).should be_nil
|
237
|
-
end
|
155
|
+
it "should raise exception if actor method is unknown" do
|
156
|
+
req = RightScale::Request.new('/foo/bar-none', 'you', :token => 'token')
|
157
|
+
lambda { @dispatcher.dispatch(req) }.should raise_error(RightScale::Dispatcher::InvalidRequestType)
|
158
|
+
end
|
238
159
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
res.token.should == req.token
|
248
|
-
res.results.should == ['hello', 'you']
|
249
|
-
end
|
160
|
+
it "should call the on_exception callback if something goes wrong" do
|
161
|
+
@log.should_receive(:error).once
|
162
|
+
req = RightScale::Request.new('/foo/i_kill_you', nil)
|
163
|
+
flexmock(@actor).should_receive(:handle_exception).with(:i_kill_you, req, Exception).once
|
164
|
+
res = @dispatcher.dispatch(req)
|
165
|
+
res.results.error?.should be_true
|
166
|
+
(res.results.content =~ /Could not handle \/foo\/i_kill_you request/).should be_true
|
167
|
+
end
|
250
168
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
169
|
+
it "should call on_exception Procs defined in a subclass with the correct arguments" do
|
170
|
+
@log.should_receive(:error).once
|
171
|
+
actor = Bar.new
|
172
|
+
@registry.register(actor, nil)
|
173
|
+
req = RightScale::Request.new('/bar/i_kill_you', nil)
|
174
|
+
@dispatcher.dispatch(req)
|
175
|
+
called_with = actor.instance_variable_get("@called_with")
|
176
|
+
called_with[0].should == :i_kill_you
|
177
|
+
called_with[1].should == req
|
178
|
+
called_with[2].should be_kind_of(RuntimeError)
|
179
|
+
called_with[2].message.should == 'I kill you!'
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should call on_exception Procs defined in a subclass in the scope of the actor" do
|
183
|
+
@log.should_receive(:error).once
|
184
|
+
actor = Bar.new
|
185
|
+
@registry.register(actor, nil)
|
186
|
+
req = RightScale::Request.new('/bar/i_kill_you', nil)
|
187
|
+
@dispatcher.dispatch(req)
|
188
|
+
actor.instance_variable_get("@scope").should == actor
|
189
|
+
end
|
260
190
|
|
261
|
-
|
262
|
-
|
263
|
-
|
191
|
+
it "should log error if dispatch fails" do
|
192
|
+
RightScale::Log.should_receive(:error).once
|
193
|
+
req = RightScale::Request.new('/foo/i_kill_you', nil)
|
194
|
+
@dispatcher.dispatch(req)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should reject requests whose time-to-live has expired" do
|
198
|
+
flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
|
199
|
+
@log.should_receive(:info).once.with(on {|arg| arg =~ /REJECT EXPIRED.*TTL 2 sec ago/})
|
264
200
|
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
265
|
-
@
|
266
|
-
|
267
|
-
@
|
268
|
-
@dispatcher.dispatch(req, @header).should be_nil
|
269
|
-
EM.stop
|
201
|
+
req = RightScale::Push.new('/foo/bar', 'you', :expires_at => @now.to_i + 8)
|
202
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
203
|
+
@dispatcher.dispatch(req).should be_nil
|
270
204
|
end
|
271
|
-
end
|
272
205
|
|
273
|
-
|
274
|
-
|
275
|
-
|
206
|
+
it "should return non-delivery result if Request is rejected because its time-to-live has expired" do
|
207
|
+
flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
|
208
|
+
@log.should_receive(:info).once.with(on {|arg| arg =~ /REJECT EXPIRED/})
|
276
209
|
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
277
|
-
@
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
EM.stop
|
210
|
+
req = RightScale::Request.new('/foo/bar', 'you', {:reply_to => @response_queue, :expires_at => @now.to_i + 8})
|
211
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
212
|
+
res = @dispatcher.dispatch(req)
|
213
|
+
res.results.non_delivery?.should be_true
|
214
|
+
res.results.content.should == RightScale::OperationResult::TTL_EXPIRATION
|
283
215
|
end
|
284
|
-
end
|
285
216
|
|
286
|
-
|
287
|
-
|
217
|
+
it "should return error result instead of non-delivery if agent does not know about non-delivery" do
|
218
|
+
flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
|
219
|
+
@log.should_receive(:info).once.with(on {|arg| arg =~ /REJECT EXPIRED/})
|
288
220
|
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
289
|
-
@
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
EM.stop
|
221
|
+
req = RightScale::Request.new('/foo/bar', 'you', {:reply_to => "rs-mapper-1-1", :expires_at => @now.to_i + 8}, [12, 13])
|
222
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
223
|
+
res = @dispatcher.dispatch(req)
|
224
|
+
res.results.error?.should be_true
|
225
|
+
res.results.content.should =~ /Could not deliver/
|
295
226
|
end
|
296
|
-
end
|
297
227
|
|
298
|
-
|
299
|
-
|
228
|
+
it "should not reject requests whose time-to-live has not expired" do
|
229
|
+
flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
|
300
230
|
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
301
|
-
@
|
302
|
-
|
303
|
-
@
|
304
|
-
|
305
|
-
|
231
|
+
req = RightScale::Request.new('/foo/bar', 'you', :expires_at => @now.to_i + 11)
|
232
|
+
flexmock(Time).should_receive(:now).and_return(@now += 10)
|
233
|
+
res = @dispatcher.dispatch(req)
|
234
|
+
res.should(be_kind_of(RightScale::Result))
|
235
|
+
res.token.should == req.token
|
236
|
+
res.results.should == ['hello', 'you']
|
306
237
|
end
|
307
|
-
end
|
308
238
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
@dispatcher.
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
@dispatcher.dispatch(req, @header).should_not be_nil
|
317
|
-
EM.stop
|
239
|
+
it "should not check age of requests with time-to-live check disabled" do
|
240
|
+
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
241
|
+
req = RightScale::Request.new('/foo/bar', 'you', :expires_at => 0)
|
242
|
+
res = @dispatcher.dispatch(req)
|
243
|
+
res.should(be_kind_of(RightScale::Result))
|
244
|
+
res.token.should == req.token
|
245
|
+
res.results.should == ['hello', 'you']
|
318
246
|
end
|
319
|
-
end
|
320
247
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
248
|
+
it "should reject duplicate request by raising exception" do
|
249
|
+
@log.should_receive(:info).once.with(on {|arg| arg =~ /REJECT DUP/})
|
250
|
+
EM.run do
|
251
|
+
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
252
|
+
req = RightScale::Request.new('/foo/bar_non', 1, :token => "try")
|
253
|
+
@cache.store(req.token)
|
254
|
+
lambda { @dispatcher.dispatch(req) }.should raise_error(RightScale::Dispatcher::DuplicateRequest)
|
255
|
+
EM.stop
|
256
|
+
end
|
330
257
|
end
|
331
|
-
end
|
332
258
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
259
|
+
it "should reject duplicate request from a retry by raising exception" do
|
260
|
+
@log.should_receive(:info).once.with(on {|arg| arg =~ /REJECT RETRY DUP/})
|
261
|
+
EM.run do
|
262
|
+
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
263
|
+
req = RightScale::Request.new('/foo/bar_non', 1, :token => "try")
|
264
|
+
req.tries.concat(["try1", "try2"])
|
265
|
+
@cache.store("try2")
|
266
|
+
lambda { @dispatcher.dispatch(req) }.should raise_error(RightScale::Dispatcher::DuplicateRequest)
|
267
|
+
EM.stop
|
268
|
+
end
|
269
|
+
end
|
343
270
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
271
|
+
it "should not reject non-duplicate requests" do
|
272
|
+
EM.run do
|
273
|
+
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
274
|
+
req = RightScale::Request.new('/foo/bar_non', 1, :token => "try")
|
275
|
+
req.tries.concat(["try1", "try2"])
|
276
|
+
@cache.store("try3")
|
277
|
+
@dispatcher.dispatch(req).should_not be_nil
|
278
|
+
EM.stop
|
279
|
+
end
|
280
|
+
end
|
350
281
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
282
|
+
it "should not reject duplicate idempotent requests" do
|
283
|
+
EM.run do
|
284
|
+
@dispatcher = RightScale::Dispatcher.new(@agent, @cache)
|
285
|
+
req = RightScale::Request.new('/foo/bar', 'you', :token => "try")
|
286
|
+
@cache.store(req.token)
|
287
|
+
@dispatcher.dispatch(req).should_not be_nil
|
288
|
+
EM.stop
|
289
|
+
end
|
290
|
+
end
|
356
291
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
292
|
+
it "should not check for duplicates if duplicate checking is disabled" do
|
293
|
+
EM.run do
|
294
|
+
@dispatcher = RightScale::Dispatcher.new(@agent, dispatched_cache = nil)
|
295
|
+
req = RightScale::Request.new('/foo/bar_non', 1, :token => "try")
|
296
|
+
req.tries.concat(["try1", "try2"])
|
297
|
+
@dispatcher.instance_variable_get(:@dispatched_cache).should be_nil
|
298
|
+
@dispatcher.dispatch(req).should_not be_nil
|
299
|
+
EM.stop
|
300
|
+
end
|
301
|
+
end
|
363
302
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
303
|
+
it "should not check for duplicates if actor method is idempotent" do
|
304
|
+
EM.run do
|
305
|
+
@dispatcher = RightScale::Dispatcher.new(@agent, dispatched_cache = nil)
|
306
|
+
req = RightScale::Request.new('/foo/bar', 1, :token => "try")
|
307
|
+
req.tries.concat(["try1", "try2"])
|
308
|
+
@dispatcher.instance_variable_get(:@dispatched_cache).should be_nil
|
309
|
+
@dispatcher.dispatch(req).should_not be_nil
|
310
|
+
EM.stop
|
311
|
+
end
|
312
|
+
end
|
370
313
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
end
|
314
|
+
it "should return error result if dispatch fails" do
|
315
|
+
@log.should_receive(:error).with(/Could not handle/, Exception, :trace).once
|
316
|
+
req = RightScale::Request.new('/foo/i_kill_you', nil)
|
317
|
+
res = @dispatcher.dispatch(req)
|
318
|
+
res.results.error?.should be_true
|
319
|
+
end
|
378
320
|
|
379
|
-
it "should not attempt to ack request if dispatch a request and there is no header" do
|
380
|
-
@header.should_receive(:ack).never
|
381
|
-
req = RightScale::Request.new('/foo/bar', 'you', :token => 'token')
|
382
|
-
@dispatcher.dispatch(req, nil)
|
383
321
|
end
|
384
322
|
|
385
323
|
end # RightScale::Dispatcher
|