right_support 2.6.17 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/.rspec +4 -0
  2. data/CHANGELOG.rdoc +37 -0
  3. data/Gemfile +29 -0
  4. data/Gemfile.lock +111 -0
  5. data/README.rdoc +2 -0
  6. data/Rakefile +62 -0
  7. data/VERSION +1 -0
  8. data/features/balancer_error_handling.feature +34 -0
  9. data/features/balancer_health_check.feature +33 -0
  10. data/features/continuous_integration.feature +51 -0
  11. data/features/continuous_integration_cucumber.feature +28 -0
  12. data/features/continuous_integration_rspec1.feature +28 -0
  13. data/features/continuous_integration_rspec2.feature +28 -0
  14. data/features/http_client_timeout.feature +19 -0
  15. data/features/serialization.feature +95 -0
  16. data/features/step_definitions/http_client_steps.rb +27 -0
  17. data/features/step_definitions/request_balancer_steps.rb +93 -0
  18. data/features/step_definitions/ruby_steps.rb +176 -0
  19. data/features/step_definitions/serialization_steps.rb +96 -0
  20. data/features/step_definitions/server_steps.rb +134 -0
  21. data/features/support/env.rb +138 -0
  22. data/features/support/file_utils_bundler_mixin.rb +45 -0
  23. data/lib/right_support/ci/java_cucumber_formatter.rb +22 -8
  24. data/lib/right_support/ci/java_spec_formatter.rb +26 -8
  25. data/lib/right_support/ci/rake_task.rb +3 -0
  26. data/lib/right_support/ci.rb +24 -0
  27. data/lib/right_support/crypto/signed_hash.rb +22 -0
  28. data/lib/right_support/data/serializer.rb +24 -2
  29. data/lib/right_support/net/address_helper.rb +20 -8
  30. data/lib/right_support/net/dns.rb +20 -8
  31. data/lib/right_support/net/http_client.rb +22 -0
  32. data/lib/right_support/net/request_balancer.rb +27 -21
  33. data/lib/right_support/net/s3_helper.rb +20 -8
  34. data/lib/right_support/net/ssl/open_ssl_patch.rb +22 -0
  35. data/lib/right_support/net/ssl.rb +20 -8
  36. data/lib/right_support/ruby/easy_singleton.rb +22 -0
  37. data/lib/right_support/ruby/object_extensions.rb +22 -0
  38. data/lib/right_support/ruby/string_extensions.rb +1 -1
  39. data/lib/right_support.rb +13 -10
  40. data/right_support.gemspec +180 -18
  41. data/right_support.rconf +8 -0
  42. data/spec/config/feature_set_spec.rb +83 -0
  43. data/spec/crypto/signed_hash_spec.rb +60 -0
  44. data/spec/data/hash_tools_spec.rb +471 -0
  45. data/spec/data/uuid_spec.rb +45 -0
  46. data/spec/db/cassandra_model_part1_spec.rb +84 -0
  47. data/spec/db/cassandra_model_part2_spec.rb +73 -0
  48. data/spec/db/cassandra_model_spec.rb +359 -0
  49. data/spec/fixtures/encrypted_priv_rsa.pem +30 -0
  50. data/spec/fixtures/good_priv_dsa.pem +12 -0
  51. data/spec/fixtures/good_priv_rsa.pem +15 -0
  52. data/spec/fixtures/good_pub_dsa.ssh +1 -0
  53. data/spec/fixtures/good_pub_rsa.pem +5 -0
  54. data/spec/fixtures/good_pub_rsa.ssh +1 -0
  55. data/spec/log/exception_logger_spec.rb +76 -0
  56. data/spec/log/filter_logger_spec.rb +8 -0
  57. data/spec/log/mixin_spec.rb +62 -0
  58. data/spec/log/multiplexer_spec.rb +54 -0
  59. data/spec/log/null_logger_spec.rb +36 -0
  60. data/spec/log/system_logger_spec.rb +92 -0
  61. data/spec/net/address_helper_spec.rb +57 -0
  62. data/spec/net/balancing/health_check_spec.rb +382 -0
  63. data/spec/net/balancing/round_robin_spec.rb +15 -0
  64. data/spec/net/balancing/sticky_policy_spec.rb +92 -0
  65. data/spec/net/dns_spec.rb +152 -0
  66. data/spec/net/http_client_spec.rb +171 -0
  67. data/spec/net/request_balancer_spec.rb +579 -0
  68. data/spec/net/s3_helper_spec.rb +160 -0
  69. data/spec/net/ssl_spec.rb +42 -0
  70. data/spec/net/string_encoder_spec.rb +58 -0
  71. data/spec/rack/log_setter_spec.rb +5 -0
  72. data/spec/rack/request_logger_spec.rb +68 -0
  73. data/spec/rack/request_tracker_spec.rb +5 -0
  74. data/spec/ruby/easy_singleton_spec.rb +72 -0
  75. data/spec/ruby/object_extensions_spec.rb +27 -0
  76. data/spec/ruby/string_extensions_spec.rb +98 -0
  77. data/spec/spec_helper.rb +181 -0
  78. data/spec/stats/activity_spec.rb +193 -0
  79. data/spec/stats/exceptions_spec.rb +123 -0
  80. data/spec/stats/helpers_spec.rb +603 -0
  81. data/spec/validation/openssl_spec.rb +37 -0
  82. data/spec/validation/ssh_spec.rb +39 -0
  83. metadata +218 -19
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ # NOTE: this spec does not get defined or executed unless SystemLogger
4
+ # is defined, which only happens if Ruby standard-library 'syslog' module is
5
+ # available at runtime. Thus, the spec does not run on Windows.
6
+ #
7
+ # The selective execution is accomplished with a trailing 'if' for this
8
+ # outermost describe block. Crude but effective. You have been warned!
9
+ describe 'RightSupport::Log::SystemLogger' do
10
+ subject { RightSupport::Log::SystemLogger }
11
+
12
+ # Duplicate constants for easier reading
13
+ SEVERITY_MAP = RightSupport::Log::SystemLogger::SEVERITY_MAP
14
+ FACILITY_MAP = RightSupport::Log::SystemLogger::FACILITY_MAP
15
+
16
+ before(:each) do
17
+ @mock_syslog = flexmock(Syslog)
18
+ #indicates we sent a bogus severity to syslog!
19
+ @mock_syslog.should_receive(SEVERITY_MAP[Logger::UNKNOWN]).never
20
+ flexmock(Syslog).should_receive(:open).and_return(@mock_syslog).by_default
21
+ end
22
+
23
+ after(:each) do
24
+ subject.instance_eval { class_variable_set(:@@syslog, nil) }
25
+ end
26
+
27
+ context :initialize do
28
+ context 'with :facility option' do
29
+ FACILITY_MAP.each_pair do |name, const|
30
+ it "should handle #{name}" do
31
+ @mock_syslog.should_receive(:open).with('unit tests', nil, const)
32
+ subject.new('unit tests', :facility=>name)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ context :add do
39
+ context 'severity levels' do
40
+ levels = { :debug=>:debug,
41
+ :info=>:info, :warn=>:notice,
42
+ :error=>:warning, :fatal=>:err }
43
+
44
+ levels.each_pair do |logger_method, syslog_method|
45
+ it "translates Logger##{logger_method} to Syslog##{syslog_method}" do
46
+ @logger = subject.new('spec')
47
+ @mock_syslog.should_receive(syslog_method).with('moo bah oink')
48
+ @logger.__send__(logger_method, 'moo bah oink')
49
+ end
50
+ end
51
+ end
52
+
53
+ it 'escapes % characters to avoid confusing printf()' do
54
+ @logger = subject.new('spec')
55
+ flexmock(@logger).should_receive(:emit_syslog).with(Integer, 'All systems 100%% -- %%licious!')
56
+
57
+ @logger.info('All systems 100% -- %licious!')
58
+ end
59
+
60
+ context 'given :split option' do
61
+ it 'when true, splits multi-line messages' do
62
+ @logger = subject.new('spec', :split=>true)
63
+ flexmock(@logger).should_receive(:emit_syslog).times(5)
64
+
65
+ @logger.info("This is a\nmulti line\r\nlog message\n\rwith all kinds\n\n\rof stuff")
66
+ end
67
+
68
+ it 'when false, passes through multi-line messages' do
69
+ @logger = subject.new('spec', :split=>false)
70
+ flexmock(@logger).should_receive(:emit_syslog).times(1)
71
+
72
+ @logger.info("This is a\nmulti line\r\nlog message\n\rwith all kinds\n\n\rof stuff")
73
+ end
74
+ end
75
+
76
+ context 'given :color option' do
77
+ it 'when true, passes through ANSI color codes' do
78
+ @logger = subject.new('spec', :color=>true)
79
+ flexmock(@logger).should_receive(:emit_syslog).with(Integer, /[\e]/)
80
+
81
+ @logger.info("This has \e[16;32mcolor\e[7;0m inside it!")
82
+ end
83
+
84
+ it 'when false, strips out ANSI color codes' do
85
+ @logger = subject.new('spec', :color=>false)
86
+ flexmock(@logger).should_receive(:emit_syslog).with(Integer, /[^\e]/)
87
+
88
+ @logger.info("This has \e[16;32mcolor\e[7;0m inside it!")
89
+ end
90
+ end
91
+ end
92
+ end if defined?(RightSupport::Log::SystemLogger)
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe RightSupport::Net::AddressHelper do
4
+ class AddressHelperTarget
5
+ include RightSupport::Net::AddressHelper
6
+ end
7
+
8
+ before(:each) do
9
+ @helper = AddressHelperTarget.new
10
+ end
11
+
12
+ describe :my_ipv4_addresses do
13
+
14
+ end
15
+
16
+ describe :my_ipv4_address do
17
+ it 'consistently chooses the lowest-numbered address' do
18
+ p = ['10.0.1.17', '192.168.7.5', '192.168.4.2', '127.0.0.1']
19
+ flexmock(@helper).should_receive(:local_routable_address).with(Socket::AF_INET).and_return(p[0])
20
+ flexmock(@helper).should_receive(:local_hostname_addresses).with(Socket::AF_INET).once.ordered.and_return([ p[3], p[2] ])
21
+ flexmock(@helper).should_receive(:local_hostname_addresses).with(Socket::AF_INET).once.ordered.and_return([ p[1], p[2] ])
22
+
23
+ one = @helper.my_ipv4_address(:private)
24
+ two = @helper.my_ipv4_address(:private)
25
+ one.should == p[0]
26
+ two.should == p[0]
27
+ end
28
+ end
29
+
30
+ describe :my_ipv4_addresses do
31
+ before(:each) do
32
+ routable_addr = '67.2.204.5'
33
+ hostname_addrs = ['127.0.0.1', '10.0.0.15', '67.2.204.5']
34
+ flexmock(@helper).should_receive(:local_routable_address).with(Socket::AF_INET).and_return(routable_addr)
35
+ flexmock(@helper).should_receive(:local_hostname_addresses).with(Socket::AF_INET).and_return(hostname_addrs)
36
+ flexmock(@helper).should_receive(:local_hostname_addresses).with(Socket::AF_INET).and_return(hostname_addrs)
37
+ end
38
+
39
+ context 'with :loopback option' do
40
+ it 'returns only loopback addresses' do
41
+ @helper.my_ipv4_addresses(:loopback).should == ['127.0.0.1']
42
+ end
43
+ end
44
+
45
+ context 'with :private option' do
46
+ it 'returns only private addresses' do
47
+ @helper.my_ipv4_addresses(:private).should == ['10.0.0.15']
48
+ end
49
+ end
50
+
51
+ context 'with :public option' do
52
+ it 'returns only public addresses' do
53
+ @helper.my_ipv4_addresses(:public).should == ['67.2.204.5']
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,382 @@
1
+ require 'spec_helper'
2
+
3
+ describe RightSupport::Net::LB::HealthCheck do
4
+ context :initialize do
5
+
6
+ end
7
+
8
+ before(:each) do
9
+ @endpoints = [1,2,3,4,5]
10
+ @yellow_states = 4
11
+ @reset_time = 60
12
+ @policy = RightSupport::Net::LB::HealthCheck.new(
13
+ :yellow_states => @yellow_states,
14
+ :reset_time => @reset_time)
15
+ @policy.set_endpoints(@endpoints)
16
+ @trials = 2500
17
+ end
18
+
19
+ context :initialize do
20
+ it 'starts endpoints in yellow-1 state' do
21
+ stats = @policy.get_stats
22
+ @endpoints.each { |ep| stats[ep].should == 'yellow-1' }
23
+ end
24
+ end
25
+
26
+ context :good do
27
+ context 'given a red server' do
28
+ before(:each) do
29
+ @red = @endpoints.first
30
+ @yellow_states.times { @policy.bad(@red, 0, Time.now) }
31
+ @policy.should have_red_endpoint(@red)
32
+ end
33
+
34
+ it "changes to yellow-N" do
35
+ @policy.good(@red, 0, Time.now)
36
+ @policy.should have_yellow_endpoint(@red, @yellow_states-1)
37
+ end
38
+ end
39
+
40
+ context 'given a yellow-N server' do
41
+ before(:each) do
42
+ @yellow = @endpoints.first
43
+ (@yellow_states-1).times { @policy.bad(@yellow, 0, Time.now) }
44
+ @policy.should have_yellow_endpoint(@yellow, @yellow_states)
45
+ end
46
+
47
+ it 'decreases the yellow level to N-1' do
48
+ @policy.good(@yellow, 0, Time.now)
49
+ @policy.should have_yellow_endpoint(@yellow, @yellow_states-1)
50
+ end
51
+
52
+ context 'when N == 1' do
53
+ before(:each) do
54
+ @yellow = @endpoints[1] #this should be yellow-1 since we haven't tampered with it yet
55
+ @policy.get_stats[@yellow].should == 'yellow-1'
56
+ end
57
+
58
+ it 'changes to green' do
59
+ @policy.should have_yellow_endpoint(@yellow, 1)
60
+ @policy.good(@yellow, 0, Time.now)
61
+ @policy.should have_green_endpoint(@yellow)
62
+ end
63
+ end
64
+ end
65
+
66
+ context 'when on_health_change callback is enabled' do
67
+ before(:each) do
68
+ @yellow_states = 3
69
+ @health_updates = []
70
+ @policy = RightSupport::Net::LB::HealthCheck.new({
71
+ :yellow_states => @yellow_states, :reset_time => @reset_time,
72
+ :on_health_change => lambda { |health| @health_updates << health }})
73
+ @policy.set_endpoints(@endpoints)
74
+
75
+ # put everyone into green state, then forget all health updates. this helps us write an
76
+ # easier test.
77
+ @endpoints.each { |ep| @policy.good(ep, 0, Time.now) }
78
+ @health_updates = []
79
+ end
80
+
81
+ it "notifies of overall improving health only at transition points" do
82
+ endpoints = @endpoints.shuffle
83
+ endpoints.shuffle.each { |ep| @policy.bad(ep, 0, Time.now) }
84
+ endpoints.shuffle.each { |ep| @policy.bad(ep, 0, Time.now) }
85
+ endpoints.shuffle.each { |ep| @policy.bad(ep, 0, Time.now) }
86
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red']
87
+ @policy.good(endpoints[0], 0, Time.now)
88
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red', 'yellow-2']
89
+ endpoints[1..-1].each { |ep| @policy.good(ep, 0, Time.now) }
90
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red', 'yellow-2']
91
+ @policy.good(endpoints[0], 0, Time.now)
92
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red', 'yellow-2', 'yellow-1']
93
+ @policy.bad(endpoints[0], 0, Time.now)
94
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red', 'yellow-2', 'yellow-1', 'yellow-2']
95
+ @policy.good(endpoints[0], 0, Time.now)
96
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red', 'yellow-2', 'yellow-1', 'yellow-2', 'yellow-1']
97
+ 2.times { @policy.good(endpoints[1], 0, Time.now) }
98
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red', 'yellow-2', 'yellow-1', 'yellow-2', 'yellow-1', 'green']
99
+ endpoints.each { |ep| @policy.good(ep, 0, Time.now) }
100
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red', 'yellow-2', 'yellow-1', 'yellow-2', 'yellow-1', 'green']
101
+ endpoints.each { |ep| @policy.good(ep, 0, Time.now) }
102
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red', 'yellow-2', 'yellow-1', 'yellow-2', 'yellow-1', 'green']
103
+ end
104
+ end
105
+ end
106
+
107
+ context :bad do
108
+ context 'given a green server' do
109
+ before(:each) do
110
+ @green = @endpoints.first
111
+ @policy.good(@green, 0, Time.now)
112
+ @policy.should have_green_endpoint(@green)
113
+ end
114
+
115
+ it 'changes to yellow-1' do
116
+ @policy.bad(@green, 0, Time.now)
117
+ @policy.should have_yellow_endpoint(@green, 1)
118
+ end
119
+ end
120
+
121
+ context 'given a yellow-N server' do
122
+ before(:each) do
123
+ @yellow = @endpoints.first
124
+ end
125
+
126
+ it 'increases the yellow level to N+1' do
127
+ @policy.bad(@yellow, 0, Time.now)
128
+ @policy.should have_yellow_endpoint(@yellow, 2)
129
+ end
130
+
131
+ context 'when N == yellow_states-1' do
132
+ before(:each) do
133
+ n = @yellow_states - 2
134
+ n.times { @policy.bad(@yellow, 0, Time.now) }
135
+ @policy.should have_yellow_endpoint(@yellow, n+1)
136
+ end
137
+
138
+ it 'changes to red' do
139
+ @policy.bad(@yellow, 0, Time.now)
140
+ @policy.should have_red_endpoint(@yellow)
141
+ end
142
+ end
143
+ end
144
+
145
+ context 'given a red server' do
146
+ it 'does nothing' do
147
+ @red = @endpoints.first
148
+ @yellow_states.times { @policy.bad(@red, 0, Time.now) }
149
+ @policy.should have_red_endpoint(@red)
150
+
151
+ @policy.bad(@red, 0, Time.now)
152
+ @policy.should have_red_endpoint(@red)
153
+ end
154
+ end
155
+
156
+ context 'when on_health_change callback is enabled' do
157
+ before(:each) do
158
+ @yellow_states = 3
159
+ @health_updates = []
160
+ @policy = RightSupport::Net::LB::HealthCheck.new({
161
+ :yellow_states => @yellow_states, :reset_time => @reset_time,
162
+ :on_health_change => lambda { |health| @health_updates << health }})
163
+ @policy.set_endpoints(@endpoints)
164
+
165
+ # put everyone into green state, then forget all health updates. this helps us write an
166
+ # easier test.
167
+ @endpoints.each { |ep| @policy.good(ep, 0, Time.now) }
168
+ @health_updates = []
169
+ end
170
+
171
+ it "notifies of overall worsening health only at transition points" do
172
+ # make most endpoints yellow-1
173
+ endpoints = @endpoints.shuffle
174
+ endpoints[1..-1].each { |ep| @policy.bad(ep, 0, Time.now) }
175
+ @policy.bad(endpoints[0], 0, Time.now)
176
+ @health_updates.should == ['yellow-1']
177
+ endpoints.each { |ep| @policy.bad(ep, 0, Time.now) }
178
+ @health_updates.should == ['yellow-1', 'yellow-2']
179
+ endpoints[1..-1].each { |ep| @policy.bad(ep, 0, Time.now) }
180
+ @health_updates.should == ['yellow-1', 'yellow-2']
181
+ @policy.bad(endpoints[0], 0, Time.now)
182
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red']
183
+ @policy.bad(endpoints[0], 0, Time.now)
184
+ @health_updates.should == ['yellow-1', 'yellow-2', 'red']
185
+ end
186
+ end
187
+ end
188
+
189
+ context :next do
190
+
191
+ context 'given all green servers' do
192
+ it 'chooses fairly' do
193
+ test_random_distribution do
194
+ @policy.next
195
+ end
196
+ end
197
+ end
198
+
199
+ context 'given all red servers' do
200
+ it 'returns nil to indicate no servers are available' do
201
+ @endpoints.each do |endpoint|
202
+ @yellow_states.times { @policy.bad(endpoint, 0, Time.now) }
203
+ @policy.should have_red_endpoint(endpoint)
204
+ end
205
+ @policy.next.should be_nil
206
+ end
207
+ end
208
+
209
+ context 'given a mixture of servers' do
210
+ it 'never chooses red servers' do
211
+ @red = @endpoints.first
212
+ @yellow_states.times { @policy.bad(@red, 0, Time.now) }
213
+ @policy.should have_red_endpoint(@red)
214
+
215
+ seen = find_empirical_distribution(@trials,@endpoints) do
216
+ @policy.next
217
+ end
218
+
219
+ seen.include?(@red).should be_false
220
+ end
221
+
222
+ it 'chooses fairly from the green and yellow servers' do
223
+ @red = @endpoints.first
224
+ @yellow_states.times { @policy.bad(@red, 0, Time.now) }
225
+ @policy.should have_red_endpoint(@red)
226
+
227
+ seen = find_empirical_distribution(@trials,@endpoints) do
228
+ @policy.next
229
+ end
230
+
231
+ seen.include?(@red).should be_false
232
+ should_be_chosen_fairly(seen, @trials, @endpoints.size - 1)
233
+ end
234
+
235
+ it 'demands a health check for yellow servers' do
236
+ pending
237
+ end
238
+
239
+ it "maintains the same order of green and yellow servers" do
240
+ actual = []
241
+ expected = [3,4,1,2]
242
+
243
+ endpoints = [1,2,3,4]
244
+ policy = RightSupport::Net::LB::HealthCheck.new(:yellow_states => 1)
245
+ policy.set_endpoints(endpoints)
246
+ policy.instance_variable_set(:@counter, 1)
247
+ endpoints.size.times do
248
+ endpoint, yellow = policy.next
249
+ policy.bad(endpoint, 0, Time.now) unless endpoint == 4
250
+ actual << endpoint
251
+ end
252
+
253
+ actual.should == expected
254
+ end
255
+ end
256
+
257
+ context 'given a red server' do
258
+ before(:each) do
259
+ @red = @endpoints.first
260
+ (@yellow_states-1).times { @policy.bad(@red, 0, Time.now - 60) }
261
+ end
262
+
263
+ context 'when @reset_time passes' do
264
+ it 'resets the server to yellow' do
265
+ @policy.should have_red_endpoint(@red)
266
+ @policy.next
267
+ @policy.should have_yellow_endpoint(@red, @yellow_states-1)
268
+ end
269
+ end
270
+
271
+ end
272
+
273
+ context 'given a yellow-2 server' do
274
+ before(:each) do
275
+ @yellow = @endpoints.first
276
+ @policy.bad(@yellow, 0, Time.now - 60)
277
+ @policy.should have_yellow_endpoint(@yellow, 2)
278
+ end
279
+
280
+ context 'when @reset_time passes' do
281
+ it 'decreases the yellow level to N-1' do
282
+ @policy.next
283
+ @policy.should have_yellow_endpoint(@yellow, 1)
284
+ end
285
+ end
286
+ end
287
+
288
+ context 'given a yellow-1 server' do
289
+ before(:each) do
290
+ @yellow = @endpoints.first
291
+ @policy.good(@yellow, 0, Time.now - 60)
292
+ @policy.bad(@yellow, 0, Time.now - 60)
293
+ @policy.should have_yellow_endpoint(@yellow, 1)
294
+ end
295
+
296
+ context 'when @reset_time passes' do
297
+ it 'resets the server to green' do
298
+ @policy.next
299
+ @policy.should have_green_endpoint(@yellow)
300
+ end
301
+ end
302
+ end
303
+ end
304
+
305
+ context :get_stats do
306
+ context 'given all green servers' do
307
+ before(:each) do
308
+ @endpoints.each { |ep| @policy.good(ep, 0, Time.now) }
309
+ end
310
+
311
+ it 'reports all endpoints as green' do
312
+ expected_stats = {}
313
+ @endpoints.each { |ep| expected_stats[ep] = 'green' }
314
+
315
+ @policy.get_stats.should_not be_nil
316
+ @policy.get_stats.should == expected_stats
317
+ end
318
+ end
319
+
320
+ context 'given all red servers' do
321
+ it 'reports all endpoints as red' do
322
+ expected_stats = {}
323
+ @endpoints.each { |ep| expected_stats[ep] = 'red' }
324
+
325
+ @endpoints.each do |endpoint|
326
+ @yellow_states.times { @policy.bad(endpoint, 0, Time.now) }
327
+ end
328
+
329
+ @policy.get_stats.should_not be_nil
330
+ @policy.get_stats.should == expected_stats
331
+ end
332
+ end
333
+
334
+ context 'given all yellow-N servers' do
335
+ it 'reports all endpoints as yellow-N' do
336
+ expected_stats = {}
337
+ @endpoints.each { |ep| expected_stats[ep] = "yellow-#{@yellow_states - 1}" }
338
+
339
+ @endpoints.each do |endpoint|
340
+ @yellow_states.times { @policy.bad(endpoint, 0, Time.now) }
341
+ @policy.good(endpoint, 0, Time.now)
342
+ end
343
+
344
+ @policy.get_stats.should_not be_nil
345
+ @policy.get_stats.should == expected_stats
346
+ end
347
+ end
348
+ end
349
+
350
+ context :set_endpoints do
351
+ context 'given endpoints stack does not exist' do
352
+ before(:each) do
353
+ @policy = RightSupport::Net::LB::HealthCheck.new({
354
+ :yellow_states => @yellow_states, :reset_time => @reset_time})
355
+ end
356
+
357
+ it 'acts as initializer' do
358
+ @policy.set_endpoints(@endpoints)
359
+ @endpoints.include?(@policy.next.first).should be_true
360
+ end
361
+ end
362
+
363
+ context 'given an existing endpoints stack' do
364
+ it 'updates composition and saves previous statuses of endpoints' do
365
+ expected_stats = {}
366
+ @endpoints.each { |ep| expected_stats[ep] = "yellow-#{@yellow_states - 1}" }
367
+
368
+ @endpoints.each do |endpoint|
369
+ @yellow_states.times { @policy.bad(endpoint, 0, Time.now) }
370
+ @policy.good(endpoint, 0, Time.now)
371
+ end
372
+
373
+ @new_endpoins = [6,7]
374
+ @new_endpoins.each { |ep| expected_stats[ep] = "yellow-1" }
375
+
376
+ @updated_endpoints = @endpoints + @new_endpoins
377
+ @policy.set_endpoints(@updated_endpoints)
378
+ @policy.get_stats.should eql(expected_stats)
379
+ end
380
+ end
381
+ end #:set_endpoints
382
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe RightSupport::Net::LB::RoundRobin do
4
+ before(:each) do
5
+ @endpoints = [1,2,3,4,5]
6
+ @policy = RightSupport::Net::LB::RoundRobin.new()
7
+ @policy.set_endpoints(@endpoints)
8
+ end
9
+
10
+ it 'chooses fairly' do
11
+ test_random_distribution do
12
+ @policy.next
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe RightSupport::Net::LB::Sticky do
4
+
5
+ before(:all) do
6
+ @endpoints = [1,2,3,4,5]
7
+ @trials = 2500
8
+ end
9
+
10
+ before(:each) do
11
+ @policy = RightSupport::Net::LB::Sticky.new({})
12
+ end
13
+
14
+ context :initialize do
15
+ it 'creates policy with an empty list of endpoints' do
16
+ @policy.next.should be_nil
17
+ end
18
+ end
19
+
20
+ context :next do
21
+
22
+ before(:each) do
23
+ @policy.set_endpoints(@endpoints)
24
+ end
25
+
26
+ context 'given all servers are healthy' do
27
+ it 'sticks to chosen one' do
28
+ chance = 1.0
29
+ seen = find_empirical_distribution(@trials,@endpoints) do
30
+ @policy.next
31
+ end
32
+ seen.each_pair do |_, count|
33
+ (Float(count) / Float(@trials)).should be_within(0.025).of(chance) #allow 5% margin of error
34
+ end
35
+ end
36
+ end
37
+
38
+ context 'given a chosen server becomes unavailable' do
39
+ it 'chooses the next server and sticks to it' do
40
+ @ep1 = @policy.next.first
41
+
42
+ seen = find_empirical_distribution(@trials,@endpoints) do
43
+ @policy.next
44
+ end
45
+ seen[[@ep1,false]].should eql(@trials)
46
+
47
+ @policy.bad(@chosen,0,0)
48
+ @ep2 = @policy.next.first
49
+
50
+ seen = find_empirical_distribution(@trials,@endpoints) do
51
+ @policy.next
52
+ end
53
+ seen[[@ep1,false]].should be_nil
54
+ seen[[@ep2,false]].should eql(@trials)
55
+ end
56
+ end
57
+ end
58
+
59
+ context :set_endpoints do
60
+ context 'given an empty list of endpoints' do
61
+ it 'acts as initializer' do
62
+ @policy.next.should be_nil
63
+ @policy.set_endpoints(@endpoints)
64
+ @endpoints.include?(@policy.next.first).should be_true
65
+ end
66
+ end
67
+
68
+ context 'given an existing list endpoints' do
69
+ before(:each) do
70
+ @policy.set_endpoints(@endpoints)
71
+ end
72
+
73
+ context 'and updated list of endpoints contains a chosen server' do
74
+ it 'updates composition, but still using chosen server' do
75
+ @chosen_endpoint = @policy.next.first
76
+ @updated_endpoints = @endpoints + [6,7]
77
+ @policy.set_endpoints(@updated_endpoints)
78
+ @policy.next.first.should be_eql(@chosen_endpoint)
79
+ end
80
+ end
81
+
82
+ context 'and updated list of endpoints does not contain a chosen server' do
83
+ it 'updates composition and chooses new server' do
84
+ @chosen_endpoint = @policy.next.first
85
+ @updated_endpoints = @endpoints - [@chosen_endpoint]
86
+ @policy.set_endpoints(@updated_endpoints)
87
+ @policy.next.first.should_not be_eql(@chosen_endpoint)
88
+ end
89
+ end
90
+ end
91
+ end #:set_endpoints
92
+ end