right_support 2.11.3 → 2.12.1

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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -0
  3. data/VERSION +1 -1
  4. data/lib/right_support/notifiers/airbrake.rb +194 -0
  5. data/lib/right_support/notifiers/base.rb +73 -0
  6. data/lib/right_support/notifiers/blacklisters/base.rb +48 -0
  7. data/lib/right_support/notifiers/blacklisters/canonical.rb +60 -0
  8. data/lib/right_support/notifiers/blacklisters/regular_expression.rb +86 -0
  9. data/{features/support/file_utils_bundler_mixin.rb → lib/right_support/notifiers/blacklisters/simple.rb} +21 -20
  10. data/lib/right_support/notifiers/blacklisters/snake_case.rb +60 -0
  11. data/lib/right_support/notifiers/blacklisters/wildcard.rb +65 -0
  12. data/lib/right_support/notifiers/blacklisters.rb +34 -0
  13. data/lib/right_support/notifiers/logger.rb +94 -0
  14. data/lib/right_support/notifiers/notification.rb +57 -0
  15. data/lib/right_support/notifiers/utilities/backtrace_decoder.rb +234 -0
  16. data/lib/right_support/notifiers/utilities.rb +29 -0
  17. data/lib/right_support/notifiers.rb +32 -0
  18. data/lib/right_support/rack/request_logger.rb +13 -9
  19. data/lib/right_support.rb +1 -0
  20. data/right_support.gemspec +19 -70
  21. metadata +17 -69
  22. data/.coveralls.yml +0 -2
  23. data/.rspec +0 -3
  24. data/.simplecov +0 -6
  25. data/.travis.yml +0 -13
  26. data/Gemfile +0 -51
  27. data/Gemfile.lock +0 -153
  28. data/features/balancer_error_handling.feature +0 -34
  29. data/features/balancer_health_check.feature +0 -33
  30. data/features/hash_tools.feature +0 -27
  31. data/features/http_client_timeout.feature +0 -19
  32. data/features/serialization.feature +0 -113
  33. data/features/step_definitions/hash_tools_steps.rb +0 -41
  34. data/features/step_definitions/http_client_steps.rb +0 -27
  35. data/features/step_definitions/request_balancer_steps.rb +0 -93
  36. data/features/step_definitions/ruby_steps.rb +0 -176
  37. data/features/step_definitions/serialization_steps.rb +0 -133
  38. data/features/step_definitions/server_steps.rb +0 -134
  39. data/features/support/env.rb +0 -148
  40. data/right_support.rconf +0 -9
  41. data/spec/config/feature_set_spec.rb +0 -83
  42. data/spec/crypto/signed_hash_spec.rb +0 -73
  43. data/spec/data/hash_tools_spec.rb +0 -602
  44. data/spec/data/mash_spec.rb +0 -313
  45. data/spec/data/token_spec.rb +0 -21
  46. data/spec/data/uuid_spec.rb +0 -45
  47. data/spec/db/cassandra_model_part1_spec.rb +0 -84
  48. data/spec/db/cassandra_model_part2_spec.rb +0 -73
  49. data/spec/db/cassandra_model_spec.rb +0 -375
  50. data/spec/fixtures/encrypted_priv_rsa.pem +0 -30
  51. data/spec/fixtures/good_priv_dsa.pem +0 -12
  52. data/spec/fixtures/good_priv_rsa.pem +0 -15
  53. data/spec/fixtures/good_pub_dsa.ssh +0 -1
  54. data/spec/fixtures/good_pub_rsa.pem +0 -5
  55. data/spec/fixtures/good_pub_rsa.ssh +0 -1
  56. data/spec/log/exception_logger_spec.rb +0 -76
  57. data/spec/log/filter_logger_spec.rb +0 -66
  58. data/spec/log/mixin_spec.rb +0 -141
  59. data/spec/log/multiplexer_spec.rb +0 -54
  60. data/spec/log/null_logger_spec.rb +0 -36
  61. data/spec/log/step_level_logger_spec.rb +0 -49
  62. data/spec/log/system_logger_spec.rb +0 -172
  63. data/spec/net/address_helper_spec.rb +0 -57
  64. data/spec/net/dns_spec.rb +0 -187
  65. data/spec/net/http_client_spec.rb +0 -181
  66. data/spec/net/lb/health_check_spec.rb +0 -417
  67. data/spec/net/lb/round_robin_spec.rb +0 -15
  68. data/spec/net/lb/sticky_spec.rb +0 -92
  69. data/spec/net/request_balancer_spec.rb +0 -690
  70. data/spec/net/s3_helper_spec.rb +0 -160
  71. data/spec/net/ssl_spec.rb +0 -42
  72. data/spec/net/string_encoder_spec.rb +0 -58
  73. data/spec/rack/log_setter_spec.rb +0 -5
  74. data/spec/rack/request_logger_spec.rb +0 -225
  75. data/spec/rack/request_tracker_spec.rb +0 -115
  76. data/spec/rack/runtime_spec.rb +0 -49
  77. data/spec/ruby/easy_singleton_spec.rb +0 -72
  78. data/spec/ruby/object_extensions_spec.rb +0 -27
  79. data/spec/ruby/string_extensions_spec.rb +0 -98
  80. data/spec/spec_helper.rb +0 -188
  81. data/spec/stats/activity_spec.rb +0 -425
  82. data/spec/stats/exceptions_spec.rb +0 -247
  83. data/spec/stats/helpers_spec.rb +0 -685
  84. data/spec/validation/openssl_spec.rb +0 -37
  85. data/spec/validation/ssh_spec.rb +0 -39
