|
@@ -8,10 +8,18 @@ import (
|
|
|
"fmt"
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
|
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
|
|
+ internalConfig "github.com/aws/aws-sdk-go-v2/internal/configsources"
|
|
|
+ "github.com/aws/aws-sdk-go-v2/internal/endpoints"
|
|
|
+ "github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn"
|
|
|
internalendpoints "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/internal/endpoints"
|
|
|
+ smithyauth "github.com/aws/smithy-go/auth"
|
|
|
+ smithyendpoints "github.com/aws/smithy-go/endpoints"
|
|
|
"github.com/aws/smithy-go/middleware"
|
|
|
+ "github.com/aws/smithy-go/ptr"
|
|
|
smithyhttp "github.com/aws/smithy-go/transport/http"
|
|
|
+ "net/http"
|
|
|
"net/url"
|
|
|
+ "os"
|
|
|
"strings"
|
|
|
)
|
|
|
|
|
@@ -39,13 +47,6 @@ func (fn EndpointResolverFunc) ResolveEndpoint(region string, options EndpointRe
|
|
|
return fn(region, options)
|
|
|
}
|
|
|
|
|
|
-func resolveDefaultEndpointConfiguration(o *Options) {
|
|
|
- if o.EndpointResolver != nil {
|
|
|
- return
|
|
|
- }
|
|
|
- o.EndpointResolver = NewDefaultEndpointResolver()
|
|
|
-}
|
|
|
-
|
|
|
// EndpointResolverFromURL returns an EndpointResolver configured using the
|
|
|
// provided endpoint url. By default, the resolved endpoint resolver uses the
|
|
|
// client region as signing region, and the endpoint source is set to
|
|
@@ -79,6 +80,10 @@ func (*ResolveEndpoint) ID() string {
|
|
|
func (m *ResolveEndpoint) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) (
|
|
|
out middleware.SerializeOutput, metadata middleware.Metadata, err error,
|
|
|
) {
|
|
|
+ if !awsmiddleware.GetRequiresLegacyEndpoints(ctx) {
|
|
|
+ return next.HandleSerialize(ctx, in)
|
|
|
+ }
|
|
|
+
|
|
|
req, ok := in.Request.(*smithyhttp.Request)
|
|
|
if !ok {
|
|
|
return out, metadata, fmt.Errorf("unknown transport type %T", in.Request)
|
|
@@ -94,6 +99,11 @@ func (m *ResolveEndpoint) HandleSerialize(ctx context.Context, in middleware.Ser
|
|
|
var endpoint aws.Endpoint
|
|
|
endpoint, err = m.Resolver.ResolveEndpoint(awsmiddleware.GetRegion(ctx), eo)
|
|
|
if err != nil {
|
|
|
+ nf := (&aws.EndpointNotFoundError{})
|
|
|
+ if errors.As(err, &nf) {
|
|
|
+ ctx = awsmiddleware.SetRequiresLegacyEndpoints(ctx, false)
|
|
|
+ return next.HandleSerialize(ctx, in)
|
|
|
+ }
|
|
|
return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err)
|
|
|
}
|
|
|
|
|
@@ -129,27 +139,10 @@ func removeResolveEndpointMiddleware(stack *middleware.Stack) error {
|
|
|
|
|
|
type wrappedEndpointResolver struct {
|
|
|
awsResolver aws.EndpointResolverWithOptions
|
|
|
- resolver EndpointResolver
|
|
|
}
|
|
|
|
|
|
func (w *wrappedEndpointResolver) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) {
|
|
|
- if w.awsResolver == nil {
|
|
|
- goto fallback
|
|
|
- }
|
|
|
- endpoint, err = w.awsResolver.ResolveEndpoint(ServiceID, region, options)
|
|
|
- if err == nil {
|
|
|
- return endpoint, nil
|
|
|
- }
|
|
|
-
|
|
|
- if nf := (&aws.EndpointNotFoundError{}); !errors.As(err, &nf) {
|
|
|
- return endpoint, err
|
|
|
- }
|
|
|
-
|
|
|
-fallback:
|
|
|
- if w.resolver == nil {
|
|
|
- return endpoint, fmt.Errorf("default endpoint resolver provided was nil")
|
|
|
- }
|
|
|
- return w.resolver.ResolveEndpoint(region, options)
|
|
|
+ return w.awsResolver.ResolveEndpoint(ServiceID, region, options)
|
|
|
}
|
|
|
|
|
|
type awsEndpointResolverAdaptor func(service, region string) (aws.Endpoint, error)
|
|
@@ -160,12 +153,13 @@ func (a awsEndpointResolverAdaptor) ResolveEndpoint(service, region string, opti
|
|
|
|
|
|
var _ aws.EndpointResolverWithOptions = awsEndpointResolverAdaptor(nil)
|
|
|
|
|
|
-// withEndpointResolver returns an EndpointResolver that first delegates endpoint resolution to the awsResolver.
|
|
|
-// If awsResolver returns aws.EndpointNotFoundError error, the resolver will use the the provided
|
|
|
-// fallbackResolver for resolution.
|
|
|
+// withEndpointResolver returns an aws.EndpointResolverWithOptions that first delegates endpoint resolution to the awsResolver.
|
|
|
+// If awsResolver returns aws.EndpointNotFoundError error, the v1 resolver middleware will swallow the error,
|
|
|
+// and set an appropriate context flag such that fallback will occur when EndpointResolverV2 is invoked
|
|
|
+// via its middleware.
|
|
|
//
|
|
|
-// fallbackResolver must not be nil
|
|
|
-func withEndpointResolver(awsResolver aws.EndpointResolver, awsResolverWithOptions aws.EndpointResolverWithOptions, fallbackResolver EndpointResolver) EndpointResolver {
|
|
|
+// If another error (besides aws.EndpointNotFoundError) is returned, then that error will be propagated.
|
|
|
+func withEndpointResolver(awsResolver aws.EndpointResolver, awsResolverWithOptions aws.EndpointResolverWithOptions) EndpointResolver {
|
|
|
var resolver aws.EndpointResolverWithOptions
|
|
|
|
|
|
if awsResolverWithOptions != nil {
|
|
@@ -176,7 +170,6 @@ func withEndpointResolver(awsResolver aws.EndpointResolver, awsResolverWithOptio
|
|
|
|
|
|
return &wrappedEndpointResolver{
|
|
|
awsResolver: resolver,
|
|
|
- resolver: fallbackResolver,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -198,3 +191,345 @@ func finalizeClientEndpointResolverOptions(options *Options) {
|
|
|
}
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+func resolveEndpointResolverV2(options *Options) {
|
|
|
+ if options.EndpointResolverV2 == nil {
|
|
|
+ options.EndpointResolverV2 = NewDefaultEndpointResolverV2()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func resolveBaseEndpoint(cfg aws.Config, o *Options) {
|
|
|
+ if cfg.BaseEndpoint != nil {
|
|
|
+ o.BaseEndpoint = cfg.BaseEndpoint
|
|
|
+ }
|
|
|
+
|
|
|
+ _, g := os.LookupEnv("AWS_ENDPOINT_URL")
|
|
|
+ _, s := os.LookupEnv("AWS_ENDPOINT_URL_CLOUDWATCH_LOGS")
|
|
|
+
|
|
|
+ if g && !s {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ value, found, err := internalConfig.ResolveServiceBaseEndpoint(context.Background(), "CloudWatch Logs", cfg.ConfigSources)
|
|
|
+ if found && err == nil {
|
|
|
+ o.BaseEndpoint = &value
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// EndpointParameters provides the parameters that influence how endpoints are
|
|
|
+// resolved.
|
|
|
+type EndpointParameters struct {
|
|
|
+ // The AWS region used to dispatch the request.
|
|
|
+ //
|
|
|
+ // Parameter is
|
|
|
+ // required.
|
|
|
+ //
|
|
|
+ // AWS::Region
|
|
|
+ Region *string
|
|
|
+
|
|
|
+ // When true, use the dual-stack endpoint. If the configured endpoint does not
|
|
|
+ // support dual-stack, dispatching the request MAY return an error.
|
|
|
+ //
|
|
|
+ // Defaults to
|
|
|
+ // false if no value is provided.
|
|
|
+ //
|
|
|
+ // AWS::UseDualStack
|
|
|
+ UseDualStack *bool
|
|
|
+
|
|
|
+ // When true, send this request to the FIPS-compliant regional endpoint. If the
|
|
|
+ // configured endpoint does not have a FIPS compliant endpoint, dispatching the
|
|
|
+ // request will return an error.
|
|
|
+ //
|
|
|
+ // Defaults to false if no value is
|
|
|
+ // provided.
|
|
|
+ //
|
|
|
+ // AWS::UseFIPS
|
|
|
+ UseFIPS *bool
|
|
|
+
|
|
|
+ // Override the endpoint used to send this request
|
|
|
+ //
|
|
|
+ // Parameter is
|
|
|
+ // required.
|
|
|
+ //
|
|
|
+ // SDK::Endpoint
|
|
|
+ Endpoint *string
|
|
|
+}
|
|
|
+
|
|
|
+// ValidateRequired validates required parameters are set.
|
|
|
+func (p EndpointParameters) ValidateRequired() error {
|
|
|
+ if p.UseDualStack == nil {
|
|
|
+ return fmt.Errorf("parameter UseDualStack is required")
|
|
|
+ }
|
|
|
+
|
|
|
+ if p.UseFIPS == nil {
|
|
|
+ return fmt.Errorf("parameter UseFIPS is required")
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// WithDefaults returns a shallow copy of EndpointParameterswith default values
|
|
|
+// applied to members where applicable.
|
|
|
+func (p EndpointParameters) WithDefaults() EndpointParameters {
|
|
|
+ if p.UseDualStack == nil {
|
|
|
+ p.UseDualStack = ptr.Bool(false)
|
|
|
+ }
|
|
|
+
|
|
|
+ if p.UseFIPS == nil {
|
|
|
+ p.UseFIPS = ptr.Bool(false)
|
|
|
+ }
|
|
|
+ return p
|
|
|
+}
|
|
|
+
|
|
|
+// EndpointResolverV2 provides the interface for resolving service endpoints.
|
|
|
+type EndpointResolverV2 interface {
|
|
|
+ // ResolveEndpoint attempts to resolve the endpoint with the provided options,
|
|
|
+ // returning the endpoint if found. Otherwise an error is returned.
|
|
|
+ ResolveEndpoint(ctx context.Context, params EndpointParameters) (
|
|
|
+ smithyendpoints.Endpoint, error,
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+// resolver provides the implementation for resolving endpoints.
|
|
|
+type resolver struct{}
|
|
|
+
|
|
|
+func NewDefaultEndpointResolverV2() EndpointResolverV2 {
|
|
|
+ return &resolver{}
|
|
|
+}
|
|
|
+
|
|
|
+// ResolveEndpoint attempts to resolve the endpoint with the provided options,
|
|
|
+// returning the endpoint if found. Otherwise an error is returned.
|
|
|
+func (r *resolver) ResolveEndpoint(
|
|
|
+ ctx context.Context, params EndpointParameters,
|
|
|
+) (
|
|
|
+ endpoint smithyendpoints.Endpoint, err error,
|
|
|
+) {
|
|
|
+ params = params.WithDefaults()
|
|
|
+ if err = params.ValidateRequired(); err != nil {
|
|
|
+ return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err)
|
|
|
+ }
|
|
|
+ _UseDualStack := *params.UseDualStack
|
|
|
+ _UseFIPS := *params.UseFIPS
|
|
|
+
|
|
|
+ if exprVal := params.Endpoint; exprVal != nil {
|
|
|
+ _Endpoint := *exprVal
|
|
|
+ _ = _Endpoint
|
|
|
+ if _UseFIPS == true {
|
|
|
+ return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported")
|
|
|
+ }
|
|
|
+ if _UseDualStack == true {
|
|
|
+ return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported")
|
|
|
+ }
|
|
|
+ uriString := _Endpoint
|
|
|
+
|
|
|
+ uri, err := url.Parse(uriString)
|
|
|
+ if err != nil {
|
|
|
+ return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
|
|
|
+ }
|
|
|
+
|
|
|
+ return smithyendpoints.Endpoint{
|
|
|
+ URI: *uri,
|
|
|
+ Headers: http.Header{},
|
|
|
+ }, nil
|
|
|
+ }
|
|
|
+ if exprVal := params.Region; exprVal != nil {
|
|
|
+ _Region := *exprVal
|
|
|
+ _ = _Region
|
|
|
+ if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil {
|
|
|
+ _PartitionResult := *exprVal
|
|
|
+ _ = _PartitionResult
|
|
|
+ if _UseFIPS == true {
|
|
|
+ if _UseDualStack == true {
|
|
|
+ if true == _PartitionResult.SupportsFIPS {
|
|
|
+ if true == _PartitionResult.SupportsDualStack {
|
|
|
+ uriString := func() string {
|
|
|
+ var out strings.Builder
|
|
|
+ out.WriteString("https://logs-fips.")
|
|
|
+ out.WriteString(_Region)
|
|
|
+ out.WriteString(".")
|
|
|
+ out.WriteString(_PartitionResult.DualStackDnsSuffix)
|
|
|
+ return out.String()
|
|
|
+ }()
|
|
|
+
|
|
|
+ uri, err := url.Parse(uriString)
|
|
|
+ if err != nil {
|
|
|
+ return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
|
|
|
+ }
|
|
|
+
|
|
|
+ return smithyendpoints.Endpoint{
|
|
|
+ URI: *uri,
|
|
|
+ Headers: http.Header{},
|
|
|
+ }, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if _UseFIPS == true {
|
|
|
+ if _PartitionResult.SupportsFIPS == true {
|
|
|
+ if _Region == "us-gov-east-1" {
|
|
|
+ uriString := "https://logs.us-gov-east-1.amazonaws.com"
|
|
|
+
|
|
|
+ uri, err := url.Parse(uriString)
|
|
|
+ if err != nil {
|
|
|
+ return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
|
|
|
+ }
|
|
|
+
|
|
|
+ return smithyendpoints.Endpoint{
|
|
|
+ URI: *uri,
|
|
|
+ Headers: http.Header{},
|
|
|
+ }, nil
|
|
|
+ }
|
|
|
+ if _Region == "us-gov-west-1" {
|
|
|
+ uriString := "https://logs.us-gov-west-1.amazonaws.com"
|
|
|
+
|
|
|
+ uri, err := url.Parse(uriString)
|
|
|
+ if err != nil {
|
|
|
+ return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
|
|
|
+ }
|
|
|
+
|
|
|
+ return smithyendpoints.Endpoint{
|
|
|
+ URI: *uri,
|
|
|
+ Headers: http.Header{},
|
|
|
+ }, nil
|
|
|
+ }
|
|
|
+ uriString := func() string {
|
|
|
+ var out strings.Builder
|
|
|
+ out.WriteString("https://logs-fips.")
|
|
|
+ out.WriteString(_Region)
|
|
|
+ out.WriteString(".")
|
|
|
+ out.WriteString(_PartitionResult.DnsSuffix)
|
|
|
+ return out.String()
|
|
|
+ }()
|
|
|
+
|
|
|
+ uri, err := url.Parse(uriString)
|
|
|
+ if err != nil {
|
|
|
+ return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
|
|
|
+ }
|
|
|
+
|
|
|
+ return smithyendpoints.Endpoint{
|
|
|
+ URI: *uri,
|
|
|
+ Headers: http.Header{},
|
|
|
+ }, nil
|
|
|
+ }
|
|
|
+ return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS")
|
|
|
+ }
|
|
|
+ if _UseDualStack == true {
|
|
|
+ if true == _PartitionResult.SupportsDualStack {
|
|
|
+ uriString := func() string {
|
|
|
+ var out strings.Builder
|
|
|
+ out.WriteString("https://logs.")
|
|
|
+ out.WriteString(_Region)
|
|
|
+ out.WriteString(".")
|
|
|
+ out.WriteString(_PartitionResult.DualStackDnsSuffix)
|
|
|
+ return out.String()
|
|
|
+ }()
|
|
|
+
|
|
|
+ uri, err := url.Parse(uriString)
|
|
|
+ if err != nil {
|
|
|
+ return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
|
|
|
+ }
|
|
|
+
|
|
|
+ return smithyendpoints.Endpoint{
|
|
|
+ URI: *uri,
|
|
|
+ Headers: http.Header{},
|
|
|
+ }, nil
|
|
|
+ }
|
|
|
+ return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack")
|
|
|
+ }
|
|
|
+ uriString := func() string {
|
|
|
+ var out strings.Builder
|
|
|
+ out.WriteString("https://logs.")
|
|
|
+ out.WriteString(_Region)
|
|
|
+ out.WriteString(".")
|
|
|
+ out.WriteString(_PartitionResult.DnsSuffix)
|
|
|
+ return out.String()
|
|
|
+ }()
|
|
|
+
|
|
|
+ uri, err := url.Parse(uriString)
|
|
|
+ if err != nil {
|
|
|
+ return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString)
|
|
|
+ }
|
|
|
+
|
|
|
+ return smithyendpoints.Endpoint{
|
|
|
+ URI: *uri,
|
|
|
+ Headers: http.Header{},
|
|
|
+ }, nil
|
|
|
+ }
|
|
|
+ return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.")
|
|
|
+ }
|
|
|
+ return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region")
|
|
|
+}
|
|
|
+
|
|
|
+type endpointParamsBinder interface {
|
|
|
+ bindEndpointParams(*EndpointParameters)
|
|
|
+}
|
|
|
+
|
|
|
+func bindEndpointParams(input interface{}, options Options) *EndpointParameters {
|
|
|
+ params := &EndpointParameters{}
|
|
|
+
|
|
|
+ params.Region = aws.String(endpoints.MapFIPSRegion(options.Region))
|
|
|
+ params.UseDualStack = aws.Bool(options.EndpointOptions.UseDualStackEndpoint == aws.DualStackEndpointStateEnabled)
|
|
|
+ params.UseFIPS = aws.Bool(options.EndpointOptions.UseFIPSEndpoint == aws.FIPSEndpointStateEnabled)
|
|
|
+ params.Endpoint = options.BaseEndpoint
|
|
|
+
|
|
|
+ if b, ok := input.(endpointParamsBinder); ok {
|
|
|
+ b.bindEndpointParams(params)
|
|
|
+ }
|
|
|
+
|
|
|
+ return params
|
|
|
+}
|
|
|
+
|
|
|
+type resolveEndpointV2Middleware struct {
|
|
|
+ options Options
|
|
|
+}
|
|
|
+
|
|
|
+func (*resolveEndpointV2Middleware) ID() string {
|
|
|
+ return "ResolveEndpointV2"
|
|
|
+}
|
|
|
+
|
|
|
+func (m *resolveEndpointV2Middleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (
|
|
|
+ out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
|
|
+) {
|
|
|
+ if awsmiddleware.GetRequiresLegacyEndpoints(ctx) {
|
|
|
+ return next.HandleFinalize(ctx, in)
|
|
|
+ }
|
|
|
+
|
|
|
+ req, ok := in.Request.(*smithyhttp.Request)
|
|
|
+ if !ok {
|
|
|
+ return out, metadata, fmt.Errorf("unknown transport type %T", in.Request)
|
|
|
+ }
|
|
|
+
|
|
|
+ if m.options.EndpointResolverV2 == nil {
|
|
|
+ return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil")
|
|
|
+ }
|
|
|
+
|
|
|
+ params := bindEndpointParams(getOperationInput(ctx), m.options)
|
|
|
+ endpt, err := m.options.EndpointResolverV2.ResolveEndpoint(ctx, *params)
|
|
|
+ if err != nil {
|
|
|
+ return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if endpt.URI.RawPath == "" && req.URL.RawPath != "" {
|
|
|
+ endpt.URI.RawPath = endpt.URI.Path
|
|
|
+ }
|
|
|
+ req.URL.Scheme = endpt.URI.Scheme
|
|
|
+ req.URL.Host = endpt.URI.Host
|
|
|
+ req.URL.Path = smithyhttp.JoinPath(endpt.URI.Path, req.URL.Path)
|
|
|
+ req.URL.RawPath = smithyhttp.JoinPath(endpt.URI.RawPath, req.URL.RawPath)
|
|
|
+ for k := range endpt.Headers {
|
|
|
+ req.Header.Set(k, endpt.Headers.Get(k))
|
|
|
+ }
|
|
|
+
|
|
|
+ rscheme := getResolvedAuthScheme(ctx)
|
|
|
+ if rscheme == nil {
|
|
|
+ return out, metadata, fmt.Errorf("no resolved auth scheme")
|
|
|
+ }
|
|
|
+
|
|
|
+ opts, _ := smithyauth.GetAuthOptions(&endpt.Properties)
|
|
|
+ for _, o := range opts {
|
|
|
+ rscheme.SignerProperties.SetAll(&o.SignerProperties)
|
|
|
+ }
|
|
|
+
|
|
|
+ return next.HandleFinalize(ctx, in)
|
|
|
+}
|