github.com/aavshr/aws-sdk-go@v1.41.3/aws/endpoints/endpoints.go (about)

     1  package endpoints
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/aavshr/aws-sdk-go/aws/awserr"
     9  )
    10  
    11  // Options provide the configuration needed to direct how the
    12  // endpoints will be resolved.
    13  type Options struct {
    14  	// DisableSSL forces the endpoint to be resolved as HTTP.
    15  	// instead of HTTPS if the service supports it.
    16  	DisableSSL bool
    17  
    18  	// Sets the resolver to resolve the endpoint as a dualstack endpoint
    19  	// for the service. If dualstack support for a service is not known and
    20  	// StrictMatching is not enabled a dualstack endpoint for the service will
    21  	// be returned. This endpoint may not be valid. If StrictMatching is
    22  	// enabled only services that are known to support dualstack will return
    23  	// dualstack endpoints.
    24  	UseDualStack bool
    25  
    26  	// Enables strict matching of services and regions resolved endpoints.
    27  	// If the partition doesn't enumerate the exact service and region an
    28  	// error will be returned. This option will prevent returning endpoints
    29  	// that look valid, but may not resolve to any real endpoint.
    30  	StrictMatching bool
    31  
    32  	// Enables resolving a service endpoint based on the region provided if the
    33  	// service does not exist. The service endpoint ID will be used as the service
    34  	// domain name prefix. By default the endpoint resolver requires the service
    35  	// to be known when resolving endpoints.
    36  	//
    37  	// If resolving an endpoint on the partition list the provided region will
    38  	// be used to determine which partition's domain name pattern to the service
    39  	// endpoint ID with. If both the service and region are unknown and resolving
    40  	// the endpoint on partition list an UnknownEndpointError error will be returned.
    41  	//
    42  	// If resolving and endpoint on a partition specific resolver that partition's
    43  	// domain name pattern will be used with the service endpoint ID. If both
    44  	// region and service do not exist when resolving an endpoint on a specific
    45  	// partition the partition's domain pattern will be used to combine the
    46  	// endpoint and region together.
    47  	//
    48  	// This option is ignored if StrictMatching is enabled.
    49  	ResolveUnknownService bool
    50  
    51  	// Specifies the EC2 Instance Metadata Service default endpoint selection mode (IPv4 or IPv6)
    52  	EC2MetadataEndpointMode EC2IMDSEndpointModeState
    53  
    54  	// STS Regional Endpoint flag helps with resolving the STS endpoint
    55  	STSRegionalEndpoint STSRegionalEndpoint
    56  
    57  	// S3 Regional Endpoint flag helps with resolving the S3 endpoint
    58  	S3UsEast1RegionalEndpoint S3UsEast1RegionalEndpoint
    59  }
    60  
    61  // EC2IMDSEndpointModeState is an enum configuration variable describing the client endpoint mode.
    62  type EC2IMDSEndpointModeState uint
    63  
    64  // Enumeration values for EC2IMDSEndpointModeState
    65  const (
    66  	EC2IMDSEndpointModeStateUnset EC2IMDSEndpointModeState = iota
    67  	EC2IMDSEndpointModeStateIPv4
    68  	EC2IMDSEndpointModeStateIPv6
    69  )
    70  
    71  // SetFromString sets the EC2IMDSEndpointModeState based on the provided string value. Unknown values will default to EC2IMDSEndpointModeStateUnset
    72  func (e *EC2IMDSEndpointModeState) SetFromString(v string) error {
    73  	v = strings.TrimSpace(v)
    74  
    75  	switch {
    76  	case len(v) == 0:
    77  		*e = EC2IMDSEndpointModeStateUnset
    78  	case strings.EqualFold(v, "IPv6"):
    79  		*e = EC2IMDSEndpointModeStateIPv6
    80  	case strings.EqualFold(v, "IPv4"):
    81  		*e = EC2IMDSEndpointModeStateIPv4
    82  	default:
    83  		return fmt.Errorf("unknown EC2 IMDS endpoint mode, must be either IPv6 or IPv4")
    84  	}
    85  	return nil
    86  }
    87  
    88  // STSRegionalEndpoint is an enum for the states of the STS Regional Endpoint
    89  // options.
    90  type STSRegionalEndpoint int
    91  
    92  func (e STSRegionalEndpoint) String() string {
    93  	switch e {
    94  	case LegacySTSEndpoint:
    95  		return "legacy"
    96  	case RegionalSTSEndpoint:
    97  		return "regional"
    98  	case UnsetSTSEndpoint:
    99  		return ""
   100  	default:
   101  		return "unknown"
   102  	}
   103  }
   104  
   105  const (
   106  
   107  	// UnsetSTSEndpoint represents that STS Regional Endpoint flag is not specified.
   108  	UnsetSTSEndpoint STSRegionalEndpoint = iota
   109  
   110  	// LegacySTSEndpoint represents when STS Regional Endpoint flag is specified
   111  	// to use legacy endpoints.
   112  	LegacySTSEndpoint
   113  
   114  	// RegionalSTSEndpoint represents when STS Regional Endpoint flag is specified
   115  	// to use regional endpoints.
   116  	RegionalSTSEndpoint
   117  )
   118  
   119  // GetSTSRegionalEndpoint function returns the STSRegionalEndpointFlag based
   120  // on the input string provided in env config or shared config by the user.
   121  //
   122  // `legacy`, `regional` are the only case-insensitive valid strings for
   123  // resolving the STS regional Endpoint flag.
   124  func GetSTSRegionalEndpoint(s string) (STSRegionalEndpoint, error) {
   125  	switch {
   126  	case strings.EqualFold(s, "legacy"):
   127  		return LegacySTSEndpoint, nil
   128  	case strings.EqualFold(s, "regional"):
   129  		return RegionalSTSEndpoint, nil
   130  	default:
   131  		return UnsetSTSEndpoint, fmt.Errorf("unable to resolve the value of STSRegionalEndpoint for %v", s)
   132  	}
   133  }
   134  
   135  // S3UsEast1RegionalEndpoint is an enum for the states of the S3 us-east-1
   136  // Regional Endpoint options.
   137  type S3UsEast1RegionalEndpoint int
   138  
   139  func (e S3UsEast1RegionalEndpoint) String() string {
   140  	switch e {
   141  	case LegacyS3UsEast1Endpoint:
   142  		return "legacy"
   143  	case RegionalS3UsEast1Endpoint:
   144  		return "regional"
   145  	case UnsetS3UsEast1Endpoint:
   146  		return ""
   147  	default:
   148  		return "unknown"
   149  	}
   150  }
   151  
   152  const (
   153  
   154  	// UnsetS3UsEast1Endpoint represents that S3 Regional Endpoint flag is not
   155  	// specified.
   156  	UnsetS3UsEast1Endpoint S3UsEast1RegionalEndpoint = iota
   157  
   158  	// LegacyS3UsEast1Endpoint represents when S3 Regional Endpoint flag is
   159  	// specified to use legacy endpoints.
   160  	LegacyS3UsEast1Endpoint
   161  
   162  	// RegionalS3UsEast1Endpoint represents when S3 Regional Endpoint flag is
   163  	// specified to use regional endpoints.
   164  	RegionalS3UsEast1Endpoint
   165  )
   166  
   167  // GetS3UsEast1RegionalEndpoint function returns the S3UsEast1RegionalEndpointFlag based
   168  // on the input string provided in env config or shared config by the user.
   169  //
   170  // `legacy`, `regional` are the only case-insensitive valid strings for
   171  // resolving the S3 regional Endpoint flag.
   172  func GetS3UsEast1RegionalEndpoint(s string) (S3UsEast1RegionalEndpoint, error) {
   173  	switch {
   174  	case strings.EqualFold(s, "legacy"):
   175  		return LegacyS3UsEast1Endpoint, nil
   176  	case strings.EqualFold(s, "regional"):
   177  		return RegionalS3UsEast1Endpoint, nil
   178  	default:
   179  		return UnsetS3UsEast1Endpoint,
   180  			fmt.Errorf("unable to resolve the value of S3UsEast1RegionalEndpoint for %v", s)
   181  	}
   182  }
   183  
   184  // Set combines all of the option functions together.
   185  func (o *Options) Set(optFns ...func(*Options)) {
   186  	for _, fn := range optFns {
   187  		fn(o)
   188  	}
   189  }
   190  
   191  // DisableSSLOption sets the DisableSSL options. Can be used as a functional
   192  // option when resolving endpoints.
   193  func DisableSSLOption(o *Options) {
   194  	o.DisableSSL = true
   195  }
   196  
   197  // UseDualStackOption sets the UseDualStack option. Can be used as a functional
   198  // option when resolving endpoints.
   199  func UseDualStackOption(o *Options) {
   200  	o.UseDualStack = true
   201  }
   202  
   203  // StrictMatchingOption sets the StrictMatching option. Can be used as a functional
   204  // option when resolving endpoints.
   205  func StrictMatchingOption(o *Options) {
   206  	o.StrictMatching = true
   207  }
   208  
   209  // ResolveUnknownServiceOption sets the ResolveUnknownService option. Can be used
   210  // as a functional option when resolving endpoints.
   211  func ResolveUnknownServiceOption(o *Options) {
   212  	o.ResolveUnknownService = true
   213  }
   214  
   215  // STSRegionalEndpointOption enables the STS endpoint resolver behavior to resolve
   216  // STS endpoint to their regional endpoint, instead of the global endpoint.
   217  func STSRegionalEndpointOption(o *Options) {
   218  	o.STSRegionalEndpoint = RegionalSTSEndpoint
   219  }
   220  
   221  // A Resolver provides the interface for functionality to resolve endpoints.
   222  // The build in Partition and DefaultResolver return value satisfy this interface.
   223  type Resolver interface {
   224  	EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error)
   225  }
   226  
   227  // ResolverFunc is a helper utility that wraps a function so it satisfies the
   228  // Resolver interface. This is useful when you want to add additional endpoint
   229  // resolving logic, or stub out specific endpoints with custom values.
   230  type ResolverFunc func(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error)
   231  
   232  // EndpointFor wraps the ResolverFunc function to satisfy the Resolver interface.
   233  func (fn ResolverFunc) EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error) {
   234  	return fn(service, region, opts...)
   235  }
   236  
   237  var schemeRE = regexp.MustCompile("^([^:]+)://")
   238  
   239  // AddScheme adds the HTTP or HTTPS schemes to a endpoint URL if there is no
   240  // scheme. If disableSSL is true HTTP will set HTTP instead of the default HTTPS.
   241  //
   242  // If disableSSL is set, it will only set the URL's scheme if the URL does not
   243  // contain a scheme.
   244  func AddScheme(endpoint string, disableSSL bool) string {
   245  	if !schemeRE.MatchString(endpoint) {
   246  		scheme := "https"
   247  		if disableSSL {
   248  			scheme = "http"
   249  		}
   250  		endpoint = fmt.Sprintf("%s://%s", scheme, endpoint)
   251  	}
   252  
   253  	return endpoint
   254  }
   255  
   256  // EnumPartitions a provides a way to retrieve the underlying partitions that
   257  // make up the SDK's default Resolver, or any resolver decoded from a model
   258  // file.
   259  //
   260  // Use this interface with DefaultResolver and DecodeModels to get the list of
   261  // Partitions.
   262  type EnumPartitions interface {
   263  	Partitions() []Partition
   264  }
   265  
   266  // RegionsForService returns a map of regions for the partition and service.
   267  // If either the partition or service does not exist false will be returned
   268  // as the second parameter.
   269  //
   270  // This example shows how  to get the regions for DynamoDB in the AWS partition.
   271  //    rs, exists := endpoints.RegionsForService(endpoints.DefaultPartitions(), endpoints.AwsPartitionID, endpoints.DynamodbServiceID)
   272  //
   273  // This is equivalent to using the partition directly.
   274  //    rs := endpoints.AwsPartition().Services()[endpoints.DynamodbServiceID].Regions()
   275  func RegionsForService(ps []Partition, partitionID, serviceID string) (map[string]Region, bool) {
   276  	for _, p := range ps {
   277  		if p.ID() != partitionID {
   278  			continue
   279  		}
   280  		if _, ok := p.p.Services[serviceID]; !(ok || serviceID == Ec2metadataServiceID) {
   281  			break
   282  		}
   283  
   284  		s := Service{
   285  			id: serviceID,
   286  			p:  p.p,
   287  		}
   288  		return s.Regions(), true
   289  	}
   290  
   291  	return map[string]Region{}, false
   292  }
   293  
   294  // PartitionForRegion returns the first partition which includes the region
   295  // passed in. This includes both known regions and regions which match
   296  // a pattern supported by the partition which may include regions that are
   297  // not explicitly known by the partition. Use the Regions method of the
   298  // returned Partition if explicit support is needed.
   299  func PartitionForRegion(ps []Partition, regionID string) (Partition, bool) {
   300  	for _, p := range ps {
   301  		if _, ok := p.p.Regions[regionID]; ok || p.p.RegionRegex.MatchString(regionID) {
   302  			return p, true
   303  		}
   304  	}
   305  
   306  	return Partition{}, false
   307  }
   308  
   309  // A Partition provides the ability to enumerate the partition's regions
   310  // and services.
   311  type Partition struct {
   312  	id, dnsSuffix string
   313  	p             *partition
   314  }
   315  
   316  // DNSSuffix returns the base domain name of the partition.
   317  func (p Partition) DNSSuffix() string { return p.dnsSuffix }
   318  
   319  // ID returns the identifier of the partition.
   320  func (p Partition) ID() string { return p.id }
   321  
   322  // EndpointFor attempts to resolve the endpoint based on service and region.
   323  // See Options for information on configuring how the endpoint is resolved.
   324  //
   325  // If the service cannot be found in the metadata the UnknownServiceError
   326  // error will be returned. This validation will occur regardless if
   327  // StrictMatching is enabled. To enable resolving unknown services set the
   328  // "ResolveUnknownService" option to true. When StrictMatching is disabled
   329  // this option allows the partition resolver to resolve a endpoint based on
   330  // the service endpoint ID provided.
   331  //
   332  // When resolving endpoints you can choose to enable StrictMatching. This will
   333  // require the provided service and region to be known by the partition.
   334  // If the endpoint cannot be strictly resolved an error will be returned. This
   335  // mode is useful to ensure the endpoint resolved is valid. Without
   336  // StrictMatching enabled the endpoint returned may look valid but may not work.
   337  // StrictMatching requires the SDK to be updated if you want to take advantage
   338  // of new regions and services expansions.
   339  //
   340  // Errors that can be returned.
   341  //   * UnknownServiceError
   342  //   * UnknownEndpointError
   343  func (p Partition) EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error) {
   344  	return p.p.EndpointFor(service, region, opts...)
   345  }
   346  
   347  // Regions returns a map of Regions indexed by their ID. This is useful for
   348  // enumerating over the regions in a partition.
   349  func (p Partition) Regions() map[string]Region {
   350  	rs := make(map[string]Region, len(p.p.Regions))
   351  	for id, r := range p.p.Regions {
   352  		rs[id] = Region{
   353  			id:   id,
   354  			desc: r.Description,
   355  			p:    p.p,
   356  		}
   357  	}
   358  
   359  	return rs
   360  }
   361  
   362  // Services returns a map of Service indexed by their ID. This is useful for
   363  // enumerating over the services in a partition.
   364  func (p Partition) Services() map[string]Service {
   365  	ss := make(map[string]Service, len(p.p.Services))
   366  
   367  	for id := range p.p.Services {
   368  		ss[id] = Service{
   369  			id: id,
   370  			p:  p.p,
   371  		}
   372  	}
   373  
   374  	// Since we have removed the customization that injected this into the model
   375  	// we still need to pretend that this is a modeled service.
   376  	if _, ok := ss[Ec2metadataServiceID]; !ok {
   377  		ss[Ec2metadataServiceID] = Service{
   378  			id: Ec2metadataServiceID,
   379  			p:  p.p,
   380  		}
   381  	}
   382  
   383  	return ss
   384  }
   385  
   386  // A Region provides information about a region, and ability to resolve an
   387  // endpoint from the context of a region, given a service.
   388  type Region struct {
   389  	id, desc string
   390  	p        *partition
   391  }
   392  
   393  // ID returns the region's identifier.
   394  func (r Region) ID() string { return r.id }
   395  
   396  // Description returns the region's description. The region description
   397  // is free text, it can be empty, and it may change between SDK releases.
   398  func (r Region) Description() string { return r.desc }
   399  
   400  // ResolveEndpoint resolves an endpoint from the context of the region given
   401  // a service. See Partition.EndpointFor for usage and errors that can be returned.
   402  func (r Region) ResolveEndpoint(service string, opts ...func(*Options)) (ResolvedEndpoint, error) {
   403  	return r.p.EndpointFor(service, r.id, opts...)
   404  }
   405  
   406  // Services returns a list of all services that are known to be in this region.
   407  func (r Region) Services() map[string]Service {
   408  	ss := map[string]Service{}
   409  	for id, s := range r.p.Services {
   410  		if _, ok := s.Endpoints[r.id]; ok {
   411  			ss[id] = Service{
   412  				id: id,
   413  				p:  r.p,
   414  			}
   415  		}
   416  	}
   417  
   418  	return ss
   419  }
   420  
   421  // A Service provides information about a service, and ability to resolve an
   422  // endpoint from the context of a service, given a region.
   423  type Service struct {
   424  	id string
   425  	p  *partition
   426  }
   427  
   428  // ID returns the identifier for the service.
   429  func (s Service) ID() string { return s.id }
   430  
   431  // ResolveEndpoint resolves an endpoint from the context of a service given
   432  // a region. See Partition.EndpointFor for usage and errors that can be returned.
   433  func (s Service) ResolveEndpoint(region string, opts ...func(*Options)) (ResolvedEndpoint, error) {
   434  	return s.p.EndpointFor(s.id, region, opts...)
   435  }
   436  
   437  // Regions returns a map of Regions that the service is present in.
   438  //
   439  // A region is the AWS region the service exists in. Whereas a Endpoint is
   440  // an URL that can be resolved to a instance of a service.
   441  func (s Service) Regions() map[string]Region {
   442  	rs := map[string]Region{}
   443  
   444  	service, ok := s.p.Services[s.id]
   445  
   446  	// Since ec2metadata customization has been removed we need to check
   447  	// if it was defined in non-standard endpoints.json file. If it's not
   448  	// then we can return the empty map as there is no regional-endpoints for IMDS.
   449  	// Otherwise, we iterate need to iterate the non-standard model.
   450  	if s.id == Ec2metadataServiceID && !ok {
   451  		return rs
   452  	}
   453  
   454  	for id := range service.Endpoints {
   455  		if r, ok := s.p.Regions[id]; ok {
   456  			rs[id] = Region{
   457  				id:   id,
   458  				desc: r.Description,
   459  				p:    s.p,
   460  			}
   461  		}
   462  	}
   463  
   464  	return rs
   465  }
   466  
   467  // Endpoints returns a map of Endpoints indexed by their ID for all known
   468  // endpoints for a service.
   469  //
   470  // A region is the AWS region the service exists in. Whereas a Endpoint is
   471  // an URL that can be resolved to a instance of a service.
   472  func (s Service) Endpoints() map[string]Endpoint {
   473  	es := make(map[string]Endpoint, len(s.p.Services[s.id].Endpoints))
   474  	for id := range s.p.Services[s.id].Endpoints {
   475  		es[id] = Endpoint{
   476  			id:        id,
   477  			serviceID: s.id,
   478  			p:         s.p,
   479  		}
   480  	}
   481  
   482  	return es
   483  }
   484  
   485  // A Endpoint provides information about endpoints, and provides the ability
   486  // to resolve that endpoint for the service, and the region the endpoint
   487  // represents.
   488  type Endpoint struct {
   489  	id        string
   490  	serviceID string
   491  	p         *partition
   492  }
   493  
   494  // ID returns the identifier for an endpoint.
   495  func (e Endpoint) ID() string { return e.id }
   496  
   497  // ServiceID returns the identifier the endpoint belongs to.
   498  func (e Endpoint) ServiceID() string { return e.serviceID }
   499  
   500  // ResolveEndpoint resolves an endpoint from the context of a service and
   501  // region the endpoint represents. See Partition.EndpointFor for usage and
   502  // errors that can be returned.
   503  func (e Endpoint) ResolveEndpoint(opts ...func(*Options)) (ResolvedEndpoint, error) {
   504  	return e.p.EndpointFor(e.serviceID, e.id, opts...)
   505  }
   506  
   507  // A ResolvedEndpoint is an endpoint that has been resolved based on a partition
   508  // service, and region.
   509  type ResolvedEndpoint struct {
   510  	// The endpoint URL
   511  	URL string
   512  
   513  	// The endpoint partition
   514  	PartitionID string
   515  
   516  	// The region that should be used for signing requests.
   517  	SigningRegion string
   518  
   519  	// The service name that should be used for signing requests.
   520  	SigningName string
   521  
   522  	// States that the signing name for this endpoint was derived from metadata
   523  	// passed in, but was not explicitly modeled.
   524  	SigningNameDerived bool
   525  
   526  	// The signing method that should be used for signing requests.
   527  	SigningMethod string
   528  }
   529  
   530  // So that the Error interface type can be included as an anonymous field
   531  // in the requestError struct and not conflict with the error.Error() method.
   532  type awsError awserr.Error
   533  
   534  // A EndpointNotFoundError is returned when in StrictMatching mode, and the
   535  // endpoint for the service and region cannot be found in any of the partitions.
   536  type EndpointNotFoundError struct {
   537  	awsError
   538  	Partition string
   539  	Service   string
   540  	Region    string
   541  }
   542  
   543  // A UnknownServiceError is returned when the service does not resolve to an
   544  // endpoint. Includes a list of all known services for the partition. Returned
   545  // when a partition does not support the service.
   546  type UnknownServiceError struct {
   547  	awsError
   548  	Partition string
   549  	Service   string
   550  	Known     []string
   551  }
   552  
   553  // NewUnknownServiceError builds and returns UnknownServiceError.
   554  func NewUnknownServiceError(p, s string, known []string) UnknownServiceError {
   555  	return UnknownServiceError{
   556  		awsError: awserr.New("UnknownServiceError",
   557  			"could not resolve endpoint for unknown service", nil),
   558  		Partition: p,
   559  		Service:   s,
   560  		Known:     known,
   561  	}
   562  }
   563  
   564  // String returns the string representation of the error.
   565  func (e UnknownServiceError) Error() string {
   566  	extra := fmt.Sprintf("partition: %q, service: %q",
   567  		e.Partition, e.Service)
   568  	if len(e.Known) > 0 {
   569  		extra += fmt.Sprintf(", known: %v", e.Known)
   570  	}
   571  	return awserr.SprintError(e.Code(), e.Message(), extra, e.OrigErr())
   572  }
   573  
   574  // String returns the string representation of the error.
   575  func (e UnknownServiceError) String() string {
   576  	return e.Error()
   577  }
   578  
   579  // A UnknownEndpointError is returned when in StrictMatching mode and the
   580  // service is valid, but the region does not resolve to an endpoint. Includes
   581  // a list of all known endpoints for the service.
   582  type UnknownEndpointError struct {
   583  	awsError
   584  	Partition string
   585  	Service   string
   586  	Region    string
   587  	Known     []string
   588  }
   589  
   590  // NewUnknownEndpointError builds and returns UnknownEndpointError.
   591  func NewUnknownEndpointError(p, s, r string, known []string) UnknownEndpointError {
   592  	return UnknownEndpointError{
   593  		awsError: awserr.New("UnknownEndpointError",
   594  			"could not resolve endpoint", nil),
   595  		Partition: p,
   596  		Service:   s,
   597  		Region:    r,
   598  		Known:     known,
   599  	}
   600  }
   601  
   602  // String returns the string representation of the error.
   603  func (e UnknownEndpointError) Error() string {
   604  	extra := fmt.Sprintf("partition: %q, service: %q, region: %q",
   605  		e.Partition, e.Service, e.Region)
   606  	if len(e.Known) > 0 {
   607  		extra += fmt.Sprintf(", known: %v", e.Known)
   608  	}
   609  	return awserr.SprintError(e.Code(), e.Message(), extra, e.OrigErr())
   610  }
   611  
   612  // String returns the string representation of the error.
   613  func (e UnknownEndpointError) String() string {
   614  	return e.Error()
   615  }