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

Add placeholder types for S3 Express and enable control flow to be redirected for S3 Express use case #3386

Merged
merged 22 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
dbcfe73
Add placeholder types for S3 Express
ysaito1001 Jan 25, 2024
714aaad
Enable control flow to be altered for S3 Express
ysaito1001 Jan 25, 2024
9086db2
Fix lint-related failures in CI
ysaito1001 Jan 25, 2024
1f07f92
Temporarily disable S3 Express endpoint tests
ysaito1001 Jan 25, 2024
13c3c5a
Update aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk…
ysaito1001 Jan 29, 2024
38ce80f
Make s3_express inlineable modules `pub(crate)`
ysaito1001 Jan 29, 2024
ef1e1be
Remove unnecessary `set_shared_identity_resolver`
ysaito1001 Jan 29, 2024
21ad106
Update aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk…
ysaito1001 Feb 7, 2024
10e8841
Fix clap MSRV issue (#3399)
jdisanti Feb 8, 2024
052965b
Upgrade the smithy-rs runtime crates version to 1.1.5
aws-sdk-rust-ci Feb 8, 2024
f001422
Update changelog
aws-sdk-rust-ci Feb 8, 2024
c2a2708
Make `cache_location` a method on `ResolveIdentity`
ysaito1001 Feb 8, 2024
15e89c6
Merge branch 'ysaito/s3express' into s3express-add-placeholders
ysaito1001 Feb 8, 2024
66f5eba
Merge smithy-rs-release-1.x.y back into main (#3401)
aws-sdk-rust-ci Feb 8, 2024
68ab569
Upgrade Smithy to 1.44 (#3397)
jdisanti Feb 8, 2024
0be5cb3
Add CI step to release workflow to check for semver hazards (#3383)
jdisanti Feb 9, 2024
df1103d
Implement and test retry classifier sorting (#3392)
Velfi Feb 9, 2024
166f0e2
Cap the max jitter fraction for cache refresh buffer time to 0.5 (#3402)
ysaito1001 Feb 9, 2024
457f6b8
Add a reference to docs for `IdentityCacheLocation`
ysaito1001 Feb 9, 2024
f569231
Use rustTemplate instead of rustBlockTemplate for readability
ysaito1001 Feb 10, 2024
c3d754f
Exclude `s3_express` from `aws-inlineable`'s module tree
ysaito1001 Feb 10, 2024
5b27e4d
Merge branch 'main' into s3express-add-placeholders
ysaito1001 Feb 10, 2024
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
4 changes: 4 additions & 0 deletions aws/rust-runtime/aws-inlineable/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ pub mod presigning;
/// Presigning interceptors
pub mod presigning_interceptors;

/// Supporting types for S3 Express.
#[allow(dead_code)]
pub mod s3_express;

/// Special logic for extracting request IDs from S3's responses.
#[allow(dead_code)]
pub mod s3_request_id;
Expand Down
125 changes: 125 additions & 0 deletions aws/rust-runtime/aws-inlineable/src/s3_express.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/// Supporting code for S3 Express auth
pub(crate) mod auth {
use aws_smithy_runtime_api::box_error::BoxError;
use aws_smithy_runtime_api::client::auth::{
AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, Sign,
};
use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver};
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
use aws_smithy_runtime_api::client::runtime_components::{
GetIdentityResolver, RuntimeComponents,
};
use aws_smithy_types::config_bag::ConfigBag;

/// Auth scheme ID for S3 Express.
pub(crate) const SCHEME_ID: AuthSchemeId = AuthSchemeId::new("sigv4-s3express");

/// S3 Express auth scheme.
#[derive(Debug, Default)]
pub(crate) struct S3ExpressAuthScheme {
signer: S3ExpressSigner,
}

impl S3ExpressAuthScheme {
/// Creates a new `S3ExpressAuthScheme`.
pub(crate) fn new() -> Self {
Default::default()
}
}

impl AuthScheme for S3ExpressAuthScheme {
fn scheme_id(&self) -> AuthSchemeId {
SCHEME_ID
}

fn identity_resolver(
&self,
identity_resolvers: &dyn GetIdentityResolver,
) -> Option<SharedIdentityResolver> {
identity_resolvers.identity_resolver(self.scheme_id())
}

fn signer(&self) -> &dyn Sign {
&self.signer
}
}

/// S3 Express signer.
#[derive(Debug, Default)]
pub(crate) struct S3ExpressSigner;

impl Sign for S3ExpressSigner {
fn sign_http_request(
&self,
_request: &mut HttpRequest,
_identity: &Identity,
_auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>,
_runtime_components: &RuntimeComponents,
_config_bag: &ConfigBag,
) -> Result<(), BoxError> {
todo!()
}
}
}

/// Supporting code for S3 Express identity cache
pub(crate) mod identity_cache {
/// The caching implementation for S3 Express identity.
///
/// While customers can either disable S3 Express itself or provide a custom S3 Express identity
/// provider, configuring S3 Express identity cache is not supported. Thus, this is _the_
/// implementation of S3 Express identity cache.
#[derive(Debug)]
pub(crate) struct S3ExpressIdentityCache;
}

/// Supporting code for S3 Express identity provider
pub(crate) mod identity_provider {
use crate::s3_express::identity_cache::S3ExpressIdentityCache;
use aws_smithy_runtime_api::client::identity::{
IdentityCacheLocation, IdentityFuture, ResolveIdentity,
};
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
use aws_smithy_types::config_bag::ConfigBag;

#[derive(Debug)]
pub(crate) struct DefaultS3ExpressIdentityProvider {
_cache: S3ExpressIdentityCache,
}

#[derive(Default)]
pub(crate) struct Builder;

impl DefaultS3ExpressIdentityProvider {
pub(crate) fn builder() -> Builder {
Builder
}
}

impl Builder {
pub(crate) fn build(self) -> DefaultS3ExpressIdentityProvider {
DefaultS3ExpressIdentityProvider {
_cache: S3ExpressIdentityCache,
}
}
}

impl ResolveIdentity for DefaultS3ExpressIdentityProvider {
fn resolve_identity<'a>(
&'a self,
_runtime_components: &'a RuntimeComponents,
_config_bag: &'a ConfigBag,
) -> IdentityFuture<'a> {
todo!()
}

fn cache_location(&self) -> IdentityCacheLocation {
IdentityCacheLocation::IdentityResolver
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import software.amazon.smithy.rustsdk.customize.glacier.GlacierDecorator
import software.amazon.smithy.rustsdk.customize.onlyApplyTo
import software.amazon.smithy.rustsdk.customize.route53.Route53Decorator
import software.amazon.smithy.rustsdk.customize.s3.S3Decorator
import software.amazon.smithy.rustsdk.customize.s3.S3ExpressDecorator
import software.amazon.smithy.rustsdk.customize.s3.S3ExtendedRequestIdDecorator
import software.amazon.smithy.rustsdk.customize.s3control.S3ControlDecorator
import software.amazon.smithy.rustsdk.customize.sso.SSODecorator
Expand Down Expand Up @@ -64,6 +65,7 @@ val DECORATORS: List<ClientCodegenDecorator> =
Route53Decorator().onlyApplyTo("com.amazonaws.route53#AWSDnsV20130401"),
"com.amazonaws.s3#AmazonS3".applyDecorators(
S3Decorator(),
S3ExpressDecorator(),
S3ExtendedRequestIdDecorator(),
),
S3ControlDecorator().onlyApplyTo("com.amazonaws.s3control#AWSS3ControlServiceV20180820"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.rustsdk.customize.s3

import software.amazon.smithy.aws.traits.auth.SigV4Trait
import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.configReexport
import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
import software.amazon.smithy.rustsdk.AwsRuntimeType
import software.amazon.smithy.rustsdk.InlineAwsDependency

class S3ExpressDecorator : ClientCodegenDecorator {
ysaito1001 marked this conversation as resolved.
Show resolved Hide resolved
override val name: String = "S3ExpressDecorator"
override val order: Byte = 0

private fun sigv4S3Express() =
writable {
rust(
"#T",
RuntimeType.forInlineDependency(
InlineAwsDependency.forRustFile("s3_express"),
).resolve("auth::SCHEME_ID"),
)
}

override fun authOptions(
codegenContext: ClientCodegenContext,
operationShape: OperationShape,
baseAuthSchemeOptions: List<AuthSchemeOption>,
): List<AuthSchemeOption> =
baseAuthSchemeOptions +
AuthSchemeOption.StaticAuthSchemeOption(
SigV4Trait.ID,
listOf(sigv4S3Express()),
)

override fun serviceRuntimePluginCustomizations(
codegenContext: ClientCodegenContext,
baseCustomizations: List<ServiceRuntimePluginCustomization>,
): List<ServiceRuntimePluginCustomization> =
baseCustomizations + listOf(S3ExpressServiceRuntimePluginCustomization(codegenContext))

override fun configCustomizations(
codegenContext: ClientCodegenContext,
baseCustomizations: List<ConfigCustomization>,
): List<ConfigCustomization> = baseCustomizations + listOf(S3ExpressIdentityProviderConfig(codegenContext))
}

private class S3ExpressServiceRuntimePluginCustomization(codegenContext: ClientCodegenContext) :
ServiceRuntimePluginCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig
private val codegenScope by lazy {
arrayOf(
"DefaultS3ExpressIdentityProvider" to
RuntimeType.forInlineDependency(
InlineAwsDependency.forRustFile("s3_express"),
).resolve("identity_provider::DefaultS3ExpressIdentityProvider"),
"IdentityCacheLocation" to
RuntimeType.smithyRuntimeApiClient(runtimeConfig)
.resolve("client::identity::IdentityCacheLocation"),
"S3ExpressAuthScheme" to
RuntimeType.forInlineDependency(
InlineAwsDependency.forRustFile("s3_express"),
).resolve("auth::S3ExpressAuthScheme"),
"S3_EXPRESS_SCHEME_ID" to
RuntimeType.forInlineDependency(
InlineAwsDependency.forRustFile("s3_express"),
).resolve("auth::SCHEME_ID"),
"SharedAuthScheme" to
RuntimeType.smithyRuntimeApiClient(runtimeConfig)
.resolve("client::auth::SharedAuthScheme"),
"SharedCredentialsProvider" to
configReexport(
AwsRuntimeType.awsCredentialTypes(runtimeConfig)
.resolve("provider::SharedCredentialsProvider"),
),
"SharedIdentityResolver" to
RuntimeType.smithyRuntimeApiClient(runtimeConfig)
.resolve("client::identity::SharedIdentityResolver"),
)
}

override fun section(section: ServiceRuntimePluginSection): Writable =
writable {
when (section) {
is ServiceRuntimePluginSection.RegisterRuntimeComponents -> {
section.registerAuthScheme(this) {
rustTemplate(
"#{SharedAuthScheme}::new(#{S3ExpressAuthScheme}::new())",
*codegenScope,
)
}

section.registerIdentityResolver(
this,
writable {
rustTemplate("#{S3_EXPRESS_SCHEME_ID}", *codegenScope)
},
writable {
rustTemplate("#{DefaultS3ExpressIdentityProvider}::builder().build()", *codegenScope)
},
)
}

else -> {}
}
}
}

class S3ExpressIdentityProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig
private val codegenScope =
arrayOf(
*preludeScope,
"IdentityCacheLocation" to
RuntimeType.smithyRuntimeApiClient(runtimeConfig)
.resolve("client::identity::IdentityCacheLocation"),
"ProvideCredentials" to
configReexport(
AwsRuntimeType.awsCredentialTypes(runtimeConfig)
.resolve("provider::ProvideCredentials"),
),
"SharedCredentialsProvider" to
configReexport(
AwsRuntimeType.awsCredentialTypes(runtimeConfig)
.resolve("provider::SharedCredentialsProvider"),
),
"SharedIdentityResolver" to
RuntimeType.smithyRuntimeApiClient(runtimeConfig)
.resolve("client::identity::SharedIdentityResolver"),
"S3_EXPRESS_SCHEME_ID" to
RuntimeType.forInlineDependency(
InlineAwsDependency.forRustFile("s3_express"),
).resolve("auth::SCHEME_ID"),
)

override fun section(section: ServiceConfig) =
writable {
when (section) {
ServiceConfig.BuilderImpl -> {
rustTemplate(
"""
/// Sets the credentials provider for S3 Express One Zone
pub fn express_credentials_provider(mut self, credentials_provider: impl #{ProvideCredentials} + 'static) -> Self {
self.set_express_credentials_provider(#{Some}(#{SharedCredentialsProvider}::new(credentials_provider)));
self
}
""",
*codegenScope,
)

rustBlockTemplate(
"""
/// Sets the credentials provider for S3 Express One Zone
pub fn set_express_credentials_provider(&mut self, credentials_provider: #{Option}<#{SharedCredentialsProvider}>) -> &mut Self
""",
*codegenScope,
) {
rustBlockTemplate(
"""
if let #{Some}(credentials_provider) = credentials_provider
""",
*codegenScope,
) {
rustTemplate(
"""
self.runtime_components.set_identity_resolver(#{S3_EXPRESS_SCHEME_ID}, credentials_provider);
""",
*codegenScope,
)
}
rust("self")
}
ysaito1001 marked this conversation as resolved.
Show resolved Hide resolved
}

else -> emptySection
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,17 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test:
private val model = ctx.model
private val instantiator = ClientInstantiator(ctx)

/** tests using S3 Express bucket names need to be disabled until the implementation is in place **/
private fun EndpointTestCase.isSigV4S3Express() =
expect.endpoint.orNull()?.properties?.get("authSchemes")?.asArrayNode()?.orNull()
?.map { it.expectObjectNode().expectStringMember("name").value }?.contains("sigv4-s3express") == true

fun generateInput(testOperationInput: EndpointTestOperationInput) =
writable {
val operationName = testOperationInput.operationName.toSnakeCase()
if (test.isSigV4S3Express()) {
Attribute.shouldPanic("not yet implemented").render(this)
}
tokioTest(safeName("operation_input_test_$operationName")) {
rustTemplate(
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) {
) {
writer.rust("runtime_components.push_retry_classifier(#T);", classifier)
}

fun registerIdentityResolver(
writer: RustWriter,
schemeId: Writable,
identityResolver: Writable,
) {
writer.rust("runtime_components.set_identity_resolver(#T, #T);", schemeId, identityResolver)
}
}
}
typealias ServiceRuntimePluginCustomization = NamedCustomization<ServiceRuntimePluginSection>
Expand Down
Loading
Loading