-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Account ID-based endpoints #1841
base: main
Are you sure you want to change the base?
Conversation
IntegrationTests/Package.swift
Outdated
@@ -146,6 +146,7 @@ func addIntegrationTestTarget(_ name: String) { | |||
|
|||
let servicesWithIntegrationTests: [String] = [ | |||
"AWSCloudFrontKeyValueStore", | |||
"AWSDynamoDB", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DynamoDB is used to verify that the client handles Account ID and endpoint mode correctly.
import enum AWSClientRuntime.AccountIDEndpointMode | ||
import enum ClientRuntime.EndpointError | ||
|
||
final class AccountIDEndpointModeTests: XCTestCase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an integration test (although it doesn't actually hit the service.)
Account ID & endpoint mode are passed into the service client, while a dummy HTTP client (the same one used for protocol tests) is used to inspect the resulting HTTP request.
@@ -17,43 +17,43 @@ | |||
{ | |||
"target" : { | |||
"containerPath" : "container:", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of churn in this file, but the only material change is that the DynamoDB test bundle is added to the test plan.
Package.swift
Outdated
@@ -16,7 +16,7 @@ import PackageDescription | |||
// MARK: - Dynamic Content | |||
|
|||
let clientRuntimeVersion: Version = "0.101.0" | |||
let crtVersion: Version = "0.40.0" | |||
let crtVersion: Version = "0.41.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New CRT (with support for Account ID in credentials) is added.
@@ -135,4 +135,19 @@ public class AWSClientConfigDefaultsProvider { | |||
rateLimitingMode: resolvedRateLimitingMode | |||
) | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function below is used to resolve a default value for account ID endpoint mode, if needed.
@_spi(FileBasedConfig) import AWSSDKCommon | ||
|
||
public enum AWSEndpointConfig { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function below attempts to resolve account ID endpoint mode from env var & config file, and if no luck defaults to .preferred
mode.
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The enum type below is exposed on the service client config as the "accountIDEndpointMode" config option instead of a raw string.
import struct Smithy.AttributeKey | ||
import class Smithy.Context | ||
import class Smithy.ContextBuilder | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Utilities to get/set accountIDEndpointMode
on the context.
@@ -18,35 +18,37 @@ import enum ClientRuntime.EndpointsAuthScheme | |||
import protocol ClientRuntime.EndpointsAuthSchemeResolver | |||
import protocol ClientRuntime.EndpointsRequestContextProviding | |||
|
|||
public struct EndpointResolverMiddleware<OperationStackOutput, Params: EndpointsRequestContextProviding> { | |||
@_spi(AWSEndpointResolverMiddleware) | |||
public struct AWSEndpointResolverMiddleware<OperationStackOutput, Params: EndpointsRequestContextProviding> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This middleware is changed to take a endpoint params block, since account ID cannot be resolved until after auth scheme has been resolved & credentials are sourced (and account ID along with it.)
// Handle N | ||
if let endpoint = config.endpoint, !endpoint.isEmpty { | ||
context.businessMetrics = ["ENDPOINT_OVERRIDE": "N"] | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Below, several features are added to business metrics.
context: context | ||
) | ||
|
||
let userAgent = testUserAgent() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
creating userAgent
for this test is extracted to a helper method below
import software.amazon.smithy.swift.codegen.integration.ServiceConfig | ||
import software.amazon.smithy.swift.codegen.middleware.MiddlewareExecutionGenerator | ||
import software.amazon.smithy.swift.codegen.middleware.OperationMiddleware | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a custom subclass of the smithy-swift generic HTTP protocol client generator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its sole customization is to provide a custom middleware execution generator.
writer.write( | ||
"return \"\\(\$L.clientName) - \\(region ?? \"\")\"", | ||
serviceConfig.clientName.toUpperCamelCase(), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup
} | ||
writer.write("") | ||
} | ||
|
||
private val authSchemeResolverDefaultProvider = DefaultProvider( | ||
{ "Default${AuthSchemeResolverGenerator.getSdkId(ctx)}AuthSchemeResolver()" }, | ||
{ writer.format("Default\$LAuthSchemeResolver()", AuthSchemeResolverGenerator.getSdkId(ctx)) }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup
false, | ||
false | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method below:
- hides
accountId
from being exposed as a config property since it is only to be supplied through credential resolution. - replaces the string
accountIdEndpointMode
config property with a custom Swift enum that is more user-friendly.
): MiddlewareExecutionGenerator( | ||
ctx, writer, httpBindingResolver, httpProtocolCustomizable, operationMiddleware, operationStackName | ||
) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only customization on this middleware generator is that it writes accountIdEndpointMode to context (for use in business metrics.)
// // In Linux, Foundation.URLRequest is moved to FoundationNetworking. | ||
// writer.addImport(packageName = "FoundationNetworking", importOnlyIfCanImport = true) | ||
// } | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Above, removed unused code
writer.write("return nil") | ||
} | ||
|
||
generator.render(serviceShape, op, PRESIGN_REQUEST) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trailing param to render
has been removed
writer.write( | ||
"context.set(key: \$N<EndpointParams>(name: \"EndpointParams\"), value: endpointParamsBlock(context))", | ||
SmithyTypes.AttributeKey | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup
@@ -65,14 +68,13 @@ class OperationEndpointResolverMiddleware( | |||
) { | |||
val output = MiddlewareShapeUtils.outputSymbol(ctx.symbolProvider, ctx.model, op) | |||
writer.write( | |||
"\$N<\$N, EndpointParams>(endpointResolverBlock: { [config] in try config.endpointResolver.resolve(params: \$\$0) }, endpointParams: endpointParams)", | |||
"\$N<\$N, EndpointParams>(paramsBlock: endpointParamsBlock, resolverBlock: { [config] in try config.endpointResolver.resolve(params: \$\$0) })", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated for new EndpointResolverMiddleware
signature
@@ -96,15 +98,16 @@ class OperationEndpointResolverMiddleware( | |||
operationContextParams[param.name.toString()], | |||
clientContextParams[param.name.toString()], | |||
writer, | |||
outputError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused param is removed
writer.write("let endpointParams = EndpointParams(${params.joinToString(separator = ", ")})") | ||
writer.openBlock("let endpointParamsBlock = { [config] (context: \$N) in", "}", SmithyTypes.Context) { | ||
writer.write("EndpointParams(\$L)", params.joinToString(", ")) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Endpoint params are broken onto individual lines for clarity
} | ||
param.name.toString() == "AccountIdEndpointMode" -> { | ||
"config.accountIdEndpointMode?.rawValue" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle account ID & endpoint mode specially
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial review w/ comments about re-using extension points and removing business feature metric for ACCOUNT_ID_ENDPOINT
// | ||
// Copyright Amazon.com Inc. or its affiliates. | ||
// All Rights Reserved. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
import struct Smithy.Attributes | ||
import struct Smithy.AttributeKey | ||
import class Smithy.Context | ||
import struct SmithyHTTPAPI.Endpoint | ||
|
||
public extension Context { | ||
|
||
var resolvedEndpoint: Endpoint? { | ||
get { get(key: resolvedEndpointKey) } | ||
set { set(key: resolvedEndpointKey, value: newValue) } | ||
} | ||
} | ||
|
||
private let resolvedEndpointKey = AttributeKey<Endpoint>(name: "ResolvedEndpoint") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently the ACCOUNT_ID_ENDPOINT
feature ID shouldn't be tracked atm (saving and parsing resolved endpoint) and shouldn't be implemented until future update according to the feature ID table SEP page --/FEATURES.md
.
// Put endpoint into context for use in business metrics | ||
attributes.resolvedEndpoint = endpoint |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per comment on Context+ResolvedEndpoint.swift
, should be removed
// Handle O | ||
if let endpoint = context.resolvedEndpoint, let accountID = context.resolvedAWSAccountID, endpoint.host.contains(accountID) { | ||
context.businessMetrics = ["ACCOUNT_ID_ENDPOINT": "O"] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per comment on Context+ResolvedEndpoint.swift
, should be removed
override fun customizedClientConfigProperty(property: ConfigProperty): ConfigProperty? { | ||
return when (property.name) { | ||
"accountId" -> null // do not expose accountId as a client config property | ||
"accountIdEndpointMode" -> { // expose accountIdEndpointMode as a Swift string-backed enum | ||
ConfigProperty( | ||
"accountIdEndpointMode", | ||
AWSClientRuntimeTypes.Core.AccountIDEndpointMode.toOptional(), | ||
{ writer -> | ||
writer.format( | ||
"\$N.accountIDEndpointMode()", | ||
AWSClientRuntimeTypes.Core.AWSClientConfigDefaultsProvider, | ||
) | ||
}, | ||
true | ||
) | ||
} | ||
else -> property | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrote a comment over in smithy-swift PR that has the base default implementation of this as well, but this extension should already be doable using overrideConfigProperties
. Let's remove duplicate purpose extension point and merge this to existing one. See here.
package software.amazon.smithy.aws.swift.codegen | ||
|
||
import software.amazon.smithy.swift.codegen.SwiftWriter | ||
import software.amazon.smithy.swift.codegen.config.ConfigProperty | ||
import software.amazon.smithy.swift.codegen.integration.HTTPProtocolCustomizable | ||
import software.amazon.smithy.swift.codegen.integration.HttpBindingResolver | ||
import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator | ||
import software.amazon.smithy.swift.codegen.middleware.MiddlewareExecutionGenerator | ||
import software.amazon.smithy.swift.codegen.middleware.OperationMiddleware | ||
|
||
class AWSMiddlewareExecutionGenerator( | ||
ctx: ProtocolGenerator.GenerationContext, | ||
private val writer: SwiftWriter, | ||
httpBindingResolver: HttpBindingResolver, | ||
httpProtocolCustomizable: HTTPProtocolCustomizable, | ||
operationMiddleware: OperationMiddleware, | ||
operationStackName: String | ||
) : MiddlewareExecutionGenerator( | ||
ctx, writer, httpBindingResolver, httpProtocolCustomizable, operationMiddleware, operationStackName | ||
) { | ||
|
||
override fun renderConfigPropertyToContext(configProperty: ConfigProperty) { | ||
when (configProperty.name) { | ||
"accountIdEndpointMode" -> { // Used for business metrics | ||
writer.write(" .withAccountIDEndpointMode(value: config.accountIdEndpointMode)") | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…ift into jbe/account_id_config
Description of changes
EndpointResolverMiddleware
is renamed toAWSEndpointResolverMiddleware
to make clear that it is an AWS-specialized typeAWSEndpointResolverMiddleware
is protected with Swift@_spi()
to conceal it from public usage.accountId
is no longer exposed as a client config param since it is set only via AWS credential identity resolversNote: Bump minor version when this feature ships
Also note: This is a breaking change to the DynamoDB client because client config fields have been either removed or changed in type. Expected breakage on the customer's side is zero because those fields are currently exposed but non-functional. Only DynamoDB is affected, no other client has these config fields at this time.
New/existing dependencies impact assessment, if applicable
No new dependencies were added to this change.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.