right_support 2.6.16 → 2.6.17

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.
@@ -69,14 +69,47 @@ module RightSupport::Net
69
69
  # @raise URI::InvalidURIError if endpoints contains an invalid or URI
70
70
  # @raise SocketError if endpoints contains an invalid or unresolvable hostname
71
71
  def self.resolve(endpoints, opts={})
72
+ resolved_hostnames = resolve_with_hostnames(endpoints, opts)
73
+ resolved_endpoints = []
74
+ resolved_hostnames.each_value{ |v| resolved_endpoints.concat(v) }
75
+ return resolved_endpoints
76
+ end
77
+
78
+ # Similar to resolve, but return a hash of { hostnames => [endpoints] }
79
+ #
80
+ # Perform DNS resolution on a set of endpoints, where the endpoints may be hostnames or URIs.
81
+ # Expand out the list to include one entry per distinct address that is assigned to a given
82
+ # hostname, but preserve other aspects of the endpoints --. URIs will remain URIs with the
83
+ # same protocol, path-info, and so forth, but the hostname component will be resolved to IP
84
+ # addresses and the URI will be duplicated in the output, once for each distinct IP address.
85
+ #
86
+ # Although this method does accept IPv4 dotted-quad addresses as input, it does not accept
87
+ # IPv6 addresses. However, given hostnames or URIs as input, one _can_ resolve the hostnames
88
+ # to IPv6 addresses by specifying the appropriate address_family in the options.
89
+ #
90
+ # It should never be necessary to specify a different :socket_type or :protocol, but these
91
+ # options are exposed just in case.
92
+ #
93
+ # @param [Array<String>] endpoints a mixed list of hostnames, IPv4 addresses or URIs that contain them
94
+ # @option opts [Integer] :retry number of times to retry SocketError; default is 3
95
+ # @option opts [Integer] :address_family what kind of IP addresses to resolve; default is Socket::AF_INET (IPv4)
96
+ # @option opts [Integer] :socket_type socket-type context to pass to getaddrinfo, default is Socket::SOCK_STREAM
97
+ # @option opts [Integer] :protocol protocol context to pass to getaddrinfo, default is Socket::IPPROTO_TCP
98
+ #
99
+ # @return [Hash<hostnames => [endpoints]>] Hash with keys of hostnames and values of arrays of all associated IP addresses
100
+ #
101
+ # @raise URI::InvalidURIError if endpoints contains an invalid or URI
102
+ # @raise SocketError if endpoints contains an invalid or unresolvable hostname
103
+ def self.resolve_with_hostnames(endpoints, opts={})
72
104
  opts = DEFAULT_RESOLVE_OPTIONS.merge(opts)
73
105
  endpoints = [endpoints] unless endpoints.respond_to?(:each)
74
106
 
75
- resolved_endpoints = []
107
+ hostname_hash = {}
76
108
  retries = 0
77
109
 
78
110
  endpoints.each do |endpoint|
79
111
  begin
112
+ resolved_endpoints = []
80
113
  if endpoint.include?(':')
81
114
  # It contains a colon, therefore it must be a URI -- we don't support IPv6
82
115
  uri = URI.parse(endpoint)
@@ -100,6 +133,7 @@ module RightSupport::Net
100
133
  resolved_endpoints << info[3]
101
134
  end
102
135
  end
136
+ hostname_hash[endpoint.to_s] = resolved_endpoints
103
137
  rescue SocketError => e
104
138
  retries += 1
105
139
  if retries < opts[:retry]
@@ -110,7 +144,7 @@ module RightSupport::Net
110
144
  end
111
145
  end
112
146
 
113
- resolved_endpoints
147
+ hostname_hash
114
148
  end
115
149
 
116
150
  end
@@ -190,6 +190,17 @@ module RightSupport::Net
190
190
  end
191
191
  end
192
192
 
193
+ # Un-resolve an IP address.
194
+ #
195
+ # === Parameters
196
+ # endpoint:: a network endpoint (e.g. HTTP URL) to be un-resolved
197
+ #
198
+ # === Return
199
+ # Return the first hostname that resolved to the IP (there should only ever be one)
200
+ def lookup_hostname(endpoint)
201
+ @resolved_hostnames.select{ |k,v| v.include?(endpoint) }.shift[0]
202
+ end
203
+
193
204
  # Perform a request.
194
205
  #
195
206
  # === Block
@@ -268,11 +279,18 @@ module RightSupport::Net
268
279
  return result if complete
269
280
 
270
281
  # Produce a summary message for the exception that gives a bit of detail
271
- msg = []
282
+ msg = []
283
+ stats = get_stats
272
284
  exceptions.each_pair do |endpoint, list|
273
285
  summary = []
274
286
  list.each { |e| summary << e.class }
