right_support 2.8.3 → 2.8.6

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -11,6 +11,7 @@ group :optional do
11
11
  gem "simple_uuid", "~> 0.2", :require => nil
12
12
  gem "uuid", "~> 2.3", :require => nil
13
13
  gem "yajl-ruby", "~> 1.1"
14
+ gem "iconv"
14
15
  end
15
16
 
16
17
  # Gems used during test and development of RightSupport.
data/Gemfile.lock CHANGED
@@ -34,6 +34,7 @@ GEM
34
34
  gherkin (2.12.0)
35
35
  multi_json (~> 1.3)
36
36
  git (1.2.5)
37
+ iconv (1.0.4)
37
38
  jeweler (1.8.4)
38
39
  bundler (~> 1.0)
39
40
  git (>= 1.2.5)
@@ -97,6 +98,7 @@ PLATFORMS
97
98
  DEPENDENCIES
98
99
  addressable (~> 2.2.7)
99
100
  flexmock (~> 1.0)
101
+ iconv
100
102
  jeweler (~> 1.8.3)
101
103
  net-ssh (~> 2.0)
102
104
  nokogiri (~> 1.5)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.8.3
1
+ 2.8.6
@@ -31,6 +31,14 @@ Feature: JSON serialization
31
31
  When I serialize a complex random data structure
32
32
  Then the serialized value should round-trip cleanly
33
33
 
34
+ Scenario Outline: Ruby string with non UTF-8 characters
35
+ When I serialize the Ruby value: <ruby>
36
+ Then the serialized value should be: <json>
37
+
38
+ Examples:
39
+ | ruby | json |
40
+ | "\xC0\xBCscript>\xC0\xBC/script>" | "script>/script>" |
41
+
34
42
  Scenario Outline: object-escaped Symbol
35
43
  When I serialize the Ruby value: <ruby>
36
44
  Then the serialized value should be: <json>
@@ -145,8 +145,10 @@ module RightSupport::Data
145
145
  # @param [Object] object any Ruby object
146
146
  # @return [Object] JSONish representation of input object
147
147
  def object_to_jsonish(object)
148
+ iconv = Iconv.new('UTF-8//IGNORE', 'UTF-8')
148
149
  case object
149
150
  when String
151
+ object = iconv.iconv(object)
150
152
  if (object =~ /^:/ ) || (object =~ TIME_PATTERN)
151
153
  # Strings that look like a Symbol or Time must be object-escaped.
