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,141 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class InnocentVictim
4
- include RightSupport::Log::Mixin
5
- end
6
-
7
- class Bystander < InnocentVictim
8
- end
9
-
10
- module Parasite
11
- include RightSupport::Log::Mixin
12
- end
13
-
14
- class Host
15
- include Parasite
16
- end
17
-
18
- describe RightSupport::Log::Mixin do
19
- context 'when mixed into a module' do
20
- before(:each) do
21
- @victim = Host.new
22
- end
23
-
24
- it 'provides instance-level accesssor to hosts' do
25
- @victim.should respond_to(:logger)
26
- @victim.should respond_to(:logger=)
27
- @victim.logger.info 'haha'
28
- end
29
-
30
- it 'provides a module-method-level accessor' do
31
- Parasite.should respond_to(:logger)
32
- Parasite.should respond_to(:logger=)
33
- Parasite.logger.info 'haha'
34
- end
35
- end
36
-
37
- context 'when mixed into a base class' do
38
- before(:each) do
39
- @victim = InnocentVictim.new
40
- end
41
-
42
- it 'provides a class-level accessor' do
43
- @victim.class.should respond_to(:logger)
44
- @victim.class.should respond_to(:logger=)
45
- @victim.class.logger.info 'haha'
46
- end
47
-
48
- it 'provides an instance-level accessor' do
49
- @victim.should respond_to(:logger)
50
- @victim.should respond_to(:logger=)
51
- @victim.logger.info 'haha'
52
- end
53
-
54
- context :logger do
55
- context 'without a logger' do
56
- before(:each) do
57
- @victim.class.logger = nil
58
- end
59
-
60
- it 'uses the default logger' do
61
- flexmock(RightSupport::Log::Mixin.default_logger).should_receive(:info).twice.and_return(true)
62
- @victim.class.logger.info('lalalala').should be_true
63
- @victim.logger.info('lalalala').should be_true
64
- end
65
- end
66
-
67
- context 'with class logger' do
68
- before(:each) do
69
- @logger = mock_logger
70
- @victim.class.logger = @logger
71
- end
72
-
73
- it 'uses class logger' do
74
- @logger.should_receive(:info).and_return(true).twice
75
- @victim.class.logger.info('lalalala').should be_true
76
- @victim.logger.info('lalalala').should be_true
77
- end
78
-
79
- context 'with instance logger' do
80
- before(:each) do
81
- @instance_logger = mock_logger
82
- @victim.logger = @instance_logger
83
- end
84
-
85
- it 'uses instance logger' do
86
- @instance_logger.should_receive(:info).and_return(true).once
87
- @logger.should_receive(:info).never
88
- @victim.logger.info('lalalala').should be_true
89
- end
90
- end
91
- end
92
- end
93
- end
94
-
95
- context 'when inherited from a base class' do
96
- let(:base_logger) { flexmock('base-class logger') }
97
- let(:subclass_logger) { flexmock('derived-class logger') }
98
-
99
- after(:each) do
100
- InnocentVictim.logger = nil
101
- Bystander.logger = nil
102
- end
103
-
104
- it 'delegates to base class' do
105
- InnocentVictim.logger = base_logger
106
- base_logger.should_receive(:error).with('foo').once
107
-
108
- Bystander.logger.error('foo')
109
- end
110
-
111
- it 'allows override in child class' do
112
- InnocentVictim.logger = base_logger
113
- Bystander.logger = subclass_logger
114
- base_logger.should_receive(:error).never
115
- subclass_logger.should_receive(:error).with('foo').once
116
-
117
- Bystander.logger.error('foo')
118
- end
119
- end
120
-
121
- context 'given someone has defined Class#logger' do
122
- before(:all) do
123
- InnocentVictim.logger = nil
124
- class Class
125
- def logger
126
- raise "THIS SHOULD NEVER BE CALLED"
127
- end
128
- end
129
- end
130
-
131
- after(:all) do
132
- class Class
133
- remove_method :logger
134
- end
135
- end
136
-
137
- it 'should never delegate to Class#logger' do
138
- InnocentVictim.logger.info 'test log'
139
- end
140
- end
141
- end
@@ -1,54 +0,0 @@
1
- #
2
- # Copyright (c) 2009-2012 RightScale Inc
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining
5
- # a copy of this software and associated documentation files (the
6
- # "Software"), to deal in the Software without restriction, including
7
- # without limitation the rights to use, copy, modify, merge, publish,
8
- # distribute, sublicense, and/or sell copies of the Software, and to
9
- # permit persons to whom the Software is furnished to do so, subject to
10
- # the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be
13
- # included in all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
-
23
- require 'spec_helper'
24
-
25
- describe RightSupport::Log::Multiplexer do
26
-
27
- before(:all) do
28
- @target1 = RightSupport::Log::NullLogger.new
29
- @target2 = RightSupport::Log::NullLogger.new
30
- @target3 = RightSupport::Log::NullLogger.new
31
- @multiplexer = RightSupport::Log::Multiplexer.new(@target1, @target2, @target3)
32
- end
33
-
34
- it 'multiplexes method calls to all callers' do
35
- flexmock(@target1).should_receive(:some_method).with('arg', 'arg2').once
36
- flexmock(@target2).should_receive(:some_method).with('arg', 'arg2').once
37
- flexmock(@target3).should_receive(:some_method).with('arg', 'arg2').once
38
- @multiplexer.some_method('arg', 'arg2')
39
- end
40
-
41
- it 'returns the result returned by the first target' do
42
- flexmock(@target1).should_receive(:some_method).with('arg', 'arg2').and_return('res1').once
43
- flexmock(@target2).should_receive(:some_method).with('arg', 'arg2').and_return('res2').once
44
- flexmock(@target3).should_receive(:some_method).with('arg', 'arg2').and_return('res3').once
45
- @multiplexer.some_method('arg', 'arg2').should == 'res1'
46
- end
47
-
48
- it 'multiplexes #warn by undefining Object#warn' do
49
- flexmock(@target1).should_receive(:warn).with('arg').and_return('res1')
50
- flexmock(@target2).should_receive(:warn).with('arg').and_return('res2')
51
- flexmock(@target3).should_receive(:warn).with('arg').and_return('res3')
52
- @multiplexer.warn('arg').should == 'res1'
53
- end
54
- end
@@ -1,36 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe RightSupport::Log::NullLogger do
4
- before(:each) do
5
- @logger = RightSupport::Log::NullLogger.new
6
- end
7
-
8
- context 'log levels' do
9
- [:debug, :info, :warn, :error, :fatal].each do |method|
10
- it "responds to ##{method}" do
11
- block_called = false
12
- @logger.__send__(method, 'lalalala').should be_true
13
- @logger.__send__(method) { block_called = true ; 'lalalala' }.should be_true
14
- block_called.should be_true
15
- end
16
- end
17
- end
18
-
19
- context '<< method' do
20
- it 'responds like Logger' do
21
- (@logger << 'lalalala').should == 8
22
- end
23
- end
24
-
25
- context :close do
26
- it 'responds' do
27
- @logger.close.should be_nil
28
- end
29
-
30
- it 'is idempotent' do
31
- @logger.close.should be_nil
32
- @logger.close.should be_nil
33
- @logger.close.should be_nil
34
- end
35
- end
36
- end
@@ -1,49 +0,0 @@
1
- require ::File.expand_path('../../spec_helper', __FILE__)
2
- require 'stringio'
3
-
4
- describe RightSupport::Log::StepLevelLogger do
5
- context 'with a decorated logger' do
6
- let(:buffer) { ::StringIO.new }
7
-
8
- let(:decorated_logger) { ::Logger.new(buffer) }
9
-
10
- def try_all_levels(l)
11
- l.debug('debug')
12
- l.info('info')
13
- l.warn('warn')
14
- l.error('error')
15
- l.fatal('fatal')
16
- end
17
-
18
- context 'with default level' do
19
- subject { described_class.new(decorated_logger) }
20
-
21
- it 'passes all messages' do
22
- try_all_levels(subject)
23
- buffer.string.should =~ /debug.+info.+warn.+error.+fatal/m
24
- end
25
- end
26
-
27
- context 'with error level' do
28
- subject do
29
- s = described_class.new(decorated_logger)
30
- s.level = ::Logger::ERROR
31
- s
32
- end
33
-
34
- it 'passes error and fatal messages' do
35
- try_all_levels(subject)
36
- buffer.string.should_not =~ /debug/
37
- buffer.string.should_not =~ /info/
38
- buffer.string.should_not =~ /warn/
39
- buffer.string.should =~ /error.+fatal/m
40
- end
41
-
42
- it 'does not change level of decorated logger' do
43
- try_all_levels(decorated_logger)
44
- buffer.string.should =~ /debug.+info.+warn.+error.+fatal/m
45
- end
46
- end
47
-
48
- end
49
- end
@@ -1,172 +0,0 @@
1
- require 'spec_helper'
2
- require 'socket'
3
-
4
- # NOTE: this spec does not get defined or executed unless SystemLogger
5
- # is defined, which only happens if Ruby standard-library 'syslog' module is
6
- # available at runtime. Thus, the spec does not run on Windows.
7
- #
8
- # The selective execution is accomplished with a trailing 'if' for this
9
- # outermost describe block. Crude but effective. You have been warned!
10
- describe 'RightSupport::Log::SystemLogger' do
11
- subject { RightSupport::Log::SystemLogger }
12
-
13
- # Duplicate constants for easier reading
14
- SEVERITY_MAP = RightSupport::Log::SystemLogger::SEVERITY_MAP
15
- FACILITY_MAP = RightSupport::Log::SystemLogger::FACILITY_MAP
16
-
17
- after(:each) do
18
- subject.instance_eval { class_variable_set(:@@syslog, nil) }
19
- end
20
-
21
- context :local do
22
- before(:each) do
23
- @mock_syslog = flexmock(Syslog)
24
- #indicates we sent a bogus severity to syslog!
25
- @mock_syslog.should_receive(SEVERITY_MAP[Logger::UNKNOWN]).never
26
- flexmock(Syslog).should_receive(:open).and_return(@mock_syslog).by_default
27
- @mock_syslog.should_receive(:info).with('Connected to syslog: :local').once
28
- end
29
-
30
- context :initialize do
31
- context 'with :facility option' do
32
- FACILITY_MAP.each_pair do |name, const|
33
- it "should handle #{name}" do
34
- @mock_syslog.should_receive(:open).with('unit tests', nil, const).and_return(@mock_syslog)
35
- subject.new('unit tests', :facility=>name, :connection=>:local)
36
- end
37
- end
38
- end
39
- end
40
-
41
- context :add do
42
- context 'severity levels' do
43
- levels = { :debug=>:debug,
44
- :info=>:info, :warn=>:notice,
45
- :error=>:warning, :fatal=>:err }
46
-
47
- levels.each_pair do |logger_method, syslog_method|
48
- it "translates Logger##{logger_method} to Syslog##{syslog_method}" do
49
- @logger = subject.new('spec', :connection=>:local)
50
- @mock_syslog.should_receive(syslog_method).with('moo bah oink')
51
- @logger.__send__(logger_method, 'moo bah oink')
52
- end
53
- end
54
- end
55
-
56
- it 'escapes % characters to avoid confusing printf()' do
57
- @logger = subject.new('spec', :connection=>:local)
58
- @mock_syslog.should_receive(:info).with('All systems 100%% -- %%licious!')
59
-
60
- @logger.info('All systems 100% -- %licious!')
61
- end
62
-
63
- context 'given :split option' do
64
- it 'when true, splits multi-line messages' do
65
- @logger = subject.new('spec', :split=>true, :connection=>:local)
66
-
67
- actual = []
68
- @mock_syslog.should_receive(:info).and_return do |message|
69
- actual << message
70
- true
71
- end
72
-
73
- @logger.info("This is a\nmulti line\r\nlog message\n\rwith all kinds\n\n\rof stuff")
74
- actual.should == ["This is a", "multi line", "log message", "with all kinds", "of stuff"]
75
- end
76
-
77
- it 'when false, passes through multi-line messages' do
78
- @logger = subject.new('spec', :split=>false, :connection=>:local)
79
-
80
- actual = []
81
- @mock_syslog.should_receive(:info).and_return do |message|
82
- actual << message
83
- true
84
- end
85
-
86
- msg = "This is a\nmulti line\r\nlog message\n\rwith all kinds\n\n\rof stuff"
87
- @logger.info(msg)
88
- actual.should == [msg]
89
- end
90
- end
91
-
92
- context 'given :color option' do
93
- it 'when true, passes through ANSI color codes' do
94
- @logger = subject.new('spec', :color=>true, :connection=>:local)
95
-
96
- actual = []
97
- @mock_syslog.should_receive(:info).and_return do |message|
98
- actual << message
99
- true
100
- end
101
-
102
- msg = "This has \e[16;32mcolor\e[7;0m inside it!"
103
- @logger.info(msg)
104
- actual.should == [msg]
105
- end
106
-
107
- it 'when false, strips out ANSI color codes' do
108
- @logger = subject.new('spec', :color=>false, :connection=>:local)
109
-
110
- actual = []
111
- @mock_syslog.should_receive(:info).and_return do |message|
112
- actual << message
113
- true
114
- end
115
-
116
- @logger.info("This has \e[16;32mcolor\e[7;0m inside it!")
117
- actual.should == ['This has color inside it!']
118
- end
119
- end
120
- end
121
- end
122
-
123
- context :remote do
124
- it 'sends protocol-formatted messages to a syslog service' do
125
- server = ::TCPServer.new(2345)
126
- data = nil
127
- ::Thread.start do
128
- client = server.accept
129
- data = client.readline
130
- client.close
131
- end
132
- logger = subject.new('spec', :connection=>'tcp://localhost:2345')
133
- stop_time = ::Time.now + 3
134
- while data.nil?
135
- sleep(0.1)
136
- fail 'Did not receive expected data' if ::Time.now >= stop_time
137
- end
138
-
139
- # example: "<134> Sep 23 13:45:42 ubuntu spec[3891]: Connected to syslog: \"tcp://localhost:2345\"\n"
140
- regex = /^<134> \w+ \d+ \d+:\d+:\d+ .+ spec\[\d+\]: Connected to syslog: \"tcp:\/\/localhost:2345\"$/
141
- data.chomp.should match(regex)
142
-
143
- # attempt to reconnect to closed server.
144
- server.close
145
- server = nil
146
- data = nil
147
- ::Thread.start do
148
- sleep 1 # should cause 1-2 reconnect error(s) to appear in STDERR
149
- server = ::TCPServer.new(2345)
150
- client = server.accept
151
- data = client.readline
152
- client.close
153
- server.close
154
- server = nil
155
- end
156
-
157
- # explicitly reconnect and send with delayed server restart. reconnection
158
- # happens automagically (only for remote) but explicit reconnection will
159
- # reestablish the PID, etc., which is useful for forking.
160
- logger.reconnect
161
- logger.debug("Hello syslog")
162
- stop_time = ::Time.now + 3
163
- while data.nil?
164
- sleep(0.1)
165
- fail 'Did not receive expected data' if ::Time.now >= stop_time
166
- end
167
- regex = /^<135> \w+ \d+ \d+:\d+:\d+ .+ spec\[\d+\]: Hello syslog$/
168
- data.chomp.should match(regex)
169
- end
170
- end
171
-
172
- end if defined?(RightSupport::Log::SystemLogger)
@@ -1,57 +0,0 @@
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
data/spec/net/dns_spec.rb DELETED
@@ -1,187 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe RightSupport::Net::DNS do
4
- include SpecHelper::SocketMocking
5
-
6
- subject { RightSupport::Net::DNS } #the module itself
7
-
8
- context :resolve_all_ip_addresses do
9
- before(:all) do
10
- @hostnames = ['1.1.1.1', '2.2.2.2', '3.3.3.3']
11
- @hostnames .each do |hostname|
12
- flexmock(Socket).should_receive(:getaddrinfo).with(hostname, 443, Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP).once.and_return([[0,0,0,hostname]])
13
- end
14
- end
15
- it 'resolves all ip addresses for specified array of endpoints' do
16
- @resolved_hostnames = subject.resolve_all_ip_addresses(@hostnames)
17
- (@resolved_hostnames - @hostnames).should be_eql([])
18
- end
19
- end
20
-
21
-
22
- context :resolve do
23
- context 'given default :retry => 3' do
24
- let(:endpoint) { 'www.example.com' }
25
- let(:output) { [RightSupport::Net::ResolvedEndpoint.new(['1.1.1.1', '2.2.2.2'])] }
26
-
27
- it 'retries SocketError' do
28
- mock_getaddrinfo('www.example.com', SocketError)
29
- mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
30
-
31
- subject.resolve(endpoint).should == output
32
- end
33
-
34
- it 'stops retrying SocketError after three attempts' do
35
- mock_getaddrinfo('www.example.com', SocketError, 3)
36
-
37
- expect { subject.resolve(endpoint) }.to raise_error(SocketError)
38
- end
39
-
40
- it 'does not rescue other exceptions' do
41
- mock_getaddrinfo('www.example.com', ArgumentError)
42
-
43
- expect { subject.resolve(endpoint) }.to raise_error(ArgumentError)
44
- end
45
- end
46
-
47
- context 'given :retry => 0' do
48
- it 'does not retry SocketError' do
49
- mock_getaddrinfo('www.example.com', SocketError)
50
-
51
- expect { subject.resolve('www.example.com', :retry=>0) }.to raise_error(SocketError)
52
- end
53
- end
54
-
55
- context 'given various endpoint formats' do
56
- context 'e.g. a DNS hostname' do
57
- let(:endpoint) { 'www.example.com' }
58
- let(:output) { [RightSupport::Net::ResolvedEndpoint.new(['1.1.1.1', '2.2.2.2'])] }
59
-
60
- it 'resolves to IP addresses' do
61
- mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
62
- subject.resolve(endpoint).should == output
63
- end
64
- end
65
-
66
- context 'e.g. an IPv4 address' do
67
- let(:endpoint) { '127.0.0.1' }
68
- let(:output) { [RightSupport::Net::ResolvedEndpoint.new(['127.0.0.1'])] }
69
-
70
- it 'resolves to the same address' do
71
- mock_getaddrinfo('127.0.0.1', ['127.0.0.1'])
72
- subject.resolve(endpoint).should == output
73
- end
74
- end
75
-
76
- context 'e.g. an HTTP URL' do
77
- let(:endpoint) { 'http://www.example.com' }
78
- let(:output) { ['http://1.1.1.1', 'http://2.2.2.2'] }
79
-
80
- it 'resolves to URLs with addresses substituted' do
81
- mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
82
- subject.resolve(endpoint).first.addrs.should == output
83
- end
84
-
85
- context 'with a path component' do
86
- let(:endpoint) { 'http://www.example.com/foo/bar' }
87
- let(:output) { ['http://1.1.1.1/foo/bar', 'http://2.2.2.2/foo/bar'] }
88
-
89
- it 'resolves to URLs with path component preserved' do
90
- mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
91
- subject.resolve(endpoint).first.addrs.should == output
92
- end
93
- end
94
- end
95
-
96
- # The double slash in a URI indicates that an authority follows. The authority
97
- # is interpreted as a hostname for most URL schemes we are interested in. We
98
- # can't deal with URLs unless they have a clear authority/hostname that we
99
- # can substitute.
100
- #
101
- # For more information, see: http://tools.ietf.org/html/rfc3986#section-3
102
- context 'e.g. a URI without an authority' do
103
- let(:endpoint) { 'urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66' }
104
-
105
- it 'raises URI::InvalidURIError' do
106
- lambda do
107
- subject.resolve(endpoint)
108
- end.should raise_error(URI::InvalidURIError)
109
- end
110
- end
111
-
112
- context 'e.g. several hostnames' do
113
- let(:endpoints) { ['www.example.com', 'www.example.net'] }
114
- let(:output) { [RightSupport::Net::ResolvedEndpoint.new(['1.1.1.1', '2.2.2.2']), RightSupport::Net::ResolvedEndpoint.new(['3.3.3.3', '4.4.4.4'])] }
115
-
116
- it 'resolves to IP addresses' do
117
- mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
118
- mock_getaddrinfo('www.example.net', ['3.3.3.3', '4.4.4.4'])
119
-
120
- subject.resolve(endpoints).should == output
121
- end
122
- end
123
-
124
- context 'e.g. several HTTP URLs' do
125
- let(:endpoints) { ['http://www.example.com', 'http://www.example.net'] }
126
- let(:output) { ['http://1.1.1.1', 'http://2.2.2.2', 'http://3.3.3.3', 'http://4.4.4.4'] }
127
-
128
- it 'resolves to URLs with addresses substituted' do
129
- mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
130
- mock_getaddrinfo('www.example.net', ['3.3.3.3', '4.4.4.4'])
131
-
132
- subject.resolve(endpoints).map {|ep| ep.addrs}.flatten == output
133
- end
134
- end
135
- end
136
-
137
- context 'requesting CIDR blocks' do
138
- context 'DNS resolvable addresses' do
139
- let(:endpoint) { 'www.example.com' }
140
- let(:output) { ['1.1.1.1/32', '2.2.2.2/32'] }
141
-
142
- it 'resolves hostname to CIDR /32 blocks' do
143
- mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
144
- subject.resolve(endpoint).first.blocks.should == output
145
- end
146
-
147
- it 'resolves IP addresses to CIDR /32 blocks' do
148
- mock_getaddrinfo('1.1.1.1', ['1.1.1.1'])
149
- mock_getaddrinfo('2.2.2.2', ['2.2.2.2'])
150
- subject.resolve(['1.1.1.1', '2.2.2.2']).map {|endpoint| endpoint.blocks}.flatten.should =~ output
151
- end
152
-
153
- it 'refuses to resolve a URI having a CIDR block < /32' do
154
- lambda do
155
- subject.resolve("http://cf-mirror.rightscale.com")
156
- end.should raise_error(URI::InvalidURIError)
157
- end
158
- end
159
-
160
- context 'Static CIDR blocks' do
161
- let(:endpoint) { 'cf-mirror.rightscale.com' }
162
-
163
- it 'resolves hostname to static CIDR blocks' do
164
- subject.resolve(endpoint).first.blocks.each do |addr|
165
- # Addresses should all be in CIDR form and not single /32 addresses
166
- addr.should =~ /^\d+(?:\.\d+){3}\/(?:#{1.upto(31).to_a.join('|')})$/
167
- end
168
- end
169
- end
170
- end
171
- end
172
-
173
- context :resolve_with_hostnames do
174
- context 'given common inputs' do
175
- let(:endpoints) { ['www.example1.com','www.example2.com'] }
176
- let(:output) { {'www.example1.com'=>RightSupport::Net::ResolvedEndpoint.new(['1.1.1.1', '2.2.2.2']),
177
- 'www.example2.com'=>RightSupport::Net::ResolvedEndpoint.new(['3.3.3.3', '4.4.4.4'])} }
178
-
179
- it 'resolves to IP addresses' do
180
- mock_getaddrinfo('www.example1.com', ['1.1.1.1', '2.2.2.2'])
181
- mock_getaddrinfo('www.example2.com', ['3.3.3.3', '4.4.4.4'])
182
- subject.resolve_with_hostnames(endpoints).should == output
183
- end
184
- end
185
- end
186
-
187
- end