Skip to content

Commit

Permalink
feat: support compiling to webassembly (#2254)
Browse files Browse the repository at this point in the history
* feat(aws-config): define default feature for sso

* chore(aws): disable unused features

* chore(rust-runtime): disable unused features

* chore: add missing tokio fs feature

* chore: disable spawn blocking on wasm

* chore: fix sso feature

* chore: allow unused variables when not sso

* chore: remove unnecessary dependency filter for wasm

* chore: add integration test for wasm

* chore: ensure test-util feature is applied to dev dependencies

* chore: disable retry config

Co-authored-by: John DiSanti <johndisanti@gmail.com>

* chore: simplify features as suggested

* chore: no default features for aws types

* chore: rename credentials-sso feature

* Revert "chore: simplify features as suggested"

This reverts commit d8fcf49.

* chore: set right name in default features

* chore: create smithy client test util in runtime types

* Update aws/rust-runtime/aws-types/Cargo.toml

Co-authored-by: John DiSanti <johndisanti@gmail.com>

* Update aws/rust-runtime/aws-inlineable/Cargo.toml

Co-authored-by: John DiSanti <johndisanti@gmail.com>

* Update aws/rust-runtime/aws-config/src/profile/credentials/exec.rs

Co-authored-by: John DiSanti <johndisanti@gmail.com>

* Update aws/rust-runtime/aws-config/src/profile/credentials/exec.rs

Co-authored-by: John DiSanti <johndisanti@gmail.com>

* Update aws/rust-runtime/aws-config/src/profile/credentials/exec.rs

Co-authored-by: John DiSanti <johndisanti@gmail.com>

* Update aws/rust-runtime/aws-config/src/lib.rs

Co-authored-by: John DiSanti <johndisanti@gmail.com>

* chore: use hardcoded credentials feature

* chore: make wasm example for s3 instead

* chore: fix formatting

* chore: fix kotlin formatting

* chore: fix kotlin unit tests

* chore: use timeout config from smithy types

* chore: move tests into main file

* chore: add vscode setttings to target wasi by default

* chore: fix test-util feature for smithy client

* chore: separate adapter into own module

* chore: fix test with no default features

* Fix typo

* Update changelog

* Check compilation of `aws-config` against `wasm32` in CI

* Fix Dockerfile issue and use correct cargo-wasi command

* Small Dockerfile fix

* Add missing `tar` binary to Docker image

---------

Co-authored-by: Eduardo Rodrigues <eduardomourar@users.noreply.github.com>
Co-authored-by: John DiSanti <johndisanti@gmail.com>
Co-authored-by: John DiSanti <jdisanti@amazon.com>
Co-authored-by: Russell Cohen <rcoh@amazon.com>
  • Loading branch information
5 people authored and unexge committed Apr 24, 2023
1 parent b875b73 commit f3c9e84
Show file tree
Hide file tree
Showing 27 changed files with 273 additions and 33 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.next.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "all"}
author = "jdisanti"

[[aws-sdk-rust]]
message = "The AWS SDK now compiles for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it!"
references = ["smithy-rs#2254"]
meta = { "breaking" = false, "tada" = true, "bug" = false }
author = "eduardomourar"

[[smithy-rs]]
message = "Clients now compile for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it!"
references = ["smithy-rs#2254"]
meta = { "breaking" = false, "tada" = true, "bug" = false, "target" = "client"}
author = "eduardomourar"

[[smithy-rs]]
message = "Streaming operations now emit the request ID at the `debug` log level like their non-streaming counterparts."
references = ["smithy-rs#2495"]
meta = { "breaking" = false, "tada" = false, "bug" = true }
Expand Down
15 changes: 8 additions & 7 deletions aws/rust-runtime/aws-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ client-hyper = ["aws-smithy-client/client-hyper"]
rustls = ["aws-smithy-client/rustls"]
native-tls = ["aws-smithy-client/native-tls"]
rt-tokio = ["aws-smithy-async/rt-tokio", "tokio/rt"]
credentials-sso = ["dep:aws-sdk-sso", "dep:ring", "dep:hex", "dep:zeroize"]

default = ["client-hyper", "rustls", "rt-tokio"]
default = ["client-hyper", "rustls", "rt-tokio", "credentials-sso"]

[dependencies]
aws-credential-types = { path = "../../sdk/build/aws-sdk/sdk/aws-credential-types" }
aws-http = { path = "../../sdk/build/aws-sdk/sdk/aws-http" }
aws-sdk-sso = { path = "../../sdk/build/aws-sdk/sdk/sso", default-features = false }
aws-sdk-sts = { path = "../../sdk/build/aws-sdk/sdk/sts", default-features = false }
aws-smithy-async = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-async" }
aws-smithy-client = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-client", default-features = false }
Expand All @@ -33,18 +33,19 @@ time = { version = "0.3.4", features = ["parsing"] }
tokio = { version = "1.13.1", features = ["sync"] }
tracing = { version = "0.1" }

