Oracle Cloud Infrastructure (OCI) Go SDK

repository·master·Indexed 42 days ago

https://github.com/oracle/oci-go-sdk

An open-source Golang SDK for programmatically interacting with Oracle Cloud Infrastructure (OCI) services. It provides service-specific clients, a common package for shared utilities like request signing and HTTP serialization, and support for various authentication methods including config files, programmatic configuration, and resource principals.

Tokens
8.1K
Snippets
25
Records
39
Agent score
53%

What's inside OCI Go SDK

  1. Understand the OCI Go SDK organization

    master

    The SDK is organized into several distinct parts:

    • Service packages: Each package (except common) represents a specific OCI service. They contain the client structs, service methods, and request/response models.
    • Common package: Located in the common directory, this provides shared utilities like HTTP serialization, request signing, JSON parsing, and helper functions used by all service packages.
    • cmd: Contains internal tools used by the SDK itself.
  2. Configure the OCI Go SDK

    master

    The SDK requires credentials to authenticate with Oracle Cloud Infrastructure. You can configure it in two ways:

    1. Using a Config File: Set up a standard OCI configuration file. The SDK provides common.DefaultConfigProvider() to automatically load these credentials. Note that the SDK does not support profile inheritance or custom values in the config file.
    2. Programmatic Configuration: Implement the ConfigurationProvider interface to provide credentials directly in your code.

    To use the default configuration, import the common package and call DefaultConfigProvider().

    import (
    	"github.com/oracle/oci-go-sdk/v49/common"
    	"github.com/oracle/oci-go-sdk/v49/identity"
    )
    
    // Use the default configuration provider
    configProvider := common.DefaultConfigProvider()
  3. Make requests to OCI services

    master

    To interact with an OCI service, follow these steps:

    1. Create a Client: Use the service-specific constructor following the pattern New<ServiceName>ClientWithConfigurationProvider. Pass a ConfigurationProvider (like the one from common.DefaultConfigProvider()) to the constructor.
    2. Call Service Methods: Most service methods accept a context.Context and a request struct (e.g., GetGroupRequest). These methods return a response struct and an error.

    Example: Fetching a Group from the Identity service

    import (
    	"context"
    	"fmt"
    	"github.com/oracle/oci-go-sdk/v49/common"
    	"github.com/oracle/oci-go-sdk/v49/identity"
    )
    
    func main() {
    	config := common.DefaultConfigProvider()
    	client, err := identity.NewIdentityClientWithConfigurationProvider(config)
    	if err != nil {
    	    panic(err)
    	}
    
    	id := "your_group_id"
    	response, err := client.GetGroup(context.Background(), identity.GetGroupRequest{GroupId: &id})
    	if err != nil {
    	    panic(err)
    	}
    
    	fmt.Println("Group's name is:", response.Name)
    }
  4. Install the OCI Go SDK

    master

    You can install the SDK using go get. If you are using Go modules, run the command within a folder containing a go.mod file. Ensure your import paths include the correct major version (e.g., v49).

    Note: The major version in the import path must match the version you install.

    # Install using Go modules (replace v49 with the latest major version)
    go get -d github.com/oracle/oci-go-sdk/v49@latest
    
    # To install a specific version
    go get -d github.com/oracle/oci-go-sdk/v49@v49.1.0
    
    # Run tidy to clean up dependencies
    go mod tidy
  5. Use Resource Principals in OCI Functions

    master

    Resource principals allow code running within a specific OCI resource (like an OCI Function) to act with its own identity and credentials. This enables the function to interact with other OCI services without requiring hardcoded user credentials.

    To implement this, you must configure identity and access management (IAM) in two steps:

    1. Define a Dynamic Group: Create a dynamic group that includes your function as a member based on specific attributes (like resource type and compartment).
    2. Define a Policy: Grant the dynamic group specific permissions to access resources within your tenancy or compartment.

    The example code in example/example_resource_principal_function is designed to be packaged into an OCI Functions container to demonstrate this pattern.

    ### Example dynamic-group rule for dynamic group fnfunc-example-compartment
    
    ALL {resource.type = 'fnfunc', resource.compartment.name = 'example-compartment'}
    
    ### Policy statement for policy example-compartment/fnfunc-grant-reads
    
    allow dynamic-group fnfunc-example-compartment to read all-resources in compartment example-compartment
  6. Configure SAML and OIDC settings for an App

    master

    To enable Single Sign-On (SSO) via SAML or OpenID Connect, configure the following fields in the App resource:

    • Login Mechanism: Set LoginMechanism to AppLoginMechanismEnum (e.g., OIDC or SAML).
    • Landing Page: LandingPageUrl is the first page an end user sees after successful login.
    • Logout Behavior:
      • LogoutUri: The URI used for SSO logout if the app is an OAuth Client.
      • PostLogoutRedirectUris: URIs the app can redirect to after initiating a logout flow.
      • LogoutPageUrl: The page used when an end-user signs out.
    • Service Provider Role: Set IsSamlServiceProvider to true if the app acts as a SAML SP.
  7. Configure OAuth Client settings for an App

    master

    When an application is configured to act as an OAuth Client (IsOAuthClient: true), you must provide specific parameters to support authorization flows:

    • Redirect URIs: RedirectUris (list of strings) are required for three-legged flows (authorization-code flows). Use AllUrlSchemesAllowed to permit non-standard schemes.
    • Client Type: ClientType defines the nature of the client.
    • Grants: AllowedGrants specifies the allowed grant types.
    • Operations: AllowedOperations (using AppAllowedOperationsEnum) defines if the client can use introspect or onBehalfOfUser modes.
    • Scopes: Audience defines the base URI for all scopes defined in the App.
  8. Implement pagination for list operations

    master
    List operations in OCI return results in pages. To retrieve all items, you must perform subsequent calls by passing the OpcNextPage value from the previous response into the Page field of the next request. When OpcNextPage is nil, all data has been retrieved.
  9. Configure Retry Policies

    master

    The SDK provides mechanisms to retry operations that fail due to transient errors or eventual consistency.

    Retry Precedence (Highest to Lowest)

    1. Operation level (RequestMetadata.RetryPolicy)
    2. Client level
    3. Global level
    4. Environment level default
    5. Service level default

    Default Retry Policy

    • Attempts: 1 initial request + 7 retries (8 total).
    • Strategy: Exponential backoff with jitter (base 1s, exponent 2, max 30s wait, 0-1000ms jitter).
    • Retryable Errors: HTTP 409 (IncorrectState, LockConflict), 429, 500, 502, 503, 504, and client-side connection timeouts/exceptions.

    Handling Eventual Consistency

    For Identity service changes, use a policy that handles the 'eventually consistent window' (approx. 4 minutes). The common.DefaultRetryPolicy() automatically switches to an eventually consistent strategy if a recent change is detected. It will make up to 9 attempts and retry on specific 400/404/409 error codes.

    // Use SDK's default retry policy for a single request
    defaultRetryPolicy := common.DefaultRetryPolicy()
    request.RequestMetadata = common.RequestMetadata{
    	RetryPolicy: &defaultRetryPolicy,
    }
    
    // Or set it for all requests in a client
    client.SetCustomClientConfiguration(common.CustomClientConfiguration{
    	RetryPolicy: &defaultRetryPolicy,
    })
    
    // Or set it globally for all clients
    common.GlobalRetry = &defaultRetryPolicy
  10. Handle binary request bodies with retries using OCIReadSeekCloser

    master

    When performing requests with a body (e.g., uploading a file), the SDK uses OCIReadSeekCloser to ensure that if a request needs to be retried (such as a 401 Unauthorized during a token refresh), the request body can be rewound to the beginning.

    Note: For retries to work with binary bodies, the underlying io.ReadCloser must implement the io.Seeker interface (like an os.File). If the body is not seekable, retries for that request will fail with a NonSeekableRequestRetryFailure error.

  11. Handle polymorphic JSON requests and responses

    master

    Some OCI operations use polymorphic JSON, which the SDK models as interfaces.

    • Requests: Pass a concrete struct that implements the required interface into the request object.
    • Responses: Use type assertion on the response interface to access the underlying concrete type.
    // 1. Polymorphic Request: Passing a concrete struct to an interface field
    client, _ := identity.NewIdentityClientWithConfigurationProvider(common.DefaultConfigProvider())
    rCreate := identity.CreateIdentityProviderRequest{}
    
    // CreateSaml2IdentityProviderDetails implements the CreateIdentityProviderDetails interface
    details := identity.CreateSaml2IdentityProviderDetails{}
    details.CompartmentId = common.String(tenancyID)
    details.Name = common.String("someName")
    
    rCreate.CreateIdentityProviderDetails = details
    
    // 2. Polymorphic Response: Type asserting the interface to the expected type
    rRead := identity.GetIdentityProviderRequest{}
    rRead.IdentityProviderId = common.String("aValidId")
    response, err := client.GetIdentityProvider(context.Background(), rRead)
    
    // Type assert the interface to the concrete type
    provider := response.IdentityProvider.(identity.Saml2IdentityProvider)
  12. Quickstart: Create an Identity Client and List Availability Domains

    master

    To get started with the OCI Go SDK, you can initialize a service client using a configuration provider (like the default one) and then use that client to make API calls. The following example demonstrates how to create an identityClient and list availability domains in a tenancy.

    import (
    	"context"
    	"fmt"
    
    	"github.com/oracle/oci-go-sdk/v65/common"
    	"github.com/oracle/oci-go-sdk/v65/identity"
    )
    
    func main() {
    	c, err := identity.NewIdentityClientWithConfigurationProvider(common.DefaultConfigProvider())
    	if err != nil {
    		fmt.Println("Error:", err)
    		return
    	}
    
    	// The OCID of the tenancy containing the compartment.
    	tenancyID, err := common.DefaultConfigProvider().TenancyOCID()
    	if err != nil {
    		fmt.Println("Error:", err)
    		return
    	}
    
    	request := identity.ListAvailabilityDomainsRequest{
    		CompartmentId: &tenancyID,
    	}
    
    	r, err := c.ListAvailabilityDomains(context.Background(), request)
    	if err != nil {
    		fmt.Println("Error:", err)
    		return
    	}
    
    	fmt.Printf("List of available domains: %v", r.Items)
    	return
    }