Skip to content

Commit

Permalink
service/s3: Add support for regional S3 us-east-1 endpoint
Browse files Browse the repository at this point in the history
Adds support for S3 configuring an SDK Amazon S3 client for the regional
us-east-1 endpoint instead of the default global s3 endpoint.

Adds a new configuration option, `S3UsEast1RegionalEndpoint` which when
set to RegionalS3UsEast1Endpoint, and region is `us-east-1` the S3
client will resolve the `us-east-1` regional endpoint,
`s3.us-east-1.amazonaws.com` instead of the global S3 endpoint,
`s3.amazonaws.com`. The SDK defaults to the current global S3 endpoint
resolution for backwards compatibility.

Opt-in to the `us-east-1` regional endpoint via the SDK's Config,
environment variable, `AWS_S3_US_EAST_1_REGIONAL_ENDPOINT=regional`, or
shared config option, `s3_us_east_1_regional_endpoint=regional`.

Note the SDK does not support the shared configuration file by default.
You must opt-in to that behavior via Session Option `SharedConfigState`,
or `AWS_SDK_LOAD_CONFIG=true` environment variable.
  • Loading branch information
jasdel committed Nov 12, 2019
1 parent 97fd3a0 commit 73a3dc0
Show file tree
Hide file tree
Showing 17 changed files with 674 additions and 83 deletions.
14 changes: 14 additions & 0 deletions aws/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ type Config struct {

// STSRegionalEndpoint will enable regional or legacy endpoint resolving
STSRegionalEndpoint endpoints.STSRegionalEndpoint

// S3UsEast1RegionalEndpoint will enable regional or legacy endpoint resolving
S3UsEast1RegionalEndpoint endpoints.S3UsEast1RegionalEndpoint
}

// NewConfig returns a new Config pointer that can be chained with builder
Expand Down Expand Up @@ -430,6 +433,13 @@ func (c *Config) WithSTSRegionalEndpoint(sre endpoints.STSRegionalEndpoint) *Con
return c
}

// WithS3UsEast1RegionalEndpoint will set whether or not to use regional endpoint flag
// when resolving the endpoint for a service
func (c *Config) WithS3UsEast1RegionalEndpoint(sre endpoints.S3UsEast1RegionalEndpoint) *Config {
c.S3UsEast1RegionalEndpoint = sre
return c
}