152
154
  {@marker => String.name,
@@ -44,6 +44,40 @@ if require_succeeds?('cassandra/0.8')
44
44
  end
45
45
  end
46
46
 
47
+ # Monkey patch get_range_single so that it can return a array of key slices
48
+ # rather than converting it to a Hash
49
+ def get_range_single(column_family, options = {})
50
+ return_empty_rows = options.delete(:return_empty_rows) || false
51
+ slices_please = options.delete(:slices_not_hash) || false
52
+
53
+ column_family, _, _, options =
54
+ extract_and_validate_params(column_family, "", [options],
55
+ READ_DEFAULTS.merge(:start_key => '',
56
+ :finish_key => '',
57
+ :key_count => 100,
58
+ :columns => nil,
59
+ :reversed => false
60
+ )
61
+ )
62
+
63
+ results = _get_range( column_family,
64
+ options[:start_key].to_s,
65
+ options[:finish_key].to_s,
66
+ options[:key_count],
67
+ options[:columns],
68
+ options[:start].to_s,
69
+ options[:finish].to_s,
70
+ options[:count],
71
+ options[:consistency],
72
+ options[:reversed] )
73
+
74
+ unless slices_please
75
+ multi_key_slices_to_hash(column_family, results, return_empty_rows)
76
+ else
77
+ results
78
+ end
79
+ end
80
+
47
81
  # Monkey patch get_indexed_slices so that it returns OrderedHash, otherwise cannot determine
48
82
  # next start key when getting in chunks
49
83
  def get_indexed_slices(column_family, index_clause, *columns_and_options)
@@ -124,6 +158,16 @@ module RightSupport::DB
124
158
 
125
159
  METHODS_TO_LOG = [:multi_get, :get, :get_indexed_slices, :get_columns, :insert, :remove, 'multi_get', 'get', 'get_indexed_slices', 'get_columns', 'insert', 'remove']
126
160
 
161
+ #This is required to be overwritten in order to set the read CL
162
+ def default_read_consistency
163
+ nil
164
+ end
165
+
166
+ #This is required to be overwritten in order to set the write CL
167
+ def default_write_consistency
168
+ nil
169
+ end
170
+
127
171
  # Deprecate usage of CassandraModel under Ruby < 1.9
128
172
  def inherited(base)
129
173
  raise UnsupportedRubyVersion, "Support only Ruby >= 1.9" unless RUBY_VERSION >= "1.9"
@@ -229,6 +273,23 @@ module RightSupport::DB
229
273
  @@current_keyspace = nil
230
274
  end
231
275
 
276
+ def get_connection(current=nil)
277
+ config = env_config
278
+ thrift_client_options = {:timeout => RightSupport::DB::CassandraModel::DEFAULT_TIMEOUT}
279
+ thrift_client_options = {
280
+ :timeout => RightSupport::DB::CassandraModel::DEFAULT_TIMEOUT,
281
+ :server_retry_period => nil,
282
+ }
283
+
284
+ thrift_client_options.merge!({:protocol => Thrift::BinaryProtocolAccelerated}) if defined? Thrift::BinaryProtocolAccelerated
285
+
286
+ current ||= Cassandra.new(keyspace, config["server"], thrift_client_options)
287
+ current.disable_node_auto_discovery!
288
+ current.default_write_consistency = self.default_write_consistency if self.default_write_consistency
289
+ current.default_read_consistency = self.default_read_consistency if self.default_read_consistency
290
+ current
291
+ end
292
+
232
293
  # Client connected to Cassandra server
233
294
  # Create connection if does not already exist
234
295
  # Use BinaryProtocolAccelerated if it available
@@ -237,20 +298,7 @@ module RightSupport::DB
237
298
  # (Cassandra):: Client connected to server
238
299
  def conn()
239
300
  @@connections ||= {}
240
-
241
- config = env_config
242
-
243
- thrift_client_options = {
244
- :timeout => RightSupport::DB::CassandraModel::DEFAULT_TIMEOUT,
245
- :server_retry_period => nil,
246
- }
247
-
248
- if defined? Thrift::BinaryProtocolAccelerated
249
- thrift_client_options.merge!({:protocol => Thrift::BinaryProtocolAccelerated})
250
- end
251
-
252
- @@connections[self.keyspace] ||= Cassandra.new(self.keyspace, config["server"], thrift_client_options)
253
- @@connections[self.keyspace].disable_node_auto_discovery!
301
+ @@connections[self.keyspace] = get_connection(@@connections[self.keyspace])
254
302
  @@connections[self.keyspace]
255
303
  end
256
304
 
@@ -371,48 +419,79 @@ module RightSupport::DB
371
419
  def stream_all_indexed_slices(index, key)
372
420
  expr = do_op(:create_idx_expr, index, key, "EQ")
373
421
 
374
- start_row = ''
375
- row_count = 10
376
- has_more_rows = true
422
+ start_row = ''
423
+ max_row_count = 100
424
+ max_initial_column_count = 1000 # number of columns to retrieve in the initial 2ndary index search
425
+ max_additional_column_count = 10000 # Number of columns to retrieve in a batch once we're targetting a single (long) row
377
426
 
427
+ # Loop over all CF rows, with batches of X
378
428
  while (start_row != nil)
379
- clause = do_op(:create_idx_clause, [expr], start_row, row_count)
380
-
381
- rows = self.conn.get_indexed_slices(column_family, clause, index,
382
- :key_count => row_count,
383
- :key_start => start_row)
384
-
385
- rows = rows.keys
386
- rows.shift unless start_row == ''
387
- start_row = rows.last
388
-
389
- rows.each do |row|
390
- start_column = ''
391
- column_count = 1_000
392
- has_more_columns = true
393
-
394
- while has_more_columns
395
- clause = do_op(:create_idx_clause, [expr], row, 1)
396
- chunk = self.conn.get_indexed_slices(column_family, clause, nil,
397
- :start => start_column,
398
- :count => column_count)
399
-
400
- # Get first row's columns, because where are getting only one row [see clause, for more details]
401
- key = chunk.keys.first
402
- columns = chunk[key]
403
-
404
- columns.shift unless start_column == ''
405
- yield(key, columns) unless chunk.empty?
406
-
407
- if columns.size >= column_count - 1
408
- #Assume there are more columns, use last column as start of next slice
409
- start_column = columns.last.column.name
410
- column_count = 1_001
411
- else
412
- has_more_columns = false
429
+ clause = do_op(:create_idx_clause, [expr], start_row, max_row_count)
430
+
431
+ # Now, for each batch of rows, make sure don't ask for "ALL" columns of each row, to avoid hitting rows with a huge amount of columns,
432
+ # which would cause large memory pressure here in the client, but more specially might cause long wait times and possible timeouts.
433
+ begin
434
+ rows = self.conn.get_indexed_slices(column_family, clause, :count => max_initial_column_count)
435
+ rescue Exception => e
436
+ wrapped_timeout = e.is_a?(CassandraThrift::TimedOutException)
437
+ unwrapped_timeout = e.is_a?(Thrift::TransportException) && (e.type == Thrift::TransportException::TIMED_OUT)
438
+ unwrapped_disconnect = e.is_a?(Thrift::TransportException) && (e.type == Thrift::TransportException::NOT_OPEN)
439
+
440
+ if (wrapped_timeout || unwrapped_timeout || unwrapped_disconnect) && (timeout_retries < retry_timeout)
441
+ timeout_retries += 1
442
+ retry
443
+ else
444
+ timeout_retries = 0
445
+ raise e
446
+ end
447
+ end
448
+
449
+ rows.each_pair do |row_key, columns|
450
+ # We already processed this row the previous iteration
451
+ next if row_key == start_row
452
+
453
+ yield(row_key, columns)
454
+
455
+ if columns.size >= max_initial_column_count
456
+ # Loop over all columns of the row (1000 at a time) starting at the last column name
457
+ last_column_name = columns.last.column.name
458
+ while( last_column_name != nil )
459
+ begin
460
+ # Retrieve a slice of this row excluding the first column
461
+ # as it's already been processed.
462
+ more_cols = self.conn.get_range(
463
+ column_family,
464
+ :start_key => row_key,
465
+ :finish_key => row_key,
466
+ :count => max_additional_column_count,
467
+ :start => last_column_name,
468
+ :slices_not_hash => true ).first.columns[1..-1]
469
+ rescue Exception => e
470
+ wrapped_timeout = e.is_a?(CassandraThrift::TimedOutException)
471
+ unwrapped_timeout = e.is_a?(Thrift::TransportException) && (e.type == Thrift::TransportException::TIMED_OUT)
472
+ unwrapped_disconnect = e.is_a?(Thrift::TransportException) && (e.type == Thrift::TransportException::NOT_OPEN)
473
+
474
+ if (wrapped_timeout || unwrapped_timeout || unwrapped_disconnect) && (timeout_retries < retry_timeout)
475
+ timeout_retries += 1
476
+ retry
477
+ else
478
+ timeout_retries = 0
479
+ raise e
480
+ end
481
+ end
482
+
483
+ yield(row_key, more_cols)
484
+ if more_cols.size < max_additional_column_count
485
+ last_column_name = nil
486
+ else
487
+ last_column_name = more_cols.last.column.name
488
+ end
413
489
  end
414
490
  end
415
491
  end
492
+
493
+ break if rows.size < max_row_count
494
+ start_row = rows.keys.last
416
495
  end
417
496
  end
418
497
 
@@ -573,17 +652,8 @@ module RightSupport::DB
573
652
  # === Return
574
653
  # true:: Always return true
575
654
  def reconnect
576
- config = env_config
577
-
578
655
  return false if keyspace.nil?
579
-
580
- thrift_client_options = {:timeout => RightSupport::DB::CassandraModel::DEFAULT_TIMEOUT}
581
- thrift_client_options.merge!({:protocol => Thrift::BinaryProtocolAccelerated})\
582
- if defined? Thrift::BinaryProtocolAccelerated
583
-
584
- connection = Cassandra.new(keyspace, config["server"], thrift_client_options)
585
- connection.disable_node_auto_discovery!
586
- @@connections[keyspace] = connection
656
+ @@connections[keyspace] = get_connection
587
657
  true
588
658
  end
589
659
 
@@ -20,17 +20,99 @@
20
20
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
 
23
+ require 'ipaddr'
23
24
  require 'socket'
24
25
  require 'uri'
25
26
 
26
27
  module RightSupport::Net
28
+
29
+ # IPAddr does not support returning a block of addresses in CIDR notation.
30
+ class IPNet < ::IPAddr
31
+ def cidr_block
32
+ # Ruby Math:: does not have a log2() function :(
33
+ cidr_block = 32 - (Math.log(self.to_range.count) / Math.log(2)).to_i
34
+ end
35
+
36
+ # Don't want to override to_s here since we also
37
+ # use this during URI transformation and need a way
38
+ # to get the raw dotted-quad address
39
+ #
40
+ def to_cidr
41
+ "#{self.to_s}/#{self.cidr_block}"
42
+ end
43
+ end
44
+
45
+ # A ResolvedEndpoint represents the resolution of a host which can contain
46
+ # multiple IP addresses or entire IP address ranges of varying size.
47
+ class ResolvedEndpoint
48
+ attr_accessor :uri
49
+
50
+ def initialize(ips, opts={})
51
+ @ip_addresses = []
52
+ @uri = opts[:uri]
53
+
54
+ ips.to_a.each do |address|
55
+ @ip_addresses << IPNet.new(address)
56
+ end
57
+
58
+ unless self.all_hosts? || @uri == nil
59
+ raise URI::InvalidURIError, "Cannot resolve URI with CIDR block bigger than a single host" unless self.all_hosts?
60
+ end
61
+ end
62
+
63
+ def addresses()
64
+ @ip_addresses.map do |addr|
65
+ if addr.to_range.count == 1 && @uri != nil
66
+ transformed_uri = uri.dup
67
+ transformed_uri.host = addr.to_s
68
+ transformed_uri.to_s
69
+ else
70
+ addr
71
+ end
72
+ end
73
+ end
74
+ alias addrs :addresses
75
+
76
+ def blocks()
77
+ @ip_addresses.map {|addr| addr.to_cidr}
78
+ end
79
+
80
+ def all_hosts?()
81
+ @ip_addresses.all? {|addr| addr.to_range.count == 1}
82
+ end
83
+
84
+ def ==(another_endpoint)
85
+ another_endpoint.addresses.all? {|addr| addresses.member? addr } && another_endpoint.uri == @uri
86
+ end
87
+ end
88
+
27
89
  module DNS
28
90
  DEFAULT_RESOLVE_OPTIONS = {
29
91
  :address_family => Socket::AF_INET,
30
92
  :socket_type => Socket::SOCK_STREAM,
31
93
  :protocol => Socket::IPPROTO_TCP,
32
- :retry => 3
33
- }
94
+ :retry => 3,
95
+ :uri => nil,
96
+ }.freeze
97
+
98
+ STATIC_HOSTNAME_TRANSLATIONS = {
99
+ # This list of CIDR blocks comes directly from Amazon and
100
+ # can be found here: https://forums.aws.amazon.com/ann.jspa?annID=2051
101
+ 'cf-mirror.rightscale.com' => [ '54.192.0.0/16',
102
+ '54.230.0.0/16',
103
+ '54.239.128.0/18',
104
+ '54.240.128.0/18',
105
+ '204.246.164.0/22',
106
+ '204.246.168.0/22',
107
+ '204.246.174.0/23',
108
+ '204.246.176.0/20',
109
+ '205.251.192.0/19',
110
+ '205.251.249.0/24',
111
+ '205.251.250.0/23',
112
+ '205.251.252.0/23',
113
+ '205.251.254.0/24',
114
+ '216.137.32.0/19' ],
115
+ }.freeze
34
116
 
