right_support 2.6.17 → 2.7.0
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/.rspec +4 -0
- data/CHANGELOG.rdoc +37 -0
- data/Gemfile +29 -0
- data/Gemfile.lock +111 -0
- data/README.rdoc +2 -0
- data/Rakefile +62 -0
- data/VERSION +1 -0
- data/features/balancer_error_handling.feature +34 -0
- data/features/balancer_health_check.feature +33 -0
- data/features/continuous_integration.feature +51 -0
- data/features/continuous_integration_cucumber.feature +28 -0
- data/features/continuous_integration_rspec1.feature +28 -0
- data/features/continuous_integration_rspec2.feature +28 -0
- data/features/http_client_timeout.feature +19 -0
- data/features/serialization.feature +95 -0
- data/features/step_definitions/http_client_steps.rb +27 -0
- data/features/step_definitions/request_balancer_steps.rb +93 -0
- data/features/step_definitions/ruby_steps.rb +176 -0
- data/features/step_definitions/serialization_steps.rb +96 -0
- data/features/step_definitions/server_steps.rb +134 -0
- data/features/support/env.rb +138 -0
- data/features/support/file_utils_bundler_mixin.rb +45 -0
- data/lib/right_support/ci/java_cucumber_formatter.rb +22 -8
- data/lib/right_support/ci/java_spec_formatter.rb +26 -8
- data/lib/right_support/ci/rake_task.rb +3 -0
- data/lib/right_support/ci.rb +24 -0
- data/lib/right_support/crypto/signed_hash.rb +22 -0
- data/lib/right_support/data/serializer.rb +24 -2
- data/lib/right_support/net/address_helper.rb +20 -8
- data/lib/right_support/net/dns.rb +20 -8
- data/lib/right_support/net/http_client.rb +22 -0
- data/lib/right_support/net/request_balancer.rb +27 -21
- data/lib/right_support/net/s3_helper.rb +20 -8
- data/lib/right_support/net/ssl/open_ssl_patch.rb +22 -0
- data/lib/right_support/net/ssl.rb +20 -8
- data/lib/right_support/ruby/easy_singleton.rb +22 -0
- data/lib/right_support/ruby/object_extensions.rb +22 -0
- data/lib/right_support/ruby/string_extensions.rb +1 -1
- data/lib/right_support.rb +13 -10
- data/right_support.gemspec +180 -18
- data/right_support.rconf +8 -0
- data/spec/config/feature_set_spec.rb +83 -0
- data/spec/crypto/signed_hash_spec.rb +60 -0
- data/spec/data/hash_tools_spec.rb +471 -0
- data/spec/data/uuid_spec.rb +45 -0
- data/spec/db/cassandra_model_part1_spec.rb +84 -0
- data/spec/db/cassandra_model_part2_spec.rb +73 -0
- data/spec/db/cassandra_model_spec.rb +359 -0
- data/spec/fixtures/encrypted_priv_rsa.pem +30 -0
- data/spec/fixtures/good_priv_dsa.pem +12 -0
- data/spec/fixtures/good_priv_rsa.pem +15 -0
- data/spec/fixtures/good_pub_dsa.ssh +1 -0
- data/spec/fixtures/good_pub_rsa.pem +5 -0
- data/spec/fixtures/good_pub_rsa.ssh +1 -0
- data/spec/log/exception_logger_spec.rb +76 -0
- data/spec/log/filter_logger_spec.rb +8 -0
- data/spec/log/mixin_spec.rb +62 -0
- data/spec/log/multiplexer_spec.rb +54 -0
- data/spec/log/null_logger_spec.rb +36 -0
- data/spec/log/system_logger_spec.rb +92 -0
- data/spec/net/address_helper_spec.rb +57 -0
- data/spec/net/balancing/health_check_spec.rb +382 -0
- data/spec/net/balancing/round_robin_spec.rb +15 -0
- data/spec/net/balancing/sticky_policy_spec.rb +92 -0
- data/spec/net/dns_spec.rb +152 -0
- data/spec/net/http_client_spec.rb +171 -0
- data/spec/net/request_balancer_spec.rb +579 -0
- data/spec/net/s3_helper_spec.rb +160 -0
- data/spec/net/ssl_spec.rb +42 -0
- data/spec/net/string_encoder_spec.rb +58 -0
- data/spec/rack/log_setter_spec.rb +5 -0
- data/spec/rack/request_logger_spec.rb +68 -0
- data/spec/rack/request_tracker_spec.rb +5 -0
- data/spec/ruby/easy_singleton_spec.rb +72 -0
- data/spec/ruby/object_extensions_spec.rb +27 -0
- data/spec/ruby/string_extensions_spec.rb +98 -0
- data/spec/spec_helper.rb +181 -0
- data/spec/stats/activity_spec.rb +193 -0
- data/spec/stats/exceptions_spec.rb +123 -0
- data/spec/stats/helpers_spec.rb +603 -0
- data/spec/validation/openssl_spec.rb +37 -0
- data/spec/validation/ssh_spec.rb +39 -0
- 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
|