GithubHelp home page GithubHelp logo

oracle / oci-ruby-sdk Goto Github PK

View Code? Open in Web Editor NEW
24.0 39.0 22.0 37.1 MB

Ruby SDK for Oracle Cloud Infrastructure

Home Page: https://cloud.oracle.com/cloud-infrastructure

License: Other

Ruby 100.00%
oracle-cloud-infrastructure oracle-cloud sdk ruby cloud

oci-ruby-sdk's Introduction

Oracle Cloud Infrastructure Ruby SDK

This is Oracle Cloud Infrastructure SDK for Ruby. This project is open source and maintained by Oracle Corp. The home page for the project is here.

This project is no longer being actively developed by Oracle. We will continue to address security vulnerabilities for the foreseeable future, and will respond to questions on github, but have no plans to introduce any new functionality, and may not be able to address any non-security related issues. We encourage developers to migrate to other OCI SDKs, and developers may fork this project and enhance it as they desire.

Version 2.21.1

This topic describes how to install, configure, and use the Oracle Cloud Infrastructure Ruby SDK.

SDK Overview

The Ruby SDK supports the following services:

  • Access Governance
  • Account Management
  • Analytics Cloud
  • Announcements
  • AI Anomaly Detection service
  • AI Language service
  • AI Speech service
  • AI Vision
  • API Gateway
  • Application Dependency Management
  • Application Management service
  • Application Performance Monitoring
  • Audit
  • Autonomous recovery
  • Autoscaling (Compute)
  • Bastion service
  • Big Data
  • Blockchain Platform
  • Budgets
  • Build service
  • Caching
  • Certificates service
  • Classic Migration
  • Cluster Placement Groups service
  • Cloud Bridge
  • Cloud Guard
  • Cloud Migrations
  • Compute Cloud@Customer
  • Compute Instance Agent
  • Compute Autoscaling
  • Connector Hub
  • Console Dashboard service
  • Container Engine
  • Content and Experience
  • Core Services (which includes Networking, Compute, and Block Volume)
  • Data Catalog
  • Data Flow
  • Data Labeling service
  • Database Migration
  • Database Tools service
  • Data Integration
  • Data Science
  • Data Safe
  • Data Transfer
  • Database
  • Database Management
  • DevOps service
  • Digital Assistant
  • Disaster Recovery
  • Digital Media
  • Document Understanding
  • Domain Name System
  • Email
  • Enterprise Manager Warehouse
  • Events
  • Exadata Fleet Update
  • File Storage
  • Functions
  • Fusion Apps as a Service
  • Generative AI service
  • Generative AI Inference service
  • Generic Artifacts service
  • Globally Distributed Database service
  • Golden Gate
  • Governance Rules service
  • Health Checks
  • Identity and Access Management
  • Identity Domains
  • Incident Management
  • Integration Cloud
  • Java Management Service
  • Java Management Service Downloads
  • Key Management
  • License Manager
  • Limits
  • Load Balancing
  • Logging
  • Logging Analytics
  • Logging Search
  • Logging Ingestion
  • Management Agent Cloud
  • Management Dashboard
  • Marketplace
  • Marketplace Publisher
  • Monitoring
  • MySQL Database service
  • Network Firewall
  • Network Load Balancing
  • Network Monitoring
  • Networking Topology
  • Notification
  • OCI Control Center
  • OCI Control Center service
  • OCI Registry
  • OneSubscription service
  • Operations Insights
  • Optimizer
  • Organizations
  • OS Management Hub
  • Oracle Content and Experience
  • Oracle Roving Edge Infrastructure
  • Object Storage
  • Operator Access Control service
  • Oracle NoSQL Database Cloud
  • OS Management
  • Process Automation
  • PostgreSQL service
  • Queue Service
  • Quotas
  • Resource Manager
  • Search
  • Service Catalog service
  • Secret Management (for the Vault service)
  • Secure Desktops service
  • Service Mesh
  • Service Connector Hub
  • Source Code Management service
  • Stack Monitoring
  • Streaming
  • Support Management
  • Threat Intelligence
  • Usage
  • Visual Builder
  • VMWare Solution
  • Vulnerability Scanning
  • Web Application Acceleration and Security
  • Work Requests

Licensing: This SDK and sample is dual licensed under the Universal Permissive License 1.0 and the Apache License.

SDK Attributes

The following table provides details about some of the attributes of the SDK.

Requests API methods expose required parameters as arguments and optional arguments as a hash (opts = {}). Create and update operations take request objects that mirror the properties of those objects.
Responses All API methods return a Response object, which contains an HTTP status (200, 204, etc), headers, and data, and also directly exposes some commonly used response headers such as opc-next_page, and opc-request_id.
Models Get, update, and create operations return first class objects of the corresponding type (such as User, Instance, etc) in Response.data.
Errors The main exception classes for the SDK are:
  • {OCI::Errors::ServiceError ServiceError} is thrown when an Oracle Cloud Infrastructure service returns a non-2xx HTTP status code
  • {OCI::Errors::NetworkError NetworkError} is thrown when the issue is likely to be network-related rather than an application issue. This is defined as:
    • Requests that were sent from the SDK, but for which the outcome was not a response from an Oracle Cloud Infrastructure service. Examples include a gateway timeout, read or connection timeouts from Net::HTTP, and any (Errno) exception that was generated by the request itself.
    • Requests that result in a HTTP 408 (timeout)
  • {OCI::Errors::ResponseParsingError ResponseParsingError} is thrown when a response is received from an Oracle Cloud Infrastructure service, but the SDK could not parse it into the appropriate model type for inserting into an {OCI::Response}