35
117
  # Resolve a set of DNS hostnames to the individual IP addresses to which they map. Only handles
36
118
  # IPv4 addresses.
@@ -81,10 +163,37 @@ module RightSupport::Net
81
163
  # @raise URI::InvalidURIError if endpoints contains an invalid or URI
82
164
  # @raise SocketError if endpoints contains an invalid or unresolvable hostname
83
165
  def self.resolve(endpoints, opts={})
84
- resolved_hostnames = resolve_with_hostnames(endpoints, opts)
166
+ opts = DEFAULT_RESOLVE_OPTIONS.merge(opts)
167
+ endpoints = Array(endpoints)
168
+
169
+ retries = 0
85
170
  resolved_endpoints = []
86
- resolved_hostnames.each_value{ |v| resolved_endpoints.concat(v) }
87
- return resolved_endpoints
171
+
172
+ endpoints.each do |endpoint|
173
+ begin
174
+ resolved_endpoint = nil
175
+ if endpoint.include?(':')
176
+ # It contains a colon, therefore it must be a URI -- we don't support IPv6
177
+ uri = URI.parse(endpoint)
178
+ hostname = uri.host
179
+ raise URI::InvalidURIError, "Could not parse host component of URI" unless hostname
180
+
181
+ resolved_endpoint = resolve_endpoint(hostname, opts.merge(:uri=>uri))
182
+ else
183
+ resolved_endpoint = resolve_endpoint(endpoint, opts)
184
+ end
185
+ resolved_endpoints << resolved_endpoint
186
+ rescue SocketError => e
187
+ retries += 1
188
+ if retries < opts[:retry]
189
+ retry
190
+ else
191
+ raise
192
+ end
193
+ end
194
+ end
195
+
196
+ resolved_endpoints
88
197
  end
