right_support 2.6.8 → 2.6.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,11 +9,21 @@
9
9
  # License Agreement between RightScale.com, Inc. and the licensee.
10
10
 
11
11
  require 'socket'
12
+ require 'uri'
12
13
 
13
14
  module RightSupport::Net
14
-
15
15
  module DNS
16
+ DEFAULT_RESOLVE_OPTIONS = {
17
+ :address_family => Socket::AF_INET,
18
+ :socket_type => Socket::SOCK_STREAM,
19
+ :protocol => Socket::IPPROTO_TCP,
20
+ :retry => 3
21
+ }
16
22
 
23
+ # Resolve a set of DNS hostnames to the individual IP addresses to which they map. Only handles
24
+ # IPv4 addresses.
25
+ #
26
+ # @deprecated due to broken error handling - do not use; please use #resolve instead!
17
27
  def self.resolve_all_ip_addresses(hostnames)
18
28
  ips = []
19
29
  hostnames = [hostnames] unless hostnames.respond_to?(:each)
@@ -35,6 +45,74 @@ module RightSupport::Net
35
45
  ips
36
46
  end
37
47
 
48
+ # Perform DNS resolution on a set of endpoints, where the endpoints may be hostnames or URIs.
49
+ # Expand out the list to include one entry per distinct address that is assigned to a given
50
+ # hostname, but preserve other aspects of the endpoints --. URIs will remain URIs with the
51
+ # same protocol, path-info, and so forth, but the hostname component will be resolved to IP
52
+ # addresses and the URI will be duplicated in the output, once for each distinct IP address.
53
+ #
54
+ # Although this method does accept IPv4 dotted-quad addresses as input, it does not accept
55
+ # IPv6 addresses. However, given hostnames or URIs as input, one _can_ resolve the hostnames
56
+ # to IPv6 addresses by specifying the appropriate address_family in the options.
57
+ #
58
+ # It should never be necessary to specify a different :socket_type or :protocol, but these
59
+ # options are exposed just in case.
60
+ #
61
+ # @param [Array<String>] endpoints a mixed list of hostnames, IPv4 addresses or URIs that contain them
62
+ # @option opts [Integer] :retry number of times to retry SocketError; default is 3
63
+ # @option opts [Integer] :address_family what kind of IP addresses to resolve; default is Socket::AF_INET (IPv4)
64
+ # @option opts [Integer] :socket_type socket-type context to pass to getaddrinfo, default is Socket::SOCK_STREAM
65
+ # @option opts [Integer] :protocol protocol context to pass to getaddrinfo, default is Socket::IPPROTO_TCP
66
+ #
67
+ # @return [Array<String>] larger list of endpoints with all hostnames resolved to IP addresses
68
+ #
69
+ # @raise URI::InvalidURIError if endpoints contains an invalid or URI
70
+ # @raise SocketError if endpoints contains an invalid or unresolvable hostname
71
+ def self.resolve(endpoints, opts={})
72
+ opts = DEFAULT_RESOLVE_OPTIONS.merge(opts)
73
+ endpoints = [endpoints] unless endpoints.respond_to?(:each)
74
+
75
+ resolved_endpoints = []
76
+ retries = 0
77
+
78
+ endpoints.each do |endpoint|
79
+ begin
80
+ if endpoint.include?(':')
81
+ # It contains a colon, therefore it must be a URI -- we don't support IPv6
82
+ uri = URI.parse(endpoint)
83
+ hostname = uri.host
84
+ raise URI::InvalidURIError, "Could not parse host component of URI" unless hostname
85
+
86
+ infos = Socket.getaddrinfo(hostname, nil,
87
+ opts[:address_family], opts[:socket_type], opts[:protocol])
88
+
89
+ infos.each do |info|
90
+ transformed_uri = uri.dup
91
+ transformed_uri.host = info[3]
92
+ resolved_endpoints << transformed_uri.to_s
93
+ end
94
+ else
95
+ # No colon; it's a hostname or IP address
96
+ infos = Socket.getaddrinfo(endpoint, nil,
97
+ opts[:address_family], opts[:socket_type], opts[:protocol])
98
+
99
+ infos.each do |info|
100
+ resolved_endpoints << info[3]
101
+ end
102
+ end
103
+ rescue SocketError => e
104
+ retries += 1
105
+ if retries < opts[:retry]
106
+ retry
107
+ else
108
+ raise e
109
+ end
110
+ end
111
+ end
112
+
113
+ resolved_endpoints
114
+ end
115
+
38
116
  end