Signing Requests made through the API classes are automatically signed, but you can also use the {OCI::Signer Signer} directly to sign your own requests if needed.
Automatic Paging Response objects for lists support {OCI::Response#each page enumeration}. See example code later in this file.
Waiters Responses for get requests support waiting for a particular states using {OCI::Response#wait_until wait_until}. See example code later in this file.

In addition to using waiters, you can use the CompositeOperation classes in the SDK (e.g. {OCI::Core::ComputeClientCompositeOperations}) to perform an action on a resource and wait for it to enter a particular state (or states). The CompositeOperation classes provide convenience methods so that you yourself do not have to invoke an operation and then separately invoke a waiter. An example of using the CompositeOperations classes: composite_operations_example.rb

HTTP Client 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 Configuring the HTTP Client section of this page
Instance Principals Authentication The Ruby SDK supports Instance Principals authentication via the use of the {OCI::Auth::Signers::InstancePrincipalsSecurityTokenSigner} class. An example of using Instance Principals authentication: instance_principals_example.rb
Upload Manager 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.

Managing Multipart Uploads provides links to the APIs used for raw multipart upload operations. Higher-level uploads can be performed using the {OCI::ObjectStorage::Transfer::UploadManager}.

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.

Example of using the Upload Manager: upload_manager.rb
Example of using the Upload Manager from stdin: upload_manager_stdin_example.rb

Retries By default the Ruby SDK will not retry failed service calls, however the SDK supports specifying per client and per operation retry configurations. An example of using retries: retry_example.rb

SDK Requirements

To use the Ruby SDK, you must have:

  • An Oracle Cloud Infrastructure account.
  • A user created in that account, in a group with a policy that grants the desired permissions. This can be a user for yourself, or another person/system that needs to call the API. For an example of how to set up a new user, group, compartment, and policy, see Adding Users in the Getting Started Guide. For a list of typical policies you may want to use, see Common Policies in the User Guide.
  • A keypair used for signing API requests, with the public key uploaded to Oracle. Only the user calling the API should be in possession of the private key. See the configuration information below.
  • Ruby version 2.6 or 2.7 running on Mac, Linux or Windows.

Downloading and Installing the Gem File

Installing the SDK

Install from RubyGems:

gem install oci

You can also download the SDK as a zip file containing the gem file, examples, and documentation.

Install the gem with the following command:

gem install oci-*.gem

Troubleshooting an Installation

If you see "Unable to resolve dependencies”, you can install the dependencies manually:

gem install inifile

SDK modules and namespacing

The top level module name for the Ruby SDK is OCI, however using OracleBMC as the top level namespace is also supported. For example, you can reference the configuration object as both OCI::Config and OracleBMC::Config.

Using OCI as the top level module name is preferred and it is also used in the SDK API Reference. Additionally, if you inspect the type of an SDK object it will always be reported as being under the OCI:: module.

You can start from importing oci module, this will allow you to access the whole OCI Ruby SDK.

require 'oci'

However, in some cases, if you're only using limited number of OCI services, importing the whole OCI module is not necessary and would possibly increase the loading time of your application. Instead importing the oci module, you can manually import sub modules based on your requirement:

require 'oci/common' # This is commonly required for using oci ruby sdk
require 'oci/auth/auth' # This module is commonly required to authenticate api calls
require 'oci/core/core' # Replace this with your target service

Configuring the SDK

To use any of the APIs, you must supply a {OCI::Config Config} object. You can create the object directly in code, or you can create one in a config file. The configuration includes:

  • Required credentials and settings: See SDK and Tool Configuration in the User Guide.

  • Optional SDK-specific settings: See the {OCI::Config Config} object for the full list of config options. Samples for setting the config through code.

    config = OCI::Config.new config.tenancy = 'ocid1.tenancy.oc1..aa' # Directly setting the tenancy OCID config.user = ENV['oci_user/or_some_other_name'] # Setting the user OCID from an environment variable

Note that the Ruby SDK does not support parsing custom attributes in the configuration file.

Forward Compatibility

Some response fields are enum-typed. In the future, individual services may return values not covered by existing enums for that field. To address this possibility, every enum-type response field has an additional value named "UNKNOWN_ENUM_VALUE". If a service returns a value that is not recognized by your version of the SDK, then the response field will be set to this value. Please ensure that your code handles the "UNKNOWN_ENUM_VALUE" case if you have conditional logic based on an enum-typed field.

Thread Safety

The OCI Ruby SDK is using net/http package which is not guaranteed to be thread-safe.

If you're using this SDK in a multi-threaded application, you may have to create unique clients for each thread.

New Region Support

If you are using a version of the SDK released prior to the announcement of a new region, you may still be able to reach it, depending on whether the region is in the oraclecloud.com realm.

A region is a localized geographic area. For more information on regions and how to identify them, see Regions and Availability Domains.

A realm is a set of regions that share entities. You can identify your realm by looking at the domain name at the end of the network address. For example, the realm for xyz.abc.123.oraclecloud.com is oraclecloud.com.

oraclecloud.com Realm

For regions in the oraclecloud.com realm, even if the OCI::Regions::REGION_ENUM does not contain the new region, the forward compatibility of the SDK can automatically handle it. You can pass new region names just as you would pass ones that are already defined. For more information on passing region names in the configuration, see Configuring the SDK. For details on OCI::Regions::REGION_ENUM, see OCI::Regions.

Other Realms

For regions in realms other than oraclecloud.com, you can use the following workarounds to reach new regions with earlier versions of the SDK.

You can set the endpoint when creating a new client:

identity_client = OCI::Identity::IdentityClient.new(
  endpoint: 'https://identity.us-gov-phoenix-1.oraclegovcloud.com'
)

If you are authenticating via instance principals, you can set the federation_endpoint for the region using InstancePrincipalsSecurityTokenSigner when initializing the signer:

instance_principals_signer = OCI::Auth::Signers::InstancePrincipalsSecurityTokenSigner.new(
  federation_endpoint: 'https://auth.us-gov-phoenix-1.oraclegovcloud.com/v1/x509'
)

Writing Your First Ruby Program with the SDK

require 'oci'

# This will load the config file at the default location, and will
# use the tenancy from that config as the compartment in the
# call to list_users.
api = OCI::Identity::IdentityClient.new(region: OCI::Regions::REGION_US_PHOENIX_1)
response = api.list_users(OCI.config.tenancy)
response.data.each { |user| puts user.name }

Loading Alternate Configurations

You can also load a config file from a different location, and/or specify a different profile from the config file:

require 'oci'

my_config = OCI::ConfigFileLoader.load_config(config_file_location:'my_config', profile_name:'USER_TWO')
api = OCI::Identity::IdentityClient.new(config:my_config)
response = api.get_user(my_config.user)
puts 'User Name: ' + response.data.name

Or you can create a {OCI::Config Config} programmatically. Note that the global value {OCI#config OCI.config} will always attempt to load the DEFAULT profile from the default config file location unless it has been explicitly set to another value.

The default config file location is ~/.oci/config (on Windows C:\Users\{user}\.oci\config).

Configuring the HTTP Client

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:

require 'oci'

identity = OCI::Identity::IdentityService.new

identity.api_client.request_option_overrides = {
  # The path to the PEM-formatted CA certificate file.
	# The file can contain several CA certificates
	ca_file: '/path/to/pem/file',

	# The path to a directory of PEM-formatted CA certificate files
	ca_path_cert: '/path/to/cert/directory',

	# An OpenSSL::X509::Certificate object as client certificate
  cert: OpenSSL::X509::Certificate.new(...),

	# The X509::Store to verify peer certificate.
	cert_store: OpenSSL::X509::Store.new(...),

	# One of the available ciphers from OpenSSL::SSL::SSLContext#ciphers
	ciphers: ...,

	# Close the connection immediately if the server sent no response body
    close_on_empty_response: true,

	# An OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object
	key: ...,

	# The SSL timeout in seconds
	ssl_timeout: 60,

	# The SSL version to use. See OpenSSL::SSL::SSLContext#ssl_version
	ssl_version: ...,

	# The verify callback for the server certification verification
	verify_callback: ...,

	# The maximum depth for the certificate chain verification
	verify_depth: 3,

	# Either OpenSSL::SSL::VERIFY_PEER or OpenSSL::SSL::VERIFY_NONE
	# Using OpenSSL::SSL::VERIFY_NONE (i.e. not validating the SSL certificate) is not
	# recommended
	verify_mode: OpenSSL::SSL::VERIFY_PEER
}

Note that these correspond to a subset of the keys that can be provided as opts to the Net::HTTP start method.

Setting a proxy

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:

  • Providing an environment variable; or
  • Providing an {OCI::ApiClientProxySettings} object. Here is an example of the different ways to create this object:

Proxy via environment variable

As the SDK uses Net::HTTP, the http_proxy environment variable can be used to specify a proxy:

export HTTP_PROXY="http://10.10.1.10:3128"

When using the environment variable approach, no code changes need to be made.

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.

Proxy via the ApiClientProxySettings object

An {OCI::ApiClientProxySettings} object can be used to capture proxy settings. Here are the different ways an object can be created:

# This creates an ApiClientProxySettings with a nil proxy address and will make the API client
# bypass all proxies
proxy_settings = OCI::ApiClientProxySettings.new(nil)

# This creates an ApiClientProxySettings that will use the proxy at 10.10.1.10:3128 and will
# not provide a username and password to the proxy
proxy_settings = OCI::ApiClientProxySettings.new("10.10.1.10", 3128)

# 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
# rather than having the credentials in code
proxy_settings = OCI::ApiClientProxySettings.new("10.10.1.10", 3128, "username", "pass")

Once an {OCI::ApiClientProxySettings} object has been created, it can be used when creating or modifying a client:

# At client creation time
identity = OCI::Identity::IdentityService.new(proxy_settings: proxy_settings)

# When updating a client
identity.api_client.proxy_settings = proxy_settings

# The proxy_settings can also be nil'ed out. This will make the ApiClient fall back to
# using the http_proxy environment variable (if present)
identity.api_client.proxy_settings = nil

Exceptions

Service Errors

Any operation that receives a response with a non-2xx HTTP status code from an Oracle Cloud Infrastructure service will cause an exception of type {OCI::Errors::ServiceError ServiceError} to be thrown by the SDK.

For information about common service errors returned by OCI, see API Errors.

The key attributes to inspect when dealing with a {OCI::Errors::ServiceError} are:

  • {OCI::Errors::ServiceError#request_id request_id} if you need to contact Oracle about a particular request, please provide this request ID
  • {OCI::Errors::ServiceError#status_code status_code} contains the HTTP status code of the response
  • {OCI::Errors::ServiceError#service_code service_code} should contain a value from the Error Code field as described in the API Errors page
  • {OCI::Errors::ServiceError#request_made request_made} contains the actual Net::HTTPRequest which was sent by the SDK

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.

HTTP 3xx Responses

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.

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:

require 'oci'

object_storage = OCI::ObjectStorage::ObjectStorageClient.new

begin
    get_object_response = object_storage.get_object('my_namespace', 'my_bucket', 'my_object', if_none_match: 'some_etag_value')
rescue OCI::Errors::ServiceError => err
    # Do nothing if the object exists but has not been modified (based on the etag value), otherwise raise the exception
    raise if err.status_code != 304
end

Network Errors

{OCI::Errors::NetworkError NetworkError} is thrown when the issue is likely to be network related rather than an application issue. This is defined as:

  • Requests which were sent from the SDK but the outcome was not a response from an Oracle Cloud Infrastructure service. Further examples of include:
    • Gateway timeouts
    • Read or connection timeouts
    • Any {Errno} exception which was generated by making the request
  • Requests which resulted in a HTTP 408 (timeout)

The key attributes to inspect when dealing with a {OCI::Errors::NetworkError} are:

  • {OCI::Errors::NetworkError#request_made request_made} and {OCI::Errors::NetworkError#response_received response_received} contain the Net::HTTPRequest and Net::HTTPResponse, respectively
  • {OCI::Errors::NetworkError#code code} contains the HTTP status code of the response

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.

Response Parsing Errors

{OCI::Errors::ResponseParsingError ResponseParsingError} is thrown when a response was received from an Oracle Cloud Infrastructure service but the SDK could not parse it into the appropriate model type to put into an {OCI::Response}.

The key attributes to inspect when dealing with a {OCI::Errors::ResponseParsingError} are:

  • {OCI::Errors::ResponseParsingError#request_made request_made} and {OCI::Errors::ResponseParsingError#response_received response_received} contain the Net::HTTPRequest and Net::HTTPResponse, respectively
  • {OCI::Errors::ResponseParsingError#response_body response_body} contains the response data which failed to parse

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.

Other Errors

The other errors you may encounter while using the SDK are:

  • {OCI::Errors::MaximumWaitTimeExceededError MaximumWaitTimeExceededError} when using {OCI::Response#wait_until} or when using {OCI::LoadBalancer::Util#wait_on_work_request}
  • {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
  • {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

Examples

The example code in this section shows how various parts of the Ruby SDK work. More examples can be found from the Ruby SDK on GitHub.

Management Operations on a User

The following example runs create, read, update, and delete (CRUD) operations on users.

require 'oci'

compartment = OCI.config.tenancy

api = OCI::Identity::IdentityClient.new
users = api.list_users(compartment_id = compartment).data
puts "There are currently " + users.length.to_s + " users."

# Create User
request = OCI::Identity::Models::CreateUserDetails.new
request.compartment_id = compartment
request.name = "userA"
request.description = "example user"
response =  api.create_user(request)

puts "Created user " + response.data.name
user_id = response.data.id

users = api.list_users(compartment_id = compartment).data
puts "There are now " + users.length.to_s + " users."

# Get User
response =  api.get_user(user_id = user_id)

# Update User (using a request object)
newDescription = "updated user description"
request = OCI::Identity::Models::UpdateUserDetails.new
request.description = "Updated description"
response = api.update_user(user_id, request)

puts "Updated description to:" + response.data.description

# Update User (using a hash)
newDescription = "updated without a request object"
response = api.update_user(user_id, { description: "Updated again, but using a hash instead of an object." })

# Delete User
api.delete_user(user_id)

users = api.list_users(compartment_id = compartment).data
puts "Back to " + users.length.to_s + " users."

Paging Through Results

The following example shows how to page through results. It also gives an example of supplying optional parameters.

require 'oci'

api = OCI::Identity::IdentityClient.new
compartment = OCI.config.tenancy

### Automatic paging:
api.list_users(compartment, limit:'3').each { |r| r.data.each { |user| puts user.name }}

### Manual paging:
request_number = 0
next_page = nil

loop do
  response = api.list_users(compartment, {limit: '3', page: next_page})

  puts "Page " + request_number.to_s
  response.data.each { |user| puts user.name }

  break unless response.has_next_page?
  next_page = response.next_page
  request_number += 1
end

Launching an Instance and Waiting for a State

The following example shows how to launch an instance (which assumes that you already have a subnet created), and then wait until the instance is running.

require 'oci'

ssh_public_key = File.open(File.expand_path(public_key_file), "rb").read

request = OCI::Core::Models::LaunchInstanceDetails.new
request.availability_domain = availability_domain # TODO: Set an availability domain, such as 'kIdk:PHX-AD-2'
request.compartment_id = compartment_id # TODO: set your compartment ID here
request.display_name = 'my_instance'
request.image_id = image_id # TODO: set your image ID here. You can see the available options with list_images.
request.shape = shape # TODO: set your instance shape. You can see the available options with list_shapes.
request.subnet_id = subnet_id # TODO: set your subnet ID here
request.metadata = {'ssh_authorized_keys' => ssh_public_key}

api = OCI::Core::ComputeClient.new
response = api.launch_instance(request)
instance_id = response.data.id
response = api.get_instance(instance_id).wait_until(:lifecycle_state, OCI::Core::Models::Instance::LIFECYCLE_STATE_RUNNING)

Waiting for state using a proc/lambda

Instead of waiting until the attribute of a resource reaches a given state, the wait_until method can also be passed a proc/lambda via its eval_proc keyword argument. Using eval_proc may be useful in situations where logic other than checking if an attribute has a certain value is needed, for example checking that an attribute is one of a possible set of values. eval_proc should take a single argument, which is the raw response received from the service call, and its result should be a truthy value if the waiter should stop waiting, and it should be falsey otherwise. If the eval_proc is provided, then neither the property nor state parameters should be provided.

An example using a lambda is:

require 'oci'

request = OCI::Core::Models::CreateVcnDetails.new
request.cidr_block = '10.0.0.0/16'
request.display_name = 'my_test_vcn'
request.compartment_id = compartment_id # TODO: set your compartment ID here

api = OCI::Core::VirtualNetworkClient.new
response = api.create_vcn(request)
vcn_id = response.data.id

response = api.get_vcn(vcn.id).wait_until(eval_proc: lambda { |response| response.data.lifecycle_state == OCI::Core::Models::Vcn::LIFECYCLE_STATE_AVAILABLE })

An example using a proc is:

require 'oci'

request = OCI::Core::Models::CreateVcnDetails.new
request.cidr_block = '10.0.0.0/16'
request.display_name = 'my_test_vcn'
request.compartment_id = compartment_id # TODO: set your compartment ID here

api = OCI::Core::VirtualNetworkClient.new
response = api.create_vcn(request)
vcn_id = response.data.id

check_available_proc = Proc.new do |response|
  [OCI::Core::Models::Vcn::LIFECYCLE_STATE_AVAILABLE].include?(response.data.lifecycle_state)
end

response = api.get_vcn(vcn.id).wait_until(eval_proc: check_available_proc)

Waiting on terminated/deleted resources

When waiting for a request to be terminated/deleted, there is a chance that doing a GET of that resource will return a 404 because the resource is no longer available. Instead of having to catch this, for example:

require 'oci'

api = OCI::Core::VirtualNetworkClient.new
api.delete_vcn(vcn.id)

begin
    api.get_vcn(vcn.id).wait_until(:lifecycle_state, OCI::Core::Models::Vcn::LIFECYCLE_STATE_TERMINATED)
rescue OCI::Errors::ServiceError => e
    raise unless e.status_code == 404
end

You can instruct the waiter to take care of this for you and consider that a 404 means success. For example:

require 'oci'

api = OCI::Core::VirtualNetworkClient.new
api.delete_vcn(vcn.id)
api.get_vcn(vcn.id).wait_until(:lifecycle_state, OCI::Core::Models::Vcn::LIFECYCLE_STATE_TERMINATED, succeed_on_not_found: true)

Using the succeed_on_not_found keyword argument is likely only useful when waiting on temrinated/deleted resources.

Signing a Raw Request

The {OCI::Signer} can be used to sign arbitrary requests to the Oracle Cloud Infrastructure Services. The following example uses Net::HTTP to call the IAM service directly.

require 'oci'
require 'net/http'

config = OCI::ConfigFileLoader.load_config(config_file_location:my_config_file_location)
endpoint = OCI::Regions.get_service_endpoint(config.region, :IdentityClient)

uri = URI(endpoint + '/20160918/users/' + config.user)
request = Net::HTTP::Get.new(uri)

signer = OCI::Signer.new(config.user, config.fingerprint, config.tenancy, config.key_file, pass_phrase:my_private_key_pass_phrase)
signer.sign(:get, uri.to_s, request, nil)

result = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) {|http|
  http.request(request)
}

puts result.body

Documentation

Full documentation, including prerequisites, installation, and configuration instructions can be found here.

API reference can be found here.

Changes

See CHANGELOG.

Contributing

This project welcomes contributions from the community. Before submitting a pull request, please review our contribution guide

Notifications

To be notified when a new version of the Ruby SDK is released, subscribe to the Atom feed.

Known Issues

You can find information on any known issues with the SDK here and under the Issues tab of this project's GitHub repository.

SSE Support

For SSE response, Ruby SDK is having some performance issue where the response is not returning as a TRUE streaming mode. It's actually returning in a chunked mode, which waits until it receive multiple lines of SSE response into one block and then return the result. This behavior is caused by the base HTTP client package(Net::HTTP) which is the only HTTP package we use in Ruby SDK Please refer genai_generate_text_example in examples-oci folder to use SSE support.

Questions or Feedback?

You can post an issue on the Issues tab of this project's GitHub repository.

Addtional ways to get in touch:

Security

Please consult the security guide for our responsible security vulnerability disclosure process

License

Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved.

This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.

See LICENSE for more details.

oci-ruby-sdk's People

Contributors

andy-miles avatar dayongfu avatar dshelbyo avatar jodoglevy avatar jyotisaini avatar khs28gu avatar spavlusieva avatar ziyaoqiao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

oci-ruby-sdk's Issues

OCI::ObjectStorage::ObjectStorageClient#get_object improperly deserializes JSON documents

I have a JSON document that I've stored in OCI object storage with a Content-Type of application/json. When I call get_object, I'd expect to get the raw contents of the document body. The documentation is a bit unclear about this because it sounds as if some auto-deserializing might be intended. The documented return value for get_object is A Response object with data of type String if response_target and block are not given, otherwise with nil data . Looking at the instance attribute summary for Response#data says The body of the response.. While looking at the instance attribute details for Response#data says The body of the response. For example, this may contain a User object, or a list of Users.

Nothing explicitly says that the response body will be deserialized. The instance attribute details description hints at it by saying a User object, or a list of Users may be returned. But elsewhere it looks like the raw data of the response body will be returned. At best, there's a documentation issue here.

The real problem I'm encountering is the document body is deserialized. Moreover, it uses the extra option symbolize_names to turn the String keys into Symbols, which may not be expected. But the worst part is this deserialized object then has to_s called on it and that is used as the Response#data object.

In my case, I have an array of dictionaries. Instead of getting back something like [{ "my.key" : "my.value" }] I get back something like [{ :"my.key" => "my.value" }]. This returned string isn't valid JSON. There's really not much I can do with that return value other than eval it to try to get back the object structure I really want.

The workaround for the time being is to set the Content-Type header to text/plain. This is logically incorrect, but it prevents the oci library from trying to deserialize the response body.

working code examples

The auto generated code examples are not helpful.

Background: I haven't done any ruby for about 10 years. I am familiar with dynamodb, i can write elixir and c/c++ at a bit better than novice level and novice level java. I know what a prepared statement is but I haven't messed with oracle databases since 2002. I decided to pick ruby because I thought it would provide the least barrier to me learning how to query nosql.

There are no results when trying to google "ruby" "Nosql" "PreparedStatement" select from where
There are no code examples when searching github for nosql querydetails statement language:Ruby
There are no tests to look at.
Reading the source wasn't much help.

take query: https://docs.oracle.com/en-us/iaas/tools/ruby-sdk-examples/2.15.0/nosql/query.rb.html

query_response =
  nosql_client.query(
    OCI::Nosql::Models::QueryDetails.new(
      compartment_id: 'ocid1.test.oc1..<unique_ID>EXAMPLE-compartmentId-Value',
      statement: 'EXAMPLE-statement-Value',
      is_prepared: true,
      consistency: 'EVENTUAL',
      max_read_in_kbs: 983,
      variables: { 'EXAMPLE_KEY_tSlWQ' => 'EXAMPLE--Value' },
      timeout_in_ms: 823
    )
  )

As a newbie I have no idea what EXAMPLE-statement-Value is. Further as a oracle NOSQL newebie I have no idea what the prepared syntax is.

I tried to find out what the interpolation needed was and through trial and error was able to get it to work with this:

query_response =
  ns.query(
    OCI::Nosql::Models::QueryDetails.new(
      compartment_id: cid,
      statement: "declare $hsh string; SELECT * FROM always_free_table where hsh= $hsh",
      #is_prepared: true,
      consistency: 'EVENTUAL',
      max_read_in_kbs: 983,
      variables: { '$hsh' => 'my' },
      timeout_in_ms: 823
    )
  )

So for example having to declair the variable key "$hsh" vs just "hsh" or hsh: was not obvious. Also the errors thrown while brute forcing this were not helpful

/usr/share/gems/gems/oci-2.15.0/lib/oci/api_client.rb:478:in `handle_non_success_response': QUERY: Illegal Argument: Invalid serialized prepared statement: Cannot deserialize value of type `byte[]` from String "declare $hsh string;SELECT * FROM always_free_table where hsh = $hsh": Illegal white space character (code 0x20) as character #4 of 4-char base64 unit: can only used between units (OCI::Errors::ServiceError)
 at [Source: UNKNOWN; line: -1, column: -1]
        from /usr/share/gems/gems/oci-2.15.0/lib/oci/api_client.rb:390:in `call_api_inner'
        from /usr/share/gems/gems/oci-2.15.0/lib/oci/api_client.rb:127:in `call_api'
        from /usr/share/gems/gems/oci-2.15.0/lib/oci/nosql/nosql_client.rb:1360:in `block in query'
        from /usr/share/gems/gems/oci-2.15.0/lib/oci/retry/retry.rb:24:in `make_retrying_call'
        from /usr/share/gems/gems/oci-2.15.0/lib/oci/nosql/nosql_client.rb:1359:in `query'
        from cunt.rb:53:in `<main>'

I'd like to uncomment the is_prepared flag but I don't understand what it wants.

A bit of example code that shows the string interpolation and variable format would have saved me quite a bit of time, however the bigger issue is that many would just quit and use something else with better documentation. For example, what is a working input string to prepare_statement? The below doesn't tell me much:

# Send the request to service, some parameters are not required, see API doc for more info
prepare_statement_response =
  nosql_client.prepare_statement(
    'ocid1.test.oc1..<unique_ID>EXAMPLE-compartmentId-Value',
    'EXAMPLE-statement-Value'
  )

Already initialized constant warning

Hi,
Since the release of 2.7.0, I'm seeing the following warning:

.../oci-ruby-sdk-62498bb3f567/lib/oci/identity/models/base_tag_definition_validator.rb:20: warning: already initialized constant OCI::Identity::Models::BaseTagDefinitionValidator::VALIDATOR_TYPE_ENUM
.../oci-ruby-sdk-62498bb3f567/lib/oci/identity/models/base_tag_definition_validator.rb:21: warning: previous definition of VALIDATOR_TYPE_ENUM was here

It seems to be caused by this code:

class Identity::Models::BaseTagDefinitionValidator
VALIDATOR_TYPE_ENUM = [
VALIDATOR_TYPE_ENUM = 'ENUM'.freeze,
VALIDATOR_TYPE_DEFAULT = 'DEFAULT'.freeze,
VALIDATOR_TYPE_UNKNOWN_ENUM_VALUE = 'UNKNOWN_ENUM_VALUE'.freeze
].freeze

VALIDATOR_TYPE_ENUM is declared twice, first by VALIDATOR_TYPE_ENUM = 'ENUM'.freeze and then redeclared as VALIDATOR_TYPE_ENUM = [ ... ].

When using instance principal `401` response from any OCI services leads to infinite loop

When using instance principal, a 401 response from any OCI services leads to an infinite loop. It will come out of the loop only if it gets a non 401 response.

The problem seems to because of this function https://github.com/oracle/oci-ruby-sdk/blob/master/lib/oci/api_client.rb#L632

    def instance_principals_signer_wrapped_call
      max_attempts = 2

      max_attempts.times do |attempt|
        begin
          return yield
        rescue OCI::Errors::ServiceError => e
          raise if attempt >= (max_attempts - 1) # .times is zero-based
          raise if e.status_code != 401

          @signer.refresh_security_token
          retry
        end
      end

Sample code to show use of times + rescue-retry leads to an infinite loop

max_attempts = 2
max_attempts.times do |attempt|
  begin
    puts "attempts #{attempt}"
    raise 'An error has occured'
  rescue
    raise if attempt >= (max_attempts - 1)
    retry
  end
end

Output:

 ~>ruby test.rb
attempts 0
attempts 0
attempts 0
attempts 0
attempts 0
<goes on infinitely>

OCI::ObjectStorage::ObjectStorageClient#get_object auto inflates

I have an object in OCI object storage that has its Content-Encoding set to deflate and the contents of the file are indeed compressed. When I call get_object, rather than getting back the raw bytes of the object, they're inflated. i think the raw bytes should be returned instead or there should be a way to clear the Accept-Encoding request header. In this particular case, I do want the compressed contents and I'd wind up burning CPU inflating the object (out of my control) and then deflating it again.

Add a note on thread-safety

Please consider adding a note on thread-safety. Upon brief inspection it looked like the gem is not thread-safe, but I couldn't find any documentation indicating one way or another. To be safe, I opted to use the connection pool gem and make each request in a reserved, isolated connection

Can't set Content-Type for object storage

It's not possible to set the Content-Type for an object being stored into OCI object storage due to a difference in Symbol vs String keys when preparing request headers for the OCI::ObjectStorage::ObjectStorageClient#put_object call.

header_params[:'content-type'] = opts[:content_type] if opts[:content_type]
requires the caller to supply the key as a Symbol (i.e., :content_type).

However,

header_params['content-type'] ||= 'application/octet-stream'
assumes the key is a String (i.e., 'content-type) and ends up always writing the value 'application/octet-stream'.

The line header_params['content-type'] ||= 'application/octet-stream' should probably be header_params[:'content-type'] ||= 'application/octet-stream'.

OCI::ObjectStorage::ObjectStorageClient#get_object blows up on badly compressed file

This is probably related to the auto-inflate behavior I've reported in #11.

In this case, if I have an object in OCI object storage and had set the Content-Encoding header to deflate, but the contents of the file are not deflated properly, the client blows up with the following error:

OCI::Errors::NetworkError: /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/net/http/response.rb:333:in `stream_check': undefined method `closed?' for nil:NilClass (NoMethodError)
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/net/http/response.rb:202:in `read_body'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/net/http/response.rb:229:in `body'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/oci-2.3.5/lib/oci/errors.rb:112:in `to_s'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb.rb:502:in `write'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb.rb:502:in `print'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb.rb:502:in `block (2 levels) in eval_input'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb.rb:627:in `signal_status'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb.rb:490:in `block in eval_input'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb/ruby-lex.rb:246:in `block (2 levels) in each_top_level_statement'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb/ruby-lex.rb:232:in `loop'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb/ruby-lex.rb:232:in `block in each_top_level_statement'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb/ruby-lex.rb:231:in `catch'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb/ruby-lex.rb:231:in `each_top_level_statement'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb.rb:489:in `eval_input'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb.rb:430:in `block in run'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb.rb:429:in `catch'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb.rb:429:in `run'
        from /home/kjmenard/.rbenv/versions/2.4.4/lib/ruby/2.4.0/irb.rb:385:in `start'
        from /home/kjmenard/.rbenv/versions/2.4.4/bin/irb:11:in `<main>'

In contrast, the CLI tool (oci-cli) available for Python will get the file in its original form just fine. Certainly, I shouldn't be storing objects where the encoding and the contents don't align -- for this I was just testing something. But I don't think this needs to raise a NoMethodError on nil either.

Requests failing when using non-ascii unicode characters in the request

Requests failing with Authentication failure (http 401) when using non-ascii Unicode characters in the request. I tried it with few services and the behavior is the same. There seems to be some issue with the way SDK signs the requests.
Note: I'm able to create the resource with name containing non-ascii Unicode characters using Console, oci-cli,oci-go-sdk just fine.

This issue combined with #47 caused an outage in our production environment. I appreciate it if this issue can be prioritized.

Example:

Testing with Compute service:

Code:

def launch_instance(compute_client,
  subnet_id,
  availability_domain,
  compartment_id,
  image_id,
  shape,
  ssh_key,
  user_data)
  name = "simple-instance"
  puts "Launching instance... #{name}"
  request = OCI::Core::Models::LaunchInstanceDetails.new
  request.availability_domain = availability_domain
  request.compartment_id = compartment_id
  request.display_name = name
  request.image_id = image_id
  request.shape = shape
  request.subnet_id = subnet_id
  begin
    launch_instance_response = compute_client.launch_instance(request)
    instance = launch_instance_response.data
    puts "Launched instance '#{instance.display_name}' [#{instance.id}]"
  rescue => error
    puts "Error in creating instance #{name}; Error = #{error}"
  end  
end

Creating with normal characters (value of the name in the above code is set to "simple-instance"):

/Library/Ruby/Gems/2.3.0/gems/oci-2.7.0/lib/oci/identity/models/base_tag_definition_validator.rb:20: warning: already initialized constant OCI::Identity::Models::BaseTagDefinitionValidator::VALIDATOR_TYPE_ENUM
/Library/Ruby/Gems/2.3.0/gems/oci-2.7.0/lib/oci/identity/models/base_tag_definition_validator.rb:21: warning: previous definition of VALIDATOR_TYPE_ENUM was here
Launching instance... simple-instance
Launched instance 'simple-instance' [ocid1.instance.oc1.iad.anuwcljth4gjgpychc2gf43ygdpaisn7m76hq5fv6ntucfwd6noba35gapwq]

Creating with non standard characters (value of the name in the above code is set to "Futbolín"):

/Library/Ruby/Gems/2.3.0/gems/oci-2.7.0/lib/oci/identity/models/base_tag_definition_validator.rb:20: warning: already initialized constant OCI::Identity::Models::BaseTagDefinitionValidator::VALIDATOR_TYPE_ENUM
/Library/Ruby/Gems/2.3.0/gems/oci-2.7.0/lib/oci/identity/models/base_tag_definition_validator.rb:21: warning: previous definition of VALIDATOR_TYPE_ENUM was here
Creating Cluster - Futbolín
Error in creating cluster Futbolín; Error = { 'message': 'The required information to complete authentication was not provided or was incorrect.', 'status': 401, 'code': 'NotAuthenticated', 'opc-request-id': 'EC7E185F98AA44129DD9119A814D8E10/4169A1293DEC48BEB91FB421461D30D3/2640C1B1A4AC4372A354B33DB7A14E9E' }

Testing with OKE service:

code:

def create_cluster(ce_client, ce_client_composite, compartment_id, vcn_id, lb_subnet_ids)
  cluster_name = "Futbolín"
  puts "Creating Cluster - #{cluster_name}"
  begin
    response = ce_client.create_cluster(
      OCI::ContainerEngine::Models::CreateClusterDetails.new(
        name: cluster_name,
        compartment_id: compartment_id,
        vcn_id: vcn_id,
        kubernetes_version: KUBERNETES_VERSION,
        options: OCI::ContainerEngine::Models::ClusterCreateOptions.new(
          service_lb_subnet_ids: lb_subnet_ids
        )
      )
    )
    puts "Created Cluster response #{response.status} #{cluster_name}"
  rescue => error
    puts "Error in creating cluster #{cluster_name}; Error = #{error}"
  end  
end

Creating OKE cluster with normal character:(value of the cluster_name in the above code is set to "simple-cluster")

/Library/Ruby/Gems/2.3.0/gems/oci-2.7.0/lib/oci/identity/models/base_tag_definition_validator.rb:20: warning: already initialized constant OCI::Identity::Models::BaseTagDefinitionValidator::VALIDATOR_TYPE_ENUM
/Library/Ruby/Gems/2.3.0/gems/oci-2.7.0/lib/oci/identity/models/base_tag_definition_validator.rb:21: warning: previous definition of VALIDATOR_TYPE_ENUM was here
Creating Cluster - simple-cluster
Created Cluster response 202 simple-cluster

Creating OKE cluster with non-standard character:(value of the cluster_name in the above code is set to "Futbolín")

/Library/Ruby/Gems/2.3.0/gems/oci-2.7.0/lib/oci/identity/models/base_tag_definition_validator.rb:20: warning: already initialized constant OCI::Identity::Models::BaseTagDefinitionValidator::VALIDATOR_TYPE_ENUM
/Library/Ruby/Gems/2.3.0/gems/oci-2.7.0/lib/oci/identity/models/base_tag_definition_validator.rb:21: warning: previous definition of VALIDATOR_TYPE_ENUM was here
Creating Cluster - Futbolín
Error in creating cluster Futbolín; Error = { 'message': 'The required information to complete authentication was not provided or was incorrect.', 'status': 401, 'code': 'NotAuthenticated', 'opc-request-id': '307A74099991423D9A16632FCE701B69/1BFE6AD2C6624C9EAD3CC80992ACE39C/9F4B1CC30D7046CD8ABAE1DAA989E099' }

Way to reproduce the issue:

Try creating a compute instance(or any oci service instance) with a name that contains non-ascii Unicode characters(example: Futbolín).

OCI ruby SDK dont support Instance metadata V2

Support for using instance metadata API v2 for instance principals authentication - don't working.

  • There is no usage location /v2 - https://github.com/oracle/oci-ruby-sdk/blob/master/lib/oci/auth/signers/instance_principals_security_token_signer.rb

  • Using ruby gem shows
    /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/oci-2.8.0/lib/oci/identity/models/base_tag_definition_validator.rb:23: warning: previous definition of VALIDATOR_TYPE_ENUM was here

    * chef_gem[oci] action install (up to date)
    
    ================================================================================
    Error executing action `auth_util` on resource 'vault[get ar-tran-token]'
    ================================================================================
    
    OpenSSL::PKey::RSAError
    -----------------------
    Neither PUB key nor PRIV key: nested asn1 error
    

SDK loading time is too long

I have tried to use SDK in Functions service and found that SDK loading takes too much time. This way just require "oci" leads to

"Function did not respond within its configured timeout or within the platform max timeout."
error. The ruby version of sdk takes significally much more time than python version, this is strange.
After some investigation I have found:

  1. Place 1
  2. Place 2

It is not clear why this files requiring whole SDK, I think this is a mistake.

The other thing I have found is that there is no module provided, which includes all dependencies needed for API requests signing. The only way is to require whole "oci" file. I have created a workaround, which works in my case (code) but better to have something similar in sdk repo. This helps to reduce SDK loading time and allows to require it "modular" way.

Maybe not related, but I sometimes meet the

"/usr/lib/ruby/gems/gems/oci-2.15.0/lib/oci/auth/util.rb:62:in `initialize': Neither PUB key nor PRIV key: not enough data (OpenSSL::PKey::RSAError)"

when I trying to load secrets via SDK signed by EphemeralResourcePrincipalsSigner outside of Function handler. I have checked the all variables are in place. Maybe certificates generating takes some time and I cannot be guaranteed that they be ready on cold function start? Or maybe I missed something?

Thank you for your work guys, it is really pleasure to use ruby with Oracle cloud!

Support authentication_type=security_token

We use oci session authenticate with the environment variable OCI_CLI_AUTH=security_token set. This means our ~/.oci/config file doesn't actually have a setting for user, which causes the config validation to fail.

[DEFAULT]
fingerprint =<your_fingerprint>
key_file = /home/someone/.oci/sessions/DEFAULT/oci_api_key.pem
tenancy = ocid1.tenancy.oc1..<unique_ID>
region = us-ashburn-1
security_token_file = /home/someone/.oci/sessions/DEFAULT/token

This means that I have to make some other OCI user and manage keys in order to get the ruby sdk to work. So things like @stephenpearson's kitchen-oci won't work with my normal authentication methods.

Other SDKs handle this security token auth just fine. (Notably oci-python-sdk, used by oci-cli; and oci-go-sdk used by terraform-provider-oci and packer-plugin-oracle.)

Thread safe

Hi, is the SDK thread safe? I couldn't find this information in the docs.
Thanks!

URI.escape is obsolete in Ruby 2.7

Hi,
Ruby 2.7 made URI.escape obsolete. When running the latest version of the SDK with Ruby 2.7, the following warning appears:

~/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/oci-2.10.0/lib/oci/api_client.rb:582: warning: URI.escape is obsolete

It seems to be caused by the following line:

URI.encode(endpoint + path)

URI.encode is an alias to URI.escape. It may be possible to replace it by URI.encode_www_form_component, but it seems to be necessary to split the query string part and encode it only.

The following blog post explains the issue really well: https://docs.knapsackpro.com/2020/uri-escape-is-obsolete-percent-encoding-your-query-string

Certificate parsing error

I am trying to fetch ssl certificate via get_certificate method and observing error:

irb(main):099:0> cert_client.get_certificate("ocid1.certificate.oc1.eu-blah-blah")
/usr/local/bundle/gems/oci-2.18.0/lib/oci/internal/util.rb:59:in `convert_to_type': uninitialized constant OCI::CertificatesManagement::Models::VersionStage (NameError)
                                                                             
            type = OCI.const_get(type.to_s)                                  
                      ^^^^^^^^^^                                             
Did you mean?  OCI::CertificatesManagement::Models::VERSION_STAGE_FAILED     
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/certificates_management/models/certificate_version_summary.rb:238:in `block (2 levels) in build_from_hash'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/certificates_management/models/certificate_version_summary.rb:238:in `map'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/certificates_management/models/certificate_version_summary.rb:238:in `block in build_from_hash'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/certificates_management/models/certificate_version_summary.rb:231:in `each_pair'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/certificates_management/models/certificate_version_summary.rb:231:in `build_from_hash'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/internal/util.rb:68:in `block in convert_to_type'
        from <internal:kernel>:90:in `tap'                                   
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/internal/util.rb:67:in `convert_to_type'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/certificates_management/models/certificate.rb:462:in `block in build_from_hash'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/certificates_management/models/certificate.rb:450:in `each_pair'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/certificates_management/models/certificate.rb:450:in `build_from_hash'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/internal/util.rb:68:in `block in convert_to_type'
        from <internal:kernel>:90:in `tap'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/internal/util.rb:67:in `convert_to_type'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/api_client.rb:572:in `deserialize'
        from /usr/local/bundle/gems/oci-2.18.0/lib/oci/api_client.rb:462:in `handle_success_response'
        ... 14 levels...

The same error occurs on every api call, which is expected to parse certificate response, i.e. certificate creation and update.

InstancePrincipalsSecurityTokenSigner doesn't work without explicit federation endpoint

The documentation and code samples both say that the federation_endpoint argument to the InstancePrincipalsSecurityTokenSigner constructor is optional. However, there's a logic error in the constructor's implementation that requires the federation endpoint to be supplied, otherwise it will default to a value of nil and will result in an ArgumentError in creating a URI from the federation client:

irb(main):001:0> instance_principals_signer = OCI::Auth::Signers::InstancePrincipalsSecurityTokenSigner.new
ArgumentError: bad argument (expected URI object or URI string)
	from /opt/graalvm/jre/languages/ruby/lib/mri/uri/common.rb:739:in `URI'
	from /opt/graalvm/jre/languages/ruby/lib/ruby/gems/2.4.0/gems/oci-2.3.5/lib/oci/auth/federation_client.rb:40:in `initialize'
	from /opt/graalvm/jre/languages/ruby/lib/ruby/gems/2.4.0/gems/oci-2.3.5/lib/oci/auth/signers/instance_principals_security_token_signer.rb:77:in `new'
	from /opt/graalvm/jre/languages/ruby/lib/ruby/gems/2.4.0/gems/oci-2.3.5/lib/oci/auth/signers/instance_principals_security_token_signer.rb:77:in `initialize'
	from (irb):2:in `new'
	from (irb):2
	from /opt/graalvm/jre/languages/ruby/bin/irb:29:in `<main>'

The issue is in the following block used to conditionally assign the federation endpoint value:

@federation_endpoint = if defined?(federation_endpoint)
                          federation_endpoint
                       else
                          "#{OCI::Regions.get_service_endpoint(@region, :Auth)}/v1/x509"
                       end

Since federation_endpoint is a keyword argument with a default value, it is always defined. defined?(federation_endpoint) => local-variable. The simple fix is to change the predicate to if federetion_endpoint. It's not clear if the code was implemented in this way to support an end user explicitly passing a false-y value. However, if a user does explicitly do that, the code is still going to error out several layers removed from where the error occurs.

On a side note, the published docs for the federation_endpoint value is truncated because the yard doc comment wraps. I don't know off-hand if there's a way to wrap it and generate properly or if you'd just need to accept wider-than-you-want comments for fields like this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.