@@ -1,690 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class TestException < Exception; end
4
- class OtherTestException < Exception; end
5
- class BigDeal < TestException; end
6
- class NoBigDeal < TestException; end
7
-
8
- class MockHttpError < Exception
9
- attr_reader :http_code
10
- def initialize(message=nil, code=400)
11
- super(message)
12
- @http_code = code
13
- end
14
- end
15
-
16
- class MockResourceNotFound < MockHttpError
17
- def initialize(message=nil)
18
- super(message, 404)
19
- end
20
- end
21
-
22
- class MockRequestTimeout < MockHttpError
23
- def initialize(message=nil)
24
- super(message, 408)
25
- end
26
- end
27
-
28
- describe RightSupport::Net::RequestBalancer do
29
- def test_raise(fatal, do_raise, expect)
30
- bases = []
31
- base = do_raise.superclass
32
- while base != Exception
33
- bases << base
34
- base = base.superclass
35
- end
36
-
37
- exception = expect.first
38
- count = expect.last
39
- rb = described_class.new([1,2,3], :fatal=>fatal)
40
- @tries = 0
41
-
42
- code = lambda do
43
- rb.request do |_|
44
- @tries += 1
45
- next unless do_raise
46
- if bases.include?(RestClient::ExceptionWithResponse)
47
- #Special case: RestClient exceptions need an HTTP response, but they
48
- #have stack recursion if we give them something other than a real
49
- #HTTP response. Blech!
50
- raise do_raise, nil
51
- else
52
- #Generic exception with message
53
- raise do_raise, 'Bah humbug; fie on thee!'
54
- end
55
- end
56
- end
57
-
58
- if exception
59
- code.should raise_error(expect[0])
60
- else
61
- code.should_not raise_error
62
- end
63
-
64
- @tries.should == count
65
- end
66
-
67
- def test_bad_endpoint_requests(number_of_endpoints)
68
- test = Proc.new do |endpoint|
69
- @health_checks += 1
70
- false
71
- end
72
-
73
- expect = number_of_endpoints
74
- yellow_states = 4
75
- rb = described_class.new(
76
- (1..expect).to_a,
77
- :policy => RightSupport::Net::LB::HealthCheck,
78
- :health_check => test,
79
- :yellow_states => yellow_states)
80
- @health_checks = 0
81
- tries = 0
82
- l = lambda do
83
- rb.request do |endpoint|
84
- tries += 1
85
- raise Exception
86
- end
87
- end
88
- yellow_states.times do
89
- l.should raise_error
90
- end
91
- tries.should == expect
92
- @health_checks.should == expect * (yellow_states - 1)
93
- end
94
-
95
- def make_endpoint(addresses)
96
- RightSupport::Net::ResolvedEndpoint.new(addresses)
97
- end
98
-
99
- context :initialize do
100
- it 'requires a list of endpoint URLs' do
101
- lambda do
102
- described_class.new(nil)
103
- end.should raise_exception(ArgumentError)
104
- end
105
-
106
- context 'with Integer :retry option' do
107
- [1, 2].each do |retry_value|
108
- context "and :retry=#{retry_value}" do
109
- it "stops after #{retry_value} total tries" do
110
- lambda do
111
- # note the legacy :retry integer value has no backoff.
112
- @tries = 0
113
- described_class.new([1, 2, 3], :retry => retry_value).request do |u|
114
- @tries += 1
115
- raise NoBigDeal
116
- end
117
- end.should raise_error
118
- @tries.should == retry_value
119
- end
120
- end
121
- end
122
- end
123
-
124
- context 'with Proc :retry option' do
125
- it 'stops when call evaluates to false' do
126
- @tries = 0
127
-
128
- proc = Proc.new do |ep, n|
129
- @tries < 1
130
- end
131
-
132
- balancer = described_class.new([1, 2, 3], :retry => proc)
133
- lambda do
134
- balancer.request do |u|
135
- @tries += 1
136
- raise NoBigDeal
137
- end
138
- end.should raise_error(RightSupport::Net::NoResult)
139
-
140
- @tries.should == 1
141
- end
142
-
143
- [1, 2].each do |max_attempts|
144
- context "with backoff_retry_callback(#{max_attempts})" do
145
- it "stops after #{max_attempts} total tries" do
146
- start_time = ::Time.now
147
- retry_proc = described_class.backoff_retry_callback(max_attempts)
148
- lambda do
149
- @tries = 0
150
- described_class.new([1, 2, 3], :retry => retry_proc).request do |u|
151
- @tries += 1
152
- raise NoBigDeal
153
- end
154
- end.should raise_error
155
- end_time = ::Time.now
156
- @tries.should == max_attempts
157
- if max_attempts == 2
158
- ((end_time - start_time) >= 2).should be_true # assert backoff
159
- end
160
- end
161
- end
162
- end
163
- end
164
-
165
- context ':fatal option' do
166
- it 'has reasonable defaults' do
167
- exceptions = described_class::DEFAULT_FATAL_EXCEPTIONS - [SignalException]
168
- balancer = described_class.new([1])
169
- exceptions.each do |klass|
170
- lambda do
171
- balancer.request { |ep| raise klass }
172
- end.should raise_error(klass)
173
- end
174
- end
175
-
176
- context 'with a Proc' do
177
- it 'validates the arity' do
178
- bad_lambda = lambda { |too, many, arguments| }
179
- lambda do
180
- described_class.new([1,2], :fatal=>bad_lambda)
181
- end.should raise_error(ArgumentError)
182
- end
183
-
184
- it 'delegates to the Proc' do
185
- always_retry = lambda { |e| false }
186
- balancer = described_class.new([1,2], :fatal=>always_retry)
187
-
188
- lambda do
189
- balancer.request do |ep|
190
- raise BigDeal
191
- end
192
- end.should raise_error(RightSupport::Net::NoResult)
193
-
194
- lambda do
195
- balancer.request do |ep|
196
- raise ArgumentError
197
- end
198
- end.should raise_error(RightSupport::Net::NoResult)
199
- end
200
- end
201
-
202
- context 'with an Exception' do
203
- it 'considers that class of Exception to be fatal' do
204
- balancer = described_class.new([1], :fatal=>BigDeal)
205
- lambda do
206
- balancer.request { |ep| raise BigDeal }
207
- end.should raise_error(BigDeal)
208
- end
209
- end
210
-
211
- context 'with an Array' do
212
- it 'considers any class in the array to be fatal' do
213
- exceptions = [ArgumentError, BigDeal]
214
- balancer = described_class.new([1], :fatal=>exceptions)
215
- exceptions.each do |klass|
216
- lambda do
217
- balancer.request { |ep| raise klass }
218
- end.should raise_error(klass)
219
- end
220
- end
221
- end
222
- end
223
-
224
- context 'with :on_exception option' do
225
- it 'validates the arity' do
226
- bad_lambda = lambda { |way, too, many, arguments| }
227
- lambda do
228
- described_class.new([1,2], :on_exception=>bad_lambda)
229
- end.should raise_error(ArgumentError)
230
- end
231
- end
232
-
233
- context 'with :policy option' do
234
- it 'accepts a Class' do
235
- policy = RightSupport::Net::LB::RoundRobin
236
- lambda {
237
- described_class.new([1,2], :policy=>policy)
238
- }.should_not raise_error
239
- end
240
-
241
- it 'accepts an object' do
242
- policy = RightSupport::Net::LB::RoundRobin.new([1,2])
243
- lambda {
244
- described_class.new([1,2], :policy=>policy)
245
- }.should_not raise_error
246
- end
247
-
248
- it 'checks for duck-type compatibility' do
249
- lambda {
250
- described_class.new([1,2], :policy=>String)
251
- }.should raise_error
252
- lambda {
253
- described_class.new([1,2], :policy=>'I like cheese')
254
- }.should raise_error
255
- end
256
- end
257
-
258
- context 'with :health_check option' do
259
- before(:each) do
260
- @health_check = Proc.new {|endpoint| "HealthCheck passed for #{endpoint}!" }
261
- end
262
-
263
- it 'accepts a block' do
264
- lambda {
265
- described_class.new([1,2], :health_check => @health_check)
266
- }.should_not raise_error
267
- end
268
-
269
- it 'calls specified block' do
270
- @balancer = described_class.new([1,2], :health_check => @health_check)
271
- @options = @balancer.instance_variable_get("@options")
272
- @options[:health_check].call(1).should be_eql("HealthCheck passed for 1!")
273
- end
274
-
275
- end
276
-
277
- context 'with default :health_check option' do
278
- it 'calls default block' do
279
- @balancer = described_class.new([1,2])
280
- @options = @balancer.instance_variable_get("@options")
281
- @options[:health_check].call(1).should be_true
282
- end
283
- end
284
-
285
- context 'with :on_health_change option' do
286
- before(:each) do
287
- @health_updates = []
288
- @on_health_change = Proc.new {|health| @health_updates << health }
289
- end
290
-
291
- it 'accepts a block' do
292
- lambda {
293
- described_class.new([1,2], :on_health_change => @on_health_change)
294
- }.should_not raise_error
295
- end
296
- end
297
-
298
- context 'with :resolve option' do
299
- before(:each) do
300
- flexmock(RightSupport::Net::DNS).should_receive(:resolve_with_hostnames).
301
- with(['host1', 'host2']).and_return({'host1' => make_endpoint(['1.1.1.1', '2.2.2.2']), 'host2' => make_endpoint(['3.3.3.3'])})
302
- @balancer = described_class.new(['host1', 'host2'], :resolve => 15)
303
- end
304
-
305
- it 'performs an initial resolution' do
306
- @balancer.instance_variable_get("@resolved_at").to_f.should be_within(1.0).of(Time.now.to_f)
307
- @balancer.instance_variable_get("@ips").should include('1.1.1.1')
308
- @balancer.instance_variable_get("@ips").should include('2.2.2.2')
309
- @balancer.instance_variable_get("@ips").should include('3.3.3.3')
310
- end
311
-
312
- it 'reports on the resolved endpoints' do
313
- @balancer.resolved_endpoints.sort.should == ['1.1.1.1', '2.2.2.2', '3.3.3.3']
314
- end
315
- end
316
- end
317
-
318
- context :request do
319
- it 'requires a block' do
320
- lambda do
321
- described_class.new([1]).request
322
- end.should raise_exception(ArgumentError)
323
- end
324
-
325
- context 'with a single thread' do
326
- let(:list) { [1,2,3,4,5,6,7,8,9,10] }
327
-
328
- subject { described_class.new(list) }
329
-
330
- it 'retries until a request completes' do
331
- queue = Queue.new
332
- 10.times do
333
- x = subject.request do |l|
334
- raise NoBigDeal, "Fall down go boom!" unless l == 5
335
- l
336
- end
337
- x.should == 5
338
- end
339
- end
340
- end
341
-
342
- context 'with multiple threads and thread-safety enabled' do
343
- let(:list) { [1,2,3,4,5,6,7,8,9,10] }
344
-
345
- def boom(ep)
346
- raise NoBigDeal, "Fall down go boom!" unless ep == 5
347
- true
348
- end
349
-
350
- subject do
351
- described_class.new(
352
- list,
353
- thread_safe: true,
354
- policy: ::RightSupport::Net::LB::HealthCheck,
355
- health_check: method(:boom))
356
- end
357
-
358
- it 'retries until a request completes' do
359
- count = list.size * 3
360
- expected_responses = nil
361
- expected_stats = list.inject({}) do |h, ep|
362
- begin
363
- boom(ep)
364
- h[ep] = 'green'
365
- expected_responses = [ep] * count
366
- rescue NoBigDeal
367
- h[ep] = 'red'
368
- end
369
- h
370
- end
371
-
372
- queue = Queue.new
373
- count.times do
374
- ::Thread.new do
375
- stop_time = ::Time.now + 10
376
- loop do
377
- begin
378
- x = subject.request do |ep|
379
- boom(ep)
380
- ep
381
- end
382
- queue.push(x)
383
- break
384
- rescue ::RightSupport::Net::NoResult => e
385
- # this can happen with too much thread contention for failing
386
- # endpoints. the issue is that other threads keep snaking the
387
- # only good endpoint away from this thread (i.e. keep
388
- # incrementing the counter in health-check policy) until the
389
- # allowed number of retries is exhausted. only when the other
390
- # threads die can we be sure of getting the only good endpoint.
391
- #
392
- # keep trying for the purpose of this test but do not allow
393
- # infinite retries.
394
- if ::Time.now >= stop_time
395
- queue.push(e)
396
- break
397
- end
398
- rescue ::Exception => e
399
- queue.push(e)
400
- break
401
- end
402
- end
403
- end
404
- end
405
- actual_responses = []
406
- count.times do
407
- actual_responses << queue.pop
408
- end
409
- actual_responses.should == expected_responses
410
- subject.get_stats.should == expected_stats
411
- end
412
- end
413
-
414
- it 'raises if no request completes' do
415
- lambda do
416
- described_class.request([1,2,3]) do |l|
417
- raise NoBigDeal, "Fall down go boom!"
418
- end
419
- end.should raise_exception(RightSupport::Net::NoResult, /NoBigDeal/)
420
- end
421
-
422
- context 'without :fatal option' do
423
- it 're-raises reasonable default fatal errors' do
424
- test_raise(nil, ArgumentError, [ArgumentError, 1])
425
- test_raise(nil, MockResourceNotFound, [MockResourceNotFound, 1])
426
- end
427
-
428
- it 'swallows StandardError and friends' do
429
- [SystemCallError, SocketError].each do |klass|
430
- test_raise(nil, klass, [RightSupport::Net::NoResult, 3])
431
- end
432
- end
433
- end
434
-
435
- context 'with :fatal option' do
436
- it 're-raises fatal errors' do
437
- test_raise(BigDeal, BigDeal, [BigDeal, 1])
438
- test_raise([BigDeal, NoBigDeal], NoBigDeal, [NoBigDeal, 1])
439
- test_raise(true, NoBigDeal, [NoBigDeal, 1])
440
- test_raise(lambda {|e| e.is_a? BigDeal }, BigDeal, [BigDeal, 1])
441
- end
442
-
443
- it 'swallows nonfatal errors' do
444
- test_raise(nil, BigDeal, [RightSupport::Net::NoResult, 3])
445
- test_raise(BigDeal, NoBigDeal, [RightSupport::Net::NoResult, 3])
446
- test_raise([BigDeal], NoBigDeal, [RightSupport::Net::NoResult, 3])
447
- test_raise(false, NoBigDeal, [RightSupport::Net::NoResult, 3])
448
- test_raise(lambda {|e| e.is_a? BigDeal }, NoBigDeal, [RightSupport::Net::NoResult, 3])
449
- end
450
- end
451
-
452
- context 'with default :fatal option' do
453
- it 'retries most Ruby builtin errors' do
454
- list = [1,2,3,4,5,6,7,8,9,10]
455
- rb = described_class.new(list)
456
-
457
- [IOError, SystemCallError, SocketError].each do |klass|
458
- test_raise(nil, klass, [RightSupport::Net::NoResult, 3])
459
- end
460
- end
461
-
462
- it 'does not retry program errors' do
463
- list = [1,2,3,4,5,6,7,8,9,10]
464
- rb = described_class.new(list)
465
-
466
- [ArgumentError, LoadError, NameError].each do |klass|
467
- test_raise(nil, klass, [klass, 1])
468
- end
469
- end
470
-
471
- it 'retries HTTP timeouts' do
472
- test_raise(nil, MockRequestTimeout, [RightSupport::Net::NoResult, 3])
473
- test_raise(nil, RestClient::RequestTimeout, [RightSupport::Net::NoResult, 3])
474
- end
475
-
476
- it 'does not retry HTTP 4xx other than timeout' do
477
- list = [1,2,3,4,5,6,7,8,9,10]
478
- rb = described_class.new(list)
479
-
480
- codes = [401, 402, 403, 404, 405, 406, 407, 409]
481
- codes.each do |code|
482
- lambda do
483
- rb.request { |l| raise MockHttpError.new(nil, code) }
484
- end.should raise_error(MockHttpError)
485
- end
486
- end
487
-
488
- context 'with default :retry option' do
489
- it 'marks endpoints as bad if they encounter retryable errors' do
490
- rb = described_class.new([1,2,3], :policy => RightSupport::Net::LB::HealthCheck, :health_check => Proc.new {|endpoint| false})
491
- expect = rb.get_stats
492
- codes = [401, 402, 403, 404, 405, 406, 407, 408, 409]
493
- codes.each do |code|
494
- lambda do
495
- rb.request { |l| raise MockHttpError.new(nil, code) }
496
- end.should raise_error
497
- end
498
-
499
- rb.get_stats.should_not == expect
500
- end
501
-
502
- it 'does not mark endpoints as bad if they raise fatal errors' do
503
- rb = described_class.new([1,2,3], :policy => RightSupport::Net::LB::HealthCheck, :health_check => Proc.new {|endpoint| false})
504
- codes = [401, 402, 403, 404, 405, 406, 407, 409]
505
- codes.each do |code|
506
- lambda do
507
- rb.request { |l| raise MockHttpError.new(nil, code) }
508
- end.should raise_error
509
- end
510
-
511
- # The EPs started in yellow-1, then passed an initial health check which
512
- # changed them to green, then executed a failing request, which should
513
- # not count against them. They should still be green.
514
- rb.get_stats.should == {1=>'green', 2=>'green', 3=>'green'}
515
- end
516
- end
517
- end
518
-
519
- context 'with :on_exception option' do
520
- before(:each) do
521
- @list = [1,2,3,4,5,6,7,8,9,10]
522
- @callback = flexmock('Callback proc')
523
- @callback.should_receive(:respond_to?).with(:call).and_return(true)
524
- @callback.should_receive(:respond_to?).with(:arity).and_return(true)
525
- @callback.should_receive(:arity).and_return(3)
526
- @rb = described_class.new(@list, :fatal=>BigDeal, :on_exception=>@callback)
527
- end
528
-
529
- it 'calls me back with fatal exceptions' do
530
- @callback.should_receive(:call).with(true, BigDeal, Integer)
531
- lambda {
532
- @rb.request { raise BigDeal }
533
- }.should raise_error(BigDeal)
534
- end
535
-
536
- it 'calls me back with nonfatal exceptions' do
537
- @callback.should_receive(:call).with(false, NoBigDeal, Integer)
538
- lambda {
539
- @rb.request { raise NoBigDeal }
540
- }.should raise_error(RightSupport::Net::NoResult)
541
-
542
- end
543
- end
544
-
545
- context 'given a class-level logger' do
546
- before(:all) do
547
- @logger = Logger.new(StringIO.new)
548
- described_class.logger = @logger
549
- RightSupport::Net::LB::HealthCheck.logger = @logger
550
- end
551
-
552
- after(:all) do
553
- described_class.logger = nil
554
- end
555
-
556
- context 'when a retryable exception is raised' do
557
- it 'logs an error' do
558
- flexmock(@logger).should_receive(:error).times(4)
559
-
560
- lambda {
561
- balancer = described_class.new([1,2,3])
562
- balancer.request do |ep|
563
- raise NoBigDeal, "Too many cows on the moon"
564
- end
565
- }.should raise_error(RightSupport::Net::NoResult)
566
- end
567
- end
568
-
569
- context 'when the health of an endpoint changes' do
570
- it 'logs the change' do
571
- health_check = Proc.new do |endpoint|
572
- false
573
- end
574
- flexmock(@logger).should_receive(:info).times(8)
575
-
576
- lambda {
577
- balancer = described_class.new([1,2,3,4], :policy => RightSupport::Net::LB::HealthCheck, :health_check => health_check)
578
- balancer.request do |ep|
579
- raise "Bad Endpoint"
580
- end
581
- }.should raise_error(RightSupport::Net::NoResult)
582
- end
583
- end
584
- end
585
-
586
- context 'given a class health check policy' do
587
- it 'retries and health checks the correct number of times' do
588
- (1..10).to_a.each {|endpoint| test_bad_endpoint_requests(endpoint) }
589
- end
590
- end
591
-
592
- context 'with :resolve option' do
593
- before(:each) do
594
- @endpoints = ['host1', 'host2', 'host3', 'host4']
595
- @resolved_set_1 = {'host1' => make_endpoint(['1.1.1.1', '2.2.2.2', '3.3.3.3', '4.4.4.4'])}
596
- @resolved_set_2 = {'host1'=> make_endpoint(['5.5.5.5']),'host2'=>make_endpoint(['6.6.6.6']),'host3'=>make_endpoint(['7.7.7.7']),'host4'=>make_endpoint(['8.8.8.8'])}
597
- @resolved_set_2_array = []
598
- @resolved_set_2.each_value{ |v| @resolved_set_2_array.concat(v.addrs) }
599
- @dns = flexmock(RightSupport::Net::DNS)
600
- end
601
-
602
- it 'resolves ip addresses for specified list of endpoints' do
603
- @dns.should_receive(:resolve_with_hostnames).with(@endpoints).and_return(@resolved_set_1)
604
- @rb = described_class.new(@endpoints, :resolve => 15)
605
-
606
- @rb.request { true }
607
- @policy = @rb.instance_variable_get("@policy")
608
- @resolved_set_1['host1'].addrs.include?(@policy.next.first).should be_true
609
- end
610
-
611
- it 're-resolves list of ip addresses if TTL is expired' do
612
- @dns.should_receive(:resolve_with_hostnames).with(@endpoints).twice.and_return(@resolved_set_1, @resolved_set_2)
613
- @rb = described_class.new(@endpoints, :resolve => 15)
614
-
615
- @rb.request { true }
616
- @policy = @rb.instance_variable_get("@policy")
617
- @resolved_set_1['host1'].addrs.include?(@policy.next.first).should be_true
618
-
619
- @rb.instance_variable_set("@resolved_at", Time.now.to_i - 16)
620
- @rb.request { true }
621
- @policy = @rb.instance_variable_get("@policy")
622
- @resolved_set_2_array.include?(@policy.next.first).should be_true
623
- end
624
- end
625
-
626
- context 'when a request raises NoResult' do
627
- before(:each) do
628
- @endpoints = [1,2,3,4,5,6]
629
- @exceptions = [BigDeal, NoBigDeal, OtherTestException]
630
- @rb = described_class.new(@endpoints)
631
- @tries = 0
632
- begin
633
- @rb.request do |ep|
634
- @tries += 1
635
- raise @exceptions[@tries % @exceptions.size]
636
- end
637
- rescue Exception => e
638
- @raised = e
639
- end
640
-
641
- @raised.should be_kind_of RightSupport::Net::NoResult
642
- end
643
-
644
- it 'provides detailed information about the exceptions' do
645
- # We should have details about all six endpoints
646
- @raised.details.should_not be_nil
647
- @raised.details.keys.size.should == 6
648
-
649
- # Three exception types across six endpoints, means we should
650
- # see each exception type appear twice
651
- seen = {}
652
- @raised.details.each_pair do |_, exceptions|
653
- exceptions.each do |exception|
654
- seen[exception.class] ||= []
655
- seen[exception.class] << exception
656
- end
657
- end
658
- seen.keys.size.should == 3
659
- seen.values.all? { |v| v.size == 2}.should be_true
660
- end
661
- end
662
- end
663
-
664
- context :get_stats do
665
- context 'using default balancing profile' do
666
- it 'returns stats in an endpoint-keyed hash' do
667
- expected_hash = {}
668
- list = [1,2,3,4]
669
- list.each { |k| expected_hash[k] = 'n/a' }
670
- rb = described_class.new(list)
671
-
672
- rb.get_stats.should_not be_nil
673
- rb.get_stats.should == expected_hash
674
- end
675
- end
676
-
677
- context 'using health check balancing profile' do
678
- it 'returns stats in an endpoint-keyed hash' do
679
- expected_hash = {}
680
- list = [1,2,3,4]
681
- rb = described_class.new(
682
- list,
683
- :policy => RightSupport::Net::LB::HealthCheck,
684
- :health_check => Proc.new {|endpoint| "HealthCheck passed for #{endpoint}!"})
685
- rb.get_stats.should_not be_nil
686
- rb.get_stats.should_not == expected_hash
687
- end
688
- end
689
- end
690
- end