# implementation detail of SSO credential caching
ring = "0.16"
hex = "0.4.3"
zeroize = "1"

# implementation detail of IMDS credentials provider
fastrand = "1"

bytes = "1.1.0"
http = "0.2.4"
tower = { version = "0.4.8" }

# implementation detail of SSO credential caching
aws-sdk-sso = { path = "../../sdk/build/aws-sdk/sdk/sso", default-features = false, optional = true }
ring = { version = "0.16", optional = true }
hex = { version = "0.4.3", optional = true }
zeroize = { version = "1", optional = true }

[dev-dependencies]
futures-util = { version = "0.3.16", default-features = false }
tracing-test = "0.2.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,9 @@ mod test {
make_test!(ecs_credentials);
make_test!(ecs_credentials_invalid_profile);

#[cfg(feature = "credentials-sso")]
make_test!(sso_assume_role);
#[cfg(feature = "credentials-sso")]
make_test!(sso_no_token_file);

#[tokio::test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

use crate::provider_config::ProviderConfig;
use aws_sdk_sso::config::timeout::TimeoutConfig;
use aws_smithy_types::timeout::TimeoutConfig;
use std::time::Duration;

const SDK_DEFAULT_CONNECT_TIMEOUT: Duration = Duration::from_millis(3100);
Expand Down
4 changes: 2 additions & 2 deletions aws/rust-runtime/aws-config/src/ecs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,15 +418,15 @@ async fn validate_full_uri(
}
}

#[cfg(not(feature = "rt-tokio"))]
#[cfg(any(not(feature = "rt-tokio"), target_family = "wasm"))]
fn tokio_dns() -> Option<DnsService> {
None
}