89
198
 
90
199
  # Similar to resolve, but return a hash of { hostnames => [endpoints] }
@@ -113,52 +222,44 @@ module RightSupport::Net
113
222
  # @raise URI::InvalidURIError if endpoints contains an invalid or URI
114
223
  # @raise SocketError if endpoints contains an invalid or unresolvable hostname
115
224
  def self.resolve_with_hostnames(endpoints, opts={})
116
- opts = DEFAULT_RESOLVE_OPTIONS.merge(opts)
117
- endpoints = [endpoints] unless endpoints.respond_to?(:each)
118
-
119
225
  hostname_hash = {}
120
- retries = 0
121
-
122
- endpoints.each do |endpoint|
123
- begin
124
- resolved_endpoints = []
125
- if endpoint.include?(':')
126
- # It contains a colon, therefore it must be a URI -- we don't support IPv6
127
- uri = URI.parse(endpoint)
128
- hostname = uri.host
129
- raise URI::InvalidURIError, "Could not parse host component of URI" unless hostname
226
+ endpoints.each {|endpoint| hostname_hash[endpoint] = resolve(endpoint, opts).first}
227
+ hostname_hash
228
+ end
130
229
 
131
- infos = Socket.getaddrinfo(hostname, nil,
132
- opts[:address_family], opts[:socket_type], opts[:protocol])
230
+ private
133
231
 
