right_support 2.6.16 → 2.6.17

Sign up to get free protection for your applications and to get access to all the features.
@@ -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