Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(services/s3): Cleanup the logic of client init #876

Merged
merged 4 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions services/s3/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[![Services Test S3](https://github.com/beyondstorage/go-storage/actions/workflows/services-test-s3.yml/badge.svg)](https://github.com/beyondstorage/go-storage/actions/workflows/services-test-s3.yml)

# s3

AWS S3 service support for [go-storage](https://github.com/beyondstorage/go-storage).
Expand Down
105 changes: 54 additions & 51 deletions services/s3/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/aws/smithy-go"
"github.com/aws/smithy-go/middleware"

"go.beyondstorage.io/credential"
"go.beyondstorage.io/endpoint"
ps "go.beyondstorage.io/v5/pairs"
Expand All @@ -27,7 +26,8 @@ import (

// Service is the s3 service config.
type Service struct {
cfg *aws.Config
cfg aws.Config
options []func(*s3.Options)
service *s3.Client
defaultPairs DefaultServicePairs
features ServiceFeatures
Expand Down Expand Up @@ -94,39 +94,19 @@ func newServicer(pairs ...typ.Pair) (srv *Service, err error) {
return nil, err
}

//Set s3 config's endpoint
customResolver := aws.EndpointResolverFunc(func(service, region string) (aws.Endpoint, error) {
if opt.HasEndpoint {
ep, err := endpoint.Parse(opt.Endpoint)
if err != nil {
return aws.Endpoint{}, err
}

var url string
switch ep.Protocol() {
case endpoint.ProtocolHTTP:
url, _, _ = ep.HTTP()
case endpoint.ProtocolHTTPS:
url, _, _ = ep.HTTPS()
default:
return aws.Endpoint{}, services.PairUnsupportedError{Pair: ps.WithEndpoint(opt.Endpoint)}
}
return aws.Endpoint{
URL: url,
}, nil
}
// returning EndpointNotFoundError will allow the service to fallback to it's default resolution
return aws.Endpoint{}, &aws.EndpointNotFoundError{}
})

cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithEndpointResolver(customResolver))
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithHTTPClient(httpclient.New(opt.HTTPClientOptions)))
if err != nil {
return nil, err
}

// Set s3 config's http client
cfg.HTTPClient = httpclient.New(opt.HTTPClientOptions)
if opt.HasHTTPClientOptions {
cfg.HTTPClient = httpclient.New(opt.HTTPClientOptions)
}
JinnyYi marked this conversation as resolved.
Show resolved Hide resolved

var opts []func(*s3.Options)

// Handle credential
cp, err := credential.Parse(opt.Credential)
if err != nil {
return nil, err
Expand All @@ -139,9 +119,48 @@ func newServicer(pairs ...typ.Pair) (srv *Service, err error) {
return nil, services.PairUnsupportedError{Pair: ps.WithCredential(opt.Credential)}
}

// Parse endpoint.
if opt.HasEndpoint {
ep, err := endpoint.Parse(opt.Endpoint)
if err != nil {
return nil, err
}

var url string
switch ep.Protocol() {
case endpoint.ProtocolHTTP:
url, _, _ = ep.HTTP()
case endpoint.ProtocolHTTPS:
url, _, _ = ep.HTTPS()
default:
return nil, services.PairUnsupportedError{Pair: ps.WithEndpoint(opt.Endpoint)}
}
opts = append(opts, s3.WithEndpointResolver(s3.EndpointResolverFromURL(url)))
}

// Handle s3 API options.
//
// S3 will calculate payload's content-sha256 by default, we change this behavior for following reasons:
// - To support uploading content without seek support: stdin, bytes.Reader
// - To allow user decide when to calculate the hash, especially for big files
//
// We will ignore all errors returned by middleware.Stack handler, as we don't know whether this middleware exists.
apiOptions := s3.WithAPIOptions(func(stack *middleware.Stack) error {
// With removing PayloadSHA256 and adding UnsignedPayload, signer will set "X-Amz-Content-Sha256" to "UNSIGNED-PAYLOAD"
_ = signerv4.RemoveComputePayloadSHA256Middleware(stack)
_ = signerv4.AddUnsignedPayloadMiddleware(stack)
_ = signerv4.RemoveContentSHA256HeaderMiddleware(stack)
_ = signerv4.AddContentSHA256HeaderMiddleware(stack)
return nil
})
opts = append(opts, apiOptions)

service := s3.NewFromConfig(cfg, opts...)

srv = &Service{
cfg: &cfg,
service: newS3Service(&cfg),
cfg: cfg,
options: opts,
service: service,
}

if opt.HasDefaultServicePairs {
Expand Down Expand Up @@ -200,37 +219,21 @@ func formatError(err error) error {
}
}

func newS3Service(cfgs *aws.Config) (srv *s3.Client) {
// S3 will calculate payload's content-sha256 by default, we change this behavior for following reasons:
// - To support uploading content without seek support: stdin, bytes.Reader
// - To allow user decide when to calculate the hash, especially for big files
srv = s3.NewFromConfig(*cfgs, func(options *s3.Options) {
options.APIOptions = append(options.APIOptions,
func(stack *middleware.Stack) error {
// With removing PayloadSHA256 and adding UnsignedPayload, signer will set "X-Amz-Content-Sha256" to "UNSIGNED-PAYLOAD"
signerv4.RemoveComputePayloadSHA256Middleware(stack)
signerv4.AddUnsignedPayloadMiddleware(stack)
signerv4.RemoveContentSHA256HeaderMiddleware(stack)
return signerv4.AddContentSHA256HeaderMiddleware(stack)
})
})

return
}

// newStorage will create a new client.
func (s *Service) newStorage(pairs ...typ.Pair) (st *Storage, err error) {
optStorage, err := parsePairStorageNew(pairs)
if err != nil {
return nil, err
}

// Copy config to prevent change the service config.
cfg := s.cfg
if optStorage.HasLocation {
s.cfg.Region = optStorage.Location
cfg.Region = optStorage.Location
}

st = &Storage{
service: newS3Service(s.cfg),
service: s3.NewFromConfig(cfg, s.options...),
name: optStorage.Name,
workDir: "/",
}
Expand Down