right_support 0.8.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 +20 -0
- data/README.rdoc +66 -0
- data/lib/right_support/filter_logger.rb +122 -0
- data/lib/right_support/kernel_extensions.rb +30 -0
- data/lib/right_support/net/request_balancer.rb +47 -0
- data/lib/right_support/net/rest.rb +43 -0
- data/lib/right_support/net.rb +34 -0
- data/lib/right_support/rack/custom_logger.rb +62 -0
- data/lib/right_support/system_logger.rb +175 -0
- data/lib/right_support/tag_logger.rb +72 -0
- data/lib/right_support/validation/openssl.rb +90 -0
- data/lib/right_support/validation/ssh.rb +70 -0
- data/lib/right_support/validation.rb +45 -0
- data/lib/right_support.rb +9 -0
- data/right_support.gemspec +31 -0
- metadata +188 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 RightScale, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
RightSupport is a library of reusable, unit-tested Ruby code that RightScale has found broadly useful.
|
2
|
+
|
3
|
+
== What Does It Do?
|
4
|
+
|
5
|
+
=== Logging
|
6
|
+
|
7
|
+
SystemLogger is a rewrite of the seattle.rb SyslogLogger class that features the following improvements:
|
8
|
+
* Contains several bugfixes vs. SyslogLogger 1.4.0
|
9
|
+
* Inherits from standard Ruby Logger class for guaranteed compatibility
|
10
|
+
* Can be configured with options about how to handle newlines, ANSI escape codes, etc
|
11
|
+
|
12
|
+
@logger = SystemLogger.new('my_cool_app', :split=>true, :color=>false)
|
13
|
+
@logger.info "Hello world\nThis will appear on separate lines\nand without \e[33;0mbeautiful colors"
|
14
|
+
|
15
|
+
CustomLogger is a Rack middleware that allows a Rack app to use any log sink it wishes. The
|
16
|
+
stock Rack logger middleware is inflexible and gives the end user no control over which logger is used
|
17
|
+
or where the log entries go.
|
18
|
+
|
19
|
+
require 'right_support/rack/custom_logger'
|
20
|
+
|
21
|
+
my_logger = MyAwesomeLogger.new
|
22
|
+
use RightSupport::Rack::CustomLogger, Logger::INFO, my_logger
|
23
|
+
|
24
|
+
== Input Validation
|
25
|
+
|
26
|
+
The Validation module contains several format-checkers that can be used to validate
|
27
|
+
your web app's models before saving, check for preconditions in your controllers, and
|
28
|
+
so forth.
|
29
|
+
|
30
|
+
You can use it as a mixin by including the appropriate child module of
|
31
|
+
RightSupport::Validation, but you really don't want to do that, do you? Instead, you
|
32
|
+
want to call the module methods of RightSupport::Validation, which contains all of
|
33
|
+
the same mixin methods.
|
34
|
+
|
35
|
+
the_key = STDIN.read
|
36
|
+
raise ArgumentError unless RightSupport::Validation.ssh_public_key?(the_key)
|
37
|
+
|
38
|
+
== Request Balancing
|
39
|
+
|
40
|
+
The RequestBalancer class will randomly choose endpoints for a network request,
|
41
|
+
which lets you perform easy client-side load balancing:
|
42
|
+
|
43
|
+
include RightSupport::Net
|
44
|
+
|
45
|
+
urls = ['http://localhost', 'http://otherhost']
|
46
|
+
RequestBalancer.request(urls, :fatal=>RestClient::ResourceNotFound) do |url|
|
47
|
+
REST.get(url)
|
48
|
+
end
|
49
|
+
|
50
|
+
The balancer will keep trying requests until one of them succeeds without throwing
|
51
|
+
any exceptions. (NB: a nil return value counts as success!!)
|
52
|
+
|
53
|
+
== HTTP REST Client
|
54
|
+
|
55
|
+
We provide a very thin wrapper around the rest-client gem that enables simple but
|
56
|
+
robust rest requests with a timeout, headers, etc.
|
57
|
+
|
58
|
+
The RightSupport::Net::REST module is interface-compatible with the RestClient
|
59
|
+
module, but allows an optional timeout to be specified as an extra parameter.
|
60
|
+
|
61
|
+
# Default timeout is 5 seconds
|
62
|
+
RightSupport::Net::REST.get('http://localhost')
|
63
|
+
|
64
|
+
# Make sure the REST request fails after 1 second so we can report an error
|
65
|
+
# and move on!
|
66
|
+
RightSupport::Net::REST.get('http://localhost', {'X-Hello'=>'hi!'}, 1)
|
@@ -0,0 +1,122 @@
|
|
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
|
+
require 'logger'
|
24
|
+
|
25
|
+
module RightSupport
|
26
|
+
# A logger than encapsulates an underlying Logger object and filters log entries
|
27
|
+
# before they are passed to the underlying Logger. Can be used to for various log-
|
28
|
+
# processing tasks such as filtering sensitive data or tagging log lines with a
|
29
|
+
# context marker.
|
30
|
+
class FilterLogger < Logger
|
31
|
+
# Initialize a new instance of this class.
|
32
|
+
#
|
33
|
+
# === Parameters
|
34
|
+
# actual_logger(Logger):: The actual, underlying Logger object
|
35
|
+
#
|
36
|
+
def initialize(actual_logger)
|
37
|
+
@actual_logger = actual_logger
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
# Add a log line, filtering the severity and message before calling through
|
42
|
+
# to the underlying logger's #add method.
|
43
|
+
#
|
44
|
+
# === Parameters
|
45
|
+
# severity(Integer):: one of the Logger severity constants
|
46
|
+
# message(String):: the message to log, or nil
|
47
|
+
# progname(String):: the program name, or nil
|
48
|
+
#
|
49
|
+
# === Block
|
50
|
+
# If message == nil and a block is given, yields to the block in order to
|
51
|
+
# capture the log message. This matches the behavior of Logger, but ensures
|
52
|
+
# the severity and message are still filtered.
|
53
|
+
#
|
54
|
+
# === Return
|
55
|
+
# the result of the underlying logger's #add
|
56
|
+
def add(severity, message = nil, progname = nil, &block)
|
57
|
+
severity ||= UNKNOWN
|
58
|
+
return true if severity < level
|
59
|
+
|
60
|
+
if message.nil?
|
61
|
+
if block_given?
|
62
|
+
message = yield
|
63
|
+
else
|
64
|
+
message = progname
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
severity, message = filter(severity, message)
|
69
|
+
return @actual_logger.add(severity, message) if message
|
70
|
+
end
|
71
|
+
|
72
|
+
# Proxies to the encapsulated Logger object. See Logger#<< for info.
|
73
|
+
def <<(msg)
|
74
|
+
@actual_logger << msg
|
75
|
+
end
|
76
|
+
|
77
|
+
# Proxies to the encapsulated Logger object. See Logger#close for info.
|
78
|
+
def close
|
79
|
+
@actual_logger.close
|
80
|
+
end
|
81
|
+
|
82
|
+
# Proxies to the encapsulated Logger object. See Logger#level for info.
|
83
|
+
def level
|
84
|
+
@actual_logger.level
|
85
|
+
end
|
86
|
+
|
87
|
+
# Proxies to the encapsulated Logger object. See Logger#level= for info.
|
88
|
+
def level=(new_level)
|
89
|
+
@actual_logger.level = new_level
|
90
|
+
end
|
91
|
+
|
92
|
+
# Proxies to the encapsulated Logger object. See Logger#debug? for info.
|
93
|
+
def debug?; @actual_logger.debug?; end
|
94
|
+
|
95
|
+
# Proxies to the encapsulated Logger object. See Logger#info? for info.
|
96
|
+
def info?; @actual_logger.info?; end
|
97
|
+
|
98
|
+
# Proxies to the encapsulated Logger object. See Logger#warn? for info.
|
99
|
+
def warn?; @actual_logger.warn?; end
|
100
|
+
|
101
|
+
# Proxies to the encapsulated Logger object. See Logger#error? for info.
|
102
|
+
def error?; @actual_logger.error?; end
|
103
|
+
|
104
|
+
# Proxies to the encapsulated Logger object. See Logger#fatal? for info.
|
105
|
+
def fatal?; @actual_logger.fatal?; end
|
106
|
+
|
107
|
+
protected
|
108
|
+
|
109
|
+
# Filter a log line, transforming its severity and/or message before it is
|
110
|
+
# passed to the underlying logger.
|
111
|
+
#
|
112
|
+
# === Parameters
|
113
|
+
# severity(Integer):: one of the severity constants defined by Logger
|
114
|
+
# messgae(String):: the log message
|
115
|
+
#
|
116
|
+
# === Return
|
117
|
+
# Returns a pair consisting of the filtered [severity, message].
|
118
|
+
def filter(severity, message)
|
119
|
+
return [severity, message]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RightSupport
|
2
|
+
module KernelExtensions
|
3
|
+
# Attempt to require one or more source files; if the require succeeds (or
|
4
|
+
# if the files have already been successfully required), yield to the block.
|
5
|
+
#
|
6
|
+
# This method is useful to conditionally define code depending on the availability
|
7
|
+
# of gems or standard-library source files.
|
8
|
+
#
|
9
|
+
# === Parameters
|
10
|
+
# Uses a parameters glob to pass all of its parameters transparently through to
|
11
|
+
# Kernel#require.
|
12
|
+
#
|
13
|
+
# === Block
|
14
|
+
# The block will be called if the require succeeds (if it does not raise LoadError).
|
15
|
+
#
|
16
|
+
# === Return
|
17
|
+
# Preserves the return value of Kernel#require (generally either true or false).
|
18
|
+
def if_require_succeeds(*args)
|
19
|
+
result = require(*args)
|
20
|
+
yield if block_given?
|
21
|
+
return result
|
22
|
+
rescue LoadError => e
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Object
|
29
|
+
include RightSupport::KernelExtensions
|
30
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RightSupport::Net
|
2
|
+
class NoResponse < Exception; end
|
3
|
+
|
4
|
+
# Utility class that allows network requests to be randomly distributed across
|
5
|
+
# a set of network endpoints. Generally used for REST requests by passing an
|
6
|
+
# Array of HTTP service endpoint URLs.
|
7
|
+
#
|
8
|
+
# The balancer does not actually perform requests by itself, which makes this
|
9
|
+
# class usable for various network protocols, and potentially even for non-
|
10
|
+
# networking purposes. The block does all the work; the balancer merely selects
|
11
|
+
# a random request endpoint to pass to the block.
|
12
|
+
class RequestBalancer
|
13
|
+
def self.request(endpoints, options={}, &block)
|
14
|
+
new(endpoints, options).request(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(endpoints, options={})
|
18
|
+
raise ArgumentError, "Must specify at least one endpoint" unless endpoints && !endpoints.empty?
|
19
|
+
@endpoints = endpoints.shuffle
|
20
|
+
@options = options.dup
|
21
|
+
end
|
22
|
+
|
23
|
+
def request
|
24
|
+
raise ArgumentError, "Must call this method with a block" unless block_given?
|
25
|
+
|
26
|
+
exception = nil
|
27
|
+
result = nil
|
28
|
+
|
29
|
+
@endpoints.each do |host|
|
30
|
+
begin
|
31
|
+
result = yield(host)
|
32
|
+
break unless result.nil?
|
33
|
+
rescue Exception => e
|
34
|
+
fatal = @options[:fatal]
|
35
|
+
safe = @options[:safe]
|
36
|
+
raise e if (fatal && e.kind_of?(fatal)) && !(safe && e.kind_of?(safe))
|
37
|
+
exception = e
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
return result if result
|
42
|
+
raise exception if exception
|
43
|
+
raise NoResponse, "Tried all URLs with neither result nor exception!"
|
44
|
+
end
|
45
|
+
end # RequestBalancer
|
46
|
+
|
47
|
+
end # RightScale
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RightSupport::Net
|
2
|
+
if_require_succeeds('restclient') do
|
3
|
+
HAS_REST_CLIENT = true
|
4
|
+
end
|
5
|
+
|
6
|
+
class NoProvider < Exception; end
|
7
|
+
|
8
|
+
#
|
9
|
+
# A wrapper for the rest-client gem that provides timeouts and other
|
10
|
+
# useful features while preserving the simplicity and ease of use of
|
11
|
+
# RestClient's simple, static (module-level) interface.
|
12
|
+
#
|
13
|
+
module REST
|
14
|
+
DEFAULT_TIMEOUT = 5
|
15
|
+
|
16
|
+
def self.get(url, headers={}, timeout=DEFAULT_TIMEOUT, &block)
|
17
|
+
request(:method=>:get, :url=>url, :timeout=>timeout, :headers=>headers, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.post(url, payload, headers={}, timeout=DEFAULT_TIMEOUT, &block)
|
21
|
+
request(:method=>:post, :url=>url, :payload=>payload,
|
22
|
+
:timeout=>timeout, :headers=>headers, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.put(url, payload, headers={}, timeout=DEFAULT_TIMEOUT, &block)
|
26
|
+
request(:method=>:put, :url=>url, :payload=>payload,
|
27
|
+
:timeout=>timeout, :headers=>headers, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.delete(url, headers={}, timeout=DEFAULT_TIMEOUT, &block)
|
31
|
+
request(:method=>:delete, :url=>url, :timeout=>timeout, :headers=>headers, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.request(options, &block)
|
35
|
+
if HAS_REST_CLIENT
|
36
|
+
RestClient::Request.execute(options, &block)
|
37
|
+
else
|
38
|
+
raise NoProvider, "Cannot find a suitable HTTP client library"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
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
|
24
|
+
#
|
25
|
+
# A namespace for useful networking tools.
|
26
|
+
#
|
27
|
+
module Net
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Dir[File.expand_path('../net/*.rb', __FILE__)].each do |filename|
|
33
|
+
require filename
|
34
|
+
end
|
@@ -0,0 +1,62 @@
|
|
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
|
+
require 'logger'
|
24
|
+
|
25
|
+
module RightSupport
|
26
|
+
module Rack
|
27
|
+
# A Rack middleware that allows an arbitrary object to be used as the Rack logger.
|
28
|
+
# This is more flexible than Rack's built-in Logger middleware, which always logs
|
29
|
+
# to a file-based Logger and doesn't allow you to control anything other than the
|
30
|
+
# filename.
|
31
|
+
class CustomLogger
|
32
|
+
# Initialize an instance of the middleware.
|
33
|
+
#
|
34
|
+
# === Parameters
|
35
|
+
# app(Object):: the inner application or middleware layer; must respond to #call
|
36
|
+
# level(Integer):: one of the Logger constants: DEBUG, INFO, WARN, ERROR, FATAL
|
37
|
+
# logger(Logger):: (optional) the Logger object to use, if other than default
|
38
|
+
#
|
39
|
+
def initialize(app, level = ::Logger::INFO, logger = nil)
|
40
|
+
@app, @level = app, level
|
41
|
+
|
42
|
+
logger ||= ::Logger.new(env['rack.errors'])
|
43
|
+
logger.level = @level
|
44
|
+
@logger = logger
|
45
|
+
end
|
46
|
+
|
47
|
+
# Add a logger to the Rack environment and call the next middleware.
|
48
|
+
#
|
49
|
+
# === Parameters
|
50
|
+
# env(Hash):: the Rack environment
|
51
|
+
#
|
52
|
+
# === Return
|
53
|
+
# always returns whatever value is returned by the next layer of middleware
|
54
|
+
def call(env)
|
55
|
+
env['rack.logger'] = @logger
|
56
|
+
return @app.call(env)
|
57
|
+
ensure
|
58
|
+
@logger.close
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,175 @@
|
|
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
|
+
require 'logger'
|
24
|
+
|
25
|
+
module RightSupport
|
26
|
+
if_require_succeeds('syslog') do
|
27
|
+
# A logger that forwards log entries to the Unix syslog facility, but complies
|
28
|
+
# with the interface of the Ruby Logger object and faithfully translates log
|
29
|
+
# severities and other concepts. Provides optional cleanup/filtering in order
|
30
|
+
# to keep the syslog from having weird characters or being susceptible to log
|
31
|
+
# forgery.
|
32
|
+
class SystemLogger < Logger
|
33
|
+
LOGGER_LEVELS = {
|
34
|
+
UNKNOWN => :alert,
|
35
|
+
FATAL => :err,
|
36
|
+
ERROR => :warning,
|
37
|
+
WARN => :notice,
|
38
|
+
INFO => :info,
|
39
|
+
DEBUG => :debug
|
40
|
+
}
|
41
|
+
|
42
|
+
SYSLOG_LEVELS = LOGGER_LEVELS.invert
|
43
|
+
DEFAULT_SYSLOG_LEVEL = :alert
|
44
|
+
|
45
|
+
DEFAULT_OPTIONS = {:split=>false, :color=>false}
|
46
|
+
|
47
|
+
@@syslog = nil
|
48
|
+
|
49
|
+
# Initialize this process's syslog facilities and construct a new syslog
|
50
|
+
# logger object.
|
51
|
+
#
|
52
|
+
# === Parameters
|
53
|
+
# program_name(String):: the syslog program name, 'ruby' by default
|
54
|
+
# options(Hash):: (optional) configuration options to use, see below
|
55
|
+
#
|
56
|
+
# === Options
|
57
|
+
# :facility:: the syslog facility to use for messages, 'local0' by default
|
58
|
+
# :split(true|false):: if true, splits multi-line messages into separate syslog entries
|
59
|
+
# :color(true|false):: if true, passes ANSI escape sequences through to syslog
|
60
|
+
#
|
61
|
+
def initialize(program_name='ruby', options={})
|
62
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
63
|
+
@level = Logger::DEBUG
|
64
|
+
|
65
|
+
facility = options[:facility] || 'local0'
|
66
|
+
fac_map = {'user'=>8}
|
67
|
+
(0..7).each { |i| fac_map['local'+i.to_s] = 128+8*i }
|
68
|
+
@@syslog ||= Syslog.open(program_name, nil, fac_map[facility.to_s])
|
69
|
+
end
|
70
|
+
|
71
|
+
# Log a message if the given severity is high enough. This is the generic
|
72
|
+
# logging method. Users will be more inclined to use #debug, #info, #warn,
|
73
|
+
# #error, and #fatal.
|
74
|
+
#
|
75
|
+
# === Parameters
|
76
|
+
# severity(Integer):: one of the severity constants defined by Logger
|
77
|
+
# message(Object):: the message to be logged
|
78
|
+
# progname(String):: ignored, the program name is fixed at initialization
|
79
|
+
#
|
80
|
+
# === Block
|
81
|
+
# If message is nil and a block is supplied, this method will yield to
|
82
|
+
# obtain the log message.
|
83
|
+
#
|
84
|
+
# === Return
|
85
|
+
# true:: always returns true
|
86
|
+
#
|
87
|
+
def add(severity, message = nil, progname = nil, &block)
|
88
|
+
severity ||= UNKNOWN
|
89
|
+
if @@syslog.nil? or severity < @level
|
90
|
+
return true
|
91
|
+
end
|
92
|
+
|
93
|
+
progname ||= @progname
|
94
|
+
|
95
|
+
if message.nil?
|
96
|
+
if block_given?
|
97
|
+
message = yield
|
98
|
+
else
|
99
|
+
message = progname
|
100
|
+
progname = @progname
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
parts = clean(message)
|
105
|
+
parts.each { |part| emit_syslog(severity, part) }
|
106
|
+
return true
|
107
|
+
end
|
108
|
+
|
109
|
+
# Emit a log entry at INFO severity.
|
110
|
+
#
|
111
|
+
# === Parameters
|
112
|
+
# msg(Object):: the message to log
|
113
|
+
#
|
114
|
+
# === Return
|
115
|
+
# true:: always returns true
|
116
|
+
#
|
117
|
+
def <<(msg)
|
118
|
+
info(msg)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Do nothing. This method is provided for Logger interface compatibility.
|
122
|
+
#
|
123
|
+
# === Return
|
124
|
+
# true:: always returns true
|
125
|
+
#
|
126
|
+
def close
|
127
|
+
return true
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
# Call the syslog function to emit a syslog entry.
|
133
|
+
#
|
134
|
+
# === Parameters
|
135
|
+
# severity(Integer):: one of the Logger severity constants
|
136
|
+
# message(String):: the log message
|
137
|
+
#
|
138
|
+
# === Return
|
139
|
+
# true:: always returns true
|
140
|
+
def emit_syslog(severity, message)
|
141
|
+
level = SYSLOG_LEVELS[severity] || DEFAULT_SYSLOG_LEVEL
|
142
|
+
@@syslog.send(level, message)
|
143
|
+
return true
|
144
|
+
end
|
145
|
+
|
146
|
+
# Perform cleanup, output escaping and splitting on message.
|
147
|
+
# The operations that it performs can vary, depending on the
|
148
|
+
# options that were passed to this logger at initialization
|
149
|
+
# time.
|
150
|
+
#
|
151
|
+
# === Parameters
|
152
|
+
# message(String):: raw log message
|
153
|
+
#
|
154
|
+
# === Return
|
155
|
+
# log_lines([String]):: an array of String messages that should be logged separately to syslog
|
156
|
+
def clean(message)
|
157
|
+
message = message.to_s.dup
|
158
|
+
message.strip!
|
159
|
+
message.gsub!(/%/, '%%') # syslog(3) freaks on % (printf)
|
160
|
+
|
161
|
+
unless @options[:color]
|
162
|
+
message.gsub!(/\e\[[^m]*m/, '') # remove useless ansi color codes
|
163
|
+
end
|
164
|
+
|
165
|
+
if @options[:split]
|
166
|
+
bits = message.split(/[\n\r]+/)
|
167
|
+
else
|
168
|
+
bits = [message]
|
169
|
+
end
|
170
|
+
|
171
|
+
return bits
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,72 @@
|
|
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
|
+
require 'logger'
|
24
|
+
|
25
|
+
module RightSupport
|
26
|
+
# A logger that prepends a tag to every message that is emitted. Can be used to
|
27
|
+
# correlate logs with a Web session ID, transaction ID or other context.
|
28
|
+
#
|
29
|
+
# The user of this logger is responsible for calling #tag= to set the tag as
|
30
|
+
# appropriate, e.g. in a Web request around-filter.
|
31
|
+
#
|
32
|
+
# This logger uses thread-local storage (TLS) to provide tagging on a per-thread
|
33
|
+
# basis; however, it does not account for EventMachine, neverblock, the use of
|
34
|
+
# Ruby fibers, or any other phenomenon that can "hijack" a thread's call stack.
|
35
|
+
#
|
36
|
+
class TagLogger < FilterLogger
|
37
|
+
# Prepend the current tag to the log message; return the same severity and
|
38
|
+
# the modified message.
|
39
|
+
#
|
40
|
+
# === Parameters
|
41
|
+
# severity(Integer):: one of the severity constants defined by Logger
|
42
|
+
# messgae(String):: the log message
|
43
|
+
#
|
44
|
+
# === Return
|
45
|
+
# Returns a pair consisting of the filtered [severity, message].
|
46
|
+
#
|
47
|
+
def filter(severity, message)
|
48
|
+
@tls_id ||= "tag_logger_#{self.object_id}"
|
49
|
+
tag = Thread.current[@tls_id] || ''
|
50
|
+
if tag
|
51
|
+
return [severity, tag + message]
|
52
|
+
else
|
53
|
+
return [severity, message]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_reader :tag
|
58
|
+
|
59
|
+
# Set the tag for this logger.
|
60
|
+
#
|
61
|
+
# === Parameters
|
62
|
+
# tag(String|nil):: the new tag, or nil to remove the tag
|
63
|
+
#
|
64
|
+
# === Return
|
65
|
+
# String:: returns the new tag
|
66
|
+
def tag=(tag)
|
67
|
+
@tag = tag
|
68
|
+
@tls_id ||= "tag_logger_#{self.object_id}"
|
69
|
+
Thread.current[@tls_id] = @tag
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,90 @@
|
|
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
|
+
require 'openssl'
|
24
|
+
|
25
|
+
module RightSupport::Validation
|
26
|
+
# Validation methods pertaining to OpenSSL cryptography, e.g. various
|
27
|
+
# widely-used key formats and encoding/envelope formats.
|
28
|
+
module OpenSSL
|
29
|
+
# Determine whether a string is a PEM-encoded public or private key.
|
30
|
+
# Does not determine whether the key is valid, only that it is well-formed.
|
31
|
+
#
|
32
|
+
# === Parameters
|
33
|
+
# key_material(String):: the putative key material
|
34
|
+
#
|
35
|
+
# === Return
|
36
|
+
# If the key is well-formed, return the OpenSSL class that can be used
|
37
|
+
# to process the key material (e.g. OpenSSL::PKey::RSA). Otherwise, return
|
38
|
+
# false.
|
39
|
+
def pem_key?(key_material)
|
40
|
+
return false if key_material.nil? || key_material.empty?
|
41
|
+
m = /BEGIN ([A-Z]+) (PUBLIC|PRIVATE) KEY/.match(key_material)
|
42
|
+
return false unless m
|
43
|
+
case m[1]
|
44
|
+
when 'DSA' then return ::OpenSSL::PKey::DSA
|
45
|
+
when 'RSA' then return ::OpenSSL::PKey::RSA
|
46
|
+
else return false
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
# Determine whether a string is a valid PEM-encoded private key.
|
52
|
+
# Actually parses the key to prove validity as well as well-formedness.
|
53
|
+
# If the key is passphrase-protected, the passphrase is required in
|
54
|
+
# order to decrypt it; am incorrect passphrase will result in the key
|
55
|
+
# being recognized as not a valid key!
|
56
|
+
#
|
57
|
+
# === Parameters
|
58
|
+
# key_material(String):: the putative key material
|
59
|
+
# passphrase(String):: the encryption passphrase, if needed
|
60
|
+
#
|
61
|
+
# === Return
|
62
|
+
# If the key is well-formed and valid, return true. Otherwise, return false.
|
63
|
+
#
|
64
|
+
def pem_private_key?(key_material, passphrase=nil)
|
65
|
+
alg = pem_key?(key_material)
|
66
|
+
return false unless alg
|
67
|
+
key = alg.new(key_material, passphrase || 'dummy passphrase, should never work')
|
68
|
+
return key.private?
|
69
|
+
rescue ::OpenSSL::PKey::PKeyError, NotImplementedError
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
|
73
|
+
# Determine whether a string is a valid PEM-encoded public key.
|
74
|
+
# Actually parses the key to prove validity as well as well-formedness.
|
75
|
+
#
|
76
|
+
# === Parameters
|
77
|
+
# key_material(String):: the putative key material
|
78
|
+
#
|
79
|
+
# === Return
|
80
|
+
# If the key is well-formed and valid, return true. Otherwise, return false.
|
81
|
+
def pem_public_key?(key_material)
|
82
|
+
alg = pem_key?(key_material)
|
83
|
+
return false unless alg
|
84
|
+
key = alg.new(key_material)
|
85
|
+
return key.public?
|
86
|
+
rescue ::OpenSSL::PKey::PKeyError, NotImplementedError
|
87
|
+
return false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,70 @@
|
|
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
|
+
if_require_succeeds('net/ssh') do
|
24
|
+
module RightSupport::Validation
|
25
|
+
# Validation methods pertaining to the Secure Shell (SSH) protocol.
|
26
|
+
module SSH
|
27
|
+
# Determine whether a string is a valid PEM-encoded private key.
|
28
|
+
# Actually parses the key to prove validity as well as well-formedness.
|
29
|
+
# Relies on the OpenSSL Validation module to parse the private key
|
30
|
+
# since PEM is a standard non-SSH-specific key format.
|
31
|
+
#
|
32
|
+
# === Parameters
|
33
|
+
# key_material(String):: the putative key material
|
34
|
+
# passphrase(String):: the encryption passphrase, if needed
|
35
|
+
#
|
36
|
+
# === Return
|
37
|
+
# If the key is well-formed and valid, return true. Otherwise, return false.
|
38
|
+
def ssh_private_key?(key_material, passphrase=nil)
|
39
|
+
return RightSupport::Validation.pem_private_key?(key_material, passphrase)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Determine whether a string is a valid public key in SSH public-key
|
43
|
+
# notation as might be found in an SSH authorized_keys file.
|
44
|
+
#
|
45
|
+
# However, authorized-key options are not allowed as they would be in an
|
46
|
+
# actual line of the authorized_keys file. The caller is responsible for
|
47
|
+
# stripping out any options. The string can consist of the following three
|
48
|
+
# whitespace-separated fields:
|
49
|
+
# * algorithm (e.g. "ssh-rsa")
|
50
|
+
# * key material (base64-encoded blob)
|
51
|
+
# * comments (e.g. "user@localhost"); optional
|
52
|
+
#
|
53
|
+
# This method actually parses the public key to prove validity as well as
|
54
|
+
# well-formedness.
|
55
|
+
#
|
56
|
+
# === Parameters
|
57
|
+
# key_material(String):: the putative key material
|
58
|
+
#
|
59
|
+
# === Return
|
60
|
+
# If the key is well-formed and valid, return true. Otherwise, return false.
|
61
|
+
def ssh_public_key?(key_material)
|
62
|
+
return false if key_material.nil? || key_material.empty?
|
63
|
+
::Net::SSH::KeyFactory.load_data_public_key(key_material)
|
64
|
+
return true
|
65
|
+
rescue ::Net::SSH::Exception, ::OpenSSL::PKey::PKeyError, NotImplementedError
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,45 @@
|
|
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
|
24
|
+
# The Validation module acts as a namespace for various submodules that provide
|
25
|
+
# validation functions. These submodules can be mixed into classes in order to
|
26
|
+
# add validation logic.
|
27
|
+
#
|
28
|
+
# As a convenience, to discourage mixin abuse, the Validation module includes
|
29
|
+
# all of its submodules into its eigenclass at load-time. This means that every
|
30
|
+
# validation method "is_foo" provided by _any_ submodule can be accessed simply
|
31
|
+
# with a call to Validation.is_foo.
|
32
|
+
#
|
33
|
+
module Validation
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Dir[File.expand_path('../validation/*.rb', __FILE__)].each do |filename|
|
39
|
+
require filename
|
40
|
+
end
|
41
|
+
|
42
|
+
RightSupport::Validation.constants.each do |const|
|
43
|
+
const = RightSupport::Validation.const_get(const) #string to constant
|
44
|
+
RightSupport::Validation.extend(const)
|
45
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'right_support/kernel_extensions'
|
2
|
+
require 'right_support/filter_logger'
|
3
|
+
require 'right_support/system_logger'
|
4
|
+
require 'right_support/tag_logger'
|
5
|
+
require 'right_support/rack/custom_logger'
|
6
|
+
|
7
|
+
require 'right_support/validation'
|
8
|
+
|
9
|
+
require 'right_support/net'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- mode: ruby; encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
spec = Gem::Specification.new do |s|
|
6
|
+
s.required_rubygems_version = nil if s.respond_to? :required_rubygems_version=
|
7
|
+
s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
|
8
|
+
|
9
|
+
s.name = 'right_support'
|
10
|
+
s.version = '0.8.0'
|
11
|
+
s.date = '2011-04-09'
|
12
|
+
|
13
|
+
s.authors = ['Tony Spataro']
|
14
|
+
s.email = 'tony@rightscale.com'
|
15
|
+
s.homepage= 'https://github.com/xeger/right_support'
|
16
|
+
|
17
|
+
s.summary = %q{Reusable foundation code.}
|
18
|
+
s.description = %q{A toolkit of useful foundation code: logging, input validation, etc.}
|
19
|
+
|
20
|
+
s.add_development_dependency('rake', [">= 0.8.7"])
|
21
|
+
s.add_development_dependency('ruby-debug', [">= 0.10"])
|
22
|
+
s.add_development_dependency('rspec', ["~> 1.3"])
|
23
|
+
s.add_development_dependency('cucumber', ["~> 0.8"])
|
24
|
+
s.add_development_dependency('flexmock', ["~> 0.8"])
|
25
|
+
s.add_development_dependency('net-ssh', ["~> 2.0"])
|
26
|
+
s.add_development_dependency('rest-client', ["~> 1.6"])
|
27
|
+
|
28
|
+
basedir = File.dirname(__FILE__)
|
29
|
+
candidates = ['right_support.gemspec', 'LICENSE', 'README.rdoc'] + Dir['lib/**/*']
|
30
|
+
s.files = candidates.sort
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: right_support
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 63
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 8
|
9
|
+
- 0
|
10
|
+
version: 0.8.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Tony Spataro
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-04-09 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rake
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 49
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 8
|
33
|
+
- 7
|
34
|
+
version: 0.8.7
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: ruby-debug
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 31
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 10
|
49
|
+
version: "0.10"
|
50
|
+
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: rspec
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 9
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 3
|
64
|
+
version: "1.3"
|
65
|
+
type: :development
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: cucumber
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 27
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
- 8
|
79
|
+
version: "0.8"
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id004
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: flexmock
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 27
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
- 8
|
94
|
+
version: "0.8"
|
95
|
+
type: :development
|
96
|
+
version_requirements: *id005
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: net-ssh
|
99
|
+
prerelease: false
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ~>
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 2
|
108
|
+
- 0
|
109
|
+
version: "2.0"
|
110
|
+
type: :development
|
111
|
+
version_requirements: *id006
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: rest-client
|
114
|
+
prerelease: false
|
115
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ~>
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
hash: 3
|
121
|
+
segments:
|
122
|
+
- 1
|
123
|
+
- 6
|
124
|
+
version: "1.6"
|
125
|
+
type: :development
|
126
|
+
version_requirements: *id007
|
127
|
+
description: "A toolkit of useful foundation code: logging, input validation, etc."
|
128
|
+
email: tony@rightscale.com
|
129
|
+
executables: []
|
130
|
+
|
131
|
+
extensions: []
|
132
|
+
|
133
|
+
extra_rdoc_files: []
|
134
|
+
|
135
|
+
files:
|
136
|
+
- LICENSE
|
137
|
+
- README.rdoc
|
138
|
+
- lib/right_support.rb
|
139
|
+
- lib/right_support/filter_logger.rb
|
140
|
+
- lib/right_support/kernel_extensions.rb
|
141
|
+
- lib/right_support/net.rb
|
142
|
+
- lib/right_support/net/request_balancer.rb
|
143
|
+
- lib/right_support/net/rest.rb
|
144
|
+
- lib/right_support/rack/custom_logger.rb
|
145
|
+
- lib/right_support/system_logger.rb
|
146
|
+
- lib/right_support/tag_logger.rb
|
147
|
+
- lib/right_support/validation.rb
|
148
|
+
- lib/right_support/validation/openssl.rb
|
149
|
+
- lib/right_support/validation/ssh.rb
|
150
|
+
- right_support.gemspec
|
151
|
+
has_rdoc: true
|
152
|
+
homepage: https://github.com/xeger/right_support
|
153
|
+
licenses: []
|
154
|
+
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
|
158
|
+
require_paths:
|
159
|
+
- lib
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
hash: 57
|
166
|
+
segments:
|
167
|
+
- 1
|
168
|
+
- 8
|
169
|
+
- 7
|
170
|
+
version: 1.8.7
|
171
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
|
+
none: false
|
173
|
+
requirements:
|
174
|
+
- - ">="
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
hash: 3
|
177
|
+
segments:
|
178
|
+
- 0
|
179
|
+
version: "0"
|
180
|
+
requirements: []
|
181
|
+
|
182
|
+
rubyforge_project:
|
183
|
+
rubygems_version: 1.3.7
|
184
|
+
signing_key:
|
185
|
+
specification_version: 3
|
186
|
+
summary: Reusable foundation code.
|
187
|
+
test_files: []
|
188
|
+
|