Skip to content

Commit

Permalink
UseDualStackEndpoint Endpoint Resolver Support
Browse files Browse the repository at this point in the history
  • Loading branch information
skmcgrail committed Jun 2, 2021
1 parent adb81ee commit a2ddb39
Show file tree
Hide file tree
Showing 20 changed files with 11,925 additions and 10,478 deletions.
18 changes: 18 additions & 0 deletions aws/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,22 @@ type Config struct {
// svc := s3.New(sess, &aws.Config{
// UseDualStack: aws.Bool(true),
// })
//
// Deprecated: This option will continue to function for S3 and S3 Control for backwards compatibility.
// DualStackEndpoint should be used to enable usage of a service's dual-stack endpoint for all service clients
// moving forward. For S3 and S3 Control, when DualStackEndpoint is set to a non-zero value it takes higher
// precedence then this option.
UseDualStack *bool

// Sets the resolver to resolve the endpoint as a dual-stack endpoint
// for the service.
//
// When enabled, the resolver may in some cases return an endpoint using either the partition or services
// dual-stack endpoint pattern which may not be valid or available. In the event that the service or partition
// does not have a default dual-stack pattern, and the client's configured region is not an explicitly modeled
// endpoint an error will be returned.
DualStackEndpoint endpoints.DualStackEndpoint

// SleepDelay is an override for the func the SDK will call when sleeping
// during the lifecycle of a request. Specifically this will be used for
// request delays. This value should only be used for testing. To adjust
Expand Down Expand Up @@ -554,6 +568,10 @@ func mergeInConfig(dst *Config, other *Config) {
dst.UseDualStack = other.UseDualStack
}

if other.DualStackEndpoint != endpoints.DualStackEndpointUnset {
dst.DualStackEndpoint = other.DualStackEndpoint
}

if other.EC2MetadataDisableTimeoutOverride != nil {
dst.EC2MetadataDisableTimeoutOverride = other.EC2MetadataDisableTimeoutOverride
}
Expand Down
24 changes: 1 addition & 23 deletions aws/endpoints/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resol
for i := 0; i < len(ps); i++ {
p := &ps[i]
custAddEC2Metadata(p)
custAddS3DualStack(p)
custRegionalS3(p)
custRmIotDataService(p)
custFixAppAutoscalingChina(p)
Expand All @@ -92,15 +91,6 @@ func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resol
return ps, nil
}

func custAddS3DualStack(p *partition) {
if !(p.ID == "aws" || p.ID == "aws-cn" || p.ID == "aws-us-gov") {
return
}

custAddDualstack(p, "s3")
custAddDualstack(p, "s3-control")
}

func custRegionalS3(p *partition) {
if p.ID != "aws" {
return
Expand Down Expand Up @@ -128,18 +118,6 @@ func custRegionalS3(p *partition) {
p.Services["s3"] = service
}

func custAddDualstack(p *partition, svcName string) {
s, ok := p.Services[svcName]
if !ok {
return
}

s.Defaults.HasDualStack = boxedTrue
s.Defaults.DualStackHostname = "{service}.dualstack.{region}.{dnsSuffix}"

p.Services[svcName] = s
}

func custAddEC2Metadata(p *partition) {
p.Services["ec2metadata"] = service{
IsRegionalized: boxedFalse,
Expand Down Expand Up @@ -169,7 +147,7 @@ func custFixAppAutoscalingChina(p *partition) {
}

const expectHostname = `autoscaling.{region}.amazonaws.com`
if e, a := s.Defaults.Hostname, expectHostname; e != a {
if e, a := expectHostname, s.Defaults.Hostname; e != a {
fmt.Printf("custFixAppAutoscalingChina: ignoring customization, expected %s, got %s\n", e, a)
return
}
Expand Down
64 changes: 52 additions & 12 deletions aws/endpoints/decode_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// +build go1.9

package endpoints

import (
Expand All @@ -6,8 +8,7 @@ import (
)

func TestDecodeEndpoints_V3(t *testing.T) {
const v3Doc = `
{
const v3Doc = `{
"version": 3,
"partitions": [
{
Expand All @@ -20,7 +21,15 @@ func TestDecodeEndpoints_V3(t *testing.T) {
"v4"
]
},
"dualstackDefaults": {
"protocols": [
"http",
"https"
],
"hostname": "{service}.{region}.{dualstackDnsSuffix}"
},
"dnsSuffix": "amazonaws.com",
"dualstackDnsSuffix": "aws",
"partition": "aws",
"partitionName": "AWS Standard",
"regionRegex": "^(us|eu|ap|sa|ca)\\-\\w+\\-\\d+$",
Expand All @@ -32,13 +41,27 @@ func TestDecodeEndpoints_V3(t *testing.T) {
"services": {
"acm": {
"endpoints": {
"ap-northeast-1": {}
}
"ap-northeast-1": {}
}
},
"s3": {
"dualstackDefaults": {
"protocols": [
"http",
"https"
],
"hostname": "{service}.dualstack.{region}.{dualstackDnsSuffix}"
},
"dualstackDnsSuffix": "amazonaws.com",
"endpoints": {
"ap-northeast-1": {}
}
"ap-northeast-1": {}
},
"dualstackEndpoints": {
"us-west-2": {
"hostname": "s3.dualstack.us-west-2.amazonaws.com",
"signatureVersions": ["s3", "s3v4"]
}
}
}
}
}
Expand All @@ -61,20 +84,37 @@ func TestDecodeEndpoints_V3(t *testing.T) {

p := resolver.(partitions)[0]

s3Defaults := p.Services["s3"].Defaults
if a, e := s3Defaults.HasDualStack, boxedTrue; a != e {
t.Errorf("expect s3 service to have dualstack enabled")
}
if a, e := s3Defaults.DualStackHostname, "{service}.dualstack.{region}.{dnsSuffix}"; a != e {
t.Errorf("expect s3 dualstack host pattern to be %q, got %q", e, a)
resolved, err := p.EndpointFor("s3", "us-west-2", func(options *Options) {
options.DualStackEndpoint = DualStackEndpointEnabled
})
if err != nil {
t.Fatalf("expect no error, got %v", err)
}

assertEndpoint(t, resolved, "https://s3.dualstack.us-west-2.amazonaws.com", "s3", "us-west-2")

ec2metaEndpoint := p.Services["ec2metadata"].Endpoints["aws-global"]
if a, e := ec2metaEndpoint.Hostname, "169.254.169.254/latest"; a != e {
t.Errorf("expect ec2metadata host to be %q, got %q", e, a)
}
}

func assertEndpoint(t *testing.T, endpoint ResolvedEndpoint, expectedURL, expectedSigningName, expectedSigningRegion string) {
t.Helper()

if e, a := expectedURL, endpoint.URL; e != a {
t.Errorf("expect %v, got %v", e, a)
}

if e, a := expectedSigningName, endpoint.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}

if e, a := expectedSigningRegion, endpoint.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
}

func TestDecodeEndpoints_NoPartitions(t *testing.T) {
const doc = `{ "version": 3 }`

Expand Down
49 changes: 49 additions & 0 deletions aws/endpoints/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ import (
"github.com/aws/aws-sdk-go/aws/awserr"
)

// DualStackEndpoint is a constant to describe the dual-stack endpoint resolution
// behavior.
type DualStackEndpoint uint

const (
// DualStackEndpointUnset is the default value behavior for dual-stack endpoint
// resolution.
DualStackEndpointUnset DualStackEndpoint = iota

// DualStackEndpointEnabled enable dual-stack endpoint resolution for endpoints.
DualStackEndpointEnabled

// DualStackEndpointDisabled disables dual-stack endpoint resolution for endpoints.
DualStackEndpointDisabled
)

// Options provide the configuration needed to direct how the
// endpoints will be resolved.
type Options struct {
Expand All @@ -21,8 +37,22 @@ type Options struct {
// be returned. This endpoint may not be valid. If StrictMatching is
// enabled only services that are known to support dualstack will return
// dualstack endpoints.
//
// Deprecated: This option will continue to function for S3 and S3 Control for backwards compatibility.
// DualStackEndpoint should be used to enable usage of a service's dual-stack endpoint for all service clients
// moving forward. For S3 and S3 Control, when DualStackEndpoint is set to a non-zero value it takes higher
// precedence then this option.
UseDualStack bool

// Sets the resolver to resolve the endpoint as a dual-stack endpoint
// for the service.
//
// When enabled, the resolver may in some cases return an endpoint using either the partition or services
// dual-stack endpoint pattern which may not be valid or available. In the event that the service or partition
// does not have a default dual-stack pattern, and the client's configured region is not an explicitly modeled
// endpoint an error will be returned.
DualStackEndpoint DualStackEndpoint

// Enables strict matching of services and regions resolved endpoints.
// If the partition doesn't enumerate the exact service and region an
// error will be returned. This option will prevent returning endpoints
Expand Down Expand Up @@ -55,6 +85,16 @@ type Options struct {
S3UsEast1RegionalEndpoint S3UsEast1RegionalEndpoint
}

func (o Options) isUseDualStackEndpoint(service string) (v bool) {
if o.DualStackEndpoint != DualStackEndpointUnset {
return o.DualStackEndpoint == DualStackEndpointEnabled
}
if service == "s3" || service == "s3-control" {
return o.UseDualStack
}
return false
}

// STSRegionalEndpoint is an enum for the states of the STS Regional Endpoint
// options.
type STSRegionalEndpoint int
Expand Down Expand Up @@ -166,10 +206,19 @@ func DisableSSLOption(o *Options) {

// UseDualStackOption sets the UseDualStack option. Can be used as a functional
// option when resolving endpoints.
//
// Deprecated: DualStackEndpointOption should be used to enable usage of a service's dual-stack endpoint.
// When DualStackEndpoint is set to a non-zero value it takes higher precedence then this option.
func UseDualStackOption(o *Options) {
o.UseDualStack = true
}

// DualStackEndpointOption sets the DualStackEndpoint option to enabled. Can be used as a functional
// option when resolving endpoints.
func DualStackEndpointOption(o *Options) {
o.DualStackEndpoint = DualStackEndpointEnabled
}

// StrictMatchingOption sets the StrictMatching option. Can be used as a functional
// option when resolving endpoints.
func StrictMatchingOption(o *Options) {
Expand Down
13 changes: 8 additions & 5 deletions aws/endpoints/endpoints_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// +build go1.9

package endpoints

import "testing"
Expand Down Expand Up @@ -109,7 +111,7 @@ func TestEnumServicesEndpoints(t *testing.T) {

ss := p.Services()

if a, e := len(ss), 5; a != e {
if a, e := len(ss), 6; a != e {
t.Errorf("expect %d regions got %d", e, a)
}

Expand Down Expand Up @@ -253,12 +255,13 @@ func TestResolverFunc(t *testing.T) {

func TestOptionsSet(t *testing.T) {
var actual Options
actual.Set(DisableSSLOption, UseDualStackOption, StrictMatchingOption)
actual.Set(DisableSSLOption, UseDualStackOption, StrictMatchingOption, DualStackEndpointOption)

expect := Options{
DisableSSL: true,
UseDualStack: true,
StrictMatching: true,
DisableSSL: true,
UseDualStack: true,
DualStackEndpoint: DualStackEndpointEnabled,
StrictMatching: true,
}

if actual != expect {
Expand Down
2 changes: 2 additions & 0 deletions aws/endpoints/example_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// +build go1.9

package endpoints_test

import (
Expand Down
Loading

0 comments on commit a2ddb39

Please sign in to comment.