134
- infos.each do |info|
135
- transformed_uri = uri.dup
136
- transformed_uri.host = info[3]
137
- resolved_endpoints << transformed_uri.to_s
138
- end
139
- else
140
- # No colon; it's a hostname or IP address
141
- infos = Socket.getaddrinfo(endpoint, nil,
142
- opts[:address_family], opts[:socket_type], opts[:protocol])
143
-
144
- infos.each do |info|
145
- resolved_endpoints << info[3]
146
- end
147
- end
148
- hostname_hash[endpoint.to_s] = resolved_endpoints
149
- rescue SocketError => e
150
- retries += 1
151
- if retries < opts[:retry]
152
- retry
153
- else
154
- raise
155
- end
156
- end
232
+ # Lookup the address(es) associated with the given endpoints.
233
+ #
234
+ # Perform an address lookup in this order:
235
+ # 1. Hardcoded translation table.
236
+ # 2. System address lookup (e.g. DNS, hosts files...etc)
237
+ #
238
+ # Although this method does accept IPv4 dotted-quad addresses as input, it does not accept
239
+ # IPv6 addresses. However, given hostnames as input, one _can_ resolve the hostnames
240
+ # to IPv6 addresses by specifying the appropriate address_family in the options.
241
+ #
242
+ # It should never be necessary to specify a different :socket_type or :protocol, but these
243
+ # options are exposed just in case.
244
+ #
245
+ # @param [String] endpoint as a hostname or IPv4 address
246
+ # @option opts [Integer] :retry number of times to retry SocketError; default is 3
247
+ # @option opts [Integer] :address_family what kind of IP addresses to resolve; default is Socket::AF_INET (IPv4)
248
+ # @option opts [Integer] :socket_type socket-type context to pass to getaddrinfo, default is Socket::SOCK_STREAM
249
+ # @option opts [Integer] :protocol protocol context to pass to getaddrinfo, default is Socket::IPPROTO_TCP
250
+ #
251
+ # @return [Array<String>] List of resolved IPv4/IPv6 addresses.
252
+ #
253
+ # @raise SocketError if endpoints contains an invalid or unresolvable hostname
254
+ def self.resolve_endpoint(endpoint, opts=DEFAULT_RESOLVE_OPTIONS)
255
+ if STATIC_HOSTNAME_TRANSLATIONS.has_key?(endpoint)
256
+ ResolvedEndpoint.new(STATIC_HOSTNAME_TRANSLATIONS[endpoint], opts)
257
+ else
258
+ infos = Socket.getaddrinfo(endpoint, nil,
259
+ opts[:address_family], opts[:socket_type], opts[:protocol])
260
+ ResolvedEndpoint.new(infos.map { |info| info[3] }, opts)
157
261
  end
158
-
159
- hostname_hash
160
262
  end
161
-
162
263
  end
163
264
  end
164
265
 
@@ -151,6 +151,10 @@ module RightSupport::Net
151
151
  request(:delete, *args)
152
152
  end
153
153
 
154
+ def patch(*args)
155
+ request(:patch, *args)
156
+ end
157
+
154
158
  # A very thin wrapper around RestClient::Request.execute.
155
159
  #
156
160
  # === Parameters
@@ -204,7 +204,7 @@ module RightSupport::Net
204
204
  # === Return
205
205
  # Return the first hostname that resolved to the IP (there should only ever be one)
206
206
  def lookup_hostname(endpoint)
207
- @resolved_hostnames.select{ |k,v| v.include?(endpoint) }.shift[0]
207
+ @resolved_hostnames.select{ |k,v| v.addresses.include?(endpoint) }.shift[0]
208
208
  end
209
209
 
210
210
  # Perform a request.
@@ -369,7 +369,7 @@ module RightSupport::Net
369
369
  def resolve
370
370
  @resolved_hostnames = RightSupport::Net::DNS.resolve_with_hostnames(@endpoints)
371
371
  resolved_endpoints = []
372
- @resolved_hostnames.each_value{ |v| resolved_endpoints.concat(v) }
372
+ @resolved_hostnames.each_value{ |v| resolved_endpoints.concat(v.addrs.map {|addr| addr.to_s}) }
373
373
  logger.info("RequestBalancer: resolved #{@endpoints.inspect} to #{resolved_endpoints.inspect}") if resolved_endpoints != @ips
374
374
  @ips = resolved_endpoints
375
375
  @policy.set_endpoints(@ips)
@@ -4,14 +4,14 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{right_support}
8
- s.version = "2.8.3"
7
+ s.name = "right_support"
8
+ s.version = "2.8.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tony Spataro", "Sergey Sergyenko", "Ryan Williamson", "Lee Kirchhoff", "Alexey Karpik", "Scott Messier"]
12
- s.date = %q{2013-10-03}
13
- s.description = %q{A toolkit of useful, reusable foundation code created by RightScale.}
14
- s.email = %q{support@rightscale.com}
12
+ s.date = "2013-12-18"
13
+ s.description = "A toolkit of useful, reusable foundation code created by RightScale."
14
+ s.email = "support@rightscale.com"
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
17
17
  "README.rdoc"
@@ -136,14 +136,13 @@ Gem::Specification.new do |s|
136
136
  "spec/validation/openssl_spec.rb",
137
137
  "spec/validation/ssh_spec.rb"
138
138
  ]
139
- s.homepage = %q{https://github.com/rightscale/right_support}
139
+ s.homepage = "https://github.com/rightscale/right_support"
140
140
  s.licenses = ["MIT"]
141
141
  s.require_paths = ["lib"]
142
- s.rubygems_version = %q{1.3.7}
143
- s.summary = %q{Reusable foundation code.}
142
+ s.rubygems_version = "1.8.15"
143
+ s.summary = "Reusable foundation code."
144
144
 
145
145
  if s.respond_to? :specification_version then
146
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
147
146
  s.specification_version = 3
148
147
 
149
148
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
@@ -58,14 +58,14 @@ describe RightSupport::DB::CassandraModel do
58
58
  ,'ring1, ring2, ring3' => ['ring1', 'ring2', 'ring3'] \
59
59
  ,['ring1', 'ring2', 'ring3'] => ['ring1', 'ring2', 'ring3']
60
60
  }.each do |config_string, congig_test|