func mergeInConfig(dst *Config, other *Config) {
if other == nil {
return
Expand Down Expand Up @@ -534,6 +544,10 @@ func mergeInConfig(dst *Config, other *Config) {
if other.STSRegionalEndpoint != endpoints.UnsetSTSEndpoint {
dst.STSRegionalEndpoint = other.STSRegionalEndpoint
}

if other.S3UsEast1RegionalEndpoint != endpoints.UnsetS3UsEast1Endpoint {
dst.S3UsEast1RegionalEndpoint = other.S3UsEast1RegionalEndpoint
}
}

// Copy will return a shallow copy of the Config object. If any additional
Expand Down
28 changes: 28 additions & 0 deletions aws/endpoints/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resol
p := &ps[i]
custAddEC2Metadata(p)
custAddS3DualStack(p)
custRegionalS3(p)
custRmIotDataService(p)
custFixAppAutoscalingChina(p)
custFixAppAutoscalingUsGov(p)
Expand All @@ -100,6 +101,33 @@ func custAddS3DualStack(p *partition) {
custAddDualstack(p, "s3-control")
}

func custRegionalS3(p *partition) {
if p.ID != "aws" {
return
}

service, ok := p.Services["s3"]
if !ok {
return
}

// If global endpoint already exists no customization needed.
if _, ok := service.Endpoints["aws-global"]; ok {
return
}

service.PartitionEndpoint = "aws-global"
service.Endpoints["us-east-1"] = endpoint{}
service.Endpoints["aws-global"] = endpoint{
Hostname: "s3.amazonaws.com",
CredentialScope: credentialScope{
Region: "us-east-1",
},
}

p.Services["s3"] = service
}

func custAddDualstack(p *partition, svcName string) {
s, ok := p.Services[svcName]
if !ok {
Expand Down
13 changes: 8 additions & 5 deletions aws/endpoints/defaults.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 67 additions & 2 deletions aws/endpoints/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,28 @@ type Options struct {

// STS Regional Endpoint flag helps with resolving the STS endpoint
STSRegionalEndpoint STSRegionalEndpoint

// S3 Regional Endpoint flag helps with resolving the S3 endpoint
S3UsEast1RegionalEndpoint S3UsEast1RegionalEndpoint
}

// STSRegionalEndpoint is an enum type alias for int
// It is used internally by the core sdk as STS Regional Endpoint flag value
// STSRegionalEndpoint is an enum for the states of the STS Regional Endpoint
// options.
type STSRegionalEndpoint int

func (e STSRegionalEndpoint) String() string {
switch e {
case LegacySTSEndpoint:
return "legacy"
case RegionalSTSEndpoint:
return "regional"
case UnsetSTSEndpoint:
return ""
default:
return "unknown"
}
}

const (

// UnsetSTSEndpoint represents that STS Regional Endpoint flag is not specified.
Expand Down Expand Up @@ -86,6 +102,55 @@ func GetSTSRegionalEndpoint(s string) (STSRegionalEndpoint, error) {
}
}

// S3UsEast1RegionalEndpoint is an enum for the states of the S3 us-east-1
// Regional Endpoint options.
type S3UsEast1RegionalEndpoint int

func (e S3UsEast1RegionalEndpoint) String() string {
switch e {
case LegacyS3UsEast1Endpoint:
return "legacy"
case RegionalS3UsEast1Endpoint:
return "regional"
case UnsetS3UsEast1Endpoint:
return ""
default:
return "unknown"
}
}

const (

// UnsetS3UsEast1Endpoint represents that S3 Regional Endpoint flag is not
// specified.
UnsetS3UsEast1Endpoint S3UsEast1RegionalEndpoint = iota

// LegacyS3UsEast1Endpoint represents when S3 Regional Endpoint flag is
// specified to use legacy endpoints.
LegacyS3UsEast1Endpoint

// RegionalS3UsEast1Endpoint represents when S3 Regional Endpoint flag is
// specified to use regional endpoints.
RegionalS3UsEast1Endpoint
)

// GetS3UsEast1RegionalEndpoint function returns the S3UsEast1RegionalEndpointFlag based
// on the input string provided in env config or shared config by the user.
//
// `legacy`, `regional` are the only case-insensitive valid strings for
// resolving the S3 regional Endpoint flag.
func GetS3UsEast1RegionalEndpoint(s string) (S3UsEast1RegionalEndpoint, error) {
switch {
case strings.EqualFold(s, "legacy"):
return LegacyS3UsEast1Endpoint, nil
case strings.EqualFold(s, "regional"):
return RegionalS3UsEast1Endpoint, nil
default:
return UnsetS3UsEast1Endpoint,
fmt.Errorf("unable to resolve the value of S3UsEast1RegionalEndpoint for %v", s)
}
}

// Set combines all of the option functions together.
func (o *Options) Set(optFns ...func(*Options)) {
for _, fn := range optFns {
Expand Down
24 changes: 24 additions & 0 deletions aws/endpoints/legacy_regions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package endpoints

var legacyGlobalRegions = map[string]map[string]struct{}{
"sts": {
"ap-northeast-1": {},
"ap-south-1": {},
"ap-southeast-1": {},
"ap-southeast-2": {},
"ca-central-1": {},
"eu-central-1": {},
"eu-north-1": {},
"eu-west-1": {},
"eu-west-2": {},
"eu-west-3": {},
"sa-east-1": {},
"us-east-1": {},
"us-east-2": {},
"us-west-1": {},
"us-west-2": {},
},
"s3": {
"us-east-1": {},
},
}
19 changes: 0 additions & 19 deletions aws/endpoints/sts_legacy_regions.go

This file was deleted.

31 changes: 16 additions & 15 deletions aws/endpoints/v3model.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ func (p partition) EndpointFor(service, region string, opts ...func(*Options)) (
region = s.PartitionEndpoint
}

if service == "sts" && opt.STSRegionalEndpoint != RegionalSTSEndpoint {
if _, ok := stsLegacyGlobalRegions[region]; ok {
if (service == "sts" && opt.STSRegionalEndpoint != RegionalSTSEndpoint) ||
(service == "s3" && opt.S3UsEast1RegionalEndpoint != RegionalS3UsEast1Endpoint) {
if _, ok := legacyGlobalRegions[service][region]; ok {
region = "aws-global"
}
}
Expand Down Expand Up @@ -240,11 +241,23 @@ func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs [
merged.mergeIn(e)
e = merged

hostname := e.Hostname
signingRegion := e.CredentialScope.Region
if len(signingRegion) == 0 {
signingRegion = region
}

signingName := e.CredentialScope.Service
var signingNameDerived bool
if len(signingName) == 0 {
signingName = service
signingNameDerived = true
}

hostname := e.Hostname
// Offset the hostname for dualstack if enabled
if opts.UseDualStack && e.HasDualStack == boxedTrue {
hostname = e.DualStackHostname
region = signingRegion
}

u := strings.Replace(hostname, "{service}", service, 1)
Expand All @@ -254,18 +267,6 @@ func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs [
scheme := getEndpointScheme(e.Protocols, opts.DisableSSL)
u = fmt.Sprintf("%s://%s", scheme, u)

signingRegion := e.CredentialScope.Region
if len(signingRegion) == 0 {
signingRegion = region
}

signingName := e.CredentialScope.Service
var signingNameDerived bool
if len(signingName) == 0 {
signingName = service
signingNameDerived = true
}

return ResolvedEndpoint{
URL: u,
PartitionID: partitionID,
Expand Down
Loading

0 comments on commit 73a3dc0

Please sign in to comment.