right_cloud_api_base 0.1.0 → 0.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8f33d6964b838a9bb131d41d06239ff7dbeb2c4f
4
- data.tar.gz: c099b7d9948e970eb00035cf9888d53973238d10
3
+ metadata.gz: 93876f7645c63a599b1cdde9e5752490efcb6b9e
4
+ data.tar.gz: cf0a599994e10d1c17c2cf014e03ab303a18deed
5
5
  SHA512:
6
- metadata.gz: e2a7cbfac9fef859a80fef19ed779085762db032ee19475ea74f5d33de230d5905bd157be7d5804fc833a011b249766ca62327c5777a7d8c75b1e089a5a18b97
7
- data.tar.gz: 04a958fe2e8f940286a3fb0c0dfa2712393ad3bc61aeff17c7998553dd4ece5abc734131e360a0fe2cec375b2b9edcd6734882a2a5fcaf16ba25b4fef0868354
6
+ metadata.gz: 76b486fbb4c8392dff74a77e953c051b7eabeb8a2e723f795d0efb1bf66c3cebbcb6c543bc36e4674e41adee214e8bac4955fda97cd69b8a79e11ad7a4752e9c
7
+ data.tar.gz: ba3b90502877bbe162ceae9bfba958bb4682cc5ee2083fc1cd71c36b435c1ca8bb078cd522861683fb91fa30d3bf82855758936e803c2b04275e654a06e8b44a
data/HISTORY CHANGED
@@ -1,2 +1,6 @@
1
1
  == 2013-06-28
2
- - pre-release candidate created
2
+ - pre-release candidate created
3
+
4
+ == 2014-11-03
5
+ - Fix object._blank? issue for IO objects.
6
+ - version bumped to 0.1.1
@@ -179,6 +179,10 @@ module RightScale
179
179
  # @option options [Integer] :connection_retry_delay
180
180
  # Defines how long we wait on a low level connection error (in seconds)
181
181
  #
182
+ # @option options [Integer] :connection_verify_mode
183
+ # SSL connection cert check: either OpenSSL::SSL::VERIFY_PEER (default) or
184
+ # OpenSSL::SSL::VERIFY_NONE
185
+ #
182
186
  # @option options [Hash] :creds
183
187
  # A set of optional extra creds a cloud may require
184
188
  # (see right_cloud_stack_api gem which supports :tenant_name and :tenant_id)
@@ -116,26 +116,21 @@ module RightScale
116
116
  #
117
117
  def body=(new_body)
118
118
  # Set a request body
119
- if new_body._blank?
120
- @body = nil
121
- self['content-length'] = 0
122
- else
123
- if new_body.is_a?(IO)
124
- @body = file = new_body
125
- # Make sure the file is openned in binmode
126
- file.binmode if file.respond_to?(:binmode)
127
- # Fix 'content-length': it must not be bigger than a piece of a File left to be read or a String body size.
128
- # Otherwise the connection may behave like crazy causing 4xx or 5xx responses
129
- # KD: Make sure this code is used with the patched RightHttpConnection gem (see net_fix.rb)
130
- file_size = file.respond_to?(:lstat) ? file.lstat.size : file.size
131
- bytes_to_read = [ file_size - file.pos, self['content-length'].first ].compact.map{|v| v.to_i }.sort.first # remove nils then make values Integers
132
- if self['content-length'].first._blank? || self['content-length'].first.to_i > bytes_to_read
133
- self['content-length'] = bytes_to_read
134
- end
135
- else
136
- @body = new_body
137
- self['content-length'] = body.size if self['content-length'].first.to_i > body.size
119
+ if new_body.is_a?(IO)
120
+ @body = file = new_body
121
+ # Make sure the file is openned in binmode
122
+ file.binmode if file.respond_to?(:binmode)
123
+ # Fix 'content-length': it must not be bigger than a piece of a File left to be read or a String body size.
124
+ # Otherwise the connection may behave like crazy causing 4xx or 5xx responses
125
+ # KD: Make sure this code is used with the patched RightHttpConnection gem (see net_fix.rb)
126
+ file_size = file.respond_to?(:lstat) ? file.lstat.size : file.size
127
+ bytes_to_read = [ file_size - file.pos, self['content-length'].first ].compact.map{|v| v.to_i }.sort.first # remove nils then make values Integers
128
+ if self['content-length'].first._blank? || self['content-length'].first.to_i > bytes_to_read
129
+ self['content-length'] = bytes_to_read
138
130
  end
131
+ else
132
+ @body = new_body.to_s
133
+ self['content-length'] = @body.bytesize if self['content-length'].first.to_i > @body.bytesize
139
134
  end