/// DNS resolver that uses tokio::spawn_blocking
///
/// DNS resolution is required to validate that provided URIs point to the loopback interface
#[cfg(feature = "rt-tokio")]
#[cfg(all(feature = "rt-tokio", not(target_family = "wasm")))]
fn tokio_dns() -> Option<DnsService> {
use aws_smithy_client::erase::boxclone::BoxFuture;
use std::io::ErrorKind;
Expand Down
2 changes: 1 addition & 1 deletion aws/rust-runtime/aws-config/src/imds/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use crate::imds::client::token::TokenMiddleware;
use crate::provider_config::ProviderConfig;
use crate::PKG_VERSION;
use aws_http::user_agent::{ApiMetadata, AwsUserAgent, UserAgentStage};
use aws_sdk_sso::config::timeout::TimeoutConfig;
use aws_smithy_client::http_connector::ConnectorSettings;
use aws_smithy_client::{erase::DynConnector, SdkSuccess};
use aws_smithy_client::{retry, SdkError};
Expand All @@ -28,6 +27,7 @@ use aws_smithy_http_tower::map_request::{
};
use aws_smithy_types::error::display::DisplayErrorContext;
use aws_smithy_types::retry::{ErrorKind, RetryKind};
use aws_smithy_types::timeout::TimeoutConfig;
use aws_types::os_shim_internal::Env;
use bytes::Bytes;
use http::{Response, Uri};
Expand Down
2 changes: 1 addition & 1 deletion aws/rust-runtime/aws-config/src/imds/client/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use crate::imds::client::ImdsResponseRetryClassifier;
use aws_credential_types::cache::ExpiringCache;
use aws_credential_types::time_source::TimeSource;
use aws_http::user_agent::UserAgentStage;
use aws_sdk_sso::config::timeout::TimeoutConfig;
use aws_smithy_async::rt::sleep::AsyncSleep;
use aws_smithy_client::erase::DynConnector;
use aws_smithy_client::retry;
Expand All @@ -31,6 +30,7 @@ use aws_smithy_http::operation::Operation;
use aws_smithy_http::operation::{Metadata, Request};
use aws_smithy_http::response::ParseStrictResponse;
use aws_smithy_http_tower::map_request::MapRequestLayer;
use aws_smithy_types::timeout::TimeoutConfig;
use http::{HeaderValue, Uri};
use std::fmt::{Debug, Formatter};
use std::future::Future;
Expand Down
2 changes: 1 addition & 1 deletion aws/rust-runtime/aws-config/src/imds/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ mod test {
#[tokio::test]
#[cfg(any(feature = "rustls", feature = "native-tls"))]
async fn external_timeout_during_credentials_refresh_should_yield_last_retrieved_credentials() {
use aws_sdk_sso::config::AsyncSleep;
use aws_smithy_async::rt::sleep::AsyncSleep;
let client = crate::imds::Client::builder()
// 240.* can never be resolved
.endpoint(http::Uri::from_static("http://240.0.0.0"))
Expand Down
1 change: 1 addition & 0 deletions aws/rust-runtime/aws-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ pub mod meta;
pub mod profile;
pub mod provider_config;
pub mod retry;
#[cfg(feature = "credentials-sso")]
pub mod sso;
pub(crate) mod standard_property;
pub mod sts;
Expand Down
25 changes: 18 additions & 7 deletions aws/rust-runtime/aws-config/src/profile/credentials/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::repr::{self, BaseProvider};
use crate::credential_process::CredentialProcessProvider;
use crate::profile::credentials::ProfileFileError;
use crate::provider_config::ProviderConfig;
#[cfg(feature = "credentials-sso")]
use crate::sso::{SsoConfig, SsoCredentialsProvider};
use crate::sts;
use crate::web_identity_token::{StaticConfiguration, WebIdentityTokenCredentialsProvider};
Expand Down Expand Up @@ -117,19 +118,29 @@ impl ProviderChain {
.build();
Arc::new(provider)
}
#[allow(unused_variables)]
BaseProvider::Sso {
sso_account_id,
sso_region,
sso_role_name,
sso_start_url,
} => {
let sso_config = SsoConfig {
account_id: sso_account_id.to_string(),
role_name: sso_role_name.to_string(),
start_url: sso_start_url.to_string(),
region: Region::new(sso_region.to_string()),
};
Arc::new(SsoCredentialsProvider::new(provider_config, sso_config))
#[cfg(feature = "credentials-sso")]
{
let sso_config = SsoConfig {
account_id: sso_account_id.to_string(),
role_name: sso_role_name.to_string(),
start_url: sso_start_url.to_string(),
region: Region::new(sso_region.to_string()),
};
Arc::new(SsoCredentialsProvider::new(provider_config, sso_config))
}
#[cfg(not(feature = "credentials-sso"))]
{
Err(ProfileFileError::UnknownProvider {
name: "sso".to_string(),
})?
}
}
};
tracing::info!(base = ?repr.base(), "first credentials will be loaded from {:?}", repr.base());
Expand Down
2 changes: 1 addition & 1 deletion aws/rust-runtime/aws-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repository = "https://github.com/awslabs/smithy-rs"

[features]
# This feature is to be used only for doc comments
examples = ["aws-smithy-client/client-hyper", "aws-smithy-client/rustls", "hyper-rustls"]
examples = ["dep:hyper-rustls", "aws-smithy-client/client-hyper", "aws-smithy-client/rustls"]

[dependencies]
aws-credential-types = { path = "../aws-credential-types" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ class EndpointsCredentialsTest {
assert!(auth_header.contains("/us-west-2/foobaz/aws4_request"), "{}", auth_header);
""",
"capture_request" to RuntimeType.captureRequest(context.runtimeConfig),
"Credentials" to AwsCargoDependency.awsCredentialTypes(context.runtimeConfig)
.withFeature("test-util").toType().resolve("Credentials"),
"Credentials" to AwsRuntimeType.awsCredentialTypesTestUtil(context.runtimeConfig)
.resolve("Credentials"),
"Region" to AwsRuntimeType.awsTypes(context.runtimeConfig).resolve("region::Region"),
)
}
Expand All @@ -120,8 +120,8 @@ class EndpointsCredentialsTest {
assert!(auth_header.contains("/region-custom-auth/name-custom-auth/aws4_request"), "{}", auth_header);
""",
"capture_request" to RuntimeType.captureRequest(context.runtimeConfig),
"Credentials" to AwsCargoDependency.awsCredentialTypes(context.runtimeConfig)
.withFeature("test-util").toType().resolve("Credentials"),
"Credentials" to AwsRuntimeType.awsCredentialTypesTestUtil(context.runtimeConfig)
.resolve("Credentials"),
"Region" to AwsRuntimeType.awsTypes(context.runtimeConfig).resolve("region::Region"),
)
}
Expand Down
1 change: 1 addition & 0 deletions aws/sdk/integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ members = [
"sts",
"transcribestreaming",
"using-native-tls-instead-of-rustls",
"webassembly",
]
5 changes: 5 additions & 0 deletions aws/sdk/integration-tests/webassembly/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[build]
target = "wasm32-wasi"

[target.wasm32-wasi]
rustflags = ["-C", "opt-level=1"]
3 changes: 3 additions & 0 deletions aws/sdk/integration-tests/webassembly/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"rust-analyzer.cargo.target": "wasm32-wasi"
}
30 changes: 30 additions & 0 deletions aws/sdk/integration-tests/webassembly/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This Cargo.toml is unused in generated code. It exists solely to enable these tests to compile in-situ
[package]
name = "webassembly"
version = "0.1.0"
authors = ["Eduardo Rodrigues <16357187+eduardomourar@users.noreply.github.com>"]
description = """
These tests ensure that things will fail (or not fail) as expected
when target is set to wasm32-wasi for all SDK and runtime crates.
"""
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/awslabs/smithy-rs"
publish = false

[lib]
crate-type = ["cdylib"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
aws-config = { path = "../../build/aws-sdk/sdk/aws-config", default-features = false, features = ["rt-tokio"]}
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["hardcoded-credentials"] }
aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3", default-features = false }
aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", default-features = false }
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" }
aws-types = { path = "../../build/aws-sdk/sdk/aws-types" }
http = "0.2.8"
tokio = { version = "1.24.2", features = ["macros", "rt"] }
tower = "0.4.13"
29 changes: 29 additions & 0 deletions aws/sdk/integration-tests/webassembly/src/adapter/http_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

use aws_smithy_http::body::SdkBody;

pub(crate) fn make_request(_req: http::Request<SdkBody>) -> Result<http::Response<SdkBody>, ()> {
// Consumers here would pass the HTTP request to
// the Wasm host in order to get the response back
let body = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<ListAllMyBucketsResult>
<Buckets>
<Bucket>
<CreationDate>2023-01-23T11:59:03.575496Z</CreationDate>
<Name>doc-example-bucket</Name>
</Bucket>
<Bucket>
<CreationDate>2023-01-23T23:32:13.125238Z</CreationDate>
<Name>doc-example-bucket2</Name>
</Bucket>
</Buckets>
<Owner>
<DisplayName>account-name</DisplayName>
<ID>a3a42310-42d0-46d1-9745-0cee9f4fb851</ID>
</Owner>
</ListAllMyBucketsResult>";
Ok(http::Response::new(SdkBody::from(body)))
}
44 changes: 44 additions & 0 deletions aws/sdk/integration-tests/webassembly/src/adapter/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

mod http_client;

use aws_smithy_client::erase::DynConnector;
use aws_smithy_client::http_connector::HttpConnector;
use aws_smithy_http::body::SdkBody;
use aws_smithy_http::result::ConnectorError;
use std::task::{Context, Poll};
use tower::Service;

#[derive(Default, Debug, Clone)]
pub(crate) struct Adapter {}

impl Adapter {
pub fn to_http_connector() -> impl Into<HttpConnector> {
DynConnector::new(Adapter::default())
}
}

impl Service<http::Request<SdkBody>> for Adapter {
type Response = http::Response<SdkBody>;

type Error = ConnectorError;

#[allow(clippy::type_complexity)]
type Future = std::pin::Pin<
Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>,
>;

fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}

fn call(&mut self, req: http::Request<SdkBody>) -> Self::Future {
println!("Adapter: sending request...");
let res = http_client::make_request(req).unwrap();
println!("{:?}", res);
Box::pin(async move { Ok(res) })
}
}
33 changes: 33 additions & 0 deletions aws/sdk/integration-tests/webassembly/src/default_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

use aws_config::retry::RetryConfig;
use aws_credential_types::Credentials;
use aws_smithy_types::timeout::TimeoutConfig;
use aws_types::region::Region;
use std::future::Future;

use crate::adapter::Adapter;

pub(crate) fn get_default_config() -> impl Future<Output = aws_config::SdkConfig> {
aws_config::from_env()
.region(Region::from_static("us-west-2"))
.credentials_provider(Credentials::from_keys(
"access_key",
"secret_key",
Some("session_token".to_string()),
))
.timeout_config(TimeoutConfig::disabled())
.retry_config(RetryConfig::disabled())
.http_connector(Adapter::to_http_connector())
.load()
}

#[tokio::test]
pub async fn test_default_config() {
let shared_config = get_default_config().await;
let client = aws_sdk_s3::Client::new(&shared_config);
assert_eq!(client.conf().region().unwrap().to_string(), "us-west-2")
}
13 changes: 13 additions & 0 deletions aws/sdk/integration-tests/webassembly/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

mod adapter;
mod default_config;
mod list_buckets;

#[tokio::main(flavor = "current_thread")]
pub async fn main() {
crate::list_buckets::s3_list_buckets().await
}
Loading

0 comments on commit f3c9e84

Please sign in to comment.