39
117
  end
40
118
 
@@ -112,12 +112,13 @@ module RightSupport::Net
112
112
  end
113
113
 
114
114
  def resolve(endpoints)
115
- endpoints = RightSupport::Net::DNS.resolve_all_ip_addresses(endpoints)
115
+ resolved_endpoints = RightSupport::Net::DNS.resolve(endpoints)
116
116
  @resolved_at = Time.now.to_i
117
- endpoints
117
+ logger.info("RequestBalancer: resolved #{endpoints.inspect} to #{resolved_endpoints.inspect}")
118
+ resolved_endpoints
118
119
  end
119
120
 
120
- def expired?
121
+ def need_resolve?
121
122
  @options[:resolve] && Time.now.to_i - @resolved_at > @options[:resolve]
122
123
  end
123
124
 
@@ -125,12 +126,20 @@ module RightSupport::Net
125
126
  # creation time; however, the ordering of the endpoints does not change thereafter
126
127
  # and the sequence is tried from the beginning for every request.
127
128
  #
129
+ # If you pass the :resolve option, then the list of endpoints is treated as a list
130
+ # of hostnames (or URLs containing hostnames) and the list is expanded out into a
131
+ # larger list with each hostname replaced by several entries, one for each of its IP
132
+ # addresses. If a single DNS hostname is associated with multiple A records, the
133
+ # :resolve option allows the balancer to treat each backing server as a distinct
134
+ # endpoint with its own health state, etc.
135
+ #
128
136
  # === Parameters
129
137
  # endpoints(Array):: a set of network endpoints (e.g. HTTP URLs) to be load-balanced
130
138
  #
131
139
  # === Options
132
140
  # retry:: a Class, array of Class or decision Proc to determine whether to keep retrying; default is to try all endpoints
133
141
  # fatal:: a Class, array of Class, or decision Proc to determine whether an exception is fatal and should not be retried
142
+ # resolve(Integer):: how often to re-resolve DNS hostnames of endpoints; default is nil (never resolve)
134
143
  # on_exception(Proc):: notification hook that accepts three arguments: whether the exception is fatal, the exception itself,
135
144
  # and the endpoint for which the exception happened
136
145
  # health_check(Proc):: callback that allows balancer to check an endpoint health; should raise an exception if the endpoint
@@ -197,7 +206,8 @@ module RightSupport::Net
197
206
  # Return the first non-nil value provided by the block.
198
207
  def request
199
208
  raise ArgumentError, "Must call this method with a block" unless block_given?
200
- if self.expired?
209
+
210
+ if need_resolve?
201
211
  @ips = self.resolve(@endpoints)
202
212
  @policy.set_endpoints(@ips)
203
213
  end
@@ -9,10 +9,12 @@
9
9
  # License Agreement between RightScale.com, Inc. and the licensee.
10
10
 
11
11
  module RightSupport::Net
12
- # Class provides S3 functionality.
12
+ # Class that provides S3 functionality.
13
13
  # As part of RightSupport S3Helper does not include Rightscale::S3 and Encryptor modules.
14
14
  # This modules must be included in application.
15
15
  #
16
+ # @deprecated do not use; please use RightServices::Util::S3Storage instead!
17
+ #
16
18
  # Example:
17
19
  #
18
20
  # require 'right_support'
@@ -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.8'
11
- s.date = '2012-12-14'
10
+ s.version = '2.6.9'
11
+ s.date = '2013-01-01'
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: 7
4
+ hash: 5
5
5
  prerelease: false
6
6
  segments:
7
7
  - 2
8
8
  - 6
9
- - 8
10
- version: 2.6.8
9
+ - 9
10
+ version: 2.6.9
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: 2012-12-14 00:00:00 -08:00
24
+ date: 2013-01-01 00:00:00 -08:00
25
25
  default_executable:
26
26
  dependencies: []
27
27