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