right_agent 1.0.1 → 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/README.rdoc +10 -8
  2. data/Rakefile +31 -5
  3. data/lib/right_agent.rb +6 -1
  4. data/lib/right_agent/actor.rb +4 -20
  5. data/lib/right_agent/actors/agent_manager.rb +1 -1
  6. data/lib/right_agent/agent.rb +357 -144
  7. data/lib/right_agent/agent_config.rb +7 -6
  8. data/lib/right_agent/agent_identity.rb +13 -11
  9. data/lib/right_agent/agent_tag_manager.rb +60 -64
  10. data/{spec/results_mock.rb → lib/right_agent/clients.rb} +10 -24
  11. data/lib/right_agent/clients/api_client.rb +383 -0
  12. data/lib/right_agent/clients/auth_client.rb +247 -0
  13. data/lib/right_agent/clients/balanced_http_client.rb +369 -0
  14. data/lib/right_agent/clients/base_retry_client.rb +495 -0
  15. data/lib/right_agent/clients/right_http_client.rb +279 -0
  16. data/lib/right_agent/clients/router_client.rb +493 -0
  17. data/lib/right_agent/command/command_io.rb +4 -4
  18. data/lib/right_agent/command/command_parser.rb +2 -2
  19. data/lib/right_agent/command/command_runner.rb +1 -1
  20. data/lib/right_agent/connectivity_checker.rb +179 -0
  21. data/lib/right_agent/core_payload_types/secure_document_location.rb +2 -2
  22. data/lib/right_agent/dispatcher.rb +12 -10
  23. data/lib/right_agent/enrollment_result.rb +16 -12
  24. data/lib/right_agent/exceptions.rb +34 -20
  25. data/lib/right_agent/history.rb +10 -5
  26. data/lib/right_agent/log.rb +5 -5
  27. data/lib/right_agent/minimal.rb +1 -0
  28. data/lib/right_agent/multiplexer.rb +1 -1
  29. data/lib/right_agent/offline_handler.rb +270 -0
  30. data/lib/right_agent/packets.rb +7 -7
  31. data/lib/right_agent/payload_formatter.rb +1 -1
  32. data/lib/right_agent/pending_requests.rb +128 -0
  33. data/lib/right_agent/platform.rb +1 -1
  34. data/lib/right_agent/protocol_version_mixin.rb +69 -0
  35. data/lib/right_agent/{idempotent_request.rb → retryable_request.rb} +7 -7
  36. data/lib/right_agent/scripts/agent_controller.rb +28 -26
  37. data/lib/right_agent/scripts/agent_deployer.rb +37 -22
  38. data/lib/right_agent/scripts/common_parser.rb +10 -3
  39. data/lib/right_agent/secure_identity.rb +1 -1
  40. data/lib/right_agent/sender.rb +299 -785
  41. data/lib/right_agent/serialize/secure_serializer.rb +3 -1
  42. data/lib/right_agent/serialize/secure_serializer_initializer.rb +2 -2
  43. data/lib/right_agent/serialize/serializable.rb +8 -3
  44. data/right_agent.gemspec +49 -18
  45. data/spec/agent_config_spec.rb +7 -7
  46. data/spec/agent_identity_spec.rb +7 -4
  47. data/spec/agent_spec.rb +43 -7
  48. data/spec/agent_tag_manager_spec.rb +72 -83
  49. data/spec/clients/api_client_spec.rb +423 -0
  50. data/spec/clients/auth_client_spec.rb +272 -0
  51. data/spec/clients/balanced_http_client_spec.rb +576 -0
  52. data/spec/clients/base_retry_client_spec.rb +635 -0
  53. data/spec/clients/router_client_spec.rb +594 -0
  54. data/spec/clients/spec_helper.rb +111 -0
  55. data/spec/command/command_io_spec.rb +1 -1
  56. data/spec/command/command_parser_spec.rb +1 -1
  57. data/spec/connectivity_checker_spec.rb +83 -0
  58. data/spec/dispatcher_spec.rb +3 -2
  59. data/spec/enrollment_result_spec.rb +2 -2
  60. data/spec/history_spec.rb +51 -39
  61. data/spec/offline_handler_spec.rb +340 -0
  62. data/spec/pending_requests_spec.rb +136 -0
  63. data/spec/{idempotent_request_spec.rb → retryable_request_spec.rb} +73 -73
  64. data/spec/sender_spec.rb +835 -1052
  65. data/spec/serialize/secure_serializer_spec.rb +3 -2
  66. data/spec/spec_helper.rb +54 -1
  67. metadata +71 -12
@@ -0,0 +1,340 @@
1
+ #
2
+ # Copyright (c) 2013 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
24
+
25
+ describe RightScale::OfflineHandler do
26
+
27
+ include FlexMock::ArgumentTypes
28
+
29
+ before(:each) do
30
+ @vote = 0
31
+ @restart_callback = lambda { @vote += 1 }
32
+ @offline_stats = RightSupport::Stats::Activity.new
33
+ @handler = RightScale::OfflineHandler.new(@restart_callback, @offline_stats)
34
+ end
35
+
36
+ context :initialize do
37
+ it "sets initial state to created and mode to initializing" do
38
+ @handler.state == :created
39
+ @handler.mode == :initializing
40
+ @handler.queue.size.should == 0
41
+ end
42
+ end
43
+
44
+ context :init do
45
+ it "advances state from created to initializing so that new requests get prepended to queue" do
46
+ @handler.state.should == :created
47
+ @handler.init.should be_true
48
+ @handler.state.should == :initializing
49
+ end
50
+
51
+ it "does nothing if not in the created state" do
52
+ @handler.init
53
+ @handler.start
54
+ state = @handler.state
55
+ state.should_not == :created
56
+ @handler.init.should be_true
57
+ @handler.state.should == state
58
+ end
59
+ end
60
+
61
+ context :start do
62
+ it "sets state to running if in offline mode" do
63
+ @handler.init
64
+ flexmock(@handler).should_receive(:start_timer)
65
+ @handler.enable
66
+ @handler.start.should be_true
67
+ @handler.state.should == :running
68
+ @handler.mode.should == :offline
69
+ end
70
+
71
+ it "sets state to flushing and flushes if not in offline mode" do
72
+ @handler.init
73
+ flexmock(@handler).should_receive(:flush).once
74
+ @handler.start.should be_true
75
+ @handler.state.should == :flushing
76
+ @handler.mode.should == :online
77
+ end
78
+
79
+ it "does nothing if not in initializing state" do
80
+ @handler.state.should == :created
81
+ @handler.start.should be_true
82
+ @handler.state.should == :created
83
+ end
84
+ end
85
+
86
+ context :offline? do
87
+ it "indicates that offline when initially created" do
88
+ @handler.offline?.should be_true
89
+ end
90
+
91
+ it "indicates that not offline after initialize offline queueing" do
92
+ @handler.init
93
+ @handler.offline?.should be_false
94
+ end
95
+
96
+ it "indicates that offline when offline has been enabled" do
97
+ @handler.init
98
+ flexmock(@handler).should_receive(:start_timer)
99
+ @handler.enable
100
+ @handler.offline?.should be_true
101
+ end
102
+ end
103
+
104
+ context :queueing? do
105
+ it "indicates that should queue when initially created" do
106
+ @handler.queueing?.should be_true
107
+ end
108
+
109
+ it "indicates that should not queue once initialize offline queueing" do
110
+ @handler.init
111
+ @handler.queueing?.should be_false
112
+ end
113
+
114
+ it "indicates that should queue when offline has been enabled" do
115
+ @handler.init
116
+ flexmock(@handler).should_receive(:start_timer)
117
+ @handler.enable
118
+ @handler.queueing?.should be_true
119
+ end
120
+
121
+ it "indicates that should not queue if offline but currently flushing" do
122
+ @handler.init
123
+ @handler.queueing?.should be_false
124
+ @handler.start
125
+ @handler.queueing?.should be_false
126
+ flexmock(@handler).should_receive(:start_timer)
127
+ @handler.enable
128
+ @handler.queueing?.should be_true
129
+ flexmock(EM).should_receive(:add_timer)
130
+ @handler.disable
131
+ @handler.state.should == :flushing
132
+ @handler.mode.should == :offline
133
+ @handler.queueing?.should be_false
134
+ end
135
+ end
136
+
137
+ context :enable do
138
+ it "goes into offline mode if not offline now" do
139
+ @handler.init
140
+ @handler.start
141
+ flexmock(@handler).should_receive(:start_timer)
142
+ @handler.enable.should be_true
143
+ @handler.state.should == :running
144
+ @handler.mode.should == :offline
145
+ end
146
+
147
+ it "starts restart vote timer when after going into offline mode" do
148
+ @handler.init
149
+ @handler.start
150
+ flexmock(EM::Timer).should_receive(:new).once
151
+ @handler.enable.should be_true
152
+ end
153
+
154
+ it "sets state to running if was offline and now in flushing state" do
155
+ @handler.init
156
+ @handler.start
157
+ flexmock(@handler).should_receive(:start_timer)
158
+ @handler.enable
159
+ flexmock(EM).should_receive(:add_timer)
160
+ @handler.disable
161
+ @handler.enable.should be_true
162
+ @handler.state.should == :running
163
+ @handler.mode.should == :offline
164
+ end
165
+ end
166
+
167
+ context :disable do
168
+ it "sets state to flushing and starts timer to begin flushing" do
169
+ @handler.init
170
+ @handler.start
171
+ flexmock(@handler).should_receive(:start_timer)
172
+ @handler.enable
173
+ flexmock(@handler).should_receive(:cancel_timer).once
174
+ flexmock(EM).should_receive(:add_timer).once
175
+ @handler.disable.should be_true
176
+ @handler.state.should == :flushing
177
+ end
178
+
179
+ it "does nothing if in created state" do
180
+ @handler.disable.should be_true
181
+ end
182
+
183
+ it "does nothing if not offline" do
184
+ @handler.init
185
+ @handler.disable.should be_true
186
+ end
187
+ end
188
+
189
+ context :queue_request do
190
+ before(:each) do
191
+ @kind = :send_request
192
+ @type = "/foo/bar"
193
+ @payload = {:pay => "load"}
194
+ @target = "target"
195
+ @callback = lambda { |_| }
196
+ end
197
+
198
+ it "queues request at head of queue if still initializing" do
199
+ @handler.init
200
+ @handler.queue_request(@kind, @type, @payload, "target1", @callback).should be_true
201
+ @handler.queue.size.should == 1
202
+ @handler.queue_request(@kind, @type, @payload, "target2", @callback).should be_true
203
+ @handler.queue.size.should == 2
204
+ @handler.queue.first[:target] == "target2"
205
+ end
206
+
207
+ it "queues request at end of queue if no longer initializing" do
208
+ @handler.init
209
+ @handler.start
210
+ @handler.queue_request(@kind, @type, @payload, "target1", @callback)
211
+ @handler.queue.size.should == 1
212
+ @handler.queue_request(@kind, @type, @payload, "target2", @callback)
213
+ @handler.queue.size.should == 2
214
+ @handler.queue.first[:target] == "target1"
215
+ end
216
+
217
+ it "votes to restart if restart vote count has exceeded max queue length" do
218
+ @handler.init
219
+ @handler.start
220
+ flexmock(@handler).should_receive(:vote_to_restart).once
221
+ RightScale::OfflineHandler::MAX_QUEUED_REQUESTS.times do |i|
222
+ @handler.queue_request(@kind, @type, @payload, @target, @callback)
223
+ end
224
+ end
225
+ end
226
+
227
+ context :terminate do
228
+ it "sets state to terminating and cancels all timers" do
229
+ @handler.init
230
+ @handler.start
231
+ @handler.terminate
232
+ @handler.state.should == :terminating
233
+ end
234
+ end
235
+
236
+ context :flush do
237
+ before(:each) do
238
+ @sender = flexmock("sender")
239
+ flexmock(RightScale::Sender).should_receive(:instance).and_return(@sender)
240
+ @kind = :send_request
241
+ @type = "/foo/bar"
242
+ @payload = {:pay => "load"}
243
+ @target = "target"
244
+ @result = nil
245
+ @callback = lambda { |result| @result = result }
246
+ end
247
+
248
+ context "when in flushing state" do
249
+ before(:each) do
250
+ @handler.init
251
+ @handler.start
252
+ flexmock(@handler).should_receive(:start_timer)
253
+ @handler.enable
254
+ @handler.queue_request(:send_push, @type, @payload, @target, nil)
255
+ @handler.queue_request(:send_request, @type, @payload, @target, @callback)
256
+ @handler.queue.size.should == 2
257
+ flexmock(EM).should_receive(:next_tick).and_yield.once
258
+ @sender.should_receive(:send_push).with(@type, @payload, @target).once.ordered
259
+ @sender.should_receive(:send_request).with(@type, @payload, @target, Proc).and_yield("result").once.ordered
260
+ flexmock(EM).should_receive(:add_timer).and_yield.once
261
+ log = flexmock(RightScale::Log)
262
+ log.should_receive(:info).with(/Connection to RightNet re-established/).once.ordered
263
+ log.should_receive(:info).with(/Starting to flush request queue/).once.ordered
264
+ log.should_receive(:info).with(/Request queue flushed/).once.ordered
265
+ @handler.disable.should be_true
266
+ end
267
+
268
+ it "submits all queued messages to the sender" do
269
+ @handler.queue.size.should == 0
270
+ end
271
+
272
+ it "sets up for callback to be executed" do
273
+ @result.should == "result"
274
+ end
275
+
276
+ it "changes state to running and mode to online" do
277
+ @handler.state.should == :running
278
+ @handler.mode.should == :online
279
+ end
280
+ end
281
+
282
+ it "does nothing if not in flushing state" do
283
+ @handler.init
284
+ @handler.start
285
+ @sender.should_receive(:send_push).never
286
+ @sender.should_receive(:send_request).never
287
+ @handler.send(:flush).should be_true
288
+ end
289
+ end
290
+
291
+ context :vote_to_restart do
292
+ it "makes a restart vote callback" do
293
+ @handler.send(:vote_to_restart).should be_true
294
+ @vote.should == 1
295
+ @handler.instance_variable_get(:@restart_vote_count).should == 0
296
+ end
297
+
298
+ it "starts a vote timer if requested" do
299
+ flexmock(@handler).should_receive(:start_timer).once
300
+ @handler.send(:vote_to_restart, timer_trigger = true).should be_true
301
+ end
302
+
303
+ it "does nothing if there is no restart vote callback" do
304
+ @handler = RightScale::OfflineHandler.new(restart_callback = nil, @offline_stats)
305
+ flexmock(@handler).should_receive(:start_timer).never
306
+ @handler.send(:vote_to_restart, timer_trigger = true).should be_true
307
+ end
308
+ end
309
+
310
+ context :start_timer do
311
+ it "starts a re-vote timer" do
312
+ timer = flexmock("timer")
313
+ flexmock(EM::Timer).should_receive(:new).and_return(timer).once
314
+ @handler.send(:start_timer).should be_true
315
+ @handler.instance_variable_get(:@restart_vote_timer).should == timer
316
+ end
317
+
318
+ it "does nothing if there is not restart vote callback or if terminating" do
319
+ @handler = RightScale::OfflineHandler.new(restart_callback = nil, @offline_stats)
320
+ flexmock(EM::Timer).should_receive(:new).never
321
+ @handler.send(:start_timer).should be_true
322
+ end
323
+ end
324
+
325
+ context :cancel_timer do
326
+ it "cancels restart vote timer and resets the vote count" do
327
+ timer = flexmock("timer")
328
+ timer.should_receive(:cancel).once
329
+ flexmock(EM::Timer).should_receive(:new).and_return(timer)
330
+ @handler.send(:start_timer)
331
+ @handler.send(:cancel_timer).should be_true
332
+ @handler.instance_variable_get(:@restart_vote_timer).should be_nil
333
+ @handler.instance_variable_get(:@restart_vote_count).should == 0
334
+ end
335
+
336
+ it "does nothing if the restart vote timer is not running" do
337
+ @handler.send(:cancel_timer).should be_true
338
+ end
339
+ end
340
+ end
@@ -0,0 +1,136 @@
1
+ #
2
+ # Copyright (c) 2013 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
24
+
25
+ describe RightScale::PendingRequest do
26
+
27
+ context :initialize do
28
+ it "creates pending request" do
29
+ now = Time.now
30
+ response_handler = lambda { |_| }
31
+ pending_request = RightScale::PendingRequest.new(:send_request, now, response_handler)
32
+ pending_request.kind.should == :send_request
33
+ pending_request.receive_time.should == now
34
+ pending_request.response_handler.should == response_handler
35
+ pending_request.retry_parent_token.should be_nil
36
+ pending_request.non_delivery.should be_nil
37
+ end
38
+ end
39
+
40
+ context :retry_parent_token do
41
+ it "can be set" do
42
+ pending_request = RightScale::PendingRequest.new(:send_request, Time.now, lambda { |_| })
43
+ pending_request.retry_parent_token = "retry token"
44
+ pending_request.retry_parent_token.should == "retry token"
45
+ end
46
+ end
47
+
48
+ context :non_delivery do
49
+ it "can be set" do
50
+ pending_request = RightScale::PendingRequest.new(:send_request, Time.now, lambda { |_| })
51
+ pending_request.non_delivery = "because"
52
+ pending_request.non_delivery.should == "because"
53
+ end
54
+ end
55
+ end
56
+
57
+ describe RightScale::PendingRequests do
58
+
59
+ # Add specified kinds of pending requests to pending_requests hash
60
+ def add_requests(pending_requests, kinds)
61
+ i = 0
62
+ kinds.each do |kind|
63
+ i += 1
64
+ pending_requests["token#{i}"] = RightScale::PendingRequest.new(kind, Time.now, lambda { |_| })
65
+ end
66
+ end
67
+
68
+ before(:all) do
69
+ @push = :send_push
70
+ @request = :send_request
71
+ end
72
+
73
+ context :initialize do
74
+ it "is a hash" do
75
+ pending_requests = RightScale::PendingRequests.new
76
+ pending_requests.should be_a(Hash)
77
+ pending_requests.size.should == 0
78
+ end
79
+ end
80
+
81
+ context :[]= do
82
+ it "stores pending request" do
83
+ pending_requests = RightScale::PendingRequests.new
84
+ pending_request = RightScale::PendingRequest.new(:send_request, Time.now, lambda { |_| })
85
+ pending_requests["token"] = pending_request
86
+ pending_requests["token"].should == pending_request
87
+ end
88
+
89
+ it "deletes old pending send_push requests" do
90
+ now = Time.now
91
+ age = RightScale::PendingRequests::MAX_PUSH_AGE + 21
92
+ flexmock(Time).should_receive(:now).and_return(now, now + 10, now + 10, now + 20, now + 20, now + age, now + age)
93
+ pending_requests = RightScale::PendingRequests.new
94
+ add_requests(pending_requests, [@request, @push, @push])
95
+ pending_requests.size.should == 2
96
+ pending_requests["token1"].should_not be_nil
97
+ pending_requests["token2"].should be_nil
98
+ pending_requests["token3"].should_not be_nil
99
+ pending_requests.instance_variable_get(:@last_cleanup).should == now + age
100
+ end
101
+ end
102
+
103
+ context :kind do
104
+ it "returns pending requests of specified kind" do
105
+ pending_requests = RightScale::PendingRequests.new
106
+ add_requests(pending_requests, [@request, @push, @push])
107
+ requests = pending_requests.kind(:send_request)
108
+ requests.size.should == 1
109
+ requests["token1"].should_not be_nil
110
+ requests = pending_requests.kind(:send_push)
111
+ requests.size.should == 2
112
+ requests["token2"].should_not be_nil
113
+ requests["token3"].should_not be_nil
114
+ end
115
+ end
116
+
117
+ context :youngest_age do
118
+ it "returns age of youngest pending request" do
119
+ now = Time.now
120
+ flexmock(Time).should_receive(:now).and_return(now, now + 10, now + 10, now + 20, now + 20, now + 30)
121
+ pending_requests = RightScale::PendingRequests.new
122
+ add_requests(pending_requests, [@request, @push])
123
+ pending_requests.youngest_age.should == 10
124
+ end
125
+ end
126
+
127
+ context :oldest_age do
128
+ it "returns age of oldest pending request" do
129
+ now = Time.now
130
+ flexmock(Time).should_receive(:now).and_return(now, now + 10, now + 10, now + 20, now + 20, now + 30)
131
+ pending_requests = RightScale::PendingRequests.new
132
+ add_requests(pending_requests, [@request, @push])
133
+ pending_requests.oldest_age.should == 20
134
+ end
135
+ end
136
+ end