275
- msg << "'#{endpoint}' => [#{summary.uniq.join(', ')}]"
287
+ health = stats[endpoint] if stats[endpoint] != 'n/a'
288
+ if @resolved_hostnames
289
+ hostname = lookup_hostname(endpoint)
290
+ msg << "'#{hostname}' (#{endpoint}#{", "+health if health}) => [#{summary.uniq.join(', ')}]"
291
+ else
292
+ msg << "'#{endpoint}' #{"("+health+")" if health} => [#{summary.uniq.join(', ')}]"
293
+ end
276
294
  end
277
295
  message = "Request failed after #{n} tries to #{exceptions.keys.size} endpoints: (#{msg.join(', ')})"
278
296
 
@@ -284,7 +302,7 @@ module RightSupport::Net
284
302
  # its endpoints. Merely proxies the balancing policy's get_stats method. If
285
303
  # no method exists in the balancing policy, a hash of endpoints with "n/a" is
286
304
  # returned.
287
- #
305
+ #
288
306
  # Examples
289
307
  #
290
308
  # A RequestBalancer created with endpoints [1,2,3,4,5] and using a HealthCheck
@@ -310,8 +328,9 @@ module RightSupport::Net
310
328
  def handle_exception(endpoint, e, t0)
311
329
  fatal = fatal_exception?(e)
312
330
  duration = sprintf('%.4f', Time.now - t0)
313
- msg = "RequestBalancer: rescued #{fatal ? 'fatal' : 'retryable'} #{e.class.name} " +
314
- "during request to #{endpoint}: #{e.message} after #{duration} seconds"
331
+ ept = @resolved_hostnames ? "#{lookup_hostname(endpoint)}(#{endpoint})" : "#{endpoint}"
332
+ msg = "RequestBalancer: rescued #{fatal ? 'fatal' : 'retryable'} #{e.class.name} " +
333
+ "during request to #{ept}: #{e.message} after #{duration} seconds"
315
334
  logger.error msg
316
335
  @options[:on_exception].call(fatal, e, endpoint) if @options[:on_exception]
317
336
 
@@ -342,7 +361,9 @@ module RightSupport::Net
342
361
  end
343
362
 
344
363
  def resolve
345
- resolved_endpoints = RightSupport::Net::DNS.resolve(@endpoints)
364
+ @resolved_hostnames = RightSupport::Net::DNS.resolve_with_hostnames(@endpoints)
365
+ resolved_endpoints = []
366
+ @resolved_hostnames.each_value{ |v| resolved_endpoints.concat(v) }
346
367
  logger.info("RequestBalancer: resolved #{@endpoints.inspect} to #{resolved_endpoints.inspect}")
347
368
  @ips = resolved_endpoints
348
369
  @policy.set_endpoints(@ips)
@@ -106,13 +106,15 @@ module RightSupport
106
106
  # === Return
107
107
  # (String|Array|Hash):: Value(s) converted to decimal digit string
108
108
  def self.enough_precision(value)
109
- scale = [1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0]
110
- enough = lambda { |v| (v >= 10.0 ? 0 :
111
- (v >= 1.0 ? 1 :
112
- (v >= 0.1 ? 2 :
113
- (v >= 0.01 ? 3 :
114
- (v > 0.001 ? 4 :
115
- (v > 0.0 ? 5 : 0)))))) }
109
+ scale = [1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0]
110
+ enough = lambda { |v| v = v.abs
111
+ (v >= 10.0 ? 0 :
112
+ (v >= 1.0 ? 1 :
113
+ (v >= 0.1 ? 2 :
114
+ (v >= 0.01 ? 3 :
115
+ (v >= 0.001 ? 4 :
116
+ (v >= 0.0001 ? 5 :
117
+ (v >= 0.0000005 ? 6 : 0))))))) }
116
118
  digit_str = lambda { |p, v| sprintf("%.#{p}f", (v * scale[p]).round / scale[p])}
117
119
 
118
120
  if value.is_a?(Float)
@@ -7,8 +7,8 @@ spec = Gem::Specification.new do |s|
7
7
  s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
8
8
 
9
9
  s.name = 'right_support'
10
- s.version = '2.6.16'
11
- s.date = '2013-01-28'
10
+ s.version = '2.6.17'
11
+ s.date = '2013-01-29'
12
12
 
13
13
  s.authors = ['Tony Spataro', 'Sergey Sergyenko', 'Ryan Williamson', 'Lee Kirchhoff', 'Sergey Enin', 'Alexey Karpik', 'Scott Messier']
14
14
  s.email = 'support@rightscale.com'
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: 55
4
+ hash: 53
5
5
  prerelease: false
6
6
  segments:
7
7
  - 2
8
8
  - 6
9
- - 16
10
- version: 2.6.16
9
+ - 17
10
+ version: 2.6.17
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tony Spataro
@@ -21,7 +21,7 @@ autorequire:
21
21
  bindir: bin
22
22
  cert_chain: []
23
23
 
24
- date: 2013-01-28 00:00:00 -08:00
24
+ date: 2013-01-29 00:00:00 -08:00
25
25
  default_executable:
26
26
  dependencies: []
27
27