140
135
  end
141
136
 
@@ -164,7 +159,7 @@ module RightScale
164
159
  if is_io?
165
160
  "#{body.class.name}, size: #{body.respond_to?(:lstat) ? body.lstat.size : body.size}, pos: #{body.pos}"
166
161
  else
167
- "size: #{body.to_s.size}, first #{BODY_BYTES_TO_LOG} bytes:\n#{body.to_s[0...BODY_BYTES_TO_LOG]}"
162
+ "size: #{body.to_s.bytesize}, first #{BODY_BYTES_TO_LOG} bytes:\n#{body.to_s[0...BODY_BYTES_TO_LOG]}"
168
163
  end
169
164
  end
170
165
  end
@@ -49,7 +49,7 @@ module RightScale
49
49
  def self.base64en(string)
50
50
  Base64::encode64(string.to_s).strip
51
51
  end
52
-
52
+
53
53
  # Makes a URL params string from a given hash. If block is given the it invokes the block on
54
54
  # every value so that one could escape it in his own way. If there is no block it url_encode
55
55
  # values automatically.
@@ -169,9 +169,9 @@ module RightScale
169
169
  # :code! => Condition, # Response code must not match Condition
170
170
  # :response! => Condition, # Response body must not match Condition
171
171
  # :if => Proc::new{ |opts| do something } # Extra condition: should return true | false
172
- #
172
+ #
173
173
  # (Condition above is /RegExp/ or String or Symbol)
174
- #
174
+ #
175
175
  # Opts is a Hash:
176
176
  # :request => Object, # HTTP request instance
177
177
  # :response => Object, # HTTP response instance
@@ -210,7 +210,7 @@ module RightScale
210
210
  return nil if pattern[:if] && !pattern[:if].call(opts)
211
211
  true
212
212
  end
213
-
213
+
214
214
  # Returns an Array with the current Thread and Fiber (if exists) instances.
215
215
  #
216
216
  # @return [Array] The first item is the current Thread instance and the second item
@@ -233,7 +233,7 @@ module RightScale
233
233
  end
234
234
  end
235
235
  end
236
-
236
+
237
237
  # Transforms body (when it is a Hash) into String
238
238
  #
239
239
  # @param [Hash] body The request body as a Hash instance.
@@ -254,13 +254,14 @@ module RightScale
254
254
  def self.contentify_body(body, content_type)
255
255
  return body unless body.is_a?(Hash)
256
256
  # Transform
257
+ ct = dearrayify(content_type).to_s
257
258
  case dearrayify(content_type).to_s
258
259
  when /json/ then body.to_json
259
260
  when /xml/ then body._to_xml!
260
- else fail Error::new("Can't transform body from Hash into #{content_type.inspect} type String")
261
+ else fail Error::new("Cannot transform Hash body into #{ct.inspect} content type")
261
262
  end
262
263
  end
263
-
264
+
264
265
  # Generates a unique token (Uses UUID when possible)
265
266
  #
266
267
  # @return [String] A random 28-symbols string.
@@ -275,7 +276,7 @@ module RightScale
275
276
  # Use UUID gem if it is
276
277
  if defined?(UUID) && UUID::respond_to?(:new)
277
278
  uuid = UUID::new
278
- return uuid.generate if uuid.respond_to?(:generate)
279
+ return uuid.generate if uuid.respond_to?(:generate)
279
280
  end
280
281
  # Otherwise generate a random token
281
282
  time = Time::now.utc
@@ -374,7 +375,7 @@ module RightScale
374
375
  end
375
376
  chain
376
377
  end
377
-
378
+
378
379
  end
379
380
  end
380
381
  end
@@ -24,7 +24,7 @@
24
24
  module RightScale
25
25
  module CloudApi
26
26
  class ConnectionProxy
27
-
27
+
28
28
  class NetHttpPersistentProxy
29
29
  class Error < CloudApi::Error
30
30
  end
@@ -40,7 +40,8 @@ module RightScale
40
40
  def log(message)
41
41
  @data[:options][:cloud_api_logger].log(message, :connection_proxy, :warn)
42
42
  end
43
-
43
+
44
+
44
45
  # Performs an HTTP request.
45
46
  #
46
47
  # @param [Hash] data The API request +data+ storage.
@@ -51,48 +52,101 @@ module RightScale
51
52
  #
52
53
  def request(data)
53
54
  require "net/http/persistent"
54
-
55
- @data = data
55
+ # Initialize things:
56
+ @data = data
56
57
  @data[:response] = {}
