github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/backend/oracleobjectstorage/client.go (about)

     1  //go:build !plan9 && !solaris && !js
     2  
     3  package oracleobjectstorage
     4  
     5  import (
     6  	"context"
     7  	"crypto/rsa"
     8  	"errors"
     9  	"net/http"
    10  	"os"
    11  	"path"
    12  	"strings"
    13  
    14  	"github.com/oracle/oci-go-sdk/v65/common"
    15  	"github.com/oracle/oci-go-sdk/v65/common/auth"
    16  	"github.com/oracle/oci-go-sdk/v65/objectstorage"
    17  	"github.com/rclone/rclone/fs"
    18  	"github.com/rclone/rclone/fs/fserrors"
    19  	"github.com/rclone/rclone/fs/fshttp"
    20  )
    21  
    22  func expandPath(filepath string) (expandedPath string) {
    23  	if filepath == "" {
    24  		return filepath
    25  	}
    26  	cleanedPath := path.Clean(filepath)
    27  	expandedPath = cleanedPath
    28  	if strings.HasPrefix(cleanedPath, "~") {
    29  		rest := cleanedPath[2:]
    30  		home, err := os.UserHomeDir()
    31  		if err != nil {
    32  			return expandedPath
    33  		}
    34  		expandedPath = path.Join(home, rest)
    35  	}
    36  	return
    37  }
    38  
    39  func getConfigurationProvider(opt *Options) (common.ConfigurationProvider, error) {
    40  	switch opt.Provider {
    41  	case instancePrincipal:
    42  		return auth.InstancePrincipalConfigurationProvider()
    43  	case userPrincipal:
    44  		expandConfigFilePath := expandPath(opt.ConfigFile)
    45  		if expandConfigFilePath != "" && !fileExists(expandConfigFilePath) {
    46  			fs.Errorf(userPrincipal, "oci config file doesn't exist at %v", expandConfigFilePath)
    47  		}
    48  		return common.CustomProfileConfigProvider(expandConfigFilePath, opt.ConfigProfile), nil
    49  	case resourcePrincipal:
    50  		return auth.ResourcePrincipalConfigurationProvider()
    51  	case noAuth:
    52  		fs.Infof("client", "using no auth provider")
    53  		return getNoAuthConfiguration()
    54  	case workloadIdentity:
    55  		return auth.OkeWorkloadIdentityConfigurationProvider()
    56  	default:
    57  	}
    58  	return common.DefaultConfigProvider(), nil
    59  }
    60  
    61  func newObjectStorageClient(ctx context.Context, opt *Options) (*objectstorage.ObjectStorageClient, error) {
    62  	p, err := getConfigurationProvider(opt)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	client, err := objectstorage.NewObjectStorageClientWithConfigurationProvider(p)
    67  	if err != nil {
    68  		fs.Errorf(opt.Provider, "failed to create object storage client, %v", err)
    69  		return nil, err
    70  	}
    71  	if opt.Region != "" {
    72  		client.SetRegion(opt.Region)
    73  	}
    74  	if opt.Endpoint != "" {
    75  		client.Host = opt.Endpoint
    76  	}
    77  	modifyClient(ctx, opt, &client.BaseClient)
    78  	return &client, err
    79  }
    80  
    81  func fileExists(filePath string) bool {
    82  	if _, err := os.Stat(filePath); errors.Is(err, os.ErrNotExist) {
    83  		return false
    84  	}
    85  	return true
    86  }
    87  
    88  func modifyClient(ctx context.Context, opt *Options, client *common.BaseClient) {
    89  	client.HTTPClient = getHTTPClient(ctx)
    90  	if opt.Provider == noAuth {
    91  		client.Signer = getNoAuthSigner()
    92  	}
    93  }
    94  
    95  // getClient makes http client according to the global options
    96  // this has rclone specific options support like dump headers, body etc.
    97  func getHTTPClient(ctx context.Context) *http.Client {
    98  	return fshttp.NewClient(ctx)
    99  }
   100  
   101  var retryErrorCodes = []int{
   102  	408, // Request Timeout
   103  	429, // Rate exceeded.
   104  	500, // Get occasional 500 Internal Server Error
   105  	503, // Service Unavailable
   106  	504, // Gateway Time-out
   107  }
   108  
   109  func shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, error) {
   110  	if fserrors.ContextError(ctx, &err) {
   111  		return false, err
   112  	}
   113  	// If this is an ocierr object, try and extract more useful information to determine if we should retry
   114  	if ociError, ok := err.(common.ServiceError); ok {
   115  		// Simple case, check the original embedded error in case it's generically retryable
   116  		if fserrors.ShouldRetry(err) {
   117  			return true, err
   118  		}
   119  		// If it is a timeout then we want to retry that
   120  		if ociError.GetCode() == "RequestTimeout" {
   121  			return true, err
   122  		}
   123  	}
   124  	// Ok, not an oci error, check for generic failure conditions
   125  	return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err
   126  }
   127  
   128  func getNoAuthConfiguration() (common.ConfigurationProvider, error) {
   129  	return &noAuthConfigurator{}, nil
   130  }
   131  
   132  func getNoAuthSigner() common.HTTPRequestSigner {
   133  	return &noAuthSigner{}
   134  }
   135  
   136  type noAuthConfigurator struct {
   137  }
   138  
   139  type noAuthSigner struct {
   140  }
   141  
   142  func (n *noAuthSigner) Sign(*http.Request) error {
   143  	return nil
   144  }
   145  
   146  func (n *noAuthConfigurator) PrivateRSAKey() (*rsa.PrivateKey, error) {
   147  	return nil, nil
   148  }
   149  
   150  func (n *noAuthConfigurator) KeyID() (string, error) {
   151  	return "", nil
   152  }
   153  
   154  func (n *noAuthConfigurator) TenancyOCID() (string, error) {
   155  	return "", nil
   156  }
   157  
   158  func (n *noAuthConfigurator) UserOCID() (string, error) {
   159  	return "", nil
   160  }
   161  
   162  func (n *noAuthConfigurator) KeyFingerprint() (string, error) {
   163  	return "", nil
   164  }
   165  
   166  func (n *noAuthConfigurator) Region() (string, error) {
   167  	return "", nil
   168  }
   169  
   170  func (n *noAuthConfigurator) AuthType() (common.AuthConfig, error) {
   171  	return common.AuthConfig{
   172  		AuthType:         common.UnknownAuthenticationType,
   173  		IsFromConfigFile: false,
   174  		OboToken:         nil,
   175  	}, nil
   176  }
   177  
   178  // Check the interfaces are satisfied
   179  var (
   180  	_ common.ConfigurationProvider = &noAuthConfigurator{}
   181  	_ common.HTTPRequestSigner     = &noAuthSigner{}
   182  )