right_support 1.2.0 → 1.3.0
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.
- data/LICENSE +1 -1
- data/lib/right_support/db/cassandra_model.rb +75 -4
- data/lib/right_support/net/balancing/health_check.rb +24 -19
- data/lib/right_support/net/balancing/round_robin.rb +8 -3
- data/lib/right_support/net/balancing/sticky_policy.rb +82 -0
- data/lib/right_support/net/dns.rb +41 -0
- data/lib/right_support/net/request_balancer.rb +21 -3
- data/lib/right_support/net/ssl/open_ssl_patch.rb +67 -0
- data/lib/right_support/net/ssl.rb +33 -0
- data/lib/right_support/net.rb +3 -1
- data/lib/right_support/stats/helpers.rb +59 -24
- data/lib/right_support.rb +3 -0
- data/right_support.gemspec +2 -2
- metadata +8 -4
data/LICENSE
CHANGED
@@ -72,7 +72,43 @@ rescue LoadError => e
|
|
72
72
|
# Missing 'cassandra/0.8' indicates that the cassandra gem is not installed; we can ignore this
|
73
73
|
end
|
74
74
|
|
75
|
+
# monkey patch thrift to work with jruby
|
76
|
+
if (RUBY_PLATFORM =~ /java/)
|
77
|
+
begin
|
78
|
+
require 'thrift'
|
79
|
+
module Thrift
|
80
|
+
class Socket
|
81
|
+
def open
|
82
|
+
begin
|
83
|
+
addrinfo = ::Socket::getaddrinfo(@host, @port).first
|
84
|
+
@handle = ::Socket.new(addrinfo[4], ::Socket::SOCK_STREAM, 0)
|
85
|
+
sockaddr = ::Socket.sockaddr_in(addrinfo[1], addrinfo[3])
|
86
|
+
begin
|
87
|
+
@handle.connect_nonblock(sockaddr)
|
88
|
+
rescue Errno::EINPROGRESS
|
89
|
+
resp = IO.select(nil, [ @handle ], nil, @timeout) # 3 lines removed here, 1 line added
|
90
|
+
begin
|
91
|
+
@handle.connect_nonblock(sockaddr)
|
92
|
+
rescue Errno::EISCONN
|
93
|
+
end
|
94
|
+
end
|
95
|
+
@handle
|
96
|
+
rescue StandardError => e
|
97
|
+
raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
rescue LoadError => e
|
103
|
+
# Make sure we're dealing with a legitimate missing-file LoadError
|
104
|
+
raise e unless e.message =~ /^no such file to load/
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
75
109
|
module RightSupport::DB
|
110
|
+
# Exception that indicates database configuration info is missing.
|
111
|
+
class MissingConfiguration < Exception; end
|
76
112
|
|
77
113
|
# Base class for a column family in a keyspace
|
78
114
|
# Used to access data persisted in Cassandra
|
@@ -116,14 +152,22 @@ module RightSupport::DB
|
|
116
152
|
|
117
153
|
# Client connected to Cassandra server
|
118
154
|
# Create connection if does not already exist
|
155
|
+
# Use BinaryProtocolAccelerated if it available
|
119
156
|
#
|
120
157
|
# === Return
|
121
158
|
# (Cassandra):: Client connected to server
|
122
159
|
def conn
|
123
160
|
return @@conn if @@conn
|
124
161
|
|
162
|
+
# TODO remove hidden dependency on ENV['RACK_ENV'] (maybe require config= to accept a sub hash?)
|
125
163
|
config = @@config[ENV["RACK_ENV"]]
|
126
|
-
|
164
|
+
raise MissingConfiguration, "CassandraModel config is missing a '#{ENV['RACK_ENV']}' section" unless config
|
165
|
+
|
166
|
+
thrift_client_options = {:timeout => RightSupport::DB::CassandraModel::DEFAULT_TIMEOUT}
|
167
|
+
thrift_client_options.merge!({:protocol => Thrift::BinaryProtocolAccelerated})\
|
168
|
+
if defined? Thrift::BinaryProtocolAccelerated
|
169
|
+
|
170
|
+
@@conn = Cassandra.new(keyspace, config["server"], thrift_client_options)
|
127
171
|
@@conn.disable_node_auto_discovery!
|
128
172
|
@@conn
|
129
173
|
end
|
@@ -162,6 +206,8 @@ module RightSupport::DB
|
|
162
206
|
|
163
207
|
# Get raw row(s) for specified primary key(s)
|
164
208
|
# Unless :count is specified, a maximum of 100 columns are retrieved
|
209
|
+
# except in the case of an individual primary key request, in which
|
210
|
+
# case all columns are retrieved
|
165
211
|
#
|
166
212
|
# === Parameters
|
167
213
|
# k(String|Array):: Individual primary key or list of keys on which to match
|
@@ -173,8 +219,25 @@ module RightSupport::DB
|
|
173
219
|
def real_get(k, opt = {})
|
174
220
|
if k.is_a?(Array)
|
175
221
|
do_op(:multi_get, column_family, k, opt)
|
176
|
-
|
222
|
+
elsif opt[:count]
|
177
223
|
do_op(:get, column_family, k, opt)
|
224
|
+
else
|
225
|
+
opt = opt.clone
|
226
|
+
opt[:start] ||= ""
|
227
|
+
opt[:count] = DEFAULT_COUNT
|
228
|
+
columns = Cassandra::OrderedHash.new
|
229
|
+
while true
|
230
|
+
chunk = do_op(:get, column_family, k, opt)
|
231
|
+
columns.merge!(chunk)
|
232
|
+
if chunk.size == opt[:count]
|
233
|
+
# Assume there are more chunks, use last key as start of next get
|
234
|
+
opt[:start] = chunk.keys.last
|
235
|
+
else
|
236
|
+
# This must be the last chunk
|
237
|
+
break
|
238
|
+
end
|
239
|
+
end
|
240
|
+
columns
|
178
241
|
end
|
179
242
|
end
|
180
243
|
|
@@ -312,12 +375,19 @@ module RightSupport::DB
|
|
312
375
|
end
|
313
376
|
|
314
377
|
# Reconnect to Cassandra server
|
378
|
+
# Use BinaryProtocolAccelerated if it available
|
315
379
|
#
|
316
380
|
# === Return
|
317
381
|
# true:: Always return true
|
318
382
|
def reconnect
|
319
383
|
config = @@config[ENV["RACK_ENV"]]
|
320
|
-
|
384
|
+
raise MissingConfiguration, "CassandraModel config is missing a '#{ENV['RACK_ENV']}' section" unless config
|
385
|
+
|
386
|
+
thrift_client_options = {:timeout => RightSupport::DB::CassandraModel::DEFAULT_TIMEOUT}
|
387
|
+
thrift_client_options.merge!({:protocol => Thrift::BinaryProtocolAccelerated})\
|
388
|
+
if defined? Thrift::BinaryProtocolAccelerated
|
389
|
+
|
390
|
+
@@conn = Cassandra.new(keyspace, config["server"], thrift_client_options)
|
321
391
|
@@conn.disable_node_auto_discovery!
|
322
392
|
true
|
323
393
|
end
|
@@ -329,7 +399,8 @@ module RightSupport::DB
|
|
329
399
|
def ring
|
330
400
|
conn.ring
|
331
401
|
end
|
332
|
-
|
402
|
+
|
403
|
+
end # self
|
333
404
|
|
334
405
|
attr_accessor :key, :attributes
|
335
406
|
|
@@ -30,7 +30,7 @@ module RightSupport::Net::Balancing
|
|
30
30
|
class EndpointsStack
|
31
31
|
DEFAULT_YELLOW_STATES = 4
|
32
32
|
DEFAULT_RESET_TIME = 300
|
33
|
-
|
33
|
+
|
34
34
|
def initialize(endpoints, yellow_states=nil, reset_time=nil, on_health_change=nil)
|
35
35
|
@endpoints = Hash.new
|
36
36
|
@yellow_states = yellow_states || DEFAULT_YELLOW_STATES
|
@@ -39,11 +39,11 @@ module RightSupport::Net::Balancing
|
|
39
39
|
@min_n_level = 0
|
40
40
|
endpoints.each { |ep| @endpoints[ep] = {:n_level => @min_n_level, :timestamp => 0} }
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
def sweep
|
44
44
|
@endpoints.each { |k,v| decrease_state(k, 0, Time.now) if Float(Time.now - v[:timestamp]) > @reset_time }
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
def sweep_and_return_yellow_and_green
|
48
48
|
sweep
|
49
49
|
@endpoints.select { |k,v| v[:n_level] < @yellow_states }
|
@@ -82,14 +82,15 @@ module RightSupport::Net::Balancing
|
|
82
82
|
@endpoints.each { |k, v| stats[k] = state_color(v[:n_level]) }
|
83
83
|
stats
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
|
+
def update!(endpoints)
|
87
|
+
@endpoints.each { |k,v| endpoints.include?(k) ? endpoints.delete(k) : @endpoints.delete(k) }
|
88
|
+
endpoints.each { |ep| @endpoints[ep] = {:n_level => @min_n_level, :timestamp => 0} }
|
89
|
+
end
|
90
|
+
|
86
91
|
end
|
87
|
-
|
88
|
-
# Implementation concepts: endpoints have three states, red, yellow and green. Yellow
|
89
92
|
# has several levels (@yellow_states) to determine the health of the endpoint. The
|
90
93
|
# balancer works by avoiding "red" endpoints and retrying them after awhile. Here is a
|
91
|
-
# brief description of the state transitions:
|
92
|
-
# * green: last request was successful.
|
93
94
|
# * on success: remain green
|
94
95
|
# * on failure: change state to yellow and set it's health to healthiest (1)
|
95
96
|
# * red: skip this server
|
@@ -109,16 +110,20 @@ module RightSupport::Net::Balancing
|
|
109
110
|
# transitions from red to yellow, and so on.
|
110
111
|
|
111
112
|
class HealthCheck
|
112
|
-
|
113
|
-
def initialize(endpoints, options = {})
|
114
|
-
yellow_states = options[:yellow_states]
|
115
|
-
reset_time = options[:reset_time]
|
116
113
|
|
117
|
-
|
114
|
+
def initialize(options = {})
|
115
|
+
@options = options
|
116
|
+
end
|
118
117
|
|
119
|
-
|
120
|
-
@
|
121
|
-
|
118
|
+
def set_endpoints(endpoints)
|
119
|
+
if @stack
|
120
|
+
@stack.update!(endpoints)
|
121
|
+
else
|
122
|
+
@health_check = @options.delete(:health_check)
|
123
|
+
@counter = rand(0xffff) % endpoints.size
|
124
|
+
@last_size = endpoints.size
|
125
|
+
@stack = EndpointsStack.new(endpoints, @options[:yellow_states], @options[:reset_time], @options[:on_health_change])
|
126
|
+
end
|
122
127
|
end
|
123
128
|
|
124
129
|
def next
|
@@ -126,7 +131,7 @@ module RightSupport::Net::Balancing
|
|
126
131
|
# following structure: [ [EP1, {:n_level => ..., :timestamp => ... }], [EP2, ... ] ]
|
127
132
|
endpoints = @stack.sweep_and_return_yellow_and_green
|
128
133
|
return nil if endpoints.empty?
|
129
|
-
|
134
|
+
|
130
135
|
# Selection of the next endpoint using RoundRobin
|
131
136
|
@counter += 1 unless endpoints.size < @last_size
|
132
137
|
@counter = 0 if @counter == endpoints.size
|
@@ -151,7 +156,7 @@ module RightSupport::Net::Balancing
|
|
151
156
|
def bad(endpoint, t0, t1)
|
152
157
|
@stack.increase_state(endpoint, t0, t1)
|
153
158
|
end
|
154
|
-
|
159
|
+
|
155
160
|
def health_check(endpoint)
|
156
161
|
t0 = Time.now
|
157
162
|
result = @health_check.call(endpoint)
|
@@ -173,6 +178,6 @@ module RightSupport::Net::Balancing
|
|
173
178
|
def get_stats
|
174
179
|
@stack.get_stats
|
175
180
|
end
|
176
|
-
|
181
|
+
|
177
182
|
end
|
178
183
|
end
|
@@ -22,14 +22,19 @@
|
|
22
22
|
|
23
23
|
module RightSupport::Net::Balancing
|
24
24
|
class RoundRobin
|
25
|
-
|
26
|
-
|
25
|
+
|
26
|
+
def initialize(options ={})
|
27
|
+
@endpoints = []
|
27
28
|
@counter = rand(0xffff)
|
28
29
|
end
|
29
30
|
|
31
|
+
def set_endpoints(endpoints)
|
32
|
+
@endpoints = endpoints
|
33
|
+
end
|
34
|
+
|
30
35
|
def next
|
31
36
|
@counter += 1
|
32
|
-
[ @endpoints[@counter % @endpoints.size], false ]
|
37
|
+
[ @endpoints[@counter % @endpoints.size], false ] unless @endpoints.empty?
|
33
38
|
end
|
34
39
|
|
35
40
|
def good(endpoint, t0, t1)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2011 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
|
+
module RightSupport::Net::Balancing
|
24
|
+
|
25
|
+
# Implementation concepts: Create a policy that selects an endpoint and sticks with it.
|
26
|
+
#
|
27
|
+
# The policy should:
|
28
|
+
# - iterate through each endpoint until a valid endpoint is found;
|
29
|
+
# - continue returning the same endpoint until it is no longer valid;
|
30
|
+
# - re-iterate through each endpoint when it's endpoint loses validity;
|
31
|
+
# - return an Exception if it performs a complete iteration though each endpoint and finds none valid;
|
32
|
+
|
33
|
+
class StickyPolicy
|
34
|
+
|
35
|
+
def initialize(options = {})
|
36
|
+
@health_check = options.delete(:health_check)
|
37
|
+
@endpoints = []
|
38
|
+
@counter = rand(0xffff)
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_endpoints(endpoints)
|
42
|
+
unless @endpoints.empty?
|
43
|
+
last_chosen = self.next.first
|
44
|
+
@endpoints = []
|
45
|
+
if endpoints.include?(last_chosen)
|
46
|
+
@endpoints << last_chosen
|
47
|
+
@counter = 0
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@endpoints |= endpoints
|
51
|
+
end
|
52
|
+
|
53
|
+
def next
|
54
|
+
[ @endpoints[@counter % @endpoints.size], true ] unless @endpoints.empty?
|
55
|
+
end
|
56
|
+
|
57
|
+
def good(endpoint, t0, t1)
|
58
|
+
#no-op; round robin does not care about failures
|
59
|
+
end
|
60
|
+
|
61
|
+
def bad(endpoint, t0, t1)
|
62
|
+
@counter += 1
|
63
|
+
end
|
64
|
+
|
65
|
+
def health_check(endpoint)
|
66
|
+
t0 = Time.now
|
67
|
+
result = @health_check.call(endpoint)
|
68
|
+
t1 = Time.now
|
69
|
+
if result
|
70
|
+
return true
|
71
|
+
else
|
72
|
+
@counter += 1
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
rescue Exception => e
|
76
|
+
t1 = Time.now
|
77
|
+
@counter += 1
|
78
|
+
raise e
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Copyright (c) 2009-2011 RightScale, Inc, All Rights Reserved Worldwide.
|
2
|
+
#
|
3
|
+
# THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
|
4
|
+
# AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
|
5
|
+
# reproduction, modification, or disclosure of this program is
|
6
|
+
# strictly prohibited. Any use of this program by an authorized
|
7
|
+
# licensee is strictly subject to the terms and conditions,
|
8
|
+
# including confidentiality obligations, set forth in the applicable
|
9
|
+
# License Agreement between RightScale.com, Inc. and the licensee.
|
10
|
+
|
11
|
+
require 'socket'
|
12
|
+
|
13
|
+
module RightSupport::Net
|
14
|
+
|
15
|
+
module DNS
|
16
|
+
|
17
|
+
def self.resolve_all_ip_addresses(hostnames)
|
18
|
+
ips = []
|
19
|
+
hostnames = [hostnames] unless hostnames.respond_to?(:each)
|
20
|
+
hostnames.each do |hostname|
|
21
|
+
infos = nil
|
22
|
+
begin
|
23
|
+
infos = Socket.getaddrinfo(hostname, 443, Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)
|
24
|
+
rescue Exception => e
|
25
|
+
# NOTE: Need to figure out, which logger can we use here?
|
26
|
+
# Log.error "Rescued #{e.class.name} resolving Repose hostnames: #{e.message}; retrying"
|
27
|
+
retry
|
28
|
+
end
|
29
|
+
|
30
|
+
#Randomly permute the addrinfos of each hostname to help spread load.
|
31
|
+
infos.shuffle.each do |info|
|
32
|
+
ips << info[3]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
ips
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
@@ -2,7 +2,7 @@ module RightSupport::Net
|
|
2
2
|
# Raised to indicate the (uncommon) error condition where a RequestBalancer rotated
|
3
3
|
# through EVERY URL in a list without getting a non-nil, non-timeout response.
|
4
4
|
class NoResult < Exception; end
|
5
|
-
|
5
|
+
|
6
6
|
# Utility class that allows network requests to be randomly distributed across
|
7
7
|
# a set of network endpoints. Generally used for REST requests by passing an
|
8
8
|
# Array of HTTP service endpoint URLs.
|
@@ -74,6 +74,16 @@ module RightSupport::Net
|
|
74
74
|
new(endpoints, options).request(&block)
|
75
75
|
end
|
76
76
|
|
77
|
+
def resolve(endpoints)
|
78
|
+
endpoints = RightSupport::Net::DNS.resolve_all_ip_addresses(endpoints)
|
79
|
+
@resolved_at = Time.now.to_i
|
80
|
+
endpoints
|
81
|
+
end
|
82
|
+
|
83
|
+
def expired?
|
84
|
+
@options[:resolve] && Time.now.to_i - @resolved_at > @options[:resolve]
|
85
|
+
end
|
86
|
+
|
77
87
|
# Constructor. Accepts a sequence of request endpoints which it shuffles randomly at
|
78
88
|
# creation time; however, the ordering of the endpoints does not change thereafter
|
79
89
|
# and the sequence is tried from the beginning for every request.
|
@@ -100,7 +110,8 @@ module RightSupport::Net
|
|
100
110
|
|
101
111
|
@options[:policy] ||= RightSupport::Net::Balancing::RoundRobin
|
102
112
|
@policy = @options[:policy]
|
103
|
-
@policy = @policy.new(
|
113
|
+
@policy = @policy.new(options) if @policy.is_a?(Class)
|
114
|
+
|
104
115
|
unless test_policy_duck_type(@policy)
|
105
116
|
raise ArgumentError, ":policy must be a class/object that responds to :next, :good and :bad"
|
106
117
|
end
|
@@ -126,6 +137,12 @@ module RightSupport::Net
|
|
126
137
|
end
|
127
138
|
|
128
139
|
@endpoints = endpoints
|
140
|
+
|
141
|
+
if @options[:resolve]
|
142
|
+
@resolved_at = 0
|
143
|
+
else
|
144
|
+
@policy.set_endpoints(@endpoints)
|
145
|
+
end
|
129
146
|
end
|
130
147
|
|
131
148
|
# Perform a request.
|
@@ -144,6 +161,8 @@ module RightSupport::Net
|
|
144
161
|
def request
|
145
162
|
raise ArgumentError, "Must call this method with a block" unless block_given?
|
146
163
|
|
164
|
+
@policy.set_endpoints(self.resolve(@endpoints)) if self.expired?
|
165
|
+
|
147
166
|
exceptions = []
|
148
167
|
result = nil
|
149
168
|
complete = false
|
@@ -159,7 +178,6 @@ module RightSupport::Net
|
|
159
178
|
end
|
160
179
|
|
161
180
|
endpoint, need_health_check = @policy.next
|
162
|
-
|
163
181
|
raise NoResult, "No endpoints are available" unless endpoint
|
164
182
|
n += 1
|
165
183
|
t0 = Time.now
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module RightSupport::Net
|
2
|
+
module SSL
|
3
|
+
# A helper module that provides monkey patch for OpenSSL::SSL.verify_certificate_identity method.
|
4
|
+
#
|
5
|
+
# To start use it, all you need is to enable it and then register DNS name of the host, which
|
6
|
+
# you will trust for sure. This module automatically adds OpenSSL library and reopens
|
7
|
+
# verify_certificate_identity method, to change the SSL algorythm of hostname verification.
|
8
|
+
module OpenSSLPatch
|
9
|
+
class << self
|
10
|
+
@@status = false
|
11
|
+
|
12
|
+
def enable!
|
13
|
+
return if @@status
|
14
|
+
@@status = true
|
15
|
+
|
16
|
+
require 'openssl'
|
17
|
+
OpenSSL::SSL.module_exec do
|
18
|
+
def verify_certificate_identity(cert, hostname)
|
19
|
+
if RightSupport::Net::SSL::OpenSSLPatch.enabled?
|
20
|
+
actual_hostname = RightSupport::Net::SSL.expected_hostname
|
21
|
+
end
|
22
|
+
actual_hostname ||= hostname
|
23
|
+
verify_certificate_identity_without_hack(cert, actual_hostname)
|
24
|
+
end
|
25
|
+
module_function :verify_certificate_identity
|
26
|
+
|
27
|
+
# The original module function of OpenSSL::SSL
|
28
|
+
def verify_certificate_identity_without_hack(cert, hostname)
|
29
|
+
should_verify_common_name = true
|
30
|
+
cert.extensions.each{|ext|
|
31
|
+
next if ext.oid != "subjectAltName"
|
32
|
+
ext.value.split(/,\s+/).each{|general_name|
|
33
|
+
if /\ADNS:(.*)/ =~ general_name
|
34
|
+
should_verify_common_name = false
|
35
|
+
reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
|
36
|
+
return true if /\A#{reg}\z/i =~ hostname
|
37
|
+
elsif /\AIP Address:(.*)/ =~ general_name
|
38
|
+
should_verify_common_name = false
|
39
|
+
return true if $1 == hostname
|
40
|
+
end
|
41
|
+
}
|
42
|
+
}
|
43
|
+
if should_verify_common_name
|
44
|
+
cert.subject.to_a.each{|oid, value|
|
45
|
+
if oid == "CN"
|
46
|
+
reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
|
47
|
+
return true if /\A#{reg}\z/i =~ hostname
|
48
|
+
end
|
49
|
+
}
|
50
|
+
end
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
module_function :verify_certificate_identity_without_hack
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def disable!
|
58
|
+
@@status = false
|
59
|
+
end
|
60
|
+
|
61
|
+
def enabled?
|
62
|
+
@@status
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright (c) 2009-2011 RightScale, Inc, All Rights Reserved Worldwide.
|
2
|
+
#
|
3
|
+
# THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
|
4
|
+
# AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
|
5
|
+
# reproduction, modification, or disclosure of this program is
|
6
|
+
# strictly prohibited. Any use of this program by an authorized
|
7
|
+
# licensee is strictly subject to the terms and conditions,
|
8
|
+
# including confidentiality obligations, set forth in the applicable
|
9
|
+
# License Agreement between RightScale.com, Inc. and the licensee.
|
10
|
+
|
11
|
+
require 'right_support/net/ssl/open_ssl_patch'
|
12
|
+
|
13
|
+
module RightSupport::Net
|
14
|
+
module SSL
|
15
|
+
module_function
|
16
|
+
|
17
|
+
def expected_hostname
|
18
|
+
@expected_hostname
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_expected_hostname(hostname, &block)
|
22
|
+
@expected_hostname = hostname
|
23
|
+
block.call
|
24
|
+
rescue Exception => e
|
25
|
+
@expected_hostname = nil
|
26
|
+
raise e
|
27
|
+
ensure
|
28
|
+
@expected_hostname = nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
RightSupport::Net::SSL::OpenSSLPatch.enable!
|
data/lib/right_support/net.rb
CHANGED
@@ -34,5 +34,7 @@ require 'right_support/net/http_client'
|
|
34
34
|
require 'right_support/net/string_encoder'
|
35
35
|
require 'right_support/net/balancing'
|
36
36
|
require 'right_support/net/request_balancer'
|
37
|
+
require 'right_support/net/ssl'
|
38
|
+
require 'right_support/net/dns'
|
37
39
|
|
38
|
-
RightSupport::Net.extend(RightSupport::Net::AddressHelper)
|
40
|
+
RightSupport::Net.extend(RightSupport::Net::AddressHelper)
|
@@ -193,25 +193,43 @@ module RightSupport
|
|
193
193
|
#
|
194
194
|
# === Parameters
|
195
195
|
# stats(Hash):: Statistics with generic keys "name", "identity", "hostname", "service uptime",
|
196
|
-
# "machine uptime", "stat time", "last reset time", "version", and "broker" with
|
197
|
-
# latter two and "machine uptime"
|
198
|
-
# have an associated hash value that is displayed in sorted
|
196
|
+
# "machine uptime", "memory KB", "stat time", "last reset time", "version", and "broker" with
|
197
|
+
# the latter two and "machine uptime", "memory KB", "version", and "broker" being optional;
|
198
|
+
# any other keys ending with "stats" have an associated hash value that is displayed in sorted
|
199
|
+
# key order, unless "stats" is preceded by a non-blank, in which case that character is prepended
|
200
|
+
# to the key to drive the sort order
|
201
|
+
# options(Hash):: Formatting options
|
202
|
+
# :name_width(Integer):: Maximum characters in displayed stat name
|
203
|
+
# :sub_name_width(Integer):: Maximum characters in displayed sub-stat name
|
204
|
+
# :sub_stat_value_width(Integer):: Maximum characters in displayed sub-stat value line
|
205
|
+
# :exception_message_width(Integer):: Maximum characters displayed for exception message
|
199
206
|
#
|
200
207
|
# === Return
|
201
208
|
# (String):: Display string
|
202
|
-
def self.stats_str(stats)
|
203
|
-
name_width = MAX_STAT_NAME_WIDTH
|
209
|
+
def self.stats_str(stats, options = {})
|
210
|
+
name_width = options[:name_width] || MAX_STAT_NAME_WIDTH
|
211
|
+
|
204
212
|
str = stats["name"] ? sprintf("%-#{name_width}s#{SEPARATOR}%s\n", "name", stats["name"]) : ""
|
205
213
|
str += sprintf("%-#{name_width}s#{SEPARATOR}%s\n", "identity", stats["identity"]) +
|
206
214
|
sprintf("%-#{name_width}s#{SEPARATOR}%s\n", "hostname", stats["hostname"]) +
|
207
215
|
sprintf("%-#{name_width}s#{SEPARATOR}%s\n", "stat time", time_at(stats["stat time"])) +
|
208
216
|
sprintf("%-#{name_width}s#{SEPARATOR}%s\n", "last reset", time_at(stats["last reset time"])) +
|
209
217
|
sprintf("%-#{name_width}s#{SEPARATOR}%s\n", "service up", elapsed(stats["service uptime"]))
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
218
|
+
if stats.has_key?("machine uptime")
|
219
|
+
str += sprintf("%-#{name_width}s#{SEPARATOR}%s\n", "machine up", elapsed(stats["machine uptime"]))
|
220
|
+
end
|
221
|
+
if stats.has_key?("memory")
|
222
|
+
str += sprintf("%-#{name_width}s#{SEPARATOR}%s\n", "memory KB", stats["memory"])
|
223
|
+
end
|
224
|
+
if stats.has_key?("version")
|
225
|
+
str += sprintf("%-#{name_width}s#{SEPARATOR}%s\n", "version", stats["version"].to_i)
|
226
|
+
end
|
227
|
+
if stats.has_key?("brokers")
|
228
|
+
str += brokers_str(stats["brokers"], options)
|
229
|
+
end
|
230
|
+
stats.to_a.sort_by { |(k, v)| k.to_s =~ /(.)stats$/ ? ($1 == ' ' ? '~' : $1) + k : k }.each do |k, v|
|
231
|
+
str += sub_stats_str(k[0..-7], v, options) if k.to_s =~ /stats$/
|
232
|
+
end
|
215
233
|
str
|
216
234
|
end
|
217
235
|
|
@@ -234,13 +252,20 @@ module RightSupport
|
|
234
252
|
# "heartbeat"(Integer|nil):: Number of seconds between AMQP heartbeats, or nil if heartbeat disabled
|
235
253
|
# "returns"(Hash|nil):: Message return activity stats with keys "total", "percent", "last", and "rate"
|
236
254
|
# with percentage breakdown per request type, or nil if none
|
237
|
-
#
|
255
|
+
# options(Hash):: Formatting options
|
256
|
+
# :name_width(Integer):: Fixed width for left-justified name display
|
257
|
+
# :sub_name_width(Integer):: Maximum characters in displayed sub-stat name
|
258
|
+
# :sub_stat_value_width(Integer):: Maximum characters in displayed sub-stat value line
|
259
|
+
# :exception_message_width(Integer):: Maximum characters displayed for exception message
|
238
260
|
#
|
239
261
|
# === Return
|
240
262
|
# str(String):: Broker display with one line per broker plus exceptions
|
241
|
-
def self.brokers_str(brokers,
|
263
|
+
def self.brokers_str(brokers, options = {})
|
264
|
+
name_width = options[:name_width] || MAX_STAT_NAME_WIDTH
|
265
|
+
sub_name_width = options[:sub_name_width] || MAX_SUB_STAT_NAME_WIDTH
|
266
|
+
sub_stat_value_width = options[:sub_stat_value_width] || MAX_SUB_STAT_VALUE_WIDTH
|
267
|
+
|
242
268
|
value_indent = " " * (name_width + SEPARATOR.size)
|
243
|
-
sub_name_width = MAX_SUB_STAT_NAME_WIDTH
|
244
269
|
sub_value_indent = " " * (name_width + sub_name_width + (SEPARATOR.size * 2))
|
245
270
|
str = sprintf("%-#{name_width}s#{SEPARATOR}", "brokers")
|
246
271
|
brokers["brokers"].each do |b|
|
@@ -263,7 +288,7 @@ module RightSupport
|
|
263
288
|
str += if brokers["exceptions"].nil? || brokers["exceptions"].empty?
|
264
289
|
"none\n"
|
265
290
|
else
|
266
|
-
exceptions_str(brokers["exceptions"], sub_value_indent) + "\n"
|
291
|
+
exceptions_str(brokers["exceptions"], sub_value_indent, options) + "\n"
|
267
292
|
end
|
268
293
|
str += value_indent
|
269
294
|
str += sprintf("%-#{sub_name_width}s#{SEPARATOR}", "heartbeat")
|
@@ -277,7 +302,7 @@ module RightSupport
|
|
277
302
|
str += if brokers["returns"].nil? || brokers["returns"].empty?
|
278
303
|
"none\n"
|
279
304
|
else
|
280
|
-
wrap(activity_str(brokers["returns"]),
|
305
|
+
wrap(activity_str(brokers["returns"]), sub_stat_value_width, sub_value_indent, ", ") + "\n"
|
281
306
|
end
|
282
307
|
end
|
283
308
|
|
@@ -296,13 +321,20 @@ module RightSupport
|
|
296
321
|
# === Parameters
|
297
322
|
# name(String):: Display name for the stat
|
298
323
|
# value(Object):: Value of this stat
|
299
|
-
#
|
324
|
+
# options(Hash):: Formatting options
|
325
|
+
# :name_width(Integer):: Fixed width for left-justified name display
|
326
|
+
# :sub_name_width(Integer):: Maximum characters in displayed sub-stat name
|
327
|
+
# :sub_stat_value_width(Integer):: Maximum characters in displayed sub-stat value line
|
328
|
+
# :exception_message_width(Integer):: Maximum characters displayed for exception message
|
300
329
|
#
|
301
330
|
# === Return
|
302
331
|
# (String):: Single line display of stat
|
303
|
-
def self.sub_stats_str(name, value,
|
332
|
+
def self.sub_stats_str(name, value, options = {})
|
333
|
+
name_width = options[:name_width] || MAX_STAT_NAME_WIDTH
|
334
|
+
sub_name_width = options[:sub_name_width] || MAX_SUB_STAT_NAME_WIDTH
|
335
|
+
sub_stat_value_width = options[:sub_stat_value_width] || MAX_SUB_STAT_VALUE_WIDTH
|
336
|
+
|
304
337
|
value_indent = " " * (name_width + SEPARATOR.size)
|
305
|
-
sub_name_width = MAX_SUB_STAT_NAME_WIDTH
|
306
338
|
sub_value_indent = " " * (name_width + sub_name_width + (SEPARATOR.size * 2))
|
307
339
|
sprintf("%-#{name_width}s#{SEPARATOR}", name) + value.to_a.sort.map do |attr|
|
308
340
|
k, v = attr
|
@@ -317,13 +349,13 @@ module RightSupport
|
|
317
349
|
if v.empty? || v["total"] == 0
|
318
350
|
"none"
|
319
351
|
elsif v["total"]
|
320
|
-
wrap(activity_str(v),
|
352
|
+
wrap(activity_str(v), sub_stat_value_width, sub_value_indent, ", ")
|
321
353
|
elsif k =~ /last$/
|
322
354
|
last_activity_str(v)
|
323
355
|
elsif k == "exceptions"
|
324
|
-
exceptions_str(v, sub_value_indent)
|
356
|
+
exceptions_str(v, sub_value_indent, options)
|
325
357
|
else
|
326
|
-
wrap(hash_str(v),
|
358
|
+
wrap(hash_str(v), sub_stat_value_width, sub_value_indent, ", ")
|
327
359
|
end
|
328
360
|
else
|
329
361
|
"#{v || "none"}"
|
@@ -394,16 +426,19 @@ module RightSupport
|
|
394
426
|
# "total"(Integer):: Total exceptions for this category
|
395
427
|
# "recent"(Array):: Most recent as a hash of "count", "type", "message", "when", and "where"
|
396
428
|
# indent(String):: Indentation for each line
|
429
|
+
# options(Hash):: Formatting options
|
430
|
+
# :exception_message_width(Integer):: Maximum characters displayed for exception message
|
397
431
|
#
|
398
432
|
# === Return
|
399
433
|
# (String):: Exceptions in displayable format with line separators
|
400
|
-
def self.exceptions_str(exceptions, indent)
|
434
|
+
def self.exceptions_str(exceptions, indent, options = {})
|
435
|
+
exception_message_width = options[:exception_message_width] || MAX_EXCEPTION_MESSAGE_WIDTH
|
401
436
|
indent2 = indent + (" " * 4)
|
402
437
|
exceptions.to_a.sort.map do |k, v|
|
403
438
|
sprintf("%s total: %d, most recent:\n", k, v["total"]) + v["recent"].reverse.map do |e|
|
404
439
|
message = e["message"]
|
405
|
-
if message && message.size > (
|
406
|
-
message = e["message"][0,
|
440
|
+
if message && message.size > (exception_message_width - 3)
|
441
|
+
message = e["message"][0, exception_message_width - 3] + "..."
|
407
442
|
end
|
408
443
|
indent + "(#{e["count"]}) #{time_at(e["when"])} #{e["type"]}: #{message}\n" + indent2 + "#{e["where"]}"
|
409
444
|
end.join("\n")
|
data/lib/right_support.rb
CHANGED
data/right_support.gemspec
CHANGED
@@ -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 = '1.
|
11
|
-
s.date = '2012-
|
10
|
+
s.version = '1.3.0'
|
11
|
+
s.date = '2012-03-06'
|
12
12
|
|
13
13
|
s.authors = ['Tony Spataro', 'Sergey Sergyenko', 'Ryan Williamson', 'Lee Kirchhoff']
|
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:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 1.
|
10
|
+
version: 1.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tony Spataro
|
@@ -18,7 +18,7 @@ autorequire:
|
|
18
18
|
bindir: bin
|
19
19
|
cert_chain: []
|
20
20
|
|
21
|
-
date: 2012-
|
21
|
+
date: 2012-03-06 00:00:00 -08:00
|
22
22
|
default_executable:
|
23
23
|
dependencies: []
|
24
24
|
|
@@ -51,8 +51,12 @@ files:
|
|
51
51
|
- lib/right_support/net/balancing.rb
|
52
52
|
- lib/right_support/net/balancing/health_check.rb
|
53
53
|
- lib/right_support/net/balancing/round_robin.rb
|
54
|
+
- lib/right_support/net/balancing/sticky_policy.rb
|
55
|
+
- lib/right_support/net/dns.rb
|
54
56
|
- lib/right_support/net/http_client.rb
|
55
57
|
- lib/right_support/net/request_balancer.rb
|
58
|
+
- lib/right_support/net/ssl.rb
|
59
|
+
- lib/right_support/net/ssl/open_ssl_patch.rb
|
56
60
|
- lib/right_support/net/string_encoder.rb
|
57
61
|
- lib/right_support/rack.rb
|
58
62
|
- lib/right_support/rack/custom_logger.rb
|