61
- it "shoudl successfully intialize from #{config_string.inspect}" do
61
+ it "should successfully intialize from #{config_string.inspect}" do
62
62
 
63
63
 
64
64
  old_rack_env = ENV['RACK_ENV']
65
65
  begin
66
66
  ENV['RACK_ENV'] = env
67
67
 
68
- flexmock(Cassandra).should_receive(:new).with(/#{default_keyspace}_#{env}/, congig_test , {:timeout=>10}).and_return(default_keyspace_connection)
68
+ flexmock(Cassandra).should_receive(:new).with(/#{default_keyspace}_#{env}/, congig_test , {:timeout=>10, :server_retry_period=>nil}).and_return(default_keyspace_connection)
69
69
  default_keyspace_connection.should_receive(:disable_node_auto_discovery!).and_return(true)
70
70
 
71
71
 
@@ -81,4 +81,4 @@ describe RightSupport::DB::CassandraModel do
81
81
  end
82
82
  end
83
83
  end
84
- end
84
+ end
data/spec/net/dns_spec.rb CHANGED
@@ -22,7 +22,7 @@ describe RightSupport::Net::DNS do
22
22
  context :resolve do
23
23
  context 'given default :retry => 3' do
24
24
  let(:endpoint) { 'www.example.com' }
25
- let(:output) { ['1.1.1.1', '2.2.2.2'] }
25
+ let(:output) { [RightSupport::Net::ResolvedEndpoint.new(['1.1.1.1', '2.2.2.2'])] }
26
26
 
27
27
  it 'retries SocketError' do
28
28
  mock_getaddrinfo('www.example.com', SocketError)
@@ -55,7 +55,7 @@ describe RightSupport::Net::DNS do
55
55
  context 'given various endpoint formats' do
56
56
  context 'e.g. a DNS hostname' do
57
57
  let(:endpoint) { 'www.example.com' }
58
- let(:output) { ['1.1.1.1', '2.2.2.2'] }
58
+ let(:output) { [RightSupport::Net::ResolvedEndpoint.new(['1.1.1.1', '2.2.2.2'])] }
59
59
 
60
60
  it 'resolves to IP addresses' do
61
61
  mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
@@ -65,7 +65,7 @@ describe RightSupport::Net::DNS do
65
65
 
66
66
  context 'e.g. an IPv4 address' do
67
67
  let(:endpoint) { '127.0.0.1' }
68
- let(:output) { ['127.0.0.1'] }
68
+ let(:output) { [RightSupport::Net::ResolvedEndpoint.new(['127.0.0.1'])] }
69
69
 
70
70
  it 'resolves to the same address' do
71
71
  mock_getaddrinfo('127.0.0.1', ['127.0.0.1'])
@@ -79,7 +79,7 @@ describe RightSupport::Net::DNS do
79
79
 
80
80
  it 'resolves to URLs with addresses substituted' do
81
81
  mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
82
- subject.resolve(endpoint).should == output
82
+ subject.resolve(endpoint).first.addrs.should == output
83
83
  end
84
84
 
85
85
  context 'with a path component' do
@@ -88,7 +88,7 @@ describe RightSupport::Net::DNS do
88
88
 
89
89
  it 'resolves to URLs with path component preserved' do
90
90
  mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
91
- subject.resolve(endpoint).should == output
91
+ subject.resolve(endpoint).first.addrs.should == output
92
92
  end
93
93
  end
94
94
  end
@@ -111,13 +111,13 @@ describe RightSupport::Net::DNS do
111
111
 
112
112
  context 'e.g. several hostnames' do
113
113
  let(:endpoints) { ['www.example.com', 'www.example.net'] }
114
- let(:output) { ['1.1.1.1', '2.2.2.2', '3.3.3.3', '4.4.4.4'] }
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
115
 
116
116
  it 'resolves to IP addresses' do
117
117
  mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
118
118
  mock_getaddrinfo('www.example.net', ['3.3.3.3', '4.4.4.4'])
119
119
 
120
- subject.resolve(endpoints).sort.should == output
120
+ subject.resolve(endpoints).should == output
121
121
  end
122
122
  end
123
123
 
@@ -129,7 +129,42 @@ describe RightSupport::Net::DNS do
129
129
  mock_getaddrinfo('www.example.com', ['1.1.1.1', '2.2.2.2'])
130
130
  mock_getaddrinfo('www.example.net', ['3.3.3.3', '4.4.4.4'])
131
131
 
132
- subject.resolve(endpoints).sort.should == output.sort
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
133
168
  end
134
169
  end
135
170
  end
@@ -138,8 +173,8 @@ describe RightSupport::Net::DNS do
138
173
  context :resolve_with_hostnames do
139
174
  context 'given common inputs' do
140
175
  let(:endpoints) { ['www.example1.com','www.example2.com'] }
141
- let(:output) { {'www.example1.com'=>['1.1.1.1', '2.2.2.2'],
142
- 'www.example2.com'=>['3.3.3.3', '4.4.4.4']} }
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'])} }
143
178
 
144
179
  it 'resolves to IP addresses' do
145
180
  mock_getaddrinfo('www.example1.com', ['1.1.1.1', '2.2.2.2'])
@@ -91,6 +91,10 @@ describe RightSupport::Net::RequestBalancer do
91
91
  @health_checks.should == expect * (yellow_states - 1)
92
92
  end
93
93
 
94
+ def make_endpoint(addresses)
95
+ RightSupport::Net::ResolvedEndpoint.new(addresses)
96
+ end
97
+
94
98
  context :initialize do
95
99
  it 'requires a list of endpoint URLs' do
96
100
  lambda do
@@ -267,7 +271,7 @@ describe RightSupport::Net::RequestBalancer do
267
271
  context 'with :resolve option' do
268
272
  before(:each) do
269
273
  flexmock(RightSupport::Net::DNS).should_receive(:resolve_with_hostnames).
270
- with(['host1', 'host2']).and_return({'host1' => ['1.1.1.1', '2.2.2.2'], 'host2' => ['3.3.3.3']})
274
+ with(['host1', 'host2']).and_return({'host1' => make_endpoint(['1.1.1.1', '2.2.2.2']), 'host2' => make_endpoint(['3.3.3.3'])})
271
275
  @balancer = RightSupport::Net::RequestBalancer.new(['host1', 'host2'], :resolve => 15)
272
276
  end
273
277
 
@@ -482,10 +486,10 @@ describe RightSupport::Net::RequestBalancer do
482
486
  context 'with :resolve option' do
483
487
  before(:each) do
484
488
  @endpoints = ['host1', 'host2', 'host3', 'host4']
485
- @resolved_set_1 = {'host1' => ['1.1.1.1', '2.2.2.2', '3.3.3.3', '4.4.4.4']}
486
- @resolved_set_2 = {'host1'=>['5.5.5.5'],'host2'=>['6.6.6.6'],'host3'=>['7.7.7.7'],'host4'=>['8.8.8.8']}
489
+ @resolved_set_1 = {'host1' => make_endpoint(['1.1.1.1', '2.2.2.2', '3.3.3.3', '4.4.4.4'])}
490
+ @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'])}
487
491
  @resolved_set_2_array = []
488
- @resolved_set_2.each_value{ |v| @resolved_set_2_array.concat(v) }
492
+ @resolved_set_2.each_value{ |v| @resolved_set_2_array.concat(v.addrs) }
489
493
  @dns = flexmock(RightSupport::Net::DNS)
490
494
  end
491
495
 
@@ -495,7 +499,7 @@ describe RightSupport::Net::RequestBalancer do
495
499
 
496
500
  @rb.request { true }
497
501
  @policy = @rb.instance_variable_get("@policy")
498
- @resolved_set_1['host1'].include?(@policy.next.first).should be_true
502
+ @resolved_set_1['host1'].addrs.include?(@policy.next.first).should be_true
499
503
  end
500
504
 
501
505
  it 're-resolves list of ip addresses if TTL is expired' do
@@ -504,7 +508,7 @@ describe RightSupport::Net::RequestBalancer do
504
508
 
505
509
  @rb.request { true }
506
510
  @policy = @rb.instance_variable_get("@policy")
507
- @resolved_set_1['host1'].include?(@policy.next.first).should be_true
511
+ @resolved_set_1['host1'].addrs.include?(@policy.next.first).should be_true
508
512
 
509
513
  @rb.instance_variable_set("@resolved_at", Time.now.to_i - 16)
510
514
  @rb.request { true }
@@ -431,7 +431,7 @@ describe RightSupport::Stats do
431
431
  "/opt/rightscale/right_link/common/lib/common/serializer.rb:133:in `cascade_serializers'"}]}}
