oci 2.0.9 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +179 -10
- data/lib/oci.rb +1 -0
- data/lib/oci/api_client.rb +186 -64
- data/lib/oci/api_client_proxy_settings.rb +71 -0
- data/lib/oci/audit/audit_client.rb +6 -4
- data/lib/oci/auth/signers/instance_principals_security_token_signer.rb +1 -1
- data/lib/oci/core/blockstorage_client.rb +18 -4
- data/lib/oci/core/compute_client.rb +27 -4
- data/lib/oci/core/core.rb +5 -0
- data/lib/oci/core/models/connect_remote_peering_connections_details.rb +149 -0
- data/lib/oci/core/models/create_remote_peering_connection_details.rb +165 -0
- data/lib/oci/core/models/peer_region_for_remote_peering.rb +125 -0
- data/lib/oci/core/models/remote_peering_connection.rb +362 -0
- data/lib/oci/core/models/update_remote_peering_connection_details.rb +129 -0
- data/lib/oci/core/virtual_network_client.rb +334 -4
- data/lib/oci/database/database_client.rb +41 -4
- data/lib/oci/database/models/db_system.rb +1 -1
- data/lib/oci/database/models/db_system_shape_summary.rb +74 -2
- data/lib/oci/database/models/db_system_summary.rb +1 -1
- data/lib/oci/dns/dns_client.rb +32 -4
- data/lib/oci/email/email_client.rb +10 -4
- data/lib/oci/errors.rb +115 -19
- data/lib/oci/file_storage/file_storage_client.rb +18 -4
- data/lib/oci/global_context.rb +2 -2
- data/lib/oci/identity/identity_client.rb +68 -4
- data/lib/oci/identity/models/create_region_subscription_details.rb +1 -0
- data/lib/oci/identity/models/region.rb +2 -0
- data/lib/oci/identity/models/region_subscription.rb +2 -0
- data/lib/oci/identity/models/tenancy.rb +3 -1
- data/lib/oci/internal/util.rb +5 -0
- data/lib/oci/load_balancer/load_balancer_client.rb +59 -4
- data/lib/oci/object_storage/object_storage_client.rb +62 -4
- data/lib/oci/regions.rb +4 -2
- data/lib/oci/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 710c56b90571dcc168a349f896579265e90fe8d7
|
4
|
+
data.tar.gz: 4a77c01309de5a1b22e14b123c4038c5e714f0a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56cda480d05e618cfb517a1b1a13bc9137e13852e789dfdcbc78fab0eedfdabe4c9be1e2a28d342ac119003da1323c987edcee78d7ebc5b15b5b5a03a01769da
|
7
|
+
data.tar.gz: c47dc6cce662d968890c7b8f5c405db3eee70f70cc5a265cca84c43d35fe8a632cdf9d8757f089c26903d00697c9713c47cf667cc1efc61e4b5d40d43ce727ec
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Oracle Cloud Infrastructure Ruby SDK
|
2
|
-
**Version 2.0
|
2
|
+
**Version 2.1.0**
|
3
3
|
|
4
4
|
This topic describes how to install, configure, and use the Oracle Cloud Infrastructure Ruby SDK.
|
5
5
|
|
@@ -39,7 +39,24 @@ The following table provides details about some of the attributes of the SDK.
|
|
39
39
|
</tr>
|
40
40
|
<tr>
|
41
41
|
<td>Errors</td>
|
42
|
-
<td>
|
42
|
+
<td>
|
43
|
+
The main exception classes for the SDK are:
|
44
|
+
<ul>
|
45
|
+
<li>{OCI::Errors::ServiceError ServiceError} is thrown when an OCI service returns a non-2xx HTTP status code</li>
|
46
|
+
<li>
|
47
|
+
{OCI::Errors::NetworkError NetworkError} is thrown when the issue is likely to be network-related rather than an application issue. This is defined as:
|
48
|
+
<ul>
|
49
|
+
<li>
|
50
|
+
Requests that were sent from the SDK, but for which the outcome was not a response from an OCI service. Examples include a gateway timeout, read or connection timeouts from Net::HTTP, and any (Errno) exception that was generated by the request itself.
|
51
|
+
</li>
|
52
|
+
<li>Requests that result in a HTTP 408 (timeout)</li>
|
53
|
+
</ul>
|
54
|
+
</li>
|
55
|
+
<li>
|
56
|
+
{OCI::Errors::ResponseParsingError ResponseParsingError} is thrown when a response is received from an OCI service, but the SDK could not parse it into the appropriate model type for inserting into an {OCI::Response}
|
57
|
+
</li>
|
58
|
+
</ul>
|
59
|
+
</td>
|
43
60
|
</tr>
|
44
61
|
<tr>
|
45
62
|
<td>Signing</td>
|
@@ -55,8 +72,7 @@ The following table provides details about some of the attributes of the SDK.
|
|
55
72
|
</tr>
|
56
73
|
<tr>
|
57
74
|
<td>HTTP Client</td>
|
58
|
-
<td>The Ruby SDK uses Net::HTTP for HTTP requests,
|
59
|
-
Please check http://ruby-doc.org/stdlib-2.4.1/libdoc/net/http/rdoc/Net/HTTP.html#method-c-start for the supported options.</td>
|
75
|
+
<td>The Ruby SDK uses Net::HTTP for HTTP requests. Information on how to configure the client, including configuring a proxy, can be found in the <b>Configuring the HTTP Client</b> section of this page</td>
|
60
76
|
</tr>
|
61
77
|
<tr>
|
62
78
|
<td>Instance Principals Authentication</td>
|
@@ -69,7 +85,7 @@ The following table provides details about some of the attributes of the SDK.
|
|
69
85
|
<td>
|
70
86
|
The Object Storage service supports multipart uploads to make large object uploads easier by splitting the large object into parts. The Ruby SDK supports raw multipart upload operations for advanced use cases, as well as a higher-level upload class that uses the multipart upload APIs.
|
71
87
|
<p>
|
72
|
-
<a href="https://docs.us-phoenix-1.oraclecloud.com/Content/Object/Tasks/managingmultipartuploads.htm">Managing Multipart Uploads</a> provides links to the APIs used for raw multipart upload operations. Higher-level uploads can be performed using the {OCI::ObjectStorage::Transfer::UploadManager}.
|
88
|
+
<a href="https://docs.us-phoenix-1.oraclecloud.com/Content/Object/Tasks/managingmultipartuploads.htm">Managing Multipart Uploads</a> provides links to the APIs used for raw multipart upload operations. Higher-level uploads can be performed using the {OCI::ObjectStorage::Transfer::UploadManager}.
|
73
89
|
</p>
|
74
90
|
<p>
|
75
91
|
The UploadManager simplifies interaction with the Object Storage service by abstracting away the method used to upload objects and can handle uploading an entire object at once, or in multiple parts if it is of sufficient size (which is configurable via a {OCI::ObjectStorage::Transfer::UploadManagerConfig} object). In the latter case, the UploadManager will split a large object into parts for you, upload the parts in parallel, and then recombine and commit the parts as a single object in Object Storage.
|
@@ -154,9 +170,165 @@ Or you can create a {OCI::Config Config} programmatically. Note that the global
|
|
154
170
|
|
155
171
|
The default config file location is `~/.oci/config` (on Windows `C:\Users\{user}\.oci\config`).
|
156
172
|
|
157
|
-
##
|
173
|
+
## Configuring the HTTP Client
|
174
|
+
|
175
|
+
The underlying HTTP client used by the SDK can be configured by setting the {OCI::ApiClient#request_option_overrides ApiClient.request_option_overrides} on the client. An example on how to do this, showing all supported options, is:
|
176
|
+
|
177
|
+
require 'oci'
|
178
|
+
|
179
|
+
identity = OCI::Identity::IdentityService.new
|
180
|
+
|
181
|
+
identity.api_client.request_option_overrides = {
|
182
|
+
# The path to the PEM-formatted CA certificate file.
|
183
|
+
# The file can contain several CA certificates
|
184
|
+
ca_file: '/path/to/pem/file',
|
185
|
+
|
186
|
+
# The path to a directory of PEM-formatted CA certificate files
|
187
|
+
ca_path_cert: '/path/to/cert/directory',
|
188
|
+
|
189
|
+
# An OpenSSL::X509::Certificate object as client certificate
|
190
|
+
cert: OpenSSL::X509::Certificate.new(...),
|
191
|
+
|
192
|
+
# The X509::Store to verify peer certificate.
|
193
|
+
cert_store: OpenSSL::X509::Store.new(...),
|
194
|
+
|
195
|
+
# One of the available ciphers from OpenSSL::SSL::SSLContext#ciphers
|
196
|
+
ciphers: ...,
|
197
|
+
|
198
|
+
# Close the connection immediately if the server sent no response body
|
199
|
+
close_on_empty_response: true,
|
200
|
+
|
201
|
+
# An OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object
|
202
|
+
key: ...,
|
203
|
+
|
204
|
+
# The SSL timeout in seconds
|
205
|
+
ssl_timeout: 60,
|
206
|
+
|
207
|
+
# The SSL version to use. See OpenSSL::SSL::SSLContext#ssl_version
|
208
|
+
ssl_version: ...,
|
209
|
+
|
210
|
+
# The verify callback for the server certification verification
|
211
|
+
verify_callback: ...,
|
212
|
+
|
213
|
+
# The maximum depth for the certificate chain verification
|
214
|
+
verify_depth: 3,
|
215
|
+
|
216
|
+
# Either OpenSSL::SSL::VERIFY_PEER or OpenSSL::SSL::VERIFY_NONE
|
217
|
+
# Using OpenSSL::SSL::VERIFY_NONE (i.e. not validating the SSL certificate) is not
|
218
|
+
# recommended
|
219
|
+
verify_mode: OpenSSL::SSL::VERIFY_PEER
|
220
|
+
}
|
221
|
+
|
222
|
+
Note that these correspond to a subset of the keys that can be provided as `opts` to the `Net::HTTP` [start](https://ruby-doc.org/stdlib-2.4.3/libdoc/net/http/rdoc/Net/HTTP.html#method-c-start) method.
|
223
|
+
|
224
|
+
### Setting a proxy
|
225
|
+
If the SDK is running in an environment which requires the use of a proxy server for outgoing HTTP requests, this can be configured by providing via:
|
226
|
+
|
227
|
+
* Providing an environment variable; or
|
228
|
+
* Providing an {OCI::ApiClientProxySettings} object. Here is an example of the different ways to create this object:
|
229
|
+
|
230
|
+
#### Proxy via environment variable
|
231
|
+
As the SDK uses `Net::HTTP`, the `http_proxy` environment variable can be used to specify a proxy:
|
232
|
+
|
233
|
+
export HTTP_PROXY="http://10.10.1.10:3128"
|
234
|
+
|
235
|
+
When using the environment variable approach, no code changes need to be made.
|
236
|
+
|
237
|
+
Note that this does not support providing a username and password to the proxy, *except* when using Ruby 2.5+ on Linux, FreeBSD or macOS (Darwin). Otherwise, if a username and password is required then the {OCI::ApiClientProxySettings} object method can be used.
|
238
|
+
|
239
|
+
#### Proxy via the ApiClientProxySettings object
|
240
|
+
An {OCI::ApiClientProxySettings} object can be used to capture proxy settings. Here are the different ways an object can be created:
|
241
|
+
|
242
|
+
|
243
|
+
# This creates an ApiClientProxySettings with a nil proxy address and will make the API client
|
244
|
+
# bypass all proxies
|
245
|
+
proxy_settings = OCI::ApiClientProxySettings.new(nil)
|
246
|
+
|
247
|
+
# This creates an ApiClientProxySettings that will use the proxy at 10.10.1.10:3128 and will
|
248
|
+
# not provide a username and password to the proxy
|
249
|
+
proxy_settings = OCI::ApiClientProxySettings.new("10.10.1.10", 3128)
|
250
|
+
|
251
|
+
# This creates an ApiClientProxySettings with a username and password. In production, you should use an appropriate password/secret management mechanism to vend the username and password
|
252
|
+
# rather than having the credentials in code
|
253
|
+
proxy_settings = OCI::ApiClientProxySettings.new("10.10.1.10", 3128, "username", "pass")
|
254
|
+
|
255
|
+
Once an {OCI::ApiClientProxySettings} object has been created, it can be used when creating or modifying a client:
|
158
256
|
|
159
|
-
|
257
|
+
# At client creation time
|
258
|
+
identity = OCI::Identity::IdentityService.new(proxy_settings: proxy_settings)
|
259
|
+
|
260
|
+
# When updating a client
|
261
|
+
identity.api_client.proxy_settings = proxy_settings
|
262
|
+
|
263
|
+
# The proxy_settings can also be nil'ed out. This will make the ApiClient fall back to
|
264
|
+
# using the http_proxy environment variable (if present)
|
265
|
+
identity.api_client.proxy_settings = nil
|
266
|
+
|
267
|
+
## Exceptions
|
268
|
+
|
269
|
+
### Service Errors
|
270
|
+
Any operation that receives a response with a non-2xx HTTP status code from an OCI service will cause an exception of type {OCI::Errors::ServiceError ServiceError} to be thrown by the SDK.
|
271
|
+
|
272
|
+
For information about common service errors returned by OCI, see [API Errors](https://docs.us-phoenix-1.oraclecloud.com/Content/API/References/apierrors.htm).
|
273
|
+
|
274
|
+
The key attributes to inspect when dealing with a {OCI::Errors::ServiceError} are:
|
275
|
+
|
276
|
+
* {OCI::Errors::ServiceError#request_id request_id} if you need to contact Oracle about a particular request, please provide this request ID
|
277
|
+
* {OCI::Errors::ServiceError#status_code status_code} contains the HTTP status code of the response
|
278
|
+
* {OCI::Errors::ServiceError#service_code service_code} should contain a value from the *Error Code* field as described in the *API Errors* page
|
279
|
+
* {OCI::Errors::ServiceError#request_made request_made} contains the actual `Net::HTTPRequest` which was sent by the SDK
|
280
|
+
|
281
|
+
You can also call {OCI::Errors::ServiceError#to_s to_s} on the error to get a summary of the key information about the error.
|
282
|
+
|
283
|
+
#### HTTP 3xx Responses
|
284
|
+
The SDK will throw exceptions of type {OCI::Errors::ServiceError ServiceError} on HTTP 3xx responses. This impacts operations that support conditional GETs. This includes {OCI::ObjectStorage::ObjectStorageClient#get_object} and {OCI::ObjectStorage::ObjectStorageClient#head_object} methods as these can return responses with a HTTP status code of 304 if passed an `if_none_match`, which corresponds to the current etag of the object or bucket.
|
285
|
+
|
286
|
+
In order to account for this, you should catch {OCI::Errors::ServiceError ServiceError} and check its `status_code` attribute for the HTTP status code. For example:
|
287
|
+
|
288
|
+
require 'oci'
|
289
|
+
|
290
|
+
object_storage = OCI::ObjectStorage::ObjectStorageClient.new
|
291
|
+
|
292
|
+
begin
|
293
|
+
get_object_response = object_storage.get_object('my_namespace', 'my_bucket', 'my_object', if_none_match: 'some_etag_value')
|
294
|
+
rescue OCI::Errors::ServiceError => err
|
295
|
+
# Do nothing if the object exists but has not been modified (based on the etag value), otherwise raise the exception
|
296
|
+
raise if err.status_code != 304
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
### Network Errors
|
301
|
+
{OCI::Errors::NetworkError NetworkError} is thrown when the issue is likely to be network related rather than an application issue. This is defined as:
|
302
|
+
|
303
|
+
* Requests which were sent from the SDK but the outcome was not a response from an OCI service. Further examples of include:
|
304
|
+
* Gateway timeouts
|
305
|
+
* Read or connection timeouts
|
306
|
+
* Any {Errno} exception which was generated by making the request
|
307
|
+
* Requests which resulted in a HTTP 408 (timeout)
|
308
|
+
|
309
|
+
The key attributes to inspect when dealing with a {OCI::Errors::NetworkError} are:
|
310
|
+
|
311
|
+
* {OCI::Errors::NetworkError#request_made request_made} and {OCI::Errors::NetworkError#response_received response_received} contain the `Net::HTTPRequest` and `Net::HTTPResponse`, respectively
|
312
|
+
* {OCI::Errors::NetworkError#code code} contains the HTTP status code of the response
|
313
|
+
|
314
|
+
You can also call {OCI::Errors::NetworkError#to_s to_s} on the error to get a summary of the key information about the error. In addition, the {OCI::Errors::NetworkError#cause NetworkError.cause} of the {OCI::Errors::NetworkError NetworkError} can be inspected to see the original error that caused the {OCI::Errors::NetworkError NetworkError} to be thrown.
|
315
|
+
|
316
|
+
### Response Parsing Errors
|
317
|
+
{OCI::Errors::ResponseParsingError ResponseParsingError} is thrown when a response was received from an OCI service but the SDK could not parse it into the appropriate model type to put into an {OCI::Response}.
|
318
|
+
|
319
|
+
The key attributes to inspect when dealing with a {OCI::Errors::ResponseParsingError} are:
|
320
|
+
|
321
|
+
* {OCI::Errors::ResponseParsingError#request_made request_made} and {OCI::Errors::ResponseParsingError#response_received response_received} contain the `Net::HTTPRequest` and `Net::HTTPResponse`, respectively
|
322
|
+
* {OCI::Errors::ResponseParsingError#response_body response_body} contains the response data which failed to parse
|
323
|
+
|
324
|
+
You can also call {OCI::Errors::ResponseParsingError#to_s to_s} on the error to get a summary of the key information about the error. In addition, the {OCI::Errors::ResponseParsingError#cause ResponseParsingError.cause} of the {OCI::Errors::ResponseParsingError ResponseParsingError} can be inspected to see the original error that caused the {OCI::Errors::ResponseParsingError ResponseParsingError} to be thrown.
|
325
|
+
|
326
|
+
### Other Errors
|
327
|
+
The other errors you may encounter while using the SDK are:
|
328
|
+
|
329
|
+
* {OCI::Errors::MaximumWaitTimeExceededError MaximumWaitTimeExceededError} when using {OCI::Response#wait_until} or when using {OCI::LoadBalancer::Util#wait_on_work_request}
|
330
|
+
* {OCI::Errors::WorkRequestFailedError WorkRequestFailedError} when using {OCI::LoadBalancer::Util#wait_on_work_request}. The {OCI::Errors::WorkRequestFailedError#work_request work_request} attribute can be inspected to get the actual work request details
|
331
|
+
* {OCI::Errors::MultipartUploadError MultipartUploadError} when using {OCI::ObjectStorage::Transfer::UploadManager UploadManager}. The {OCI::Errors::MultipartUploadError#errors errors} attribute can be inspected to see what errors occurred during the upload, and the {OCI::Errors::MultipartUploadError#upload_id upload_id} can be used if you wish to try and resume the upload via the UploadManager's {OCI::ObjectStorage::Transfer::UploadManager#resume resume} method
|
160
332
|
|
161
333
|
## Examples
|
162
334
|
The example code in this section shows how various parts of the Ruby SDK work. More examples can be found in the SDK download.
|
@@ -349,6 +521,3 @@ Ways to get in touch:
|
|
349
521
|
* [Stack Overflow](https://stackoverflow.com/): Please use the [oracle-cloud-infrastructure](https://stackoverflow.com/questions/tagged/oracle-cloud-infrastructure) and [oci-ruby-sdk](https://stackoverflow.com/questions/tagged/oci-ruby-sdk) tags in your post
|
350
522
|
* [Developer Tools section](https://community.oracle.com/community/cloud_computing/bare-metal/content?filterID=contentstatus%5Bpublished%5D~category%5Bdeveloper-tools%5D&filterID=contentstatus%5Bpublished%5D~objecttype~objecttype%5Bthread%5D) of the Oracle Cloud forums
|
351
523
|
* [My Oracle Support](https://support.oracle.com)
|
352
|
-
|
353
|
-
|
354
|
-
|
data/lib/oci.rb
CHANGED
data/lib/oci/api_client.rb
CHANGED
@@ -45,7 +45,12 @@ module OCI
|
|
45
45
|
# @return [Hash]
|
46
46
|
attr_accessor :request_option_overrides
|
47
47
|
|
48
|
-
|
48
|
+
# The proxy settings which this ApiClient will use
|
49
|
+
#
|
50
|
+
# @return [OCI::ApiClientProxySettings]
|
51
|
+
attr_accessor :proxy_settings
|
52
|
+
|
53
|
+
def initialize(config, signer, proxy_settings: nil)
|
49
54
|
raise "Missing the required parameter 'config' when initializing ApiClient." if config.nil?
|
50
55
|
raise "Missing the required parameter 'signer' when initializing ApiClient." if signer.nil?
|
51
56
|
|
@@ -53,6 +58,7 @@ module OCI
|
|
53
58
|
@signer = signer
|
54
59
|
@default_headers = {}
|
55
60
|
@request_option_overrides = {}
|
61
|
+
@proxy_settings = proxy_settings
|
56
62
|
end
|
57
63
|
|
58
64
|
# Call an API with given options.
|
@@ -101,8 +107,8 @@ module OCI
|
|
101
107
|
# to support IO-like object as well like StringIO
|
102
108
|
return model if model.nil? || model.is_a?(String) || (model.respond_to?(:read) && model.respond_to?(:write))
|
103
109
|
|
104
|
-
# Supports IO-wrapping objects we can convert to an IO. An example is Rails'
|
105
|
-
# ActionDispatch::Http::UploadedFile, which wraps an IO (a Tempfile) but
|
110
|
+
# Supports IO-wrapping objects we can convert to an IO. An example is Rails'
|
111
|
+
# ActionDispatch::Http::UploadedFile, which wraps an IO (a Tempfile) but
|
106
112
|
# doesn't expose all the IO operations directly (e.g. you can't write to it, it's not seekable)
|
107
113
|
#
|
108
114
|
# This should be safe to use with IO and its subclasses as well as to_io is a method on IO:
|
@@ -177,7 +183,7 @@ module OCI
|
|
177
183
|
elsif http_method == :patch
|
178
184
|
request = Net::HTTP::Patch.new(uri)
|
179
185
|
else
|
180
|
-
|
186
|
+
raise "new http method (#{http_method}) needs to be supported!"
|
181
187
|
end
|
182
188
|
|
183
189
|
if body.respond_to?(:read) && body.respond_to?(:write)
|
@@ -204,7 +210,7 @@ module OCI
|
|
204
210
|
request['opc-request-id'] ||= build_request_id
|
205
211
|
request['User-Agent'] = build_user_agent
|
206
212
|
|
207
|
-
http =
|
213
|
+
http = get_http_object(uri.hostname, uri.port)
|
208
214
|
|
209
215
|
unless @request_option_overrides.empty?
|
210
216
|
http.methods.grep(/\A(\w+)=\z/) do |meth|
|
@@ -219,74 +225,166 @@ module OCI
|
|
219
225
|
http.open_timeout = @config.connection_timeout
|
220
226
|
http.read_timeout = @config.timeout.zero? ? 31_536_000 : @config.timeout # 31536000 means 365 days
|
221
227
|
|
228
|
+
response_ref = nil
|
222
229
|
begin
|
223
230
|
http.start do
|
224
|
-
http.request
|
225
|
-
|
226
|
-
# If the response is timeout already, does not make sense to parse the http body because partial
|
227
|
-
# response may be returned, we should skip JSON parser and raise exception immediately
|
228
|
-
if response.is_a? Net::HTTPRequestTimeOut
|
229
|
-
raise Errors::NetworkError.new(response.message, response.code.to_i)
|
230
|
-
end
|
231
|
+
http.request(request) do |response|
|
232
|
+
response_ref = response
|
231
233
|
|
232
234
|
# process headers for opc-meta- key
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
# In the case of an error, deserialize to a plain hash by indicating 'Object'.
|
252
|
-
return_type = success?(response) ? opts[:return_type] : 'Object'
|
253
|
-
data = return_type ? deserialize(response, return_type) : nil
|
254
|
-
|
255
|
-
unless success? response
|
256
|
-
if data
|
257
|
-
raise Errors::ServiceError.new(response.code.to_i,
|
258
|
-
data[:code],
|
259
|
-
response.header['opc-request-id'],
|
260
|
-
data[:message])
|
261
|
-
else
|
262
|
-
raise Errors::NetworkError.new(response.message, response.code.to_i)
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
if @config.logger
|
267
|
-
@config.logger.debug "API Response Received:\nData: #{data.inspect}\nStatus code: #{response.code}\nHeaders: #{response.header}"
|
268
|
-
end
|
269
|
-
|
270
|
-
return Response.new(response.code.to_i, response.header, data)
|
271
|
-
else
|
272
|
-
response.read_body do |chunk|
|
273
|
-
yield chunk, response
|
274
|
-
end
|
275
|
-
return Response.new(response.code.to_i, response.header, nil)
|
276
|
-
end
|
235
|
+
replace_keys_in_response_headers_with_non_prefixed_equivalents(response, 'opc-meta-')
|
236
|
+
|
237
|
+
# Either stream the body through a block (if we have one) and return, or read the body so that it can be accessed
|
238
|
+
# again later and jump out of the http.start block
|
239
|
+
#
|
240
|
+
# The idea is to terminate the HTTP connection as early as possible. There are two main reasons for
|
241
|
+
# this:
|
242
|
+
#
|
243
|
+
# 1. Net::HTTP can throw a lot of exceptions, both from itself and what bubbles up from the underlying OS
|
244
|
+
# via Errno:: so we have a very broad rescue block. This isn't great because the more work we do inside the
|
245
|
+
# http.start block the more gaps we leave for non-Net::HTTP related errors. This makes it hard to throw
|
246
|
+
# a meaningful error back to the customer (i.e. we leave ourselves gaps where Errors::NetworkError does not apply
|
247
|
+
# and could throw something disingenuous if we mask everything as a NetworkError)
|
248
|
+
#
|
249
|
+
# 2. Release resources sooner/don't hold connections open longer than necessary (e.g. we don't need the
|
250
|
+
# connection open while we're deserialising)
|
251
|
+
return process_response_block(response, &block) if success?(response) && !block.nil?
|
252
|
+
response.body
|
277
253
|
end
|
278
254
|
end
|
279
|
-
rescue
|
280
|
-
|
281
|
-
|
282
|
-
|
255
|
+
rescue StandardError => err
|
256
|
+
# Unfortunately, catching StandardError is the surest way to capture all the errors originating from Net::HTTP
|
257
|
+
code_from_response = if !response_ref.nil?
|
258
|
+
response_ref.code.to_i
|
259
|
+
else
|
260
|
+
0
|
261
|
+
end
|
262
|
+
|
263
|
+
raise Errors::NetworkError.new(
|
264
|
+
err.message,
|
265
|
+
code_from_response,
|
266
|
+
request_made: request,
|
267
|
+
response_received: response_ref
|
268
|
+
)
|
283
269
|
end
|
270
|
+
|
271
|
+
# If the response is a timeout (HTTP 408), it does not make sense to parse the http body because a partial
|
272
|
+
# response may be returned. We should skip JSON parsing and raise an error immediately
|
273
|
+
handle_timeout_response(request, response_ref)
|
274
|
+
|
275
|
+
@config.logger.debug("HTTP response body ~BEGIN~\n#{response_ref.body}\n~END~\n") if @config.logger
|
276
|
+
|
277
|
+
return handle_success_response(request, response_ref, opts[:return_type]) if success?(response_ref)
|
278
|
+
handle_non_success_response(request, response_ref)
|
279
|
+
end
|
280
|
+
|
281
|
+
def get_http_object(hostname, port)
|
282
|
+
return Net::HTTP.new(hostname, port) if @proxy_settings.nil?
|
283
|
+
return Net::HTTP.new(hostname, port, @proxy_settings.proxy_address) if @proxy_settings.proxy_address.nil?
|
284
|
+
|
285
|
+
Net::HTTP.new(
|
286
|
+
hostname,
|
287
|
+
port,
|
288
|
+
@proxy_settings.proxy_address,
|
289
|
+
@proxy_settings.proxy_port,
|
290
|
+
@proxy_settings.proxy_user,
|
291
|
+
@proxy_settings.proxy_password
|
292
|
+
)
|
284
293
|
end
|
285
294
|
|
286
295
|
def success?(response)
|
287
296
|
response.is_a?(Net::HTTPSuccess)
|
288
297
|
end
|
289
298
|
|
299
|
+
# Determines whether a response "looks like" one returned from an OCI service (regardless of whether the response itself
|
300
|
+
# represents success or failure).
|
301
|
+
#
|
302
|
+
# This is determined by whether the response includes an opc-request-id header or some header which starts with
|
303
|
+
# opc-
|
304
|
+
def response_from_oci_service?(response)
|
305
|
+
response.key?('opc-request-id') || response.to_hash.keys.any? { |k| k.downcase.start_with?('opc-') }
|
306
|
+
end
|
307
|
+
|
308
|
+
def replace_keys_in_response_headers_with_non_prefixed_equivalents(response, prefix)
|
309
|
+
raise 'Response cannot be nil' if response.nil?
|
310
|
+
raise 'A non-blank prefix must be provided' if prefix.nil? || prefix.strip.empty?
|
311
|
+
|
312
|
+
prefix_size = prefix.length.freeze
|
313
|
+
processed_keys = {}
|
314
|
+
|
315
|
+
response.each_header do |key, value|
|
316
|
+
if key.start_with?(prefix)
|
317
|
+
processed_keys[key[prefix_size..-1]] = value
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
processed_keys.each do |key, value|
|
322
|
+
response.delete("#{prefix}#{key}")
|
323
|
+
response[key] = value
|
324
|
+
end
|
325
|
+
|
326
|
+
processed_keys
|
327
|
+
end
|
328
|
+
|
329
|
+
def process_response_block(response)
|
330
|
+
raise 'A block must be provided' unless block_given?
|
331
|
+
response.read_body do |chunk|
|
332
|
+
yield chunk, response
|
333
|
+
end
|
334
|
+
|
335
|
+
Response.new(response.code.to_i, response.header, nil)
|
336
|
+
end
|
337
|
+
|
338
|
+
def handle_timeout_response(request, response)
|
339
|
+
return unless response.is_a?(Net::HTTPRequestTimeOut)
|
340
|
+
raise Errors::NetworkError.new(
|
341
|
+
response.message,
|
342
|
+
response.code.to_i,
|
343
|
+
request_made: request,
|
344
|
+
response_received: response
|
345
|
+
)
|
346
|
+
end
|
347
|
+
|
348
|
+
def handle_success_response(request, response, return_type)
|
349
|
+
data = return_type ? deserialize(request, response, return_type) : nil
|
350
|
+
|
351
|
+
@config.logger.debug("API Response Received:\nData: #{data.inspect}\nStatus code: #{response.code}\nHeaders: #{response.header}") \
|
352
|
+
if @config.logger
|
353
|
+
|
354
|
+
Response.new(response.code.to_i, response.header, data)
|
355
|
+
end
|
356
|
+
|
357
|
+
def handle_non_success_response(request, response)
|
358
|
+
# For errors, we expect a plain JSON object so pass 'Object' here in order to serialize it into a hash
|
359
|
+
data = deserialize(request, response, 'Object')
|
360
|
+
if data
|
361
|
+
raise Errors::ServiceError.new(
|
362
|
+
response.code.to_i,
|
363
|
+
data[:code],
|
364
|
+
response.header['opc-request-id'],
|
365
|
+
data[:message],
|
366
|
+
request_made: request
|
367
|
+
)
|
368
|
+
elsif response_from_oci_service?(response)
|
369
|
+
# TODO: Once we have a standard for how service errors are transmitted when there is no body (e.g. in the header),
|
370
|
+
# change how the ServiceError gets constructed to get data from those fields
|
371
|
+
raise Errors::ServiceError.new(
|
372
|
+
response.code.to_i,
|
373
|
+
nil,
|
374
|
+
response.header['opc-request-id'],
|
375
|
+
response.message,
|
376
|
+
request_made: request
|
377
|
+
)
|
378
|
+
else
|
379
|
+
raise Errors::NetworkError.new(
|
380
|
+
response.message,
|
381
|
+
response.code.to_i,
|
382
|
+
request_made: request,
|
383
|
+
response_received: response
|
384
|
+
)
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
290
388
|
def self.append_query_params(url, query_params)
|
291
389
|
if query_params.empty?
|
292
390
|
url
|
@@ -374,7 +472,7 @@ module OCI
|
|
374
472
|
#
|
375
473
|
# @param [Response] response HTTP response
|
376
474
|
# @param [String] return_type some examples: "User", "Array[User]", "Hash[String,Integer]"
|
377
|
-
def deserialize(response, return_type)
|
475
|
+
def deserialize(request, response, return_type)
|
378
476
|
body = response.body
|
379
477
|
return if body.nil? || body.empty?
|
380
478
|
|
@@ -386,15 +484,39 @@ module OCI
|
|
386
484
|
# There are some cases, the error is not returned by services but by like gateway, for example in bug
|
387
485
|
# https://jira.aka.lgl.grungy.us/browse/DEX-564, gateway timeouts and 504 is returned and content is generated
|
388
486
|
# by gateway, so there is no guarantee that the content-type will be application/json.
|
389
|
-
|
487
|
+
if !success?(response) && !response_from_oci_service?(response)
|
488
|
+
raise Errors::NetworkError.new(
|
489
|
+
response.message,
|
490
|
+
response.code.to_i,
|
491
|
+
request_made: request,
|
492
|
+
response_received: response
|
493
|
+
)
|
494
|
+
end
|
390
495
|
|
391
496
|
begin
|
392
497
|
data = JSON.parse("[#{body}]", :symbolize_names => true)[0]
|
393
|
-
rescue JSON::ParserError
|
498
|
+
rescue JSON::ParserError
|
394
499
|
if %w(String Date DateTime).include?(return_type)
|
395
500
|
data = body
|
396
501
|
else
|
397
|
-
|
502
|
+
# If we received an error here then we received a response from the OCI service that
|
503
|
+
# we could not parse. Instead of throwing a parsing error, throw out a service error
|
504
|
+
# (so the customer has all the information) and put the response body in the message
|
505
|
+
# so that they may also have a sense of what the error from the service was
|
506
|
+
unless success?(response)
|
507
|
+
raise OCI::Errors::ServiceError.new(
|
508
|
+
response.code.to_i,
|
509
|
+
nil,
|
510
|
+
response['opc-request-id'],
|
511
|
+
response.body,
|
512
|
+
request_made: request
|
513
|
+
)
|
514
|
+
end
|
515
|
+
|
516
|
+
raise OCI::Errors::ResponseParsingError.new(
|
517
|
+
request_made: request,
|
518
|
+
response_received: response
|
519
|
+
)
|
398
520
|
end
|
399
521
|
end
|
400
522
|
|