Skip to content

Commit

Permalink
codegen: Add HostnameImmutable flag setting to endpoint generator
Browse files Browse the repository at this point in the history
  • Loading branch information
jasdel committed Oct 23, 2020
1 parent 2e8b706 commit 0e72901
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 20 deletions.
58 changes: 47 additions & 11 deletions aws/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,44 @@ import (
"fmt"
)

// Endpoint represents the endpoint a service client should make requests to.
// Endpoint represents the endpoint a service client should make API operation
// calls to.
//
// The SDK will automatically resolve these endpoints per API client using an
// internal endpoint resolvers. If you'd like to provide custom endpoint
// resolving behavior you can implement the EndpointResolver interface.
type Endpoint struct {
// The URL of the endpoint.
// The base URL endpoint the SDK API clients will use to make API calls to.
// The SDK will suffix URI path and query elements to this endpoint.
URL string

// The endpoint partition
// Specifies if the endpoint's hostname can be modified by the SDK's API
// client.
//
// If the hostname is mutable the SDK API clients may modify any part of
// the hostname based on the requirements of the API, (e.g. adding, or
// removing content in the hostname). Such as, Amazon S3 API client
// prefixing "bucketname" to the hostname, or changing the
// hostname service name component from "s3." to "s3-accesspoint.dualstack."
// for the dualstack endpoint of an S3 Accesspoint resource.
//
// Care should be taken when providing a custom endpoint for an API. If the
// endpoint hostname is mutable, and the client cannot modify the endpoint
// correctly, the operation call will most likely fail, or have undefined
// behavior.
//
// If hostname is immutable, the SDK API clients will not modify the
// hostname of the URL. This may cause the API client not to function
// correctly if the API requires the operation specific hostname values
// to be used by the client.
//
// This flag does not modify the API client's behavior if this endpoint
// will be used instead of Endpoint Discovery, or if the endpoint will be
// used to perform Endpoint Discovery. That behavior is configured via the
// API Client's Options.
HostnameImmutable bool

// The AWS partition the endpoint belongs to.
PartitionID string

// The service name that should be used for signing the requests to the
Expand All @@ -24,9 +56,11 @@ type Endpoint struct {
SigningMethod string
}

// EndpointNotFoundError is a sentinel error to indicate that the EndpointResolver implementation was unable
// to resolve an endpoint for the given service and region. Resolvers should use this to indicate that
// a client should fallback and attempt to use it's default resolver to resolve the endpoint.
// EndpointNotFoundError is a sentinel error to indicate that the
// EndpointResolver implementation was unable to resolve an endpoint for the
// given service and region. Resolvers should use this to indicate that an API
// client should fallback and attempt to use it's internal default resolver to
// resolve the endpoint.
type EndpointNotFoundError struct {
Err error
}
Expand All @@ -41,18 +75,20 @@ func (e *EndpointNotFoundError) Unwrap() error {
return e.Err
}

// EndpointResolver is an endpoint resolver that can be used to provide or override an endpoint for the given
// service and region. Clients will attempt to use the EndpointResolver first to resolve an endpoint if available.
// If the EndpointResolver returns an EndpointNotFoundError error, clients will fallback to attempting to resolve the endpoint
// using their default endpoint resolver.
// EndpointResolver is an endpoint resolver that can be used to provide or
// override an endpoint for the given service and region. API clients will
// attempt to use the EndpointResolver first to resolve an endpoint if
// available. If the EndpointResolver returns an EndpointNotFoundError error,
// API clients will fallback to attempting to resolve the endpoint using its
// internal default endpoint resolver.
type EndpointResolver interface {
ResolveEndpoint(service, region string) (Endpoint, error)
}

// EndpointResolverFunc wraps a function to satisfy the EndpointResolver interface.
type EndpointResolverFunc func(service, region string) (Endpoint, error)

// ResolveEndpoint calls the wrapped function and returns the results
// ResolveEndpoint calls the wrapped function and returns the results.
func (e EndpointResolverFunc) ResolveEndpoint(service, region string) (Endpoint, error) {
return e(service, region)
}
12 changes: 3 additions & 9 deletions aws/errors.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
package aws

// MissingRegionError is an error that is returned if region configuration is not found.
// MissingRegionError is an error that is returned if region configuration
// value was not found.
type MissingRegionError struct{}

func (*MissingRegionError) Error() string {
return "could not find region configuration"
}

// MissingEndpointError is an error that is returned if an endpoint cannot be resolved for a service.
type MissingEndpointError struct{}

func (*MissingEndpointError) Error() string {
return "'Endpoint' configuration is required for this service"
return "an AWS region is required, but was not found"
}
Original file line number Diff line number Diff line change
Expand Up @@ -217,38 +217,47 @@ private void generateMiddlewareResolverBody(GoStackStepMiddlewareGenerator g, Go
w.addUseImports(SmithyGoDependency.FMT);
w.addUseImports(SmithyGoDependency.NET_URL);
w.addUseImports(AwsGoDependency.AWS_MIDDLEWARE);
w.addUseImports(SmithyGoDependency.SMITHY_MIDDLEWARE);
w.addUseImports(SmithyGoDependency.SMITHY_HTTP_TRANSPORT);

w.write("req, ok := in.Request.(*smithyhttp.Request)");
w.openBlock("if !ok {", "}", () -> {
w.write("return out, metadata, fmt.Errorf(\"unknown transport type %T\", in.Request)");
});
w.write("");

w.openBlock("if m.Resolver == nil {", "}", () -> {
w.write("return out, metadata, fmt.Errorf(\"expected endpoint resolver to not be nil\")");
});
w.write("");

w.write("var endpoint $T", SymbolUtils.createValueSymbolBuilder("Endpoint", AwsGoDependency.AWS_CORE)
.build());
w.write("endpoint, err = m.Resolver.ResolveEndpoint(awsmiddleware.GetRegion(ctx), m.Options)");
w.openBlock("if err != nil {", "}", () -> {
w.write("return out, metadata, fmt.Errorf(\"failed to resolve service endpoint\")");
});
w.write("");

w.write("req.URL, err = url.Parse(endpoint.URL)");
w.openBlock("if err != nil {", "}", () -> {
w.write("return out, metadata, fmt.Errorf(\"failed to parse endpoint URL: %w\", err)");
});
w.write("");

w.openBlock("if len(awsmiddleware.GetSigningName(ctx)) == 0 {", "}", () -> {
w.write("signingName := endpoint.SigningName");
w.openBlock("if len(signingName) == 0 {", "}", () -> {
w.write("signingName = $S", serviceShape.expectTrait(ServiceTrait.class).getArnNamespace());
});
w.write("ctx = awsmiddleware.SetSigningName(ctx, signingName)");
});
w.write("");

w.write("ctx = awsmiddleware.SetSigningRegion(ctx, endpoint.SigningRegion)");
w.write("ctx = smithyhttp.SetHostnameImmutable(ctx, endpoint.HostnameImmutable)");
w.write("");

w.write("return next.HandleSerialize(ctx, in)");
}

Expand Down Expand Up @@ -387,6 +396,12 @@ private void generateInternalResolverImplementation(GoWriter writer) {
writer.write("");
writer.writeDocs("ResolveEndpoint resolves the service endpoint for the given region and options");
writeInternalResolveEndpointImplementation(writer, resolverImplSymbol, "r", () -> {
// Currently all APIs require a region to derive the endpoint for that API. If there are ever a truly
// region-less API then this should be gated at codegen.
writer.addUseImports(AwsGoDependency.AWS_CORE);
writer.write("if len(region) == 0 { return endpoint, &aws.MissingRegionError{} }");
writer.write("");

Symbol sharedOptions = SymbolUtils.createPointableSymbolBuilder("Options",
AwsGoDependency.AWS_ENDPOINTS).build();
writer.openBlock("opt := $T{", "}", sharedOptions, () -> {
Expand Down

0 comments on commit 0e72901

Please sign in to comment.