57
- uri = @data[:connection][:uri]
58
-
59
- # Create a connection
60
- connection = Net::HTTP::Persistent.new('right_cloud_api_gem')
61
-
62
- # Create a fake HTTP request
63
- fake = @data[:request][:instance]
64
- http_request = "Net::HTTP::#{fake.verb._camelize}"._constantize::new(fake.path)
65
- if fake.is_io?
66
- http_request.body_stream = fake.body
67
- else
68
- http_request.body = fake.body
58
+ # Create a new HTTP request instance
59
+ http_request = create_new_http_request
60
+ # Create and tweak Net::HTTP::Persistent instance
61
+ connection = create_new_persistent_connection
62
+ # Make a request
63
+ begin
64
+ make_request_with_retries(connection, @data[:connection][:uri], http_request)
65
+ rescue => e
66
+ fail(ConnectionError, e.message)
67
+ ensure
68
+ connection.shutdown
69
69
  end
70
- fake.headers.each{|header, value| http_request[header] = value }
71
- fake.raw = http_request
70
+ end
72
71
 
72
+
73
+ # Creates a new connection.
74
+ #
75
+ # There is a bug in Net::HTTP::Persistent where it allows you to reuse an SSL connection
76
+ # created by another instance of Net::HTTP::Persistent, if they share the same app name.
77
+ # To avoid this, every instance of Net::HTTP::Persistent should have its own 'name'.
78
+ #
79
+ # If your app does not care about SSL certs and keys (like AWS does) then it is safe to
80
+ # reuse connections.
81
+ #
82
+ # see https://github.com/drbrain/net-http-persistent/issues/45
83
+ #
84
+ def create_new_persistent_connection
85
+ app_name = if @data[:options][:connection_ca_file] ||
86
+ @data[:credentials][:cert] ||
87
+ @data[:credentials][:key]
88
+ 'right_cloud_api_gem_%s' % Utils::generate_token
89
+ else
90
+ 'right_cloud_api_gem'
91
+ end
92
+ connection = Net::HTTP::Persistent.new(app_name)
93
+ set_persistent_connection_options!(connection)
73
94
  # Register a callback to close current connection
74
95
  @data[:callbacks][:close_current_connection] = Proc::new do |reason|
75
96
  connection.shutdown
76
97
  log "Current connection closed: #{reason}"
77
98
  end
78
-
79
- # Set all required options
80
- # P.S. :connection_retry_count, :http_connection_retry_delay are not supported by this proxy
81
- #
82
- http_request['user-agent'] ||= @data[:options][:connection_user_agent] if @data[:options].has_key?(:connection_user_agent)
83
- connection.ca_file = @data[:options][:connection_ca_file] if @data[:options].has_key?(:connection_ca_file)
84
- connection.read_timeout = @data[:options][:connection_read_timeout] if @data[:options].has_key?(:connection_read_timeout)
85
- connection.open_timeout = @data[:options][:connection_open_timeout] if @data[:options].has_key?(:connection_open_timeout)
86
- connection.cert = OpenSSL::X509::Certificate.new(@data[:credentials][:cert]) if @data[:credentials].has_key?(:cert)
87
- connection.key = OpenSSL::PKey::RSA.new(@data[:credentials][:key]) if @data[:credentials].has_key?(:key)
99
+ connection
100
+ end
88
101
 
89
- # Make a request
90
- begin
91
- make_request_with_retries(connection, uri, http_request)
92
- rescue => e
93
- connection.shutdown
94
- fail(ConnectionError, e.message)
102
+
103
+ # Sets connection_ca_file, connection_read_timeout, connection_open_timeout,
104
+ # connection_verify_mode and SSL cert and key
105
+ #
106
+ # @param [Net::HTTP::Persistent] connection
107
+ #
108
+ # @return [Net::HTTP::Persistent]
109
+ #
110
+ def set_persistent_connection_options!(connection)
111
+ [:ca_file, :read_timeout, :open_timeout, :verify_mode].each do |connection_method|
112
+ connection_option_name = "connection_#{connection_method}".to_sym
113
+ next unless @data[:options].has_key?(connection_option_name)
114
+ connection.__send__("#{connection_method}=", @data[:options][connection_option_name])
115
+ end
116
+ if @data[:credentials].has_key?(:cert)
117
+ connection.cert = OpenSSL::X509::Certificate.new(@data[:credentials][:cert])
118
+ end
119
+ if @data[:credentials].has_key?(:key)
120
+ connection.key = OpenSSL::PKey::RSA.new(@data[:credentials][:key])
121
+ end
122
+ connection
123
+ end
124
+
125
+
126
+ # Creates and configures a new HTTP request object
127
+ #
128
+ # @return [Net::HTTPRequest]
129
+ #
130
+ def create_new_http_request
131
+ # Create a new HTTP request instance
132
+ request_spec = @data[:request][:instance]
133
+ http_class = "Net::HTTP::#{request_spec.verb._camelize}"
134
+ http_request = http_class._constantize::new(request_spec.path)
135
+ # Set the request body
136
+ if request_spec.is_io?
137
+ http_request.body_stream = request_spec.body
138
+ else
139
+ http_request.body = request_spec.body
140
+ end
141
+ # Copy headers
142
+ request_spec.headers.each { |header, value| http_request[header] = value }
143
+ # Save the new request
144
+ request_spec.raw = http_request
145
+ # Set user-agent
146
+ if @data[:options].has_key?(:connection_user_agent)
147
+ http_request['user-agent'] ||= @data[:options][:connection_user_agent]
95
148
  end
