right_agent 1.0.1 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +10 -8
- data/Rakefile +31 -5
- data/lib/right_agent.rb +6 -1
- data/lib/right_agent/actor.rb +4 -20
- data/lib/right_agent/actors/agent_manager.rb +1 -1
- data/lib/right_agent/agent.rb +357 -144
- data/lib/right_agent/agent_config.rb +7 -6
- data/lib/right_agent/agent_identity.rb +13 -11
- data/lib/right_agent/agent_tag_manager.rb +60 -64
- data/{spec/results_mock.rb → lib/right_agent/clients.rb} +10 -24
- data/lib/right_agent/clients/api_client.rb +383 -0
- data/lib/right_agent/clients/auth_client.rb +247 -0
- data/lib/right_agent/clients/balanced_http_client.rb +369 -0
- data/lib/right_agent/clients/base_retry_client.rb +495 -0
- data/lib/right_agent/clients/right_http_client.rb +279 -0
- data/lib/right_agent/clients/router_client.rb +493 -0
- data/lib/right_agent/command/command_io.rb +4 -4
- data/lib/right_agent/command/command_parser.rb +2 -2
- data/lib/right_agent/command/command_runner.rb +1 -1
- data/lib/right_agent/connectivity_checker.rb +179 -0
- data/lib/right_agent/core_payload_types/secure_document_location.rb +2 -2
- data/lib/right_agent/dispatcher.rb +12 -10
- data/lib/right_agent/enrollment_result.rb +16 -12
- data/lib/right_agent/exceptions.rb +34 -20
- data/lib/right_agent/history.rb +10 -5
- data/lib/right_agent/log.rb +5 -5
- data/lib/right_agent/minimal.rb +1 -0
- data/lib/right_agent/multiplexer.rb +1 -1
- data/lib/right_agent/offline_handler.rb +270 -0
- data/lib/right_agent/packets.rb +7 -7
- data/lib/right_agent/payload_formatter.rb +1 -1
- data/lib/right_agent/pending_requests.rb +128 -0
- data/lib/right_agent/platform.rb +1 -1
- data/lib/right_agent/protocol_version_mixin.rb +69 -0
- data/lib/right_agent/{idempotent_request.rb → retryable_request.rb} +7 -7
- data/lib/right_agent/scripts/agent_controller.rb +28 -26
- data/lib/right_agent/scripts/agent_deployer.rb +37 -22
- data/lib/right_agent/scripts/common_parser.rb +10 -3
- data/lib/right_agent/secure_identity.rb +1 -1
- data/lib/right_agent/sender.rb +299 -785
- data/lib/right_agent/serialize/secure_serializer.rb +3 -1
- data/lib/right_agent/serialize/secure_serializer_initializer.rb +2 -2
- data/lib/right_agent/serialize/serializable.rb +8 -3
- data/right_agent.gemspec +49 -18
- data/spec/agent_config_spec.rb +7 -7
- data/spec/agent_identity_spec.rb +7 -4
- data/spec/agent_spec.rb +43 -7
- data/spec/agent_tag_manager_spec.rb +72 -83
- data/spec/clients/api_client_spec.rb +423 -0
- data/spec/clients/auth_client_spec.rb +272 -0
- data/spec/clients/balanced_http_client_spec.rb +576 -0
- data/spec/clients/base_retry_client_spec.rb +635 -0
- data/spec/clients/router_client_spec.rb +594 -0
- data/spec/clients/spec_helper.rb +111 -0
- data/spec/command/command_io_spec.rb +1 -1
- data/spec/command/command_parser_spec.rb +1 -1
- data/spec/connectivity_checker_spec.rb +83 -0
- data/spec/dispatcher_spec.rb +3 -2
- data/spec/enrollment_result_spec.rb +2 -2
- data/spec/history_spec.rb +51 -39
- data/spec/offline_handler_spec.rb +340 -0
- data/spec/pending_requests_spec.rb +136 -0
- data/spec/{idempotent_request_spec.rb → retryable_request_spec.rb} +73 -73
- data/spec/sender_spec.rb +835 -1052
- data/spec/serialize/secure_serializer_spec.rb +3 -2
- data/spec/spec_helper.rb +54 -1
- metadata +71 -12
@@ -22,7 +22,7 @@
|
|
22
22
|
|
23
23
|
require File.join(File.dirname(__FILE__), 'spec_helper')
|
24
24
|
|
25
|
-
describe RightScale::
|
25
|
+
describe RightScale::RetryableRequest do
|
26
26
|
|
27
27
|
module RightScale
|
28
28
|
class SenderMock
|
@@ -47,37 +47,37 @@ describe RightScale::IdempotentRequest do
|
|
47
47
|
|
48
48
|
context 'when :targets => nil' do
|
49
49
|
it 'should send target-less requests' do
|
50
|
-
request = RightScale::
|
51
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
50
|
+
request = RightScale::RetryableRequest.new('type', 'payload')
|
51
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
52
52
|
and_yield(RightScale::OperationResult.non_delivery('test')).once
|
53
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
54
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
53
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
54
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).once
|
55
55
|
request.run
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
59
|
context 'when one target is specified' do
|
60
60
|
it 'should send a targeted request' do
|
61
|
-
request = RightScale::
|
62
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
61
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :targets => ["rs-agent-1-1"])
|
62
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', {:agent_id => "rs-agent-1-1"}, Proc).
|
63
63
|
and_yield(RightScale::OperationResult.non_delivery('test')).once
|
64
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
65
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
64
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
65
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).once
|
66
66
|
request.run
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
70
|
context 'when many targets are specified' do
|
71
71
|
it 'should choose a random target' do
|
72
|
-
request = RightScale::
|
73
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
72
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :targets => ["rs-agent-1-1", "rs-agent-2-2", "rs-agent-3-3"])
|
73
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).and_return do |type, payload, target, block|
|
74
74
|
type.should == 'type'
|
75
75
|
payload.should == 'payload'
|
76
|
-
[1, 2, 3].should include(
|
76
|
+
["rs-agent-1-1", "rs-agent-2-2", "rs-agent-3-3"].should include(target[:agent_id])
|
77
77
|
block.call(RightScale::OperationResult.non_delivery('test'))
|
78
78
|
end
|
79
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
80
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
79
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
80
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).once
|
81
81
|
request.run
|
82
82
|
end
|
83
83
|
end
|
@@ -88,44 +88,44 @@ describe RightScale::IdempotentRequest do
|
|
88
88
|
|
89
89
|
context 'when not specified' do
|
90
90
|
it 'should fail if receives error response' do
|
91
|
-
request = RightScale::
|
92
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
91
|
+
request = RightScale::RetryableRequest.new('type', 'payload')
|
92
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
93
93
|
and_yield(RightScale::OperationResult.error('test')).once
|
94
94
|
flexmock(request).should_receive(:fail).once
|
95
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
95
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
96
96
|
request.run
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
100
|
context 'when specified as true' do
|
101
101
|
it 'should retry if receives error response' do
|
102
|
-
request = RightScale::
|
103
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
102
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :retry_on_error => true)
|
103
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
104
104
|
and_yield(RightScale::OperationResult.error('test')).once
|
105
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
106
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
105
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).once
|
106
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
107
107
|
request.run
|
108
108
|
end
|
109
109
|
|
110
110
|
it 'should ignore duplicate responses' do
|
111
|
-
request = RightScale::
|
112
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
111
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :retry_on_error => true)
|
112
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).and_return do |t, p, tgt, b|
|
113
113
|
5.times { b.call(RightScale::OperationResult.success('test')) }
|
114
114
|
end
|
115
115
|
flexmock(request).should_receive(:fail).never
|
116
116
|
flexmock(request).should_receive(:succeed).once
|
117
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
118
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
117
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).never
|
118
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
119
119
|
request.run
|
120
120
|
end
|
121
121
|
|
122
122
|
it 'should never retry after cancel response' do
|
123
|
-
request = RightScale::
|
123
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :retry_on_error => true)
|
124
124
|
flexmock(RightScale::Log).should_receive(:info).with("Request type canceled (enough already)").once
|
125
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
125
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
126
126
|
and_yield(RightScale::OperationResult.cancel('enough already')).once
|
127
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
128
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
127
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).never
|
128
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
129
129
|
request.run
|
130
130
|
end
|
131
131
|
end
|
@@ -136,57 +136,57 @@ describe RightScale::IdempotentRequest do
|
|
136
136
|
|
137
137
|
context 'when using default settings' do
|
138
138
|
it 'should retry non-delivery responses' do
|
139
|
-
request = RightScale::
|
139
|
+
request = RightScale::RetryableRequest.new('type', 'payload')
|
140
140
|
flexmock(RightScale::Log).should_receive(:info).with(/Retrying in 5 seconds/).once
|
141
141
|
flexmock(RightScale::Log).should_receive(:info).with("Request non-delivery (test) for type").once
|
142
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
142
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
143
143
|
and_yield(RightScale::OperationResult.non_delivery('test')).once
|
144
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
145
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
144
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).once
|
145
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
146
146
|
request.run
|
147
147
|
end
|
148
148
|
|
149
149
|
it 'should retry retry responses' do
|
150
|
-
request = RightScale::
|
150
|
+
request = RightScale::RetryableRequest.new('type', 'payload')
|
151
151
|
flexmock(RightScale::Log).should_receive(:info).with(/Retrying in 5 seconds/).once
|
152
152
|
flexmock(RightScale::Log).should_receive(:info).with("Request type failed (test) and should be retried").once
|
153
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
153
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
154
154
|
and_yield(RightScale::OperationResult.retry('test')).once
|
155
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
156
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
155
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).once
|
156
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
157
157
|
request.run
|
158
158
|
end
|
159
159
|
|
160
160
|
it 'should log default retry reason if none given' do
|
161
|
-
request = RightScale::
|
161
|
+
request = RightScale::RetryableRequest.new('type', 'payload')
|
162
162
|
flexmock(RightScale::Log).should_receive(:info).with(/Retrying in 5 seconds/).once
|
163
163
|
flexmock(RightScale::Log).should_receive(:info).with("Request type failed (RightScale not ready) and should be retried").once
|
164
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
164
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
165
165
|
and_yield(RightScale::OperationResult.retry).once
|
166
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
167
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
166
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).once
|
167
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
168
168
|
request.run
|
169
169
|
end
|
170
170
|
|
171
171
|
it 'should ignore responses that arrive post-cancel' do
|
172
|
-
request = RightScale::
|
173
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
172
|
+
request = RightScale::RetryableRequest.new('type', 'payload')
|
173
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
174
174
|
and_yield(RightScale::OperationResult.success('test')).once
|
175
175
|
flexmock(request).should_receive(:fail).once
|
176
176
|
flexmock(request).should_receive(:succeed).never
|
177
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
178
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
177
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).never
|
178
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
179
179
|
request.cancel('test')
|
180
180
|
request.run
|
181
181
|
end
|
182
182
|
|
183
183
|
it 'should never retry after cancel response' do
|
184
|
-
request = RightScale::
|
184
|
+
request = RightScale::RetryableRequest.new('type', 'payload')
|
185
185
|
flexmock(RightScale::Log).should_receive(:info).with("Request type canceled (enough already)").once
|
186
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
186
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
187
187
|
and_yield(RightScale::OperationResult.cancel('enough already')).once
|
188
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
189
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
188
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_RETRY_DELAY, Proc).never
|
189
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
190
190
|
request.run
|
191
191
|
end
|
192
192
|
end
|
@@ -194,22 +194,22 @@ describe RightScale::IdempotentRequest do
|
|
194
194
|
context 'when a :retry_delay is specified' do
|
195
195
|
it 'should control the initial retry delay' do
|
196
196
|
retry_delay = 10
|
197
|
-
request = RightScale::
|
198
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
197
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :retry_delay => retry_delay)
|
198
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
199
199
|
and_yield(RightScale::OperationResult.retry('test')).once
|
200
200
|
flexmock(EM).should_receive(:add_timer).with(retry_delay, Proc).once
|
201
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
201
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
202
202
|
flexmock(EM).should_receive(:next_tick).never
|
203
203
|
request.run
|
204
204
|
end
|
205
205
|
|
206
206
|
it 'should treat -1 as meaning no delay' do
|
207
207
|
retry_delay = -1
|
208
|
-
request = RightScale::
|
209
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
208
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :retry_delay => retry_delay)
|
209
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
210
210
|
and_yield(RightScale::OperationResult.retry('test')).once
|
211
211
|
flexmock(EM).should_receive(:add_timer).with(retry_delay, Proc).never
|
212
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
212
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
213
213
|
flexmock(EM).should_receive(:next_tick).once
|
214
214
|
request.run
|
215
215
|
end
|
@@ -219,14 +219,14 @@ describe RightScale::IdempotentRequest do
|
|
219
219
|
it 'should limit the number of retries using the :retry_delay value' do
|
220
220
|
retry_delay = 10
|
221
221
|
retry_delay_count = 1
|
222
|
-
backoff_factor = RightScale::
|
223
|
-
request = RightScale::
|
222
|
+
backoff_factor = RightScale::RetryableRequest::RETRY_BACKOFF_FACTOR
|
223
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :retry_delay => retry_delay,
|
224
224
|
:retry_delay_count => retry_delay_count)
|
225
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
225
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
226
226
|
and_yield(RightScale::OperationResult.retry('test')).twice
|
227
227
|
flexmock(EM).should_receive(:add_timer).with(retry_delay, Proc).and_yield.once
|
228
228
|
flexmock(EM).should_receive(:add_timer).with(retry_delay * backoff_factor, Proc).once
|
229
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
229
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
230
230
|
flexmock(EM).should_receive(:next_tick).never
|
231
231
|
request.run
|
232
232
|
end
|
@@ -235,16 +235,16 @@ describe RightScale::IdempotentRequest do
|
|
235
235
|
retry_delay = 10
|
236
236
|
retry_delay_count = 2
|
237
237
|
max_retry_delay = 30
|
238
|
-
backoff_factor = RightScale::
|
239
|
-
request = RightScale::
|
238
|
+
backoff_factor = RightScale::RetryableRequest::RETRY_BACKOFF_FACTOR
|
239
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :retry_delay => retry_delay,
|
240
240
|
:retry_delay_count => retry_delay_count,
|
241
241
|
:max_retry_delay => max_retry_delay)
|
242
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
242
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
243
243
|
and_yield(RightScale::OperationResult.retry('test')).times(4)
|
244
244
|
flexmock(EM).should_receive(:add_timer).with(retry_delay, Proc).and_yield.twice
|
245
245
|
flexmock(EM).should_receive(:add_timer).with(retry_delay * backoff_factor, Proc).and_yield.once
|
246
246
|
flexmock(EM).should_receive(:add_timer).with(max_retry_delay, Proc).once
|
247
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
247
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
248
248
|
flexmock(EM).should_receive(:next_tick).never
|
249
249
|
request.run
|
250
250
|
request.instance_variable_get(:@retry_delay_count).should == retry_delay_count / 2
|
@@ -256,16 +256,16 @@ describe RightScale::IdempotentRequest do
|
|
256
256
|
retry_delay = 10
|
257
257
|
retry_delay_count = 1
|
258
258
|
max_retry_delay = 30
|
259
|
-
backoff_factor = RightScale::
|
260
|
-
request = RightScale::
|
259
|
+
backoff_factor = RightScale::RetryableRequest::RETRY_BACKOFF_FACTOR
|
260
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :retry_delay => retry_delay,
|
261
261
|
:retry_delay_count => retry_delay_count,
|
262
262
|
:max_retry_delay => max_retry_delay)
|
263
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
263
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).
|
264
264
|
and_yield(RightScale::OperationResult.retry('test')).times(3)
|
265
265
|
flexmock(EM).should_receive(:add_timer).with(retry_delay, Proc).and_yield.once
|
266
266
|
flexmock(EM).should_receive(:add_timer).with(retry_delay * backoff_factor, Proc).and_yield.once
|
267
267
|
flexmock(EM).should_receive(:add_timer).with(max_retry_delay, Proc).once
|
268
|
-
flexmock(EM).should_receive(:add_timer).with(RightScale::
|
268
|
+
flexmock(EM).should_receive(:add_timer).with(RightScale::RetryableRequest::DEFAULT_TIMEOUT, Proc).once
|
269
269
|
flexmock(EM).should_receive(:next_tick).never
|
270
270
|
request.run
|
271
271
|
end
|
@@ -276,8 +276,8 @@ describe RightScale::IdempotentRequest do
|
|
276
276
|
context ':timeout option' do
|
277
277
|
context 'when disable timeout' do
|
278
278
|
it 'should not timeout' do
|
279
|
-
request = RightScale::
|
280
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
279
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :timeout => -1)
|
280
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).once
|
281
281
|
flexmock(EM).should_receive(:add_timer).never
|
282
282
|
request.run
|
283
283
|
end
|
@@ -286,17 +286,17 @@ describe RightScale::IdempotentRequest do
|
|
286
286
|
context 'when a timeout is specified' do
|
287
287
|
it 'should time the response' do
|
288
288
|
timeout = 10
|
289
|
-
request = RightScale::
|
290
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
289
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :timeout => timeout)
|
290
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).once
|
291
291
|
flexmock(EM).should_receive(:add_timer).with(timeout, Proc).once
|
292
292
|
request.run
|
293
293
|
end
|
294
294
|
|
295
295
|
it 'should log a message when timeout' do
|
296
296
|
timeout = 10
|
297
|
-
request = RightScale::
|
297
|
+
request = RightScale::RetryableRequest.new('type', 'payload', :timeout => timeout)
|
298
298
|
flexmock(RightScale::Log).should_receive(:info).with("Request type timed out after 10 seconds").once
|
299
|
-
flexmock(RightScale::Sender.instance).should_receive(:
|
299
|
+
flexmock(RightScale::Sender.instance).should_receive(:send_request).with('type', 'payload', nil, Proc).once
|
300
300
|
flexmock(EM).should_receive(:add_timer).with(timeout, Proc).and_yield.once
|
301
301
|
request.run
|
302
302
|
end
|
data/spec/sender_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2009-
|
2
|
+
# Copyright (c) 2009-2013 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
|
@@ -26,1237 +26,1020 @@ describe RightScale::Sender do
|
|
26
26
|
|
27
27
|
include FlexMock::ArgumentTypes
|
28
28
|
|
29
|
-
|
30
|
-
@log = flexmock(RightScale::Log)
|
31
|
-
@log.should_receive(:error).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
32
|
-
@log.should_receive(:warning).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
33
|
-
@timer = flexmock("timer", :cancel => true).by_default
|
34
|
-
end
|
35
|
-
|
36
|
-
describe "when fetching the instance" do
|
29
|
+
context "access instance" do
|
37
30
|
before do
|
38
31
|
RightScale::Sender.class_eval do
|
39
|
-
if class_variable_defined?(:@@instance)
|
40
|
-
remove_class_variable(:@@instance)
|
41
|
-
end
|
32
|
+
remove_class_variable(:@@instance) if class_variable_defined?(:@@instance)
|
42
33
|
end
|
43
34
|
end
|
44
|
-
|
45
|
-
it "
|
35
|
+
|
36
|
+
it "returns nil when the instance is undefined" do
|
46
37
|
RightScale::Sender.instance.should == nil
|
47
38
|
end
|
48
|
-
|
49
|
-
it "
|
50
|
-
instance =
|
51
|
-
RightScale::Sender.class_eval do
|
52
|
-
@@instance = "instance"
|
53
|
-
end
|
54
|
-
|
39
|
+
|
40
|
+
it "returns the instance if defined" do
|
41
|
+
RightScale::Sender.class_eval { @@instance = "instance" }
|
55
42
|
RightScale::Sender.instance.should_not == nil
|
56
43
|
end
|
57
44
|
end
|
58
45
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
@
|
64
|
-
|
65
|
-
@
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
@agent.should_receive(:options).and_return(:ping_interval => 1000)
|
70
|
-
flexmock(EM::Timer).should_receive(:new).with(1000, Proc).and_return(@timer).once
|
71
|
-
RightScale::Sender.new(@agent)
|
72
|
-
end
|
73
|
-
|
74
|
-
it "should not start inactivity timer at initialization time if ping disabled" do
|
75
|
-
flexmock(EM::Timer).should_receive(:new).never
|
76
|
-
RightScale::Sender.new(@agent)
|
77
|
-
end
|
78
|
-
|
79
|
-
it "should restart inactivity timer only if sufficient time has elapsed since last restart" do
|
80
|
-
@agent.should_receive(:options).and_return(:ping_interval => 1000)
|
81
|
-
flexmock(EM::Timer).should_receive(:new).with(1000, Proc).and_return(@timer).once
|
82
|
-
instance = RightScale::Sender.new(@agent)
|
83
|
-
flexmock(Time).should_receive(:now).and_return(@now + 61)
|
84
|
-
flexmock(instance.connectivity_checker).should_receive(:restart_inactivity_timer).once
|
85
|
-
instance.message_received
|
86
|
-
instance.message_received
|
87
|
-
end
|
88
|
-
|
89
|
-
it "should check connectivity if the inactivity timer times out" do
|
90
|
-
@agent.should_receive(:options).and_return(:ping_interval => 1000)
|
91
|
-
flexmock(EM::Timer).should_receive(:new).and_return(@timer).once.by_default
|
92
|
-
RightScale::Sender.new(@agent)
|
93
|
-
instance = RightScale::Sender.instance
|
94
|
-
flexmock(Time).should_receive(:now).and_return(@now + 61)
|
95
|
-
flexmock(EM::Timer).should_receive(:new).and_return(@timer).and_yield.once
|
96
|
-
flexmock(instance.connectivity_checker).should_receive(:check).once
|
97
|
-
instance.message_received
|
98
|
-
end
|
99
|
-
|
100
|
-
it "should check connectivity by sending mapper ping" do
|
101
|
-
@agent.should_receive(:options).and_return(:ping_interval => 1000)
|
102
|
-
flexmock(EM::Timer).should_receive(:new).and_return(@timer).twice
|
103
|
-
RightScale::Sender.new(@agent)
|
104
|
-
instance = RightScale::Sender.instance
|
105
|
-
broker_id = "rs-broker-1-1"
|
106
|
-
flexmock(instance).should_receive(:publish).with(on do |request|
|
107
|
-
request.type.should == "/mapper/ping";
|
108
|
-
request.from.should == "agent"
|
109
|
-
end, [broker_id]).and_return([broker_id]).once
|
110
|
-
instance.connectivity_checker.check(broker_id)
|
111
|
-
instance.pending_requests.size.should == 1
|
112
|
-
end
|
113
|
-
|
114
|
-
it "should not check connectivity if terminating" do
|
115
|
-
@agent.should_receive(:options).and_return(:ping_interval => 1000)
|
116
|
-
flexmock(EM::Timer).should_receive(:new).and_return(@timer).once
|
117
|
-
RightScale::Sender.new(@agent)
|
118
|
-
instance = RightScale::Sender.instance
|
119
|
-
flexmock(instance).should_receive(:publish).never
|
120
|
-
instance.terminate
|
121
|
-
instance.connectivity_checker.check
|
122
|
-
end
|
123
|
-
|
124
|
-
it "should not check connectivity if not connected to broker" do
|
125
|
-
@agent.should_receive(:options).and_return(:ping_interval => 1000)
|
126
|
-
flexmock(EM::Timer).should_receive(:new).and_return(@timer).once
|
127
|
-
RightScale::Sender.new(@agent)
|
128
|
-
broker_id = "rs-broker-1-1"
|
129
|
-
@broker.should_receive(:connected?).with(broker_id).and_return(false)
|
130
|
-
instance = RightScale::Sender.instance
|
131
|
-
flexmock(instance).should_receive(:publish).never
|
132
|
-
instance.connectivity_checker.check(broker_id)
|
133
|
-
end
|
134
|
-
|
135
|
-
it "should ignore ping timeout if never successfully publish ping" do
|
136
|
-
@agent.should_receive(:options).and_return(:ping_interval => 1000)
|
137
|
-
old_ping_timeout = RightScale::Sender::ConnectivityChecker::PING_TIMEOUT
|
138
|
-
begin
|
139
|
-
RightScale::Sender::ConnectivityChecker.const_set(:PING_TIMEOUT, 0.5)
|
140
|
-
EM.run do
|
141
|
-
EM.add_timer(1) { EM.stop }
|
142
|
-
RightScale::Sender.new(@agent)
|
143
|
-
@instance = RightScale::Sender.instance
|
144
|
-
flexmock(@instance).should_receive(:publish).and_return([]).once
|
145
|
-
@instance.connectivity_checker.check(id = nil)
|
146
|
-
end
|
147
|
-
ensure
|
148
|
-
RightScale::Sender::ConnectivityChecker.const_set(:PING_TIMEOUT, old_ping_timeout)
|
149
|
-
@instance.connectivity_checker.instance_variable_get(:@ping_timer).should be_nil
|
150
|
-
@instance.connectivity_checker.instance_variable_get(:@ping_id).should be_nil
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
it "should ignore messages received if ping disabled" do
|
155
|
-
@agent.should_receive(:options).and_return(:ping_interval => 0)
|
156
|
-
flexmock(EM::Timer).should_receive(:new).never
|
157
|
-
RightScale::Sender.new(@agent)
|
158
|
-
RightScale::Sender.instance.message_received
|
159
|
-
end
|
160
|
-
|
161
|
-
it "should log an exception if the connectivity check fails" do
|
162
|
-
@log.should_receive(:error).with(/Failed connectivity check/, Exception, :trace).once
|
163
|
-
@agent.should_receive(:options).and_return(:ping_interval => 1000)
|
164
|
-
flexmock(EM::Timer).should_receive(:new).and_return(@timer).once.by_default
|
46
|
+
context "use instance" do
|
47
|
+
# Create new sender with specified mode and options
|
48
|
+
# Also create mock agent and client for it with basic support for both modes
|
49
|
+
def create_sender(mode, options = {})
|
50
|
+
@broker_id = "rs-broker-1-1"
|
51
|
+
@broker_ids = [@broker_id]
|
52
|
+
@client = flexmock("client", :push => true, :request => true, :all => @broker_ids, :publish => @broker_ids).by_default
|
53
|
+
@agent_id = "rs-agent-1-1"
|
54
|
+
@agent = flexmock("agent", :identity => @agent_id, :client => @client, :mode => mode, :request_queue => "request",
|
55
|
+
:options => options).by_default
|
165
56
|
RightScale::Sender.new(@agent)
|
166
|
-
|
167
|
-
flexmock(Time).should_receive(:now).and_return(@now + 61)
|
168
|
-
flexmock(EM::Timer).should_receive(:new).and_return(@timer).and_yield.once
|
169
|
-
flexmock(instance.connectivity_checker).should_receive(:check).and_raise(Exception)
|
170
|
-
instance.message_received
|
57
|
+
RightScale::Sender.instance
|
171
58
|
end
|
172
59
|
|
173
|
-
it "should attempt to reconnect if mapper ping times out" do
|
174
|
-
@log.should_receive(:error).with(/Mapper ping via broker/).once
|
175
|
-
@agent.should_receive(:options).and_return(:ping_interval => 1000)
|
176
|
-
broker_id = "rs-broker-localhost-5672"
|
177
|
-
@broker.should_receive(:identity_parts).with(broker_id).and_return(["localhost", 5672, 0, 0]).once
|
178
|
-
@agent.should_receive(:connect).with("localhost", 5672, 0, 0, true).once
|
179
|
-
old_ping_timeout = RightScale::Sender::ConnectivityChecker::PING_TIMEOUT
|
180
|
-
old_max_ping_timeouts = RightScale::Sender::ConnectivityChecker::MAX_PING_TIMEOUTS
|
181
|
-
begin
|
182
|
-
RightScale::Sender::ConnectivityChecker.const_set(:PING_TIMEOUT, 0.5)
|
183
|
-
RightScale::Sender::ConnectivityChecker.const_set(:MAX_PING_TIMEOUTS, 1)
|
184
|
-
EM.run do
|
185
|
-
EM.add_timer(1) { EM.stop }
|
186
|
-
RightScale::Sender.new(@agent)
|
187
|
-
instance = RightScale::Sender.instance
|
188
|
-
flexmock(instance).should_receive(:publish).with(RightScale::Request, nil).and_return([broker_id])
|
189
|
-
instance.connectivity_checker.check
|
190
|
-
end
|
191
|
-
ensure
|
192
|
-
RightScale::Sender::ConnectivityChecker.const_set(:PING_TIMEOUT, old_ping_timeout)
|
193
|
-
RightScale::Sender::ConnectivityChecker.const_set(:MAX_PING_TIMEOUTS, old_max_ping_timeouts)
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
describe "when validating a target" do
|
199
60
|
before(:each) do
|
200
|
-
@
|
201
|
-
|
202
|
-
|
203
|
-
@
|
204
|
-
@
|
205
|
-
@
|
206
|
-
|
207
|
-
@
|
208
|
-
@
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
61
|
+
@log = flexmock(RightScale::Log)
|
62
|
+
@log.should_receive(:error).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
63
|
+
@log.should_receive(:warning).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
64
|
+
@sender = create_sender(:http)
|
65
|
+
@token = "token"
|
66
|
+
@type = "/foo/bar"
|
67
|
+
@action = "bar"
|
68
|
+
@payload = {:pay => "load"}
|
69
|
+
@target = "target"
|
70
|
+
@callback = lambda { |_| }
|
71
|
+
end
|
72
|
+
|
73
|
+
context :initialize do
|
74
|
+
it "creates connectivity checker if mode is amqp" do
|
75
|
+
@sender = create_sender(:amqp)
|
76
|
+
@sender.connectivity_checker.should be_a(RightScale::ConnectivityChecker)
|
77
|
+
end
|
217
78
|
end
|
218
79
|
|
219
|
-
|
220
|
-
|
221
|
-
describe "and selector is allowed" do
|
80
|
+
context "offline handling" do
|
222
81
|
|
223
|
-
|
224
|
-
|
225
|
-
@
|
82
|
+
context :initialize_offline_queue do
|
83
|
+
it "initializes offline handler" do
|
84
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
85
|
+
flexmock(@sender.offline_handler).should_receive(:init).once
|
86
|
+
@sender.initialize_offline_queue
|
226
87
|
end
|
227
88
|
|
228
|
-
it "
|
229
|
-
|
230
|
-
|
89
|
+
it "does nothing if offline queueing is disabled" do
|
90
|
+
flexmock(@sender.offline_handler).should_receive(:init).never
|
91
|
+
@sender.initialize_offline_queue
|
231
92
|
end
|
232
|
-
|
233
93
|
end
|
234
94
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
95
|
+
context :start_offline_queue do
|
96
|
+
it "starts offline handler" do
|
97
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
98
|
+
flexmock(@sender.offline_handler).should_receive(:start).once
|
99
|
+
@sender.start_offline_queue
|
240
100
|
end
|
241
101
|
|
102
|
+
it "does nothing if offline queueing is disabled" do
|
103
|
+
flexmock(@sender.offline_handler).should_receive(:start).never
|
104
|
+
@sender.start_offline_queue
|
105
|
+
end
|
242
106
|
end
|
243
107
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
@
|
248
|
-
@
|
108
|
+
context :enable_offline_mode do
|
109
|
+
it "enables offline handler" do
|
110
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
111
|
+
flexmock(@sender.offline_handler).should_receive(:enable).once
|
112
|
+
@sender.enable_offline_mode
|
249
113
|
end
|
250
114
|
|
251
|
-
it "
|
252
|
-
|
253
|
-
|
115
|
+
it "does nothing if offline queueing is disabled" do
|
116
|
+
flexmock(@sender.offline_handler).should_receive(:enable).never
|
117
|
+
@sender.enable_offline_mode
|
254
118
|
end
|
255
|
-
|
256
119
|
end
|
257
120
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
@
|
262
|
-
@
|
121
|
+
context :disable_offline_mode do
|
122
|
+
it "initializes offline handler" do
|
123
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
124
|
+
flexmock(@sender.offline_handler).should_receive(:disable).once
|
125
|
+
@sender.disable_offline_mode
|
263
126
|
end
|
264
127
|
|
265
|
-
it "
|
266
|
-
@
|
267
|
-
@
|
128
|
+
it "does nothing if offline queueing is disabled" do
|
129
|
+
flexmock(@sender.offline_handler).should_receive(:disable).never
|
130
|
+
@sender.disable_offline_mode
|
268
131
|
end
|
132
|
+
end
|
133
|
+
end
|
269
134
|
|
270
|
-
|
271
|
-
|
272
|
-
|
135
|
+
context :send_push do
|
136
|
+
it "creates Push object" do
|
137
|
+
flexmock(RightSupport::Data::UUID, :generate => "random token")
|
138
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, nil, on { |a| a.class == RightScale::Push &&
|
139
|
+
a.type == @type && a.from == @agent_id && a.target.nil? && a.persistent == true && a.confirm.nil? &&
|
140
|
+
a.token == "random token" && a.expires_at == 0 }, Time, nil).once
|
141
|
+
@sender.send_push(@type).should be_true
|
142
|
+
end
|
273
143
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
end
|
144
|
+
it "stores payload" do
|
145
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, nil, on { |a| a.payload == @payload }, Time, nil).once
|
146
|
+
@sender.send_push(@type, @payload).should be_true
|
147
|
+
end
|
279
148
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
149
|
+
it "sets specific target using string" do
|
150
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, @target, on { |a| a.target == @target &&
|
151
|
+
a.selector == :any }, Time, nil).once
|
152
|
+
@sender.send_push(@type, nil, @target).should be_true
|
153
|
+
end
|
284
154
|
|
155
|
+
it "sets specific target using :agent_id" do
|
156
|
+
target = {:agent_id => @agent_id}
|
157
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, target, on { |a| a.target == @agent_id }, Time, nil).once
|
158
|
+
@sender.send_push(@type, nil, target).should be_true
|
285
159
|
end
|
286
160
|
|
287
|
-
|
161
|
+
it "sets target tags" do
|
162
|
+
tags = ["a:b=c"]
|
163
|
+
target = {:tags => tags}
|
164
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, target, on { |a| a.tags == tags &&
|
165
|
+
a.selector == :any }, Time, nil).once
|
166
|
+
@sender.send_push(@type, nil, target).should be_true
|
167
|
+
end
|
288
168
|
|
289
|
-
|
290
|
-
|
291
|
-
|
169
|
+
it "sets target scope" do
|
170
|
+
scope = {:shard => 1, :account => 123}
|
171
|
+
target = {:scope => scope}
|
172
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, target, on { |a| a.scope == scope &&
|
173
|
+
a.selector == :any }, Time, nil).once
|
174
|
+
@sender.send_push(@type, nil, target).should be_true
|
175
|
+
end
|
292
176
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
177
|
+
it "sets target selector" do
|
178
|
+
tags = ["a:b=c"]
|
179
|
+
target = {:tags => tags, :selector => :all}
|
180
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, target, on { |a| a.selector == :all }, Time, nil).once
|
181
|
+
@sender.send_push(@type, nil, target).should be_true
|
182
|
+
end
|
297
183
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
end
|
184
|
+
it "sets token" do
|
185
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, @target, on { |a| a.token == "token2" }, Time, nil).once
|
186
|
+
@sender.send_push(@type, nil, @target, "token2").should be_true
|
187
|
+
end
|
303
188
|
|
189
|
+
it "sets applies callback for returning response" do
|
190
|
+
flexmock(@sender).should_receive(:http_send).with(:send_push, nil, on { |a| a.confirm == true }, Time, Proc).once
|
191
|
+
@sender.send_push(@type) { |_| }.should be_true
|
304
192
|
end
|
193
|
+
end
|
305
194
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
195
|
+
context :send_request do
|
196
|
+
it "creates Request object" do
|
197
|
+
flexmock(RightSupport::Data::UUID, :generate => "random token")
|
198
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, nil, on { |a| a.class == RightScale::Request &&
|
199
|
+
a.type == @type && a.from == @agent_id && a.target.nil? && a.persistent.nil? && a.selector == :any &&
|
200
|
+
a.token == "random token" && a.expires_at == 0 }, Time, Proc).once
|
201
|
+
@sender.send_request(@type) { |_| }.should be_true
|
310
202
|
end
|
311
203
|
|
312
|
-
it "
|
313
|
-
|
314
|
-
|
204
|
+
it "stores payload" do
|
205
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, nil, on { |a| a.payload == @payload }, Time, Proc)
|
206
|
+
@sender.send_request(@type, @payload) { |_| }.should be_true
|
315
207
|
end
|
316
208
|
|
317
|
-
it "
|
318
|
-
|
319
|
-
|
209
|
+
it "sets specific target using string" do
|
210
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, @agent_id, on { |a| a.target == @agent_id &&
|
211
|
+
a.selector == :any }, Time, Proc).once
|
212
|
+
@sender.send_request(@type, nil, @agent_id) { |_| }.should be_true
|
320
213
|
end
|
321
214
|
|
322
|
-
|
215
|
+
it "sets specific target using :agent_id" do
|
216
|
+
target = {:agent_id => @agent_id}
|
217
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, target, on { |a| a.target == @agent_id }, Time, Proc).once
|
218
|
+
@sender.send_request(@type, nil, target) { |_| }.should be_true
|
219
|
+
end
|
323
220
|
|
324
|
-
|
221
|
+
it "sets target tags" do
|
222
|
+
tags = ["a:b=c"]
|
223
|
+
target = {:tags => tags}
|
224
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, target, on { |a| a.tags == tags &&
|
225
|
+
a.selector == :any }, Time, Proc).once
|
226
|
+
@sender.send_request(@type, nil, target) { |_| }.should be_true
|
227
|
+
end
|
325
228
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
@agent.should_receive(:options).and_return({}).by_default
|
334
|
-
RightScale::Sender.new(@agent)
|
335
|
-
@instance = RightScale::Sender.instance
|
336
|
-
@instance.initialize_offline_queue
|
337
|
-
end
|
229
|
+
it "sets target scope" do
|
230
|
+
scope = {:shard => 1, :account => 123}
|
231
|
+
target = {:scope => scope}
|
232
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, target, on { |a| a.scope == scope &&
|
233
|
+
a.selector == :any }, Time, Proc).once
|
234
|
+
@sender.send_request(@type, nil, target) { |_| }.should be_true
|
235
|
+
end
|
338
236
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
end
|
237
|
+
it "sets token" do
|
238
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, @target, on { |a| a.token == "token2" }, Time, Proc).once
|
239
|
+
@sender.send_request(@type, nil, @target, "token2") { |_| }.should be_true
|
240
|
+
end
|
344
241
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
end
|
242
|
+
it "sets expiration time if TTL enabled" do
|
243
|
+
@sender = create_sender(:http, :time_to_live => 60)
|
244
|
+
flexmock(@sender).should_receive(:http_send).with(:send_request, nil, on { |a| a.expires_at != 0 }, Time, Proc).once
|
245
|
+
@sender.send_request(@type) { |_| }.should be_true
|
246
|
+
end
|
351
247
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
end, hsh(:persistent => false, :mandatory => true)).once
|
356
|
-
@instance.send_push('/welcome/aboard', 'iZac', 'my-target')
|
357
|
-
end
|
248
|
+
it "does not allow fanout" do
|
249
|
+
lambda { @sender.send_request(@type, nil, :selector => :all) }.should raise_error(ArgumentError)
|
250
|
+
end
|
358
251
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
push.selector.should == :all
|
363
|
-
push.scope.should == {:account => 123}
|
364
|
-
end, hsh(:persistent => false, :mandatory => true)).once
|
365
|
-
@instance.send_push('/welcome/aboard', 'iZac', :tags => ['tag'], :selector => :all, :scope => {:account => 123})
|
252
|
+
it "requires callback block" do
|
253
|
+
lambda { @sender.send_request(@type) }.should raise_error(ArgumentError)
|
254
|
+
end
|
366
255
|
end
|
367
256
|
|
368
|
-
|
369
|
-
|
370
|
-
push.tags.should == ['tag']
|
371
|
-
push.scope.should == {:account => 123}
|
372
|
-
end, hsh(:persistent => false, :mandatory => true)).once
|
373
|
-
@instance.send_push('/welcome/aboard', 'iZac', :tags => ['tag'], :scope => {:account => 123})
|
374
|
-
end
|
257
|
+
context :build_and_send_packet do
|
258
|
+
[:http, :amqp].each do |mode|
|
375
259
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
push.persistent.should be_false
|
381
|
-
push.from.should == 'agent'
|
382
|
-
push.target.should be_nil
|
383
|
-
push.confirm.should be_nil
|
384
|
-
push.expires_at.should == 0
|
385
|
-
end, hsh(:persistent => false, :mandatory => true)).once
|
386
|
-
@instance.send_push('/welcome/aboard', 'iZac')
|
387
|
-
end
|
260
|
+
context "when #{mode}" do
|
261
|
+
before(:each) do
|
262
|
+
@sender = create_sender(mode)
|
263
|
+
end
|
388
264
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
@instance.enable_offline_mode
|
396
|
-
@instance.offline_handler.mode.should == :offline
|
397
|
-
@instance.send_push('/welcome/aboard', 'iZac')
|
398
|
-
@instance.offline_handler.queue.size.should == 1
|
399
|
-
end
|
265
|
+
it "builds packet" do
|
266
|
+
packet = flexmock("packet", :type => @type, :token => @token, :selector => :any)
|
267
|
+
flexmock(@sender).should_receive(:build_packet).with(:send_push, @type, @payload, @target, @token, nil).and_return(packet).once
|
268
|
+
flexmock(@sender).should_receive("#{mode}_send".to_sym)
|
269
|
+
@sender.build_and_send_packet(:send_push, @type, @payload, @target, @token, callback = nil).should be_true
|
270
|
+
end
|
400
271
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
@instance = RightScale::Sender.instance
|
407
|
-
lambda { @instance.send_push('/welcome/aboard', 'iZac') }.should raise_error(RightScale::Sender::TemporarilyOffline)
|
408
|
-
end
|
272
|
+
it "sends packet" do
|
273
|
+
flexmock(@sender).should_receive("#{mode}_send".to_sym).with(:send_push, @target,
|
274
|
+
on { |a| a.class == RightScale::Push }, Time, nil).once
|
275
|
+
@sender.build_and_send_packet(:send_push, @type, @payload, @target, @token, callback = nil).should be_true
|
276
|
+
end
|
409
277
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
@instance.pending_requests['abc'].response_handler.should == response_handler
|
278
|
+
it "ignores nil packet result when queueing" do
|
279
|
+
flexmock(@sender).should_receive(:build_packet).with(:send_push, @type, @payload, @target, @token, nil).and_return(nil).once
|
280
|
+
flexmock(@sender).should_receive("#{mode}_send".to_sym).never
|
281
|
+
@sender.build_and_send_packet(:send_push, @type, @payload, @target, @token, callback = nil).should be_true
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
418
285
|
end
|
419
286
|
|
420
|
-
|
421
|
-
|
422
|
-
flexmock(RightScale::AgentIdentity).should_receive(:generate).and_return('abc').once
|
423
|
-
@instance.pending_requests.kind(RightScale::Sender::PendingRequests::PUSH_KINDS).youngest_age.should be_nil
|
424
|
-
@instance.send_push('/welcome/aboard', 'iZac', &response_handler)
|
425
|
-
@instance.pending_requests['abc'].receive_time.should == Time.at(1000000)
|
426
|
-
flexmock(Time).should_receive(:now).and_return(Time.at(1000100))
|
427
|
-
@instance.pending_requests.kind(RightScale::Sender::PendingRequests::PUSH_KINDS).youngest_age.should == 100
|
428
|
-
end
|
287
|
+
context :build_packet do
|
288
|
+
[:send_push, :send_request].each do |kind|
|
429
289
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
flexmock(Time).should_receive(:now).and_return(Time.at(1000121))
|
436
|
-
@instance.send_push('/welcome/aboard', 'iZac', &response_handler)
|
437
|
-
@instance.pending_requests['xyz'].should_not be_nil
|
438
|
-
@instance.pending_requests['abc'].should be_nil
|
439
|
-
end
|
290
|
+
context "when #{kind}" do
|
291
|
+
it "validates target" do
|
292
|
+
flexmock(@sender).should_receive(:validate_target).with(@target, kind == :send_push).once
|
293
|
+
@sender.build_packet(kind, @type, nil, @target, @token).should_not be_nil
|
294
|
+
end
|
440
295
|
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
end
|
446
|
-
end
|
296
|
+
it "submits request to offline handler and returns nil if queueing" do
|
297
|
+
flexmock(@sender).should_receive(:queueing?).and_return(true).once
|
298
|
+
@sender.build_packet(kind, @type, nil, @target, @token).should be_nil
|
299
|
+
end
|
447
300
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
@agent = flexmock("Agent", :identity => "agent", :broker => @broker, :options => {}).by_default
|
454
|
-
RightScale::Sender.new(@agent)
|
455
|
-
@instance = RightScale::Sender.instance
|
456
|
-
@instance.initialize_offline_queue
|
457
|
-
end
|
301
|
+
it "generates a request token if none provided" do
|
302
|
+
flexmock(RightSupport::Data::UUID, :generate => "random token")
|
303
|
+
packet = @sender.build_packet(kind, @type, nil, @target, token = nil)
|
304
|
+
packet.token.should == "random token"
|
305
|
+
end
|
458
306
|
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
@instance.send_persistent_push('/welcome/aboard', 'iZac')
|
464
|
-
end
|
307
|
+
it "sets payload" do
|
308
|
+
packet = @sender.build_packet(kind, @type, @payload, @target, @token)
|
309
|
+
packet.payload.should == @payload
|
310
|
+
end
|
465
311
|
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
push.persistent.should be_true
|
471
|
-
push.from.should == 'agent'
|
472
|
-
push.target.should be_nil
|
473
|
-
push.confirm.should be_nil
|
474
|
-
push.expires_at.should == 0
|
475
|
-
end, hsh(:persistent => true, :mandatory => true)).once
|
476
|
-
@instance.send_persistent_push('/welcome/aboard', 'iZac')
|
477
|
-
end
|
312
|
+
it "sets the packet from this agent" do
|
313
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
314
|
+
packet.from.should == @agent_id
|
315
|
+
end
|
478
316
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
end, hsh(:persistent => true, :mandatory => true)).once
|
484
|
-
@instance.send_persistent_push('/welcome/aboard', 'iZac', :tags => ['tag'], :scope => {:account => 123})
|
485
|
-
end
|
317
|
+
it "sets target if target is not a hash" do
|
318
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
319
|
+
packet.target.should == @target
|
320
|
+
end
|
486
321
|
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
322
|
+
context "when target is a hash" do
|
323
|
+
it "sets agent ID" do
|
324
|
+
target = {:agent_id => @agent_id}
|
325
|
+
packet = @sender.build_packet(kind, @type, nil, target, @token)
|
326
|
+
packet.target.should == @agent_id
|
327
|
+
end
|
328
|
+
|
329
|
+
it "sets tags" do
|
330
|
+
tags = ["a:b=c"]
|
331
|
+
target = {:tags => tags}
|
332
|
+
packet = @sender.build_packet(kind, @type, nil, target, @token)
|
333
|
+
packet.tags.should == tags
|
334
|
+
packet.scope.should be_nil
|
335
|
+
end
|
336
|
+
|
337
|
+
it "sets scope" do
|
338
|
+
scope = {:shard => 1, :account => 123}
|
339
|
+
target = {:scope => scope}
|
340
|
+
packet = @sender.build_packet(kind, @type, nil, target, @token)
|
341
|
+
packet.tags.should == []
|
342
|
+
end
|
343
|
+
end
|
496
344
|
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
345
|
+
if kind == :send_push
|
346
|
+
it "defaults selector to :any" do
|
347
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
348
|
+
packet.selector.should == :any
|
349
|
+
end
|
350
|
+
|
351
|
+
it "sets selector" do
|
352
|
+
target = {:selector => :all}
|
353
|
+
packet = @sender.build_packet(kind, @type, nil, target, @token)
|
354
|
+
packet.selector.should == :all
|
355
|
+
end
|
356
|
+
|
357
|
+
it "sets persistent flag" do
|
358
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
359
|
+
packet.persistent.should be_true
|
360
|
+
end
|
361
|
+
|
362
|
+
it "enables confirm if callback provided" do
|
363
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token, @callback)
|
364
|
+
packet.confirm.should be_true
|
365
|
+
end
|
366
|
+
|
367
|
+
it "does not enable confirm if not callback provided" do
|
368
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
369
|
+
packet.confirm.should be_false
|
370
|
+
end
|
371
|
+
|
372
|
+
it "does not set expiration time" do
|
373
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
374
|
+
packet.expires_at.should == 0
|
375
|
+
end
|
376
|
+
else
|
377
|
+
it "always sets selector to :any" do
|
378
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
379
|
+
packet.selector.should == :any
|
380
|
+
end
|
381
|
+
|
382
|
+
it "sets expiration time to specified TTL" do
|
383
|
+
@sender = create_sender(:http, :time_to_live => 99)
|
384
|
+
now = Time.now
|
385
|
+
flexmock(Time).should_receive(:now).and_return(now)
|
386
|
+
packet = @sender.build_packet(kind, @type, nil, @target, @token)
|
387
|
+
packet.expires_at.should == (now + 99).to_i
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
506
392
|
end
|
507
|
-
end
|
508
393
|
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
@agent.should_receive(:options).and_return({:ping_interval => 0, :time_to_live => 100}).by_default
|
519
|
-
RightScale::Sender.new(@agent)
|
520
|
-
@instance = RightScale::Sender.instance
|
521
|
-
@instance.initialize_offline_queue
|
522
|
-
end
|
394
|
+
context :handle_response do
|
395
|
+
before(:each) do
|
396
|
+
flexmock(RightSupport::Data::UUID, :generate => @token)
|
397
|
+
@received_at = Time.now
|
398
|
+
flexmock(Time).should_receive(:now).and_return(@received_at)
|
399
|
+
@callback = lambda { |_| }
|
400
|
+
@pending_request = RightScale::PendingRequest.new(:send_request, @received_at, @callback)
|
401
|
+
@sender.pending_requests[@token] = @pending_request
|
402
|
+
end
|
523
403
|
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
404
|
+
[:send_push, :send_request].each do |kind|
|
405
|
+
it "delivers the response for a #{kind}" do
|
406
|
+
@pending_request = RightScale::PendingRequest.new(kind, @received_at, @callback)
|
407
|
+
@sender.pending_requests[@token] = @pending_request
|
408
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
409
|
+
flexmock(@sender).should_receive(:deliver_response).with(response, @pending_request).once
|
410
|
+
@sender.handle_response(response).should be_true
|
411
|
+
end
|
412
|
+
end
|
529
413
|
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
414
|
+
it "logs a debug message if request no longer pending" do
|
415
|
+
@sender.pending_requests.delete(@token)
|
416
|
+
@log.should_receive(:debug).with(/No pending request for response/).once
|
417
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
418
|
+
@sender.handle_response(response)
|
419
|
+
end
|
536
420
|
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
request.token.should_not be_nil
|
542
|
-
request.persistent.should be_false
|
543
|
-
request.from.should == 'agent'
|
544
|
-
request.target.should be_nil
|
545
|
-
request.expires_at.should == 1000100
|
546
|
-
end, hsh(:persistent => false, :mandatory => true)).and_return(@broker_ids).once
|
547
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
548
|
-
end
|
421
|
+
it "ignores responses that are not a Result" do
|
422
|
+
flexmock(@sender).should_receive(:deliver_response).never
|
423
|
+
@sender.handle_response("response").should be_true
|
424
|
+
end
|
549
425
|
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
@broker.should_receive(:publish).with(hsh(:name => "request"), on do |request|
|
557
|
-
request.expires_at.should == 0
|
558
|
-
end, hsh(:persistent => false, :mandatory => true)).and_return(@broker_ids).once
|
559
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
560
|
-
end
|
426
|
+
context "when non-delivery" do
|
427
|
+
before(:each) do
|
428
|
+
@reason = RightScale::OperationResult::TARGET_NOT_CONNECTED
|
429
|
+
non_delivery = RightScale::OperationResult.non_delivery(@reason)
|
430
|
+
@response = RightScale::Result.new(@token, "to", non_delivery, @target)
|
431
|
+
end
|
561
432
|
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
433
|
+
it "records non-delivery regardless of whether there is a pending request" do
|
434
|
+
@sender.pending_requests.delete(@token)
|
435
|
+
non_delivery = RightScale::OperationResult.non_delivery(RightScale::OperationResult::NO_ROUTE_TO_TARGET)
|
436
|
+
response = RightScale::Result.new(@token, "to", non_delivery, @target)
|
437
|
+
@sender.handle_response(response).should be_true
|
438
|
+
@sender.instance_variable_get(:@non_delivery_stats).total.should == 1
|
439
|
+
end
|
568
440
|
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
end
|
441
|
+
it "logs non-delivery if there is no pending request" do
|
442
|
+
@sender.pending_requests.delete(@token)
|
443
|
+
@log.should_receive(:info).with(/Non-delivery of/).once
|
444
|
+
non_delivery = RightScale::OperationResult.non_delivery(RightScale::OperationResult::NO_ROUTE_TO_TARGET)
|
445
|
+
response = RightScale::Result.new(@token, "to", non_delivery, @target)
|
446
|
+
@sender.handle_response(response).should be_true
|
447
|
+
end
|
577
448
|
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
449
|
+
context "for a Request" do
|
450
|
+
|
451
|
+
context "with target not connected" do
|
452
|
+
it "logs non-delivery but does not deliver it" do
|
453
|
+
@log.should_receive(:info).with(/Non-delivery of/).once
|
454
|
+
flexmock(@sender).should_receive(:deliver_response).never
|
455
|
+
@sender.handle_response(@response).should be_true
|
456
|
+
end
|
457
|
+
|
458
|
+
it "records non-delivery reason in pending request if for parent request" do
|
459
|
+
@log.should_receive(:info).with(/Non-delivery of/).once
|
460
|
+
flexmock(@sender).should_receive(:deliver_response).never
|
461
|
+
parent_token = "parent token"
|
462
|
+
parent_pending_request = RightScale::PendingRequest.new(:send_request, @received_at, @callback)
|
463
|
+
@sender.pending_requests[parent_token] = parent_pending_request
|
464
|
+
@pending_request.retry_parent_token = parent_token
|
465
|
+
@sender.handle_response(@response).should be_true
|
466
|
+
parent_pending_request.non_delivery.should == @reason
|
467
|
+
end
|
468
|
+
|
469
|
+
it "updates non-delivery reason if for retry request" do
|
470
|
+
flexmock(@sender).should_receive(:deliver_response).never
|
471
|
+
@sender.handle_response(@response).should be_true
|
472
|
+
@pending_request.non_delivery.should == @reason
|
473
|
+
end
|
474
|
+
end
|
582
475
|
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
476
|
+
context "with retry timeout and previous non-delivery" do
|
477
|
+
it "delivers response using stored non-delivery reason" do
|
478
|
+
@reason = RightScale::OperationResult::RETRY_TIMEOUT
|
479
|
+
@response.results = RightScale::OperationResult.non_delivery(@reason)
|
480
|
+
@pending_request.non_delivery = "other reason"
|
481
|
+
flexmock(@sender).should_receive(:deliver_response).with(on { |a| a.results.content == "other reason"},
|
482
|
+
@pending_request).once
|
483
|
+
@sender.handle_response(@response).should be_true
|
484
|
+
end
|
485
|
+
end
|
589
486
|
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
487
|
+
context "otherwise" do
|
488
|
+
it "delivers non-delivery response as is" do
|
489
|
+
@response.results = RightScale::OperationResult.non_delivery("other")
|
490
|
+
flexmock(@sender).should_receive(:deliver_response).with(on { |a| a.results.content == "other"},
|
491
|
+
RightScale::PendingRequest).once
|
492
|
+
@sender.handle_response(@response).should be_true
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
598
497
|
end
|
599
498
|
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
@broker.should_receive(:publish).never
|
606
|
-
@instance.enable_offline_mode
|
607
|
-
@instance.offline_handler.mode.should == :offline
|
608
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
609
|
-
@instance.offline_handler.queue.size.should == 1
|
610
|
-
end
|
499
|
+
context :terminate do
|
500
|
+
it "terminates offline handler" do
|
501
|
+
flexmock(@sender.offline_handler).should_receive(:terminate).once
|
502
|
+
@sender.terminate
|
503
|
+
end
|
611
504
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
@instance = RightScale::Sender.instance
|
618
|
-
lambda { @instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|} }.should raise_error(RightScale::Sender::TemporarilyOffline)
|
619
|
-
end
|
505
|
+
it "terminates connectivity checker if configured" do
|
506
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
507
|
+
flexmock(@sender.connectivity_checker).should_receive(:terminate).once
|
508
|
+
@sender.terminate
|
509
|
+
end
|
620
510
|
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
511
|
+
it "returns number of pending requests and age of youngest request" do
|
512
|
+
receive_time = Time.now - 10
|
513
|
+
@sender.pending_requests[@token] = RightScale::PendingRequest.new(:send_request, receive_time, @callback)
|
514
|
+
@sender.terminate.should == [1, 10]
|
515
|
+
end
|
626
516
|
end
|
627
517
|
|
628
|
-
|
629
|
-
|
630
|
-
|
518
|
+
context :dump_requests do
|
519
|
+
it "returns array of unfinished non-push requests" do
|
520
|
+
time1 = Time.now
|
521
|
+
@sender.pending_requests["token1"] = RightScale::PendingRequest.new(:send_push, time1, @callback)
|
522
|
+
time2 = time1 + 10
|
523
|
+
@sender.pending_requests["token2"] = RightScale::PendingRequest.new(:send_request, time2, @callback)
|
524
|
+
@sender.dump_requests.should == ["#{time2.localtime} <token2>"]
|
525
|
+
end
|
631
526
|
|
632
|
-
|
633
|
-
|
634
|
-
|
527
|
+
it "returns requests in descending time order" do
|
528
|
+
time1 = Time.now
|
529
|
+
@sender.pending_requests["token1"] = RightScale::PendingRequest.new(:send_request, time1, @callback)
|
530
|
+
time2 = time1 + 10
|
531
|
+
@sender.pending_requests["token2"] = RightScale::PendingRequest.new(:send_request, time2, @callback)
|
532
|
+
@sender.dump_requests.should == ["#{time2.localtime} <token2>", "#{time1.localtime} <token1>"]
|
533
|
+
end
|
635
534
|
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
535
|
+
it "limits the number returned to 50" do
|
536
|
+
pending_request = RightScale::PendingRequest.new(:send_request, Time.now, @callback)
|
537
|
+
55.times.each { |i| @sender.pending_requests["token#{i}"] = pending_request }
|
538
|
+
result = @sender.dump_requests
|
539
|
+
result.size.should == 51
|
540
|
+
result.last.should == "..."
|
541
|
+
end
|
640
542
|
end
|
641
543
|
|
642
|
-
|
643
|
-
it "should
|
644
|
-
|
645
|
-
@agent.should_receive(:options).and_return({:retry_timeout => nil})
|
646
|
-
RightScale::Sender.new(@agent)
|
647
|
-
@instance = RightScale::Sender.instance
|
648
|
-
@broker.should_receive(:publish).and_return(@broker_ids).once
|
649
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
544
|
+
context :validate_target do
|
545
|
+
it "should accept nil target" do
|
546
|
+
@sender.send(:validate_target, nil, true).should be_true
|
650
547
|
end
|
651
548
|
|
652
|
-
it "should
|
653
|
-
|
654
|
-
@agent.should_receive(:options).and_return({:retry_interval => nil})
|
655
|
-
RightScale::Sender.new(@agent)
|
656
|
-
@instance = RightScale::Sender.instance
|
657
|
-
@broker.should_receive(:publish).and_return(@broker_ids).once
|
658
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
549
|
+
it "should accept named target" do
|
550
|
+
@sender.send(:validate_target, "name", true).should be_true
|
659
551
|
end
|
660
552
|
|
661
|
-
|
662
|
-
flexmock(EM).should_receive(:add_timer).never
|
663
|
-
@agent.should_receive(:options).and_return({:retry_timeout => 60, :retry_interval => 60})
|
664
|
-
RightScale::Sender.new(@agent)
|
665
|
-
@instance = RightScale::Sender.instance
|
666
|
-
@broker.should_receive(:publish).and_raise(Exception).once
|
667
|
-
@log.should_receive(:error).with(/Failed to publish request/, Exception, :trace).once
|
668
|
-
lambda do
|
669
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
670
|
-
end.should raise_error(RightScale::Sender::SendFailure)
|
671
|
-
end
|
553
|
+
context "when target is a hash" do
|
672
554
|
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
end
|
555
|
+
context "and agent ID is specified" do
|
556
|
+
it "should not allow other keys" do
|
557
|
+
@sender.send(:validate_target, {:agent_id => @agent_id}, true).should be_true
|
558
|
+
lambda { @sender.send(:validate_target, {:agent_id => @agent_id, :tags => ["a:b=c"]}, true) }.should \
|
559
|
+
raise_error(ArgumentError, /Invalid target/)
|
560
|
+
end
|
561
|
+
end
|
681
562
|
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
flexmock(RightScale::AgentIdentity).should_receive(:generate).and_return(token).twice
|
687
|
-
@agent.should_receive(:options).and_return({:retry_timeout => 0.3, :retry_interval => 0.1})
|
688
|
-
RightScale::Sender.new(@agent)
|
689
|
-
@instance = RightScale::Sender.instance
|
690
|
-
flexmock(@instance.connectivity_checker).should_receive(:check).once
|
691
|
-
@broker.should_receive(:publish).and_return(@broker_ids).twice
|
692
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') do |response|
|
693
|
-
result = RightScale::OperationResult.from_results(response)
|
563
|
+
context "and selector is allowed" do
|
564
|
+
it "should accept :all or :any selector" do
|
565
|
+
@sender.send(:validate_target, {:selector => :all}, true).should be_true
|
566
|
+
@sender.send(:validate_target, {"selector" => "any"}, true).should be_true
|
694
567
|
end
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
568
|
+
|
569
|
+
it "should reject values other than :all or :any" do
|
570
|
+
lambda { @sender.send(:validate_target, {:selector => :other}, true) }.
|
571
|
+
should raise_error(ArgumentError, /Invalid target selector/)
|
699
572
|
end
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
573
|
+
end
|
574
|
+
|
575
|
+
context "and selector is not allowed" do
|
576
|
+
it "should reject selector" do
|
577
|
+
lambda { @sender.send(:validate_target, {:selector => :all}, false) }.
|
578
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
704
579
|
end
|
705
580
|
end
|
706
|
-
end
|
707
581
|
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
flexmock(RightScale::AgentIdentity).should_receive(:generate).and_return(token).twice
|
713
|
-
@agent.should_receive(:options).and_return({:retry_timeout => 0.3, :retry_interval => 0.1})
|
714
|
-
RightScale::Sender.new(@agent)
|
715
|
-
@instance = RightScale::Sender.instance
|
716
|
-
flexmock(@instance.connectivity_checker).should_receive(:check).never
|
717
|
-
@broker.should_receive(:publish).and_return(@broker_ids).ordered.once
|
718
|
-
@broker.should_receive(:publish).and_raise(RightAMQP::HABrokerClient::NoConnectedBrokers).ordered.once
|
719
|
-
@log.should_receive(:error).with(/Failed to publish request/, RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
720
|
-
@log.should_receive(:error).with(/Failed retry for.*temporarily offline/).once
|
721
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') do |response|
|
722
|
-
result = RightScale::OperationResult.from_results(response)
|
723
|
-
result.retry?.should be_true
|
724
|
-
result.content.should == "lost connectivity"
|
582
|
+
context "and tags is specified" do
|
583
|
+
it "should accept tags" do
|
584
|
+
@sender.send(:validate_target, {:tags => []}, true).should be_true
|
585
|
+
@sender.send(:validate_target, {"tags" => ["tag"]}, true).should be_true
|
725
586
|
end
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
587
|
+
|
588
|
+
it "should reject non-array" do
|
589
|
+
lambda { @sender.send(:validate_target, {:tags => {}}, true) }.
|
590
|
+
should raise_error(ArgumentError, /Invalid target tags/)
|
730
591
|
end
|
731
592
|
end
|
732
|
-
end
|
733
593
|
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
flexmock(RightScale::AgentIdentity).should_receive(:generate).and_return(token).twice
|
739
|
-
@agent.should_receive(:options).and_return({:retry_timeout => 0.3, :retry_interval => 0.1})
|
740
|
-
RightScale::Sender.new(@agent)
|
741
|
-
@instance = RightScale::Sender.instance
|
742
|
-
flexmock(@instance.connectivity_checker).should_receive(:check).never
|
743
|
-
@broker.should_receive(:publish).and_return(@broker_ids).ordered.once
|
744
|
-
@broker.should_receive(:publish).and_raise(Exception.new).ordered.once
|
745
|
-
@log.should_receive(:error).with(/Failed to publish request/, Exception, :trace).once
|
746
|
-
@log.should_receive(:error).with(/Failed retry for.*send failure/).once
|
747
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') do |response|
|
748
|
-
result = RightScale::OperationResult.from_results(response)
|
749
|
-
result.non_delivery?.should be_true
|
750
|
-
result.content.should == "retry failed"
|
594
|
+
context "and scope is specified" do
|
595
|
+
it "should accept account" do
|
596
|
+
@sender.send(:validate_target, {:scope => {:account => 1}}, true).should be_true
|
597
|
+
@sender.send(:validate_target, {"scope" => {"account" => 1}}, true).should be_true
|
751
598
|
end
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
@
|
599
|
+
|
600
|
+
it "should accept shard" do
|
601
|
+
@sender.send(:validate_target, {:scope => {:shard => 1}}, true).should be_true
|
602
|
+
@sender.send(:validate_target, {"scope" => {"shard" => 1}}, true).should be_true
|
756
603
|
end
|
757
|
-
end
|
758
|
-
end
|
759
604
|
|
760
|
-
|
761
|
-
|
762
|
-
token = 'abc'
|
763
|
-
result = nil
|
764
|
-
flexmock(RightScale::AgentIdentity).should_receive(:generate).and_return(token).twice
|
765
|
-
@agent.should_receive(:options).and_return({:retry_timeout => 0.3, :retry_interval => 0.1})
|
766
|
-
RightScale::Sender.new(@agent)
|
767
|
-
@instance = RightScale::Sender.instance
|
768
|
-
flexmock(@instance.connectivity_checker).should_receive(:check).and_raise(Exception).once
|
769
|
-
@broker.should_receive(:publish).and_return(@broker_ids).twice
|
770
|
-
@log.should_receive(:error).with(/Failed retry for.*without responding/, Exception, :trace).once
|
771
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {_}
|
772
|
-
EM.add_timer(0.15) do
|
773
|
-
EM.stop
|
774
|
-
result.should be_nil
|
775
|
-
@instance.pending_requests.empty?.should be_false
|
605
|
+
it "should accept account and shard" do
|
606
|
+
@sender.send(:validate_target, {"scope" => {:shard => 1, "account" => 1}}, true).should be_true
|
776
607
|
end
|
777
|
-
end
|
778
|
-
end
|
779
608
|
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
@log.should_receive(:warning).once
|
785
|
-
@agent.should_receive(:options).and_return({:retry_timeout => 0.6, :retry_interval => 0.1})
|
786
|
-
RightScale::Sender.new(@agent)
|
787
|
-
@instance = RightScale::Sender.instance
|
788
|
-
flexmock(@instance.connectivity_checker).should_receive(:check).once
|
789
|
-
@broker.should_receive(:publish).and_return(@broker_ids).times(3)
|
790
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') do |response|
|
791
|
-
result = RightScale::OperationResult.from_results(response)
|
609
|
+
it "should reject keys other than account and shard" do
|
610
|
+
target = {"scope" => {:shard => 1, "account" => 1, :other => 2}}
|
611
|
+
lambda { @sender.send(:validate_target, target, true) }.
|
612
|
+
should raise_error(ArgumentError, /Invalid target scope/)
|
792
613
|
end
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
result.content.should == RightScale::OperationResult::RETRY_TIMEOUT
|
798
|
-
@instance.pending_requests.empty?.should be_true
|
614
|
+
|
615
|
+
it "should reject empty hash" do
|
616
|
+
lambda { @sender.send(:validate_target, {:scope => {}}, true) }.
|
617
|
+
should raise_error(ArgumentError, /Invalid target scope/)
|
799
618
|
end
|
800
619
|
end
|
801
|
-
end
|
802
620
|
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
result = RightScale::OperationResult.success
|
807
|
-
@log.should_receive(:warning).once
|
808
|
-
@agent.should_receive(:options).and_return({:retry_timeout => 0.6, :retry_interval => 0.1})
|
809
|
-
RightScale::Sender.new(@agent)
|
810
|
-
@instance = RightScale::Sender.instance
|
811
|
-
flexmock(@instance.connectivity_checker).should_receive(:check).once
|
812
|
-
@broker.should_receive(:publish).and_return(@broker_ids).times(3)
|
813
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') do |response|
|
814
|
-
result = RightScale::OperationResult.from_results(response)
|
621
|
+
context "and multiple are specified" do
|
622
|
+
it "should accept scope and tags" do
|
623
|
+
@sender.send(:validate_target, {:scope => {:shard => 1}, :tags => []}, true).should be_true
|
815
624
|
end
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
@
|
625
|
+
|
626
|
+
it "should accept scope, tags, and selector" do
|
627
|
+
target = {:scope => {:shard => 1}, :tags => ["tag"], :selector => :all}
|
628
|
+
@sender.send(:validate_target, target, true).should be_true
|
629
|
+
end
|
630
|
+
|
631
|
+
it "should reject selector if not allowed" do
|
632
|
+
target = {:scope => {:shard => 1}, :tags => ["tag"], :selector => :all}
|
633
|
+
lambda { @sender.send(:validate_target, target, false) }.
|
634
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
825
635
|
end
|
826
636
|
end
|
827
|
-
end
|
828
637
|
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
@
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
@
|
842
|
-
|
638
|
+
it "should reject keys other than selector, scope, and tags" do
|
639
|
+
target = {:scope => {:shard => 1}, :tags => [], :selector => :all, :other => 2}
|
640
|
+
lambda { @sender.send(:validate_target, target, true) }.
|
641
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
642
|
+
end
|
643
|
+
|
644
|
+
it "should reject empty hash" do
|
645
|
+
lambda { @sender.send(:validate_target, {}, true) }.
|
646
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
647
|
+
end
|
648
|
+
|
649
|
+
it "should reject value that is not nil, string, or hash" do
|
650
|
+
lambda { @sender.send(:validate_target, [], true) }.
|
651
|
+
should raise_error(ArgumentError, /Invalid target/)
|
843
652
|
end
|
844
653
|
end
|
654
|
+
end
|
655
|
+
|
656
|
+
context :http do
|
845
657
|
|
846
|
-
|
658
|
+
context :http_send do
|
847
659
|
before(:each) do
|
848
|
-
@
|
849
|
-
@
|
660
|
+
@received_at = Time.now
|
661
|
+
flexmock(Time).should_receive(:now).and_return(@received_at)
|
662
|
+
flexmock(RightSupport::Data::UUID).should_receive(:generate).and_return(@token).by_default
|
663
|
+
@packet = @sender.build_packet(:send_request, @type, @payload, @target, @token, @callback)
|
664
|
+
@response = nil
|
665
|
+
@callback = lambda { |response| @response = response }
|
850
666
|
end
|
851
667
|
|
852
|
-
it "
|
853
|
-
|
854
|
-
@
|
855
|
-
|
856
|
-
@instance.connectivity_checker.check(@broker_ids)
|
668
|
+
it "sends request using configured client" do
|
669
|
+
@packet = @sender.build_packet(:send_push, @type, @payload, @target, @token, @callback)
|
670
|
+
@client.should_receive(:push).with(@type, @payload, @target, @token).and_return(nil).once
|
671
|
+
@sender.send(:http_send, :send_push, @target, @packet, @received_at, @callback).should be_true
|
857
672
|
end
|
858
673
|
|
859
|
-
it "
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
@
|
864
|
-
@
|
674
|
+
it "responds with success result containing response" do
|
675
|
+
@client.should_receive(:request).with(@type, @payload, @target, @token).and_return("result").once
|
676
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
677
|
+
@response.token.should == @token
|
678
|
+
@response.results.success?.should be_true
|
679
|
+
@response.results.content.should == "result"
|
680
|
+
@response.from.should == @target
|
681
|
+
@response.received_at.should == @received_at.to_f
|
865
682
|
end
|
866
683
|
|
867
|
-
it "
|
868
|
-
|
869
|
-
@
|
870
|
-
|
871
|
-
|
872
|
-
@
|
873
|
-
@
|
874
|
-
@
|
875
|
-
@instance.pending_requests['abc'].response_handler.call(nil)
|
876
|
-
@instance.connectivity_checker.ping_timer.should == nil
|
684
|
+
it "responds with success result when response is nil" do
|
685
|
+
@client.should_receive(:request).with(@type, @payload, @target, @token).and_return(nil).once
|
686
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
687
|
+
@response.token.should == @token
|
688
|
+
@response.results.success?.should be_true
|
689
|
+
@response.results.content.should be_nil
|
690
|
+
@response.from.should == @target
|
691
|
+
@response.received_at.should == @received_at.to_f
|
877
692
|
end
|
878
693
|
|
879
|
-
it "
|
880
|
-
@
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
@
|
885
|
-
@
|
886
|
-
@
|
887
|
-
@
|
694
|
+
it "responds asynchronously if requested" do
|
695
|
+
@sender = create_sender(:http, :async_response => true)
|
696
|
+
flexmock(EM).should_receive(:next_tick).and_yield.once
|
697
|
+
@client.should_receive(:request).with(@type, @payload, @target, @token).and_return(nil).once
|
698
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
699
|
+
@response.token.should == @token
|
700
|
+
@response.results.success?.should be_true
|
701
|
+
@response.results.content.should be_nil
|
702
|
+
@response.from.should == @target
|
703
|
+
@response.received_at.should == @received_at.to_f
|
888
704
|
end
|
889
705
|
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
706
|
+
context "when fails" do
|
707
|
+
context "with connectivity error" do
|
708
|
+
it "queues request if queueing and does not respond" do
|
709
|
+
@sender = create_sender(:http, :offline_queueing => true)
|
710
|
+
@sender.initialize_offline_queue
|
711
|
+
@sender.enable_offline_mode
|
712
|
+
@client.should_receive(:request).and_raise(RightScale::Exceptions::ConnectivityFailure, "disconnected").once
|
713
|
+
flexmock(@sender.offline_handler).should_receive(:queue_request).with(:send_request, @type,
|
714
|
+
@payload, @target, @callback).once
|
715
|
+
flexmock(@sender).should_receive(:handle_response).never
|
716
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
717
|
+
end
|
718
|
+
|
719
|
+
it "responds with retry result if not queueing" do
|
720
|
+
@client.should_receive(:request).and_raise(RightScale::Exceptions::ConnectivityFailure, "Server not responding").once
|
721
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
722
|
+
@response.results.retry?.should be_true
|
723
|
+
@response.results.content.should == "Server not responding"
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
727
|
+
it "responds with retry result if retryable error" do
|
728
|
+
@client.should_receive(:request).and_raise(RightScale::Exceptions::RetryableError, "try again").once
|
729
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
730
|
+
@response.results.retry?.should be_true
|
731
|
+
@response.results.content.should == "try again"
|
732
|
+
end
|
733
|
+
|
734
|
+
it "responds with error result if internal error" do
|
735
|
+
@client.should_receive(:request).and_raise(RightScale::Exceptions::InternalServerError.new("unprocessable", "Router")).once
|
736
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
737
|
+
@response.results.error?.should be_true
|
738
|
+
@response.results.content.should == "Router internal error"
|
739
|
+
end
|
740
|
+
|
741
|
+
it "does not respond to request if terminating" do
|
742
|
+
@client.should_receive(:request).and_raise(RightScale::Exceptions::Terminating, "going down").once
|
743
|
+
flexmock(@sender).should_receive(:handle_response).never
|
744
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
745
|
+
end
|
746
|
+
|
747
|
+
it "responds with error result if HTTP error" do
|
748
|
+
@client.should_receive(:request).and_raise(RestExceptionMock.new(400, "bad data")).once
|
749
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
750
|
+
@response.results.error?.should be_true
|
751
|
+
@response.results.content.should == "400 Bad Request: bad data"
|
752
|
+
end
|
753
|
+
|
754
|
+
it "responds with error result if unexpected error" do
|
755
|
+
@log.should_receive(:error).with(/Failed to send/, StandardError, :trace).once
|
756
|
+
@client.should_receive(:request).and_raise(StandardError, "unexpected").once
|
757
|
+
@sender.send(:http_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
758
|
+
@response.results.error?.should be_true
|
759
|
+
@response.results.content.should == "Agent agent internal error"
|
760
|
+
end
|
898
761
|
end
|
899
762
|
end
|
900
763
|
end
|
901
|
-
end
|
902
764
|
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
RightScale::Sender.new(@agent)
|
914
|
-
@instance = RightScale::Sender.instance
|
915
|
-
@instance.initialize_offline_queue
|
916
|
-
end
|
765
|
+
context :amqp do
|
766
|
+
before(:each) do
|
767
|
+
@sender = create_sender(:amqp)
|
768
|
+
flexmock(RightSupport::Data::UUID).should_receive(:generate).and_return(@token).by_default
|
769
|
+
@packet = @sender.build_packet(:send_push, @type, @payload, @target, @token, nil)
|
770
|
+
@received_at = Time.now
|
771
|
+
flexmock(Time).should_receive(:now).and_return(@received_at)
|
772
|
+
@response = nil
|
773
|
+
@callback = lambda { |response| @response = response }
|
774
|
+
end
|
917
775
|
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
776
|
+
context :amqp_send do
|
777
|
+
it "stores pending request for use in responding if callback specified" do
|
778
|
+
@packet = @sender.build_packet(:send_push, @type, @payload, @target, @token, @callback)
|
779
|
+
flexmock(@sender).should_receive(:amqp_send_once)
|
780
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, @callback).should be_true
|
781
|
+
@sender.pending_requests[@token].should_not be_nil
|
782
|
+
end
|
924
783
|
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
request.persistent.should be_true
|
931
|
-
request.from.should == 'agent'
|
932
|
-
request.target.should be_nil
|
933
|
-
request.expires_at.should == 0
|
934
|
-
end, hsh(:persistent => true, :mandatory => true)).once
|
935
|
-
@instance.send_persistent_request('/welcome/aboard', 'iZac') {|_|}
|
936
|
-
end
|
784
|
+
it "does not store pending request if callback not specified" do
|
785
|
+
flexmock(@sender).should_receive(:amqp_send_once)
|
786
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, nil).should be_true
|
787
|
+
@sender.pending_requests[@token].should be_nil
|
788
|
+
end
|
937
789
|
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
@instance.send_persistent_request('/welcome/aboard', 'iZac', 'my-target') {|_|}
|
943
|
-
end
|
790
|
+
it "publishes without retry capability if send_push" do
|
791
|
+
flexmock(@sender).should_receive(:amqp_send_once).with(@packet).once
|
792
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, nil).should be_true
|
793
|
+
end
|
944
794
|
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
end, hsh(:persistent => true, :mandatory => true)).once
|
951
|
-
@instance.send_persistent_request('/welcome/aboard', 'iZac', :tags => ['tag'], :scope => {:account => 123}) {|_|}
|
952
|
-
end
|
795
|
+
it "publishes with retry capability if send_request" do
|
796
|
+
@packet = @sender.build_packet(:send_request, @type, @payload, @target, @token, @callback)
|
797
|
+
flexmock(@sender).should_receive(:amqp_send_retry).with(@packet, @token).once
|
798
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
799
|
+
end
|
953
800
|
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
801
|
+
context "when fails" do
|
802
|
+
|
803
|
+
context "with offline error" do
|
804
|
+
it "submits request to offline handler if queuing" do
|
805
|
+
@sender = create_sender(:amqp, :offline_queueing => true)
|
806
|
+
@sender.initialize_offline_queue
|
807
|
+
@sender.enable_offline_mode
|
808
|
+
flexmock(@sender).should_receive(:amqp_send_once).and_raise(RightScale::Sender::TemporarilyOffline).once
|
809
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, nil).should be_true
|
810
|
+
@sender.offline_handler.queue.size.should == 1
|
811
|
+
@sender.pending_requests[@token].should be_nil
|
812
|
+
end
|
813
|
+
|
814
|
+
it "responds with retry result if not queueing" do
|
815
|
+
flexmock(@sender).should_receive(:amqp_send_once).and_raise(RightScale::Sender::TemporarilyOffline).once
|
816
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, @callback).should be_true
|
817
|
+
@sender.offline_handler.queue.size.should == 0
|
818
|
+
@sender.pending_requests[@token].should_not be_nil # because send_push does not get deleted when deliver
|
819
|
+
@response.results.retry?.should be_true
|
820
|
+
@response.results.content.should == "lost RightNet connectivity"
|
821
|
+
end
|
822
|
+
end
|
958
823
|
|
959
|
-
|
960
|
-
|
961
|
-
|
824
|
+
it "responds with non-delivery result if send failure" do
|
825
|
+
flexmock(@sender).should_receive(:amqp_send_once).and_raise(RightScale::Sender::SendFailure).once
|
826
|
+
@sender.send(:amqp_send, :send_push, @target, @packet, @received_at, @callback).should be_true
|
827
|
+
@sender.pending_requests[@token].should_not be_nil # because send_push does not get deleted when deliver
|
828
|
+
@response.results.non_delivery?.should be_true
|
829
|
+
@response.results.content.should == "send failed unexpectedly"
|
830
|
+
end
|
831
|
+
end
|
832
|
+
end
|
962
833
|
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
834
|
+
context :amqp_send_once do
|
835
|
+
it "publishes request to request queue" do
|
836
|
+
@client.should_receive(:publish).with(hsh(:name => "request"), @packet, hsh(:persistent => true,
|
837
|
+
:mandatory => true, :broker_ids => nil)).and_return(@broker_ids).once
|
838
|
+
@sender.send(:amqp_send_once, @packet, @broker_ids).should == @broker_ids
|
839
|
+
end
|
967
840
|
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
RightScale::Sender.new(@agent)
|
975
|
-
@instance = RightScale::Sender.instance
|
976
|
-
flexmock(RightScale::AgentIdentity, :generate => 'token1')
|
977
|
-
end
|
841
|
+
context "when fails" do
|
842
|
+
it "raises TemporarilyOffline if no connected brokers" do
|
843
|
+
@log.should_receive(:error).with(/Failed to publish/, RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
844
|
+
@client.should_receive(:publish).and_raise(RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
845
|
+
lambda { @sender.send(:amqp_send_once, @packet) }.should raise_error(RightScale::Sender::TemporarilyOffline)
|
846
|
+
end
|
978
847
|
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
848
|
+
it "raises SendFailure if unexpected exception" do
|
849
|
+
@log.should_receive(:error).with(/Failed to publish/, StandardError, :trace).once
|
850
|
+
@client.should_receive(:publish).and_raise(StandardError, "unexpected").once
|
851
|
+
lambda { @sender.send(:amqp_send_once, @packet) }.should raise_error(RightScale::Sender::SendFailure)
|
852
|
+
end
|
853
|
+
end
|
854
|
+
end
|
985
855
|
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
end
|
856
|
+
context :amqp_send_retry do
|
857
|
+
before(:each) do
|
858
|
+
flexmock(RightSupport::Data::UUID).should_receive(:generate).and_return("retry token")
|
859
|
+
@packet = @sender.build_packet(:send_request, @type, @payload, @target, @token, @callback)
|
860
|
+
end
|
992
861
|
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
@instance.handle_response(response)
|
999
|
-
end
|
862
|
+
it "publishes request to request queue" do
|
863
|
+
@client.should_receive(:publish).with(hsh(:name => "request"), @packet, hsh(:persistent => nil,
|
864
|
+
:mandatory => true, :broker_ids => nil)).and_return(@broker_ids).once
|
865
|
+
@sender.send(:amqp_send_retry, @packet, @token).should be_true
|
866
|
+
end
|
1000
867
|
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
@instance.instance_variable_get(:@non_delivery_stats).total.should == 1
|
1007
|
-
end
|
868
|
+
it "does not rescue if publish fails" do
|
869
|
+
@log.should_receive(:error).with(/Failed to publish request/, RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
870
|
+
@client.should_receive(:publish).and_raise(RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
871
|
+
lambda { @sender.send(:amqp_send_retry, @packet, @token) }.should raise_error(RightScale::Sender::TemporarilyOffline)
|
872
|
+
end
|
1008
873
|
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
end
|
874
|
+
it "does not retry if retry timeout not set" do
|
875
|
+
@sender = create_sender(:amqp, :retry_interval => 10)
|
876
|
+
@client.should_receive(:publish).once
|
877
|
+
flexmock(EM).should_receive(:add_timer).never
|
878
|
+
@sender.send(:amqp_send_retry, @packet, @token).should be_true
|
879
|
+
end
|
1016
880
|
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
@instance.handle_response(response)
|
1024
|
-
end
|
1025
|
-
end
|
881
|
+
it "does not retry if retry interval not set" do
|
882
|
+
@sender = create_sender(:amqp, :retry_timeout => 60)
|
883
|
+
@client.should_receive(:publish).once
|
884
|
+
flexmock(EM).should_receive(:add_timer).never
|
885
|
+
@sender.send(:amqp_send_retry, @packet, @token).should be_true
|
886
|
+
end
|
1026
887
|
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
@instance = RightScale::Sender.instance
|
1035
|
-
flexmock(RightScale::AgentIdentity, :generate => 'token1')
|
1036
|
-
end
|
888
|
+
context "when retry enabled" do
|
889
|
+
it "uses timer to wait for a response" do
|
890
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
891
|
+
@client.should_receive(:publish).once
|
892
|
+
flexmock(EM).should_receive(:add_timer).once
|
893
|
+
@sender.send(:amqp_send_retry, @packet, @token).should be_true
|
894
|
+
end
|
1037
895
|
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
896
|
+
it "stops retrying if response was received" do
|
897
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
898
|
+
@client.should_receive(:publish).once
|
899
|
+
flexmock(EM).should_receive(:add_timer).and_yield.once
|
900
|
+
@sender.send(:amqp_send_retry, @packet, @token).should be_true
|
901
|
+
@sender.pending_requests[@token].should be_nil
|
902
|
+
end
|
1045
903
|
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
904
|
+
it "keeps retrying if has not exceeded retry timeout" do
|
905
|
+
EM.run do
|
906
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
907
|
+
@client.should_receive(:publish).and_return(@broker_ids).twice
|
908
|
+
flexmock(@sender.connectivity_checker).should_receive(:check).once
|
909
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
910
|
+
|
911
|
+
EM.add_timer(0.15) do
|
912
|
+
@sender.pending_requests.empty?.should be_false
|
913
|
+
result = RightScale::Result.new(@token, nil, RightScale::OperationResult.success, nil)
|
914
|
+
@sender.handle_response(result)
|
915
|
+
end
|
916
|
+
|
917
|
+
EM.add_timer(0.3) do
|
918
|
+
EM.stop
|
919
|
+
@response.results.success?.should be_true
|
920
|
+
@sender.pending_requests.empty?.should be_true
|
921
|
+
end
|
922
|
+
end
|
923
|
+
end
|
1053
924
|
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
925
|
+
it "stops retrying and responds with non-delivery result if exceeds retry timeout" do
|
926
|
+
EM.run do
|
927
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.1, :retry_interval => 0.1)
|
928
|
+
@client.should_receive(:publish).and_return(@broker_ids).once
|
929
|
+
@log.should_receive(:warning).once
|
930
|
+
flexmock(@sender.connectivity_checker).should_receive(:check).once
|
931
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
932
|
+
|
933
|
+
EM.add_timer(0.3) do
|
934
|
+
EM.stop
|
935
|
+
@response.results.non_delivery?.should be_true
|
936
|
+
@response.results.content.should == RightScale::OperationResult::RETRY_TIMEOUT
|
937
|
+
@sender.pending_requests.empty?.should be_true
|
938
|
+
end
|
939
|
+
end
|
940
|
+
end
|
1064
941
|
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
942
|
+
it "stops retrying if temporarily offline" do
|
943
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
944
|
+
@client.should_receive(:publish).and_return(@broker_ids).once.ordered
|
945
|
+
@client.should_receive(:publish).and_raise(RightAMQP::HABrokerClient::NoConnectedBrokers).once.ordered
|
946
|
+
flexmock(EM).should_receive(:add_timer).and_yield.once
|
947
|
+
@log.should_receive(:error).with(/Failed to publish request/, RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
948
|
+
@log.should_receive(:error).with(/Failed retry.*temporarily offline/).once
|
949
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
950
|
+
@sender.pending_requests[@token].should_not be_nil
|
951
|
+
@sender.pending_requests["retry token"].should_not be_nil
|
952
|
+
end
|
1073
953
|
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
954
|
+
it "stops retrying if there is a send failure" do
|
955
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
956
|
+
@client.should_receive(:publish).and_return(@broker_ids).once.ordered
|
957
|
+
@client.should_receive(:publish).and_raise(StandardError, "failed").once.ordered
|
958
|
+
flexmock(EM).should_receive(:add_timer).and_yield.once
|
959
|
+
@log.should_receive(:error).with(/Failed to publish request/, StandardError, :trace).once
|
960
|
+
@log.should_receive(:error).with(/Failed retry.*send failure/).once
|
961
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
962
|
+
@sender.pending_requests[@token].should_not be_nil
|
963
|
+
@sender.pending_requests["retry token"].should_not be_nil
|
964
|
+
end
|
1083
965
|
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
@instance.initialize_offline_queue
|
1096
|
-
@broker.should_receive(:publish).once.and_return { EM.stop }
|
1097
|
-
@instance.start_offline_queue
|
1098
|
-
EM.add_timer(1) { EM.stop }
|
966
|
+
it "stops retrying if there is an unexpected exception" do
|
967
|
+
# This will actually call amqp_send_retry 3 times recursively because add_timer always yields immediately
|
968
|
+
@sender = create_sender(:amqp, :retry_timeout => 0.3, :retry_interval => 0.1)
|
969
|
+
@client.should_receive(:publish).and_return(@broker_ids)
|
970
|
+
@log.should_receive(:error).with(/Failed retry.*without responding/, StandardError, :trace).twice
|
971
|
+
flexmock(EM).should_receive(:add_timer).and_yield
|
972
|
+
flexmock(@sender.connectivity_checker).should_receive(:check).and_raise(StandardError).once
|
973
|
+
@sender.send(:amqp_send, :send_request, @target, @packet, @received_at, @callback).should be_true
|
974
|
+
@sender.pending_requests[@token].should_not be_nil
|
975
|
+
@sender.pending_requests["retry token"].should_not be_nil
|
976
|
+
end
|
1099
977
|
end
|
1100
|
-
ensure
|
1101
|
-
RightScale::Sender::OfflineHandler.const_set(:MAX_QUEUE_FLUSH_DELAY, old_flush_delay)
|
1102
978
|
end
|
1103
979
|
end
|
1104
980
|
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
EM.run do
|
1112
|
-
@instance.send_push('/dummy', 'payload')
|
1113
|
-
@instance.offline_handler.offline?.should be_true
|
1114
|
-
@instance.offline_handler.state.should == :created
|
1115
|
-
@instance.offline_handler.instance_variable_get(:@queue).size.should == 1
|
1116
|
-
@instance.initialize_offline_queue
|
1117
|
-
@broker.should_receive(:publish).with(Hash, on {|arg| arg.type == "/dummy2"}, Hash).once
|
1118
|
-
@instance.send_push('/dummy2', 'payload')
|
1119
|
-
@instance.offline_handler.offline?.should be_false
|
1120
|
-
@instance.offline_handler.mode.should == :initializing
|
1121
|
-
@instance.offline_handler.state.should == :initializing
|
1122
|
-
@instance.offline_handler.instance_variable_get(:@queue).size.should == 1
|
1123
|
-
@instance.offline_handler.instance_variable_get(:@queue).first[:type].should == "/dummy"
|
1124
|
-
@broker.should_receive(:publish).with(Hash, on {|arg| arg.type == "/dummy"}, Hash).once
|
1125
|
-
@instance.start_offline_queue
|
1126
|
-
EM.add_timer(1) do
|
1127
|
-
@instance.offline_handler.mode.should == :online
|
1128
|
-
@instance.offline_handler.state.should == :running
|
1129
|
-
@instance.offline_handler.instance_variable_get(:@queue).size.should == 0
|
1130
|
-
EM.stop
|
1131
|
-
end
|
1132
|
-
end
|
1133
|
-
ensure
|
1134
|
-
RightScale::Sender::OfflineHandler.const_set(:MAX_QUEUE_FLUSH_DELAY, old_flush_delay)
|
981
|
+
context :deliver_response do
|
982
|
+
before(:each) do
|
983
|
+
@received_at = Time.now
|
984
|
+
flexmock(Time).should_receive(:now).and_return(@received_at)
|
985
|
+
@response = nil
|
986
|
+
@callback = lambda { |response| @response = response }
|
1135
987
|
end
|
1136
|
-
end
|
1137
988
|
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
EM.run do
|
1145
|
-
@instance.send_push('/dummy', 'payload')
|
1146
|
-
@instance.offline_handler.offline?.should be_true
|
1147
|
-
@instance.offline_handler.state.should == :created
|
1148
|
-
@instance.offline_handler.instance_variable_get(:@queue).size.should == 1
|
1149
|
-
@instance.initialize_offline_queue
|
1150
|
-
@instance.offline_handler.offline?.should be_false
|
1151
|
-
@instance.offline_handler.mode.should == :initializing
|
1152
|
-
@instance.offline_handler.state.should == :initializing
|
1153
|
-
@instance.enable_offline_mode
|
1154
|
-
@instance.send_push('/dummy2', 'payload')
|
1155
|
-
@instance.offline_handler.offline?.should be_true
|
1156
|
-
@instance.offline_handler.mode.should == :offline
|
1157
|
-
@instance.offline_handler.state.should == :initializing
|
1158
|
-
@instance.offline_handler.instance_variable_get(:@queue).size.should == 2
|
1159
|
-
@instance.offline_handler.instance_variable_get(:@queue).first[:type].should == "/dummy2"
|
1160
|
-
@instance.start_offline_queue
|
1161
|
-
@instance.offline_handler.mode.should == :offline
|
1162
|
-
@instance.offline_handler.state.should == :running
|
1163
|
-
@broker.should_receive(:publish).with(Hash, on {|arg| arg.type == "/dummy2"}, Hash).once
|
1164
|
-
@broker.should_receive(:publish).with(Hash, on {|arg| arg.type == "/dummy"}, Hash).once
|
1165
|
-
@instance.disable_offline_mode
|
1166
|
-
@instance.offline_handler.state.should == :flushing
|
1167
|
-
EM.add_timer(1) do
|
1168
|
-
@instance.offline_handler.mode.should == :online
|
1169
|
-
@instance.offline_handler.state.should == :running
|
1170
|
-
@instance.offline_handler.instance_variable_get(:@queue).size.should == 0
|
1171
|
-
EM.stop
|
1172
|
-
end
|
1173
|
-
end
|
1174
|
-
ensure
|
1175
|
-
RightScale::Sender::OfflineHandler.const_set(:MAX_QUEUE_FLUSH_DELAY, old_flush_delay)
|
989
|
+
it "calls the response handler" do
|
990
|
+
pending_request = RightScale::PendingRequest.new(:send_request, @received_at, @callback)
|
991
|
+
@sender.pending_requests[@token] = pending_request
|
992
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
993
|
+
@sender.send(:deliver_response, response, pending_request).should be_true
|
994
|
+
@response.should == response
|
1176
995
|
end
|
1177
|
-
end
|
1178
996
|
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
@
|
1183
|
-
@
|
1184
|
-
@
|
1185
|
-
EM.next_tick { EM.stop }
|
997
|
+
it "deletes associated pending request if is it a Request" do
|
998
|
+
pending_request = RightScale::PendingRequest.new(:send_request, @received_at, @callback)
|
999
|
+
@sender.pending_requests[@token] = pending_request
|
1000
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
1001
|
+
@sender.send(:deliver_response, response, pending_request).should be_true
|
1002
|
+
@sender.pending_requests[@token].should be_nil
|
1186
1003
|
end
|
1187
|
-
@instance.offline_handler.queue.size.should == RightScale::Sender::OfflineHandler::MAX_QUEUED_REQUESTS
|
1188
|
-
@instance.offline_handler.instance_variable_get(:@restart_vote_count).should == 1
|
1189
|
-
end
|
1190
1004
|
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
RightScale::
|
1195
|
-
@
|
1196
|
-
|
1197
|
-
@instance.enable_offline_mode
|
1198
|
-
@instance.send_push('/dummy', 'payload')
|
1199
|
-
EM.add_timer(0.5) { EM.stop }
|
1200
|
-
end
|
1201
|
-
@instance.offline_handler.instance_variable_get(:@restart_vote_count).should == 1
|
1202
|
-
ensure
|
1203
|
-
RightScale::Sender::OfflineHandler.const_set(:RESTART_VOTE_DELAY, old_vote_delay)
|
1005
|
+
it "does not delete pending request if it is a Push" do
|
1006
|
+
pending_request = RightScale::PendingRequest.new(:send_push, @received_at, @callback)
|
1007
|
+
@sender.pending_requests[@token] = pending_request
|
1008
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
1009
|
+
@sender.send(:deliver_response, response, pending_request).should be_true
|
1010
|
+
@sender.pending_requests[@token].should_not be_nil
|
1204
1011
|
end
|
1205
|
-
end
|
1206
1012
|
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
RightScale::Sender::OfflineHandler.const_set(:MAX_QUEUE_FLUSH_DELAY, old_flush_delay)
|
1013
|
+
it "deletes any associated retry requests" do
|
1014
|
+
@parent_token = RightSupport::Data::UUID.generate
|
1015
|
+
pending_request = RightScale::PendingRequest.new(:send_request, @received_at, @callback)
|
1016
|
+
@sender.pending_requests[@token] = pending_request
|
1017
|
+
@sender.pending_requests[@token].retry_parent_token = @parent_token
|
1018
|
+
@sender.pending_requests[@parent_token] = @sender.pending_requests[@token].dup
|
1019
|
+
response = RightScale::Result.new(@token, "to", RightScale::OperationResult.success, @target)
|
1020
|
+
@sender.send(:deliver_response, response, pending_request).should be_true
|
1021
|
+
@sender.pending_requests[@token].should be_nil
|
1022
|
+
@sender.pending_requests[@parent_token].should be_nil
|
1218
1023
|
end
|
1219
1024
|
end
|
1220
1025
|
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
EM.run do
|
1227
|
-
@instance.enable_offline_mode
|
1228
|
-
@instance.send_push('/dummy', 'payload')
|
1229
|
-
@instance.disable_offline_mode
|
1230
|
-
EM.add_timer(1) { EM.stop }
|
1231
|
-
end
|
1232
|
-
ensure
|
1233
|
-
RightScale::Sender::OfflineHandler.const_set(:MAX_QUEUE_FLUSH_DELAY, old_flush_delay)
|
1026
|
+
context :queueing? do
|
1027
|
+
it "returns true if offline handling enabled and currently in queueing mode" do
|
1028
|
+
@sender = create_sender(:http, :offline_queueing => true)
|
1029
|
+
flexmock(@sender.offline_handler).should_receive(:queueing?).and_return(true)
|
1030
|
+
@sender.send(:queueing?).should be_true
|
1234
1031
|
end
|
1235
|
-
end
|
1236
1032
|
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
@instance.offline_handler.mode.should == :offline
|
1247
|
-
@instance.enable_offline_mode
|
1248
|
-
@instance.offline_handler.state.should == :running
|
1249
|
-
@instance.offline_handler.mode.should == :offline
|
1250
|
-
EM.add_timer(1) do
|
1251
|
-
@instance.offline_handler.state.should == :running
|
1252
|
-
@instance.offline_handler.mode.should == :offline
|
1253
|
-
EM.stop
|
1254
|
-
end
|
1255
|
-
end
|
1256
|
-
ensure
|
1257
|
-
RightScale::Sender::OfflineHandler.const_set(:MAX_QUEUE_FLUSH_DELAY, old_flush_delay)
|
1033
|
+
it "returns false if offline handling disabled" do
|
1034
|
+
flexmock(@sender.offline_handler).should_receive(:queueing?).and_return(true)
|
1035
|
+
@sender.send(:queueing?).should be_false
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
it "returns false if offline handling enabled but not in queueing mode" do
|
1039
|
+
@sender = create_sender(:http, :offline_queueing => true)
|
1040
|
+
flexmock(@sender.offline_handler).should_receive(:queueing?).and_return(false)
|
1041
|
+
@sender.send(:queueing?).should be_false
|
1258
1042
|
end
|
1259
1043
|
end
|
1260
1044
|
end
|
1261
|
-
|
1262
1045
|
end
|