Skip to content
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: endpoints2: aws middleware and integration changes #751

Merged
merged 4 commits into from
Nov 18, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public class EcsCredentialsProvider internal constructor(
}
}

op.install(ResolveEndpoint(resolver = { Endpoint(url) }))
op.install(ResolveEndpoint(provider = { Endpoint(url) }, params = null))
op.install(retryMiddleware)

logger.debug { "retrieving container credentials" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public class ImdsClient private constructor(builder: Builder) : InstanceMetadata
}

// cached middleware instances
private val resolveEndpointMiddleware = ResolveEndpoint(ImdsEndpointResolver(platformProvider, endpointConfiguration))
private val resolveEndpointMiddleware = ResolveEndpoint(ImdsEndpointProvider(platformProvider, endpointConfiguration), Unit)
private val userAgentMiddleware = UserAgent(
staticMetadata = AwsUserAgentMetadata.fromEnvironment(ApiMetadata(SERVICE, "unknown")),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@ import aws.sdk.kotlin.runtime.config.AwsSdkSetting
import aws.sdk.kotlin.runtime.config.profile.loadActiveAwsProfile
import aws.sdk.kotlin.runtime.config.resolve
import aws.smithy.kotlin.runtime.http.endpoints.Endpoint
import aws.smithy.kotlin.runtime.http.endpoints.EndpointResolver
import aws.smithy.kotlin.runtime.http.endpoints.EndpointProvider
import aws.smithy.kotlin.runtime.util.PlatformProvider
import aws.smithy.kotlin.runtime.util.asyncLazy

internal const val EC2_METADATA_SERVICE_ENDPOINT_PROFILE_KEY = "ec2_metadata_service_endpoint"
internal const val EC2_METADATA_SERVICE_ENDPOINT_MODE_PROFILE_KEY = "ec2_metadata_service_endpoint_mode"

internal class ImdsEndpointResolver(
internal class ImdsEndpointProvider(
private val platformProvider: PlatformProvider,
private val endpointConfiguration: EndpointConfiguration,
) : EndpointResolver {
) : EndpointProvider<Unit> {
// cached endpoint and profile
private val resolvedEndpoint = asyncLazy(::doResolveEndpoint)
private val activeProfile = asyncLazy { loadActiveAwsProfile(platformProvider) }

override suspend fun resolve(): Endpoint = resolvedEndpoint.get()
override suspend fun resolveEndpoint(params: Unit): Endpoint = resolvedEndpoint.get()

private suspend fun doResolveEndpoint(): Endpoint = when (endpointConfiguration) {
is EndpointConfiguration.Custom -> endpointConfiguration.endpoint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
package aws.sdk.kotlin.runtime.endpoint

import aws.sdk.kotlin.runtime.InternalSdkApi
import aws.smithy.kotlin.runtime.auth.awssigning.AwsSigningAttributes
import aws.smithy.kotlin.runtime.auth.awssigning.SigningContext
import aws.smithy.kotlin.runtime.http.endpoints.Endpoint
import aws.smithy.kotlin.runtime.http.operation.SdkHttpRequest
import aws.smithy.kotlin.runtime.util.AttributeKey

/**
Expand Down Expand Up @@ -40,8 +42,21 @@ public sealed class AuthScheme {
* out the sigv4 one if present.
*/
@InternalSdkApi
public fun Endpoint.authScheme(): AuthScheme.SigV4? =
attributes.getOrNull(AuthSchemesAttributeKey)?.find { it is AuthScheme.SigV4 } as AuthScheme.SigV4?
public val Endpoint.authScheme: AuthScheme.SigV4? get() =
attributes.getOrNull(AuthSchemesAttributeKey)?.find { it is AuthScheme.SigV4 } as? AuthScheme.SigV4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: I suggest moving the get() = to the next line with the getter body (ref: Kotlin coding conventions).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surprised that ktlintFormat didn't catch this one tbh


@InternalSdkApi
public fun AuthScheme.SigV4.asSigningContext(): SigningContext = SigningContext(signingName, signingRegion)

/**
* Update a request's signing context properties with the receiving auth scheme.
*/
@InternalSdkApi
public fun AuthScheme.SigV4.applyToRequest(req: SdkHttpRequest) {
signingName?.let {
if (it.isNotBlank()) req.context[AwsSigningAttributes.SigningService] = it
}
signingRegion?.let {
if (it.isNotBlank()) req.context[AwsSigningAttributes.SigningRegion] = it
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ object AwsRuntimeTypes {
val AuthScheme = runtimeSymbol("AuthScheme", AwsKotlinDependency.AWS_ENDPOINT)
val authSchemeEndpointExt = runtimeSymbol("authScheme", AwsKotlinDependency.AWS_ENDPOINT)
val asSigningContextAuthSchemeExt = runtimeSymbol("asSigningContext", AwsKotlinDependency.AWS_ENDPOINT)
val applyToRequestAuthSchemeExt = runtimeSymbol("applyToRequest", AwsKotlinDependency.AWS_ENDPOINT)

object Functions {
val partitionFn = runtimeSymbol("partition", AwsKotlinDependency.AWS_ENDPOINT, "functions")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ class AwsServiceConfigIntegration : KotlinIntegration {
"useFips",
defaultValue = false,
documentation = """
Whether to use [FIPS](https://aws.amazon.com/compliance/fips/) endpoints when making requests.
Flag to toggle whether to use [FIPS](https://aws.amazon.com/compliance/fips/) endpoints when making requests.
""".trimIndent(),
)

val UseDualStackProp: ClientConfigProperty = ClientConfigProperty.Boolean(
"useDualStack",
defaultValue = false,
documentation = """
Whether to use dual-stack endpoints when making requests.
Flag to toggle whether to use dual-stack endpoints when making requests.
""".trimIndent(),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ class PresignerGenerator : KotlinIntegration {
symbol = RuntimeTypes.Auth.Signing.AwsSigningCommon.SigningEndpointProvider
name = "endpointProvider"
documentation =
"Provides the endpoint (hostname) and signing context to make requests to. You MUST pass a provider will resolve to an endpoint for the desired request."
"Provides the endpoint (hostname) and signing context to make requests to."
baseClass = RuntimeTypes.Auth.Signing.AwsSigningCommon.ServicePresignConfig
propertyType = ClientConfigPropertyType.Required()
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ClientConfigIntegration : KotlinIntegration {
"enableAccelerate",
defaultValue = false,
documentation = """
Whether to support [S3 transfer acceleration](https://docs.aws.amazon.com/AmazonS3/latest/userguide/transfer-acceleration.html)
Flag to support [S3 transfer acceleration](https://docs.aws.amazon.com/AmazonS3/latest/userguide/transfer-acceleration.html)
with this client.
""".trimIndent(),
)
Expand All @@ -32,15 +32,15 @@ class ClientConfigIntegration : KotlinIntegration {
"forcePathStyle",
defaultValue = false,
documentation = """
Whether to use legacy path-style addressing when making requests.
Flag to use legacy path-style addressing when making requests.
""".trimIndent(),
)

val UseArnRegionProp: ClientConfigProperty = ClientConfigProperty.Boolean(
"useArnRegion",
defaultValue = false,
documentation = """
Whether to enforce using a bucket arn with a region matching the client config when making requests with
Flag to enforce using a bucket arn with a region matching the client config when making requests with
[S3 access points](https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-points.html).
""".trimIndent(),
)
Expand All @@ -50,7 +50,7 @@ class ClientConfigIntegration : KotlinIntegration {
"disableMrap",
defaultValue = true,
documentation = """
Whether to disable [S3 multi-region access points](https://docs.aws.amazon.com/AmazonS3/latest/userguide/MultiRegionAccessPoints.html).
Flag to disable [S3 multi-region access points](https://docs.aws.amazon.com/AmazonS3/latest/userguide/MultiRegionAccessPoints.html).
""".trimIndent(),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ClientConfigIntegration : KotlinIntegration {
"useArnRegion",
defaultValue = false,
documentation = """
Whether to enforce using a bucket arn with a region matching the client config when making requests with
Flag to enforce using a bucket arn with a region matching the client config when making requests with
[S3 access points](https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-points.html).
""".trimIndent(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import software.amazon.smithy.kotlin.codegen.core.withBlock
import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes
import software.amazon.smithy.kotlin.codegen.model.buildSymbol
import software.amazon.smithy.kotlin.codegen.model.expectShape
import software.amazon.smithy.kotlin.codegen.model.getEndpointRules
import software.amazon.smithy.kotlin.codegen.model.namespace
import software.amazon.smithy.kotlin.codegen.rendering.ExceptionBaseClassGenerator
import software.amazon.smithy.kotlin.codegen.rendering.endpoints.*
Expand Down Expand Up @@ -167,33 +168,38 @@ abstract class AwsHttpBindingProtocolGenerator : HttpBindingProtocolGenerator()
val providerSymbol = EndpointProviderGenerator.getSymbol(ctx.settings)
val defaultProviderSymbol = DefaultEndpointProviderGenerator.getSymbol(ctx.settings)

ctx.delegator.useFileWriter(paramsSymbol) {
EndpointParametersGenerator(it, rules).render()
}
ctx.delegator.useFileWriter(providerSymbol) {
val generator = EndpointProviderGenerator(it, paramsSymbol)
EndpointProviderGenerator.renderAsSigningProviderExt(ctx.settings, it)
}

generator.render()
it.write("")
generator.renderAsSigningProviderExt(it, providerSymbol, paramsSymbol)
val endpointFunctions = buildMap {
putAll(awsEndpointFunctions)
put(
"aws.partition",
buildSymbol {
name = "partition"
namespace = PartitionsGenerator.getSymbol(ctx.settings).namespace
},
)
}
ctx.delegator.useFileWriter(defaultProviderSymbol) {
DefaultEndpointProviderGenerator(
it,
rules,
providerSymbol,
paramsSymbol,
awsEndpointFunctions,
awsEndpointPropertyRenderers,
).render()
DefaultEndpointProviderGenerator(it, rules, providerSymbol, paramsSymbol, endpointFunctions, awsEndpointPropertyRenderers).render()
}
}

val middlewareSymbol = ResolveEndpointMiddlewareGenerator.getSymbol(ctx.settings)
ctx.delegator.useFileWriter(middlewareSymbol) {
ResolveEndpointMiddlewareGenerator(ctx, it).render()

val builtins = rules.parameters.toList().filter(Parameter::isBuiltIn)
renderBindAwsBuiltins(ctx, it, builtins)
override fun generateEndpointProviderMiddleware(ctx: ProtocolGenerator.GenerationContext) {
ctx.delegator.useFileWriter(ResolveEndpointMiddlewareGenerator.getSymbol(ctx.settings)) {
ResolveEndpointMiddlewareGenerator(ctx, it) {
it.write(
"endpoint.#T?.#T(req)",
AwsRuntimeTypes.Endpoint.authSchemeEndpointExt,
AwsRuntimeTypes.Endpoint.applyToRequestAuthSchemeExt,
)
}.render()

val builtins = ctx.service.getEndpointRules()?.parameters?.toList()?.filter(Parameter::isBuiltIn)
it.write("")
renderBindAwsBuiltins(ctx, it, builtins ?: emptyList())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ import aws.sdk.kotlin.codegen.AwsRuntimeTypes
import software.amazon.smithy.codegen.core.CodegenException
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
import software.amazon.smithy.kotlin.codegen.core.withBlock
import software.amazon.smithy.kotlin.codegen.model.buildSymbol
import software.amazon.smithy.kotlin.codegen.rendering.endpoints.ExpressionRenderer
import software.amazon.smithy.model.node.ObjectNode
import software.amazon.smithy.rulesengine.language.syntax.expr.Expression

val awsEndpointFunctions = mapOf(
"aws.partition" to buildSymbol { name = "partition" }, // the partition function is generated per-service in the same package
"aws.parseArn" to AwsRuntimeTypes.Endpoint.Functions.parseArn,
"aws.isVirtualHostableS3Bucket" to AwsRuntimeTypes.Endpoint.Functions.isVirtualHostableS3Bucket,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,48 @@
*/
package aws.sdk.kotlin.codegen.protocols.endpoints

import aws.sdk.kotlin.codegen.AwsServiceConfigIntegration
import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.kotlin.codegen.KotlinSettings
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
import software.amazon.smithy.kotlin.codegen.core.withBlock
import software.amazon.smithy.kotlin.codegen.model.buildSymbol
import software.amazon.smithy.kotlin.codegen.model.defaultName
import software.amazon.smithy.kotlin.codegen.rendering.endpoints.EndpointParametersGenerator
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameter
import aws.sdk.kotlin.codegen.customization.s3.ClientConfigIntegration as S3ClientConfigIntegration
import aws.sdk.kotlin.codegen.customization.s3control.ClientConfigIntegration as S3ControlClientConfigIntegration

/**
* Render binding of AWS SDK endpoint parameter builtins. In practice, all of these values are sourced from the client
* config.
*/
fun renderBindAwsBuiltins(ctx: ProtocolGenerator.GenerationContext, writer: KotlinWriter, builtinParams: List<Parameter>) {
writer.withBlock(
"public fun #T.Builder.bindAwsBuiltins(config: #T.Config) {",
"internal fun #T.Builder.bindAwsBuiltins(config: #T.Config) {",
"}",
EndpointParametersGenerator.getSymbol(ctx.settings),
ctx.symbolProvider.toSymbol(ctx.service),
) {
builtinParams.forEach {
when (it.builtIn.get()) {
"AWS::Region" -> renderBasicConfigBinding(writer, it, "region")
"AWS::UseFIPS" -> renderBasicConfigBinding(writer, it, "useFips")
"AWS::UseDualStack" -> renderBasicConfigBinding(writer, it, "useDualStack")
"AWS::Region" -> renderBasicConfigBinding(writer, it, AwsServiceConfigIntegration.RegionProp.propertyName)
"AWS::UseFIPS" -> renderBasicConfigBinding(writer, it, AwsServiceConfigIntegration.UseFipsProp.propertyName)
"AWS::UseDualStack" -> renderBasicConfigBinding(writer, it, AwsServiceConfigIntegration.UseDualStackProp.propertyName)

"AWS::S3::Accelerate" -> renderBasicConfigBinding(writer, it, "enableAccelerate")
"AWS::S3::ForcePathStyle" -> renderBasicConfigBinding(writer, it, "forcePathStyle")
"AWS::S3::DisableMultiRegionAccessPoints" -> renderBasicConfigBinding(writer, it, "disableMrap")
"AWS::S3::UseArnRegion", "AWS::S3Control::UseArnRegion" -> renderBasicConfigBinding(writer, it, "useArnRegion")
"AWS::S3::Accelerate" -> renderBasicConfigBinding(writer, it, S3ClientConfigIntegration.EnableAccelerateProp.propertyName)
"AWS::S3::ForcePathStyle" -> renderBasicConfigBinding(writer, it, S3ClientConfigIntegration.ForcePathStyleProp.propertyName)
"AWS::S3::DisableMultiRegionAccessPoints" -> renderBasicConfigBinding(writer, it, S3ClientConfigIntegration.DisableMrapProp.propertyName)
"AWS::S3::UseArnRegion" -> renderBasicConfigBinding(writer, it, S3ClientConfigIntegration.DisableMrapProp.propertyName)
"AWS::S3Control::UseArnRegion" -> renderBasicConfigBinding(writer, it, S3ControlClientConfigIntegration.UseArnRegionProp.propertyName)

"SDK::Endpoint" ->
writer.write("#L = config.endpointUrl?.toString()", EndpointParametersGenerator.getParamKotlinName(it))
writer.write("#L = config.#L?.toString()", it.defaultName(), AwsServiceConfigIntegration.EndpointUrlProp.propertyName)

// as a newer SDK we do NOT support these values, they are always false
"AWS::S3::UseGlobalEndpoint", "AWS::STS::UseGlobalEndpoint" ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question/Correctness: Should we even render this for the Kotlin SDK? This seems like a legacy concern that we could filter or make internal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to force them false here if we definitely don't want to support this route in the SDK, since the ruleset could default them to true.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant was should we even be surfacing this at the config level? I guess it's fine, it's just legacy cruft that doesn't really apply to us though.

writer.write("#L = false", EndpointParametersGenerator.getParamKotlinName(it))
writer.write("#L = false", it.defaultName())
}
}
}
Expand All @@ -53,5 +58,5 @@ fun bindAwsBuiltinsSymbol(settings: KotlinSettings): Symbol =
}

private fun renderBasicConfigBinding(writer: KotlinWriter, param: Parameter, configMember: String) {
writer.write("#L = config.#L", EndpointParametersGenerator.getParamKotlinName(param), configMember)
writer.write("#L = config.#L", param.defaultName(), configMember)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@
package aws.sdk.kotlin.codegen.protocols.endpoints

import aws.sdk.kotlin.codegen.AwsRuntimeTypes
import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.kotlin.codegen.KotlinSettings
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes
import software.amazon.smithy.kotlin.codegen.core.withBlock
import software.amazon.smithy.kotlin.codegen.model.buildSymbol
import software.amazon.smithy.kotlin.codegen.rendering.endpoints.EndpointParametersGenerator
import software.amazon.smithy.kotlin.codegen.rendering.endpoints.EndpointProviderGenerator

fun EndpointProviderGenerator.renderAsSigningProviderExt(writer: KotlinWriter, provider: Symbol, params: Symbol) {
fun EndpointProviderGenerator.Companion.renderAsSigningProviderExt(settings: KotlinSettings, writer: KotlinWriter) {
writer.withBlock(
"public fun #T.asSigningProvider(params: #T): #T = {",
"internal fun #1T<#2T>.asSigningProvider(params: #2T): #3T = {",
"}",
provider,
params,
RuntimeTypes.Http.Endpoints.EndpointProvider,
EndpointParametersGenerator.getSymbol(settings),
RuntimeTypes.Auth.Signing.AwsSigningCommon.SigningEndpointProvider,
) {
write("val endpoint = resolveEndpoint(params)")
write(
"#T(endpoint, endpoint.#T()?.#T())",
"#T(endpoint, endpoint.#T?.#T())",
RuntimeTypes.Auth.Signing.AwsSigningCommon.SigningContextualizedEndpoint,
AwsRuntimeTypes.Endpoint.authSchemeEndpointExt,
AwsRuntimeTypes.Endpoint.asSigningContextAuthSchemeExt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class PartitionsGenerator(
fun getSymbol(settings: KotlinSettings): Symbol =
buildSymbol {
name = "Partitions"
namespace = "${settings.pkg.name}.endpoints"
namespace = "${settings.pkg.name}.endpoints.internal"
}
}

Expand Down
Loading