149
+ http_request
96
150
  end
97
151
 
98
152
 
@@ -188,7 +242,7 @@ module RightScale
188
242
  nil
189
243
  end
190
244
 
191
- end
245
+ end
192
246
  end
193
247
  end
194
248
  end
@@ -91,7 +91,10 @@ module RightScale
91
91
  http_connection_data[:raise_on_timeout] = @data[:options][:abort_on_timeout] if @data[:options][:abort_on_timeout]
92
92
  http_connection_data[:cert] = @data[:credentials][:cert] if @data[:credentials].has_key?(:cert)
93
93
  http_connection_data[:key] = @data[:credentials][:key] if @data[:credentials].has_key?(:key)
94
-
94
+ if @data[:options].has_key?(:connection_verify_mode)
95
+ http_connection_data[:use_server_auth] = (@data[:options][:connection_verify_mode] != OpenSSL::SSL::VERIFY_NONE)
96
+ end
97
+
95
98
  #log "HttpConnection request: #{http_connection_data.inspect}"
96
99
 
97
100
  # Make a request:
@@ -47,8 +47,8 @@ module RightScale
47
47
  connection_proxy_class = RightScale::CloudApi::ConnectionProxy::NetHttpPersistentProxy
48
48
  end
49
49
  @connection_proxy = connection_proxy_class.new
50
- end
51
-
50
+ end
51
+
52
52
  # Register a call back to close current connection
53
53
  data[:callbacks][:close_current_connection] = Proc::new do |reason|
54
54
  @connection_proxy.close_connection(nil, reason)
@@ -60,7 +60,7 @@ module RightScale
60
60
  @connection_proxy.request(data)
61
61
  end
62
62
  end
63
-
63
+
64
64
  end
65
65
  end
66
66
  end
@@ -31,7 +31,7 @@ module RightScale
31
31
  # CloudApi gem version namespace
32
32
  module VERSION
33
33
  # The gem version
34
- STRING = '0.1.0'
34
+ STRING = '0.2.1'
35
35
  end
36
36
  end
37
- end
37
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: right_cloud_api_base
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - RightScale, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-19 00:00:00.000000000 Z
11
+ date: 2015-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -193,23 +193,23 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
193
  version: '0'
194
194
  requirements: []
195
195
  rubyforge_project:
196
- rubygems_version: 2.1.11
196
+ rubygems_version: 2.4.3
197
197
  signing_key:
198
198
  specification_version: 4
199
199
  summary: The gem provides base Query and REST API management functionalities for Amazon,
200
200
  OpenStack, Rackspace, CloudStack, etc cloud services
201
201
  test_files:
202
+ - spec/spec_helper.rb
202
203
  - spec/routines/test_connection_proxy_spec.rb
203
- - spec/routines/test_result_wrapper_spec.rb
204
- - spec/routines/test_cache_validator_spec.rb
204
+ - spec/routines/test_retry_manager_spec.rb
205
+ - spec/routines/test_response_parser_spec.rb
205
206
  - spec/routines/connection_proxies/test_net_http_persistent_proxy_spec.rb
207
+ - spec/routines/test_cache_validator_spec.rb
206
208
  - spec/routines/test_response_analyzer_spec.rb
207
209
  - spec/routines/test_request_analyzer_spec.rb
208
- - spec/routines/test_response_parser_spec.rb
209
- - spec/routines/test_retry_manager_spec.rb
210
- - spec/spec_helper.rb
211
- - spec/helpers/query_api_pattern_spec.rb
210
+ - spec/routines/test_result_wrapper_spec.rb
212
211
  - spec/helpers/support_xml_spec.rb
213
212
  - spec/helpers/support_spec.rb
214
213
  - spec/helpers/utils_spec.rb
214
+ - spec/helpers/query_api_pattern_spec.rb
215
215
  has_rdoc: