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 )