|
Packit Service |
4d2de5 |
package endpoints
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
import (
|
|
Packit Service |
4d2de5 |
"fmt"
|
|
Packit Service |
4d2de5 |
"regexp"
|
|
Packit Service |
4d2de5 |
"strconv"
|
|
Packit Service |
4d2de5 |
"strings"
|
|
Packit Service |
4d2de5 |
)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type partitions []partition
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (ps partitions) EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error) {
|
|
Packit Service |
4d2de5 |
var opt Options
|
|
Packit Service |
4d2de5 |
opt.Set(opts...)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
for i := 0; i < len(ps); i++ {
|
|
Packit Service |
4d2de5 |
if !ps[i].canResolveEndpoint(service, region, opt.StrictMatching) {
|
|
Packit Service |
4d2de5 |
continue
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return ps[i].EndpointFor(service, region, opts...)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// If loose matching fallback to first partition format to use
|
|
Packit Service |
4d2de5 |
// when resolving the endpoint.
|
|
Packit Service |
4d2de5 |
if !opt.StrictMatching && len(ps) > 0 {
|
|
Packit Service |
4d2de5 |
return ps[0].EndpointFor(service, region, opts...)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return ResolvedEndpoint{}, NewUnknownEndpointError("all partitions", service, region, []string{})
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Partitions satisfies the EnumPartitions interface and returns a list
|
|
Packit Service |
4d2de5 |
// of Partitions representing each partition represented in the SDK's
|
|
Packit Service |
4d2de5 |
// endpoints model.
|
|
Packit Service |
4d2de5 |
func (ps partitions) Partitions() []Partition {
|
|
Packit Service |
4d2de5 |
parts := make([]Partition, 0, len(ps))
|
|
Packit Service |
4d2de5 |
for i := 0; i < len(ps); i++ {
|
|
Packit Service |
4d2de5 |
parts = append(parts, ps[i].Partition())
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return parts
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type partition struct {
|
|
Packit Service |
4d2de5 |
ID string `json:"partition"`
|
|
Packit Service |
4d2de5 |
Name string `json:"partitionName"`
|
|
Packit Service |
4d2de5 |
DNSSuffix string `json:"dnsSuffix"`
|
|
Packit Service |
4d2de5 |
RegionRegex regionRegex `json:"regionRegex"`
|
|
Packit Service |
4d2de5 |
Defaults endpoint `json:"defaults"`
|
|
Packit Service |
4d2de5 |
Regions regions `json:"regions"`
|
|
Packit Service |
4d2de5 |
Services services `json:"services"`
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (p partition) Partition() Partition {
|
|
Packit Service |
4d2de5 |
return Partition{
|
|
Packit Service |
4d2de5 |
dnsSuffix: p.DNSSuffix,
|
|
Packit Service |
4d2de5 |
id: p.ID,
|
|
Packit Service |
4d2de5 |
p: &p,
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (p partition) canResolveEndpoint(service, region string, strictMatch bool) bool {
|
|
Packit Service |
4d2de5 |
s, hasService := p.Services[service]
|
|
Packit Service |
4d2de5 |
_, hasEndpoint := s.Endpoints[region]
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
if hasEndpoint && hasService {
|
|
Packit Service |
4d2de5 |
return true
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
if strictMatch {
|
|
Packit Service |
4d2de5 |
return false
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return p.RegionRegex.MatchString(region)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func allowLegacyEmptyRegion(service string) bool {
|
|
Packit Service |
4d2de5 |
legacy := map[string]struct{}{
|
|
Packit Service |
4d2de5 |
"budgets": {},
|
|
Packit Service |
4d2de5 |
"ce": {},
|
|
Packit Service |
4d2de5 |
"chime": {},
|
|
Packit Service |
4d2de5 |
"cloudfront": {},
|
|
Packit Service |
4d2de5 |
"ec2metadata": {},
|
|
Packit Service |
4d2de5 |
"iam": {},
|
|
Packit Service |
4d2de5 |
"importexport": {},
|
|
Packit Service |
4d2de5 |
"organizations": {},
|
|
Packit Service |
4d2de5 |
"route53": {},
|
|
Packit Service |
4d2de5 |
"sts": {},
|
|
Packit Service |
4d2de5 |
"support": {},
|
|
Packit Service |
4d2de5 |
"waf": {},
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
_, allowed := legacy[service]
|
|
Packit Service |
4d2de5 |
return allowed
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (p partition) EndpointFor(service, region string, opts ...func(*Options)) (resolved ResolvedEndpoint, err error) {
|
|
Packit Service |
4d2de5 |
var opt Options
|
|
Packit Service |
4d2de5 |
opt.Set(opts...)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
s, hasService := p.Services[service]
|
|
Packit Service |
4d2de5 |
if len(service) == 0 || !(hasService || opt.ResolveUnknownService) {
|
|
Packit Service |
4d2de5 |
// Only return error if the resolver will not fallback to creating
|
|
Packit Service |
4d2de5 |
// endpoint based on service endpoint ID passed in.
|
|
Packit Service |
4d2de5 |
return resolved, NewUnknownServiceError(p.ID, service, serviceList(p.Services))
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
if len(region) == 0 && allowLegacyEmptyRegion(service) && len(s.PartitionEndpoint) != 0 {
|
|
Packit Service |
4d2de5 |
region = s.PartitionEndpoint
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
if (service == "sts" && opt.STSRegionalEndpoint != RegionalSTSEndpoint) ||
|
|
Packit Service |
4d2de5 |
(service == "s3" && opt.S3UsEast1RegionalEndpoint != RegionalS3UsEast1Endpoint) {
|
|
Packit Service |
4d2de5 |
if _, ok := legacyGlobalRegions[service][region]; ok {
|
|
Packit Service |
4d2de5 |
region = "aws-global"
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
e, hasEndpoint := s.endpointForRegion(region)
|
|
Packit Service |
4d2de5 |
if len(region) == 0 || (!hasEndpoint && opt.StrictMatching) {
|
|
Packit Service |
4d2de5 |
return resolved, NewUnknownEndpointError(p.ID, service, region, endpointList(s.Endpoints))
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
defs := []endpoint{p.Defaults, s.Defaults}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return e.resolve(service, p.ID, region, p.DNSSuffix, defs, opt), nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func serviceList(ss services) []string {
|
|
Packit Service |
4d2de5 |
list := make([]string, 0, len(ss))
|
|
Packit Service |
4d2de5 |
for k := range ss {
|
|
Packit Service |
4d2de5 |
list = append(list, k)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return list
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
func endpointList(es endpoints) []string {
|
|
Packit Service |
4d2de5 |
list := make([]string, 0, len(es))
|
|
Packit Service |
4d2de5 |
for k := range es {
|
|
Packit Service |
4d2de5 |
list = append(list, k)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return list
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type regionRegex struct {
|
|
Packit Service |
4d2de5 |
*regexp.Regexp
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (rr *regionRegex) UnmarshalJSON(b []byte) (err error) {
|
|
Packit Service |
4d2de5 |
// Strip leading and trailing quotes
|
|
Packit Service |
4d2de5 |
regex, err := strconv.Unquote(string(b))
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return fmt.Errorf("unable to strip quotes from regex, %v", err)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
rr.Regexp, err = regexp.Compile(regex)
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return fmt.Errorf("unable to unmarshal region regex, %v", err)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type regions map[string]region
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type region struct {
|
|
Packit Service |
4d2de5 |
Description string `json:"description"`
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type services map[string]service
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type service struct {
|
|
Packit Service |
4d2de5 |
PartitionEndpoint string `json:"partitionEndpoint"`
|
|
Packit Service |
4d2de5 |
IsRegionalized boxedBool `json:"isRegionalized,omitempty"`
|
|
Packit Service |
4d2de5 |
Defaults endpoint `json:"defaults"`
|
|
Packit Service |
4d2de5 |
Endpoints endpoints `json:"endpoints"`
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (s *service) endpointForRegion(region string) (endpoint, bool) {
|
|
Packit Service |
4d2de5 |
if s.IsRegionalized == boxedFalse {
|
|
Packit Service |
4d2de5 |
return s.Endpoints[s.PartitionEndpoint], region == s.PartitionEndpoint
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
if e, ok := s.Endpoints[region]; ok {
|
|
Packit Service |
4d2de5 |
return e, true
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Unable to find any matching endpoint, return
|
|
Packit Service |
4d2de5 |
// blank that will be used for generic endpoint creation.
|
|
Packit Service |
4d2de5 |
return endpoint{}, false
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type endpoints map[string]endpoint
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type endpoint struct {
|
|
Packit Service |
4d2de5 |
Hostname string `json:"hostname"`
|
|
Packit Service |
4d2de5 |
Protocols []string `json:"protocols"`
|
|
Packit Service |
4d2de5 |
CredentialScope credentialScope `json:"credentialScope"`
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Custom fields not modeled
|
|
Packit Service |
4d2de5 |
HasDualStack boxedBool `json:"-"`
|
|
Packit Service |
4d2de5 |
DualStackHostname string `json:"-"`
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Signature Version not used
|
|
Packit Service |
4d2de5 |
SignatureVersions []string `json:"signatureVersions"`
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// SSLCommonName not used.
|
|
Packit Service |
4d2de5 |
SSLCommonName string `json:"sslCommonName"`
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
const (
|
|
Packit Service |
4d2de5 |
defaultProtocol = "https"
|
|
Packit Service |
4d2de5 |
defaultSigner = "v4"
|
|
Packit Service |
4d2de5 |
)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
var (
|
|
Packit Service |
4d2de5 |
protocolPriority = []string{"https", "http"}
|
|
Packit Service |
4d2de5 |
signerPriority = []string{"v4", "v2"}
|
|
Packit Service |
4d2de5 |
)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func getByPriority(s []string, p []string, def string) string {
|
|
Packit Service |
4d2de5 |
if len(s) == 0 {
|
|
Packit Service |
4d2de5 |
return def
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
for i := 0; i < len(p); i++ {
|
|
Packit Service |
4d2de5 |
for j := 0; j < len(s); j++ {
|
|
Packit Service |
4d2de5 |
if s[j] == p[i] {
|
|
Packit Service |
4d2de5 |
return s[j]
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return s[0]
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs []endpoint, opts Options) ResolvedEndpoint {
|
|
Packit Service |
4d2de5 |
var merged endpoint
|
|
Packit Service |
4d2de5 |
for _, def := range defs {
|
|
Packit Service |
4d2de5 |
merged.mergeIn(def)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
merged.mergeIn(e)
|
|
Packit Service |
4d2de5 |
e = merged
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
signingRegion := e.CredentialScope.Region
|
|
Packit Service |
4d2de5 |
if len(signingRegion) == 0 {
|
|
Packit Service |
4d2de5 |
signingRegion = region
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
signingName := e.CredentialScope.Service
|
|
Packit Service |
4d2de5 |
var signingNameDerived bool
|
|
Packit Service |
4d2de5 |
if len(signingName) == 0 {
|
|
Packit Service |
4d2de5 |
signingName = service
|
|
Packit Service |
4d2de5 |
signingNameDerived = true
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
hostname := e.Hostname
|
|
Packit Service |
4d2de5 |
// Offset the hostname for dualstack if enabled
|
|
Packit Service |
4d2de5 |
if opts.UseDualStack && e.HasDualStack == boxedTrue {
|
|
Packit Service |
4d2de5 |
hostname = e.DualStackHostname
|
|
Packit Service |
4d2de5 |
region = signingRegion
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
u := strings.Replace(hostname, "{service}", service, 1)
|
|
Packit Service |
4d2de5 |
u = strings.Replace(u, "{region}", region, 1)
|
|
Packit Service |
4d2de5 |
u = strings.Replace(u, "{dnsSuffix}", dnsSuffix, 1)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
scheme := getEndpointScheme(e.Protocols, opts.DisableSSL)
|
|
Packit Service |
4d2de5 |
u = fmt.Sprintf("%s://%s", scheme, u)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return ResolvedEndpoint{
|
|
Packit Service |
4d2de5 |
URL: u,
|
|
Packit Service |
4d2de5 |
PartitionID: partitionID,
|
|
Packit Service |
4d2de5 |
SigningRegion: signingRegion,
|
|
Packit Service |
4d2de5 |
SigningName: signingName,
|
|
Packit Service |
4d2de5 |
SigningNameDerived: signingNameDerived,
|
|
Packit Service |
4d2de5 |
SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner),
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func getEndpointScheme(protocols []string, disableSSL bool) string {
|
|
Packit Service |
4d2de5 |
if disableSSL {
|
|
Packit Service |
4d2de5 |
return "http"
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return getByPriority(protocols, protocolPriority, defaultProtocol)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (e *endpoint) mergeIn(other endpoint) {
|
|
Packit Service |
4d2de5 |
if len(other.Hostname) > 0 {
|
|
Packit Service |
4d2de5 |
e.Hostname = other.Hostname
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
if len(other.Protocols) > 0 {
|
|
Packit Service |
4d2de5 |
e.Protocols = other.Protocols
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
if len(other.SignatureVersions) > 0 {
|
|
Packit Service |
4d2de5 |
e.SignatureVersions = other.SignatureVersions
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
if len(other.CredentialScope.Region) > 0 {
|
|
Packit Service |
4d2de5 |
e.CredentialScope.Region = other.CredentialScope.Region
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
if len(other.CredentialScope.Service) > 0 {
|
|
Packit Service |
4d2de5 |
e.CredentialScope.Service = other.CredentialScope.Service
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
if len(other.SSLCommonName) > 0 {
|
|
Packit Service |
4d2de5 |
e.SSLCommonName = other.SSLCommonName
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
if other.HasDualStack != boxedBoolUnset {
|
|
Packit Service |
4d2de5 |
e.HasDualStack = other.HasDualStack
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
if len(other.DualStackHostname) > 0 {
|
|
Packit Service |
4d2de5 |
e.DualStackHostname = other.DualStackHostname
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type credentialScope struct {
|
|
Packit Service |
4d2de5 |
Region string `json:"region"`
|
|
Packit Service |
4d2de5 |
Service string `json:"service"`
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type boxedBool int
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (b *boxedBool) UnmarshalJSON(buf []byte) error {
|
|
Packit Service |
4d2de5 |
v, err := strconv.ParseBool(string(buf))
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
if v {
|
|
Packit Service |
4d2de5 |
*b = boxedTrue
|
|
Packit Service |
4d2de5 |
} else {
|
|
Packit Service |
4d2de5 |
*b = boxedFalse
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
const (
|
|
Packit Service |
4d2de5 |
boxedBoolUnset boxedBool = iota
|
|
Packit Service |
4d2de5 |
boxedFalse
|
|
Packit Service |
4d2de5 |
boxedTrue
|
|
Packit Service |
4d2de5 |
)
|