forked from aws/aws-sdk-go
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
aws/endpoints: Add endpoint metadata to SDK (aws#961)
Adds Region and Endpoint metadata to the SDK. This allows you to enumerate regions and endpoint metadata based on a defined model embedded in the SDK. This adds constants for each partition, region, and service for all Regions and Endpoints metadata. The metadata are embedded in the SDK, and is accessible as an Endpoint Resolver via `DefaultResolver`. To enumerate over the partition data case the response of EndpointResolver to a `EnumPartitions` and calls Partitions.
- Loading branch information
Showing
128 changed files
with
6,343 additions
and
691 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package endpoints | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
|
||
"github.com/aws/aws-sdk-go/aws/awserr" | ||
) | ||
|
||
type modelDefinition map[string]json.RawMessage | ||
|
||
// A DecodeModelOptions are the options for how the endpoints model definition | ||
// are decoded. | ||
type DecodeModelOptions struct { | ||
SkipCustomizations bool | ||
} | ||
|
||
// Set combines all of the option functions together. | ||
func (d *DecodeModelOptions) Set(optFns ...func(*DecodeModelOptions)) { | ||
for _, fn := range optFns { | ||
fn(d) | ||
} | ||
} | ||
|
||
// DecodeModel unmarshals a Regions and Endpoint model definition file into | ||
// a endpoint Resolver. If the file format is not supported, or an error occurs | ||
// when unmarshaling the model an error will be returned. | ||
// | ||
// Casting the return value of this func to a EnumPartitions will | ||
// allow you to get a list of the partitions in the order the endpoints | ||
// will be resolved in. | ||
// | ||
// resolver, err := endpoints.DecodeModel(reader) | ||
// | ||
// partitions := resolver.(endpoints.EnumPartitions).Partitions() | ||
// for _, p := range partitions { | ||
// // ... inspect partitions | ||
// } | ||
func DecodeModel(r io.Reader, optFns ...func(*DecodeModelOptions)) (Resolver, error) { | ||
var opts DecodeModelOptions | ||
opts.Set(optFns...) | ||
|
||
// Get the version of the partition file to determine what | ||
// unmarshaling model to use. | ||
modelDef := modelDefinition{} | ||
if err := json.NewDecoder(r).Decode(&modelDef); err != nil { | ||
return nil, newDecodeModelError("failed to decode endpoints model", err) | ||
} | ||
|
||
var version string | ||
if b, ok := modelDef["version"]; ok { | ||
version = string(b) | ||
} else { | ||
return nil, newDecodeModelError("endpoints version not found in model", nil) | ||
} | ||
|
||
if version == "3" { | ||
return decodeV3Endpoints(modelDef, opts) | ||
} | ||
|
||
return nil, newDecodeModelError( | ||
fmt.Sprintf("endpoints version %s, not supported", version), nil) | ||
} | ||
|
||
func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resolver, error) { | ||
b, ok := modelDef["partitions"] | ||
if !ok { | ||
return nil, newDecodeModelError("endpoints model missing partitions", nil) | ||
} | ||
|
||
ps := partitions{} | ||
if err := json.Unmarshal(b, &ps); err != nil { | ||
return nil, newDecodeModelError("failed to decode endpoints model", err) | ||
} | ||
|
||
if opts.SkipCustomizations { | ||
return ps, nil | ||
} | ||
|
||
// Customization | ||
for i := 0; i < len(ps); i++ { | ||
p := &ps[i] | ||
custAddEC2Metadata(p) | ||
custAddS3DualStack(p) | ||
custRmIotDataService(p) | ||
} | ||
|
||
return ps, nil | ||
} | ||
|
||
func custAddS3DualStack(p *partition) { | ||
if p.ID != "aws" { | ||
return | ||
} | ||
|
||
s, ok := p.Services["s3"] | ||
if !ok { | ||
return | ||
} | ||
|
||
s.Defaults.HasDualStack = boxedTrue | ||
s.Defaults.DualStackHostname = "{service}.dualstack.{region}.{dnsSuffix}" | ||
|
||
p.Services["s3"] = s | ||
} | ||
|
||
func custAddEC2Metadata(p *partition) { | ||
p.Services["ec2metadata"] = service{ | ||
IsRegionalized: boxedFalse, | ||
PartitionEndpoint: "aws-global", | ||
Endpoints: endpoints{ | ||
"aws-global": endpoint{ | ||
Hostname: "169.254.169.254/latest", | ||
Protocols: []string{"http"}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func custRmIotDataService(p *partition) { | ||
delete(p.Services, "data.iot") | ||
} | ||
|
||
type decodeModelError struct { | ||
awsError | ||
} | ||
|
||
func newDecodeModelError(msg string, err error) decodeModelError { | ||
return decodeModelError{ | ||
awsError: awserr.New("DecodeEndpointsModelError", msg, err), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package endpoints | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestDecodeEndpoints_V3(t *testing.T) { | ||
const v3Doc = ` | ||
{ | ||
"version": 3, | ||
"partitions": [ | ||
{ | ||
"defaults": { | ||
"hostname": "{service}.{region}.{dnsSuffix}", | ||
"protocols": [ | ||
"https" | ||
], | ||
"signatureVersions": [ | ||
"v4" | ||
] | ||
}, | ||
"dnsSuffix": "amazonaws.com", | ||
"partition": "aws", | ||
"partitionName": "AWS Standard", | ||
"regionRegex": "^(us|eu|ap|sa|ca)\\-\\w+\\-\\d+$", | ||
"regions": { | ||
"ap-northeast-1": { | ||
"description": "Asia Pacific (Tokyo)" | ||
} | ||
}, | ||
"services": { | ||
"acm": { | ||
"endpoints": { | ||
"ap-northeast-1": {} | ||
} | ||
}, | ||
"s3": { | ||
"endpoints": { | ||
"ap-northeast-1": {} | ||
} | ||
} | ||
} | ||
} | ||
] | ||
}` | ||
|
||
resolver, err := DecodeModel(strings.NewReader(v3Doc)) | ||
if err != nil { | ||
t.Fatalf("expected no error, got %v", err) | ||
} | ||
|
||
endpoint, err := resolver.EndpointFor("acm", "ap-northeast-1") | ||
if err != nil { | ||
t.Fatalf("failed to resolve endpoint, %v", err) | ||
} | ||
|
||
if a, e := endpoint.URL, "https://acm.ap-northeast-1.amazonaws.com"; a != e { | ||
t.Errorf("expected %q URL got %q", e, a) | ||
} | ||
|
||
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) | ||
} | ||
|
||
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 TestDecodeEndpoints_NoPartitions(t *testing.T) { | ||
const doc = `{ "version": 3 }` | ||
|
||
resolver, err := DecodeModel(strings.NewReader(doc)) | ||
if err == nil { | ||
t.Fatalf("expected error") | ||
} | ||
|
||
if resolver != nil { | ||
t.Errorf("expect resolver to be nil") | ||
} | ||
} | ||
|
||
func TestDecodeEndpoints_UnsupportedVersion(t *testing.T) { | ||
const doc = `{ "version": 2 }` | ||
|
||
resolver, err := DecodeModel(strings.NewReader(doc)) | ||
if err == nil { | ||
t.Fatalf("expected error decoding model") | ||
} | ||
|
||
if resolver != nil { | ||
t.Errorf("expect resolver to be nil") | ||
} | ||
} | ||
|
||
func TestDecodeModelOptionsSet(t *testing.T) { | ||
var actual DecodeModelOptions | ||
actual.Set(func(o *DecodeModelOptions) { | ||
o.SkipCustomizations = true | ||
}) | ||
|
||
expect := DecodeModelOptions{ | ||
SkipCustomizations: true, | ||
} | ||
|
||
if actual != expect { | ||
t.Errorf("expect %v options got %v", expect, actual) | ||
} | ||
} |
Oops, something went wrong.