432
432
  RightSupport::Stats.exceptions_str(exceptions, "").should ==
433
433
  "receive total: 2, most recent:\n" +
434
- "(2) Sun Aug 11 15:26:19 RightScale::Serializer::SerializationError: Could not \n" +
434
+ "(2) #{Time.at(exceptions['receive']['recent'].first['when']).strftime("%a %b %d %H:%M:%S")} RightScale::Serializer::SerializationError: Could not \n" +
435
435
  " load packet using [RightScale::SecureSerializer] (Failed to load with \n" +
436
436
  " RightScale::SecureSerializer (TypeError: can't convert nil into String in /opt/\n" +
437
437
  " rightscale/right_link/common/lib/common/security/signature.rb:56:in \n" +
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: right_support
3
3
  version: !ruby/object:Gem::Version
4
- hash: 41
5
- prerelease: false
4
+ hash: 35
5
+ prerelease:
6
6
  segments:
7
7
  - 2
8
8
  - 8
9
- - 3
10
- version: 2.8.3
9
+ - 6
10
+ version: 2.8.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tony Spataro
@@ -20,8 +20,7 @@ autorequire:
20
20
  bindir: bin
21
21
  cert_chain: []
22
22
 
23
- date: 2013-10-21 00:00:00 -07:00
24
- default_executable:
23
+ date: 2013-12-19 00:00:00 Z
25
24
  dependencies:
26
25
  - !ruby/object:Gem::Dependency
27
26
  version_requirements: &id001 !ruby/object:Gem::Requirement
@@ -34,10 +33,10 @@ dependencies:
34
33
  - 0
35
34
  - 9
36
35
  version: "0.9"
37
- requirement: *id001
38
36
  type: :development
39
- name: rake
37
+ requirement: *id001
40
38
  prerelease: false
39
+ name: rake
41
40
  - !ruby/object:Gem::Dependency
42
41
  version_requirements: &id002 !ruby/object:Gem::Requirement
43
42
  none: false
@@ -50,10 +49,10 @@ dependencies:
50
49
  - 8
51
50
  - 3
52
51
  version: 1.8.3
53
- requirement: *id002
54
52
  type: :development
55
- name: jeweler
53
+ requirement: *id002
56
54
  prerelease: false
55
+ name: jeweler
57
56
  - !ruby/object:Gem::Dependency
58
57
  version_requirements: &id003 !ruby/object:Gem::Requirement
59
58
  none: false
@@ -65,10 +64,10 @@ dependencies:
65
64
  - 1
66
65
  - 0
67
66
  version: "1.0"
68
- requirement: *id003
69
67
  type: :development
70
- name: right_develop
68
+ requirement: *id003
71
69
  prerelease: false
70
+ name: right_develop
72
71
  - !ruby/object:Gem::Dependency
73
72
  version_requirements: &id004 !ruby/object:Gem::Requirement
74
73
  none: false
@@ -80,10 +79,10 @@ dependencies:
80
79
  - 0
81
80
  - 10
82
81
  version: "0.10"
83
- requirement: *id004
84
82
  type: :development
85
- name: ruby-debug
83
+ requirement: *id004
86
84
  prerelease: false
85
+ name: ruby-debug
87
86
  - !ruby/object:Gem::Dependency
88
87
  version_requirements: &id005 !ruby/object:Gem::Requirement
89
88
  none: false
@@ -96,10 +95,10 @@ dependencies:
96
95
  - 11
97
96
  - 6
98
97
  version: 0.11.6
99
- requirement: *id005
100
98
  type: :development
101
- name: ruby-debug19
99
+ requirement: *id005
102
100
  prerelease: false
101
+ name: ruby-debug19
103
102
  - !ruby/object:Gem::Dependency
104
103
  version_requirements: &id006 !ruby/object:Gem::Requirement
105
104
  none: false
@@ -112,10 +111,10 @@ dependencies:
112
111
  - 4
113
112
  - 2
114
113
  version: 2.4.2
115
- requirement: *id006
116
114
  type: :development
117
- name: rdoc
115
+ requirement: *id006
118
116
  prerelease: false
117
+ name: rdoc
119
118
  - !ruby/object:Gem::Dependency
120
119
  version_requirements: &id007 !ruby/object:Gem::Requirement
121
120
  none: false
@@ -127,10 +126,10 @@ dependencies:
127
126
  - 1
128
127
  - 0
129
128
  version: "1.0"
130
- requirement: *id007
131
129
  type: :development
132
- name: flexmock
130
+ requirement: *id007
133
131
  prerelease: false
132
+ name: flexmock
134
133
  - !ruby/object:Gem::Dependency
135
134
  version_requirements: &id008 !ruby/object:Gem::Requirement
136
135
  none: false
@@ -143,10 +142,10 @@ dependencies:
143
142
  - 0
144
143
  - 0
145
144
  version: 1.0.0
146
- requirement: *id008
147
145
  type: :development
148
- name: syntax
146
+ requirement: *id008
149
147
  prerelease: false
148
+ name: syntax
150
149
  - !ruby/object:Gem::Dependency
151
150
  version_requirements: &id009 !ruby/object:Gem::Requirement
152
151
  none: false
@@ -158,10 +157,10 @@ dependencies:
158
157
  - 1
159
158
  - 5
160
159
  version: "1.5"
161
- requirement: *id009
162
160
  type: :development
163
- name: nokogiri
161
+ requirement: *id009
164
162
  prerelease: false
163
+ name: nokogiri
165
164
  description: A toolkit of useful, reusable foundation code created by RightScale.
166
165
  email: support@rightscale.com
167
166
  executables: []
@@ -290,7 +289,6 @@ files:
290
289
  - spec/stats/helpers_spec.rb
291
290
  - spec/validation/openssl_spec.rb
292
291
  - spec/validation/ssh_spec.rb
293
- has_rdoc: true
294
292
  homepage: https://github.com/rightscale/right_support
295
293
  licenses:
296
294
  - MIT
@@ -320,7 +318,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
320
318
  requirements: []
321
319
 
322
320
  rubyforge_project:
323
- rubygems_version: 1.3.7
321
+ rubygems_version: 1.8.15
324
322
  signing_key:
325
323
  specification_version: 3
326
324
  summary: Reusable foundation code.