From d95cc86400dc5f7d0a06e2c121aa744672b165d9 Mon Sep 17 00:00:00 2001 From: Landon James Date: Tue, 27 Feb 2024 15:40:42 -0800 Subject: [PATCH] Add `aws-smithy-wasm` crate with WASI http client (#3409) ## Motivation and Context This change adds a new crate, `aws-smithy-wasm`, that exports a SDK compatible WASI http client. This is a continuation of the work in #2520 using the now stabilized WASI 0.2.0 interfaces from the [wasi crate](https://crates.io/crates/wasi). This supports, but does not finalize the work for #2087 ## Description Add a new crate, `aws-smithy-wasm` which exports a function `wasi_http_client` that will provide the user with a WASI compatible http client. This client is implemented by using the `wasi::http::outgoing_handler` [ref](https://docs.rs/wasi/0.12.0+wasi-0.2.0/wasi/http/outgoing_handler/index.html) along with some utility implementations of `TryFrom` to transform back and worth between the types from the `http` crate and the `wasi::http` types. It also exports a unit struct `WasmSleep` that impls the `AsyncSleep` trait needed by the SDK. ## Testing This is tested via an integration test in `aws/sdk/integration-tests/webassembly` that uses the wasi http-client to vuild a config and an operation (that is not sent). It is further tested in a new canary (`wasm_canary`) that calls the S3 `list_objects_v2` API. ## Checklist - [X] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Eduardo Rodrigues Co-authored-by: Eduardo de Moura Rodrigues <16357187+eduardomourar@users.noreply.github.com> Co-authored-by: ysaito1001 Co-authored-by: John DiSanti Co-authored-by: Russell Cohen Co-authored-by: John DiSanti --- CHANGELOG.next.toml | 12 + aws/rust-runtime/aws-config/Cargo.toml | 5 +- aws/sdk/integration-tests/.gitignore | 1 + aws/sdk/integration-tests/test.sh | 8 +- .../webassembly/.cargo/config.toml | 6 + .../integration-tests/webassembly/Cargo.toml | 34 +- .../webassembly/src/default_config.rs | 32 - .../integration-tests/webassembly/src/http.rs | 62 - .../webassembly/src/http_client.rs | 54 + .../integration-tests/webassembly/src/lib.rs | 12 +- .../webassembly/src/list_buckets.rs | 20 - .../integration-tests/webassembly/src/wasi.rs | 32 + buildSrc/src/main/kotlin/CrateSet.kt | 1 + rust-runtime/Cargo.toml | 1 + rust-runtime/aws-smithy-wasm/Cargo.toml | 29 + rust-runtime/aws-smithy-wasm/LICENSE | 175 +++ rust-runtime/aws-smithy-wasm/README.md | 7 + .../aws-smithy-wasm/external-types.toml | 5 + rust-runtime/aws-smithy-wasm/src/lib.rs | 20 + rust-runtime/aws-smithy-wasm/src/wasi.rs | 350 +++++ tools/ci-build/Dockerfile | 27 +- tools/ci-cdk/.gitignore | 3 + tools/ci-cdk/README.md | 2 +- tools/ci-cdk/canary-lambda/src/canary.rs | 3 +- tools/ci-cdk/canary-lambda/src/latest.rs | 1 + .../canary-lambda/src/latest/wasm_canary.rs | 172 +++ tools/ci-cdk/canary-lambda/src/main.rs | 2 +- .../ci-cdk/canary-runner/src/build_bundle.rs | 48 + tools/ci-cdk/canary-runner/src/run.rs | 2 +- tools/ci-cdk/canary-wasm/Cargo.lock | 1286 +++++++++++++++++ tools/ci-cdk/canary-wasm/Cargo.toml | 25 + tools/ci-cdk/canary-wasm/src/lib.rs | 63 + tools/ci-cdk/canary-wasm/wit/component.wit | 15 + tools/ci-scripts/check-aws-config | 2 +- ...check-aws-sdk-standalone-integration-tests | 6 + 35 files changed, 2371 insertions(+), 152 deletions(-) delete mode 100644 aws/sdk/integration-tests/webassembly/src/default_config.rs delete mode 100644 aws/sdk/integration-tests/webassembly/src/http.rs create mode 100644 aws/sdk/integration-tests/webassembly/src/http_client.rs delete mode 100644 aws/sdk/integration-tests/webassembly/src/list_buckets.rs create mode 100644 aws/sdk/integration-tests/webassembly/src/wasi.rs create mode 100644 rust-runtime/aws-smithy-wasm/Cargo.toml create mode 100644 rust-runtime/aws-smithy-wasm/LICENSE create mode 100644 rust-runtime/aws-smithy-wasm/README.md create mode 100644 rust-runtime/aws-smithy-wasm/external-types.toml create mode 100644 rust-runtime/aws-smithy-wasm/src/lib.rs create mode 100644 rust-runtime/aws-smithy-wasm/src/wasi.rs create mode 100644 tools/ci-cdk/canary-lambda/src/latest/wasm_canary.rs create mode 100644 tools/ci-cdk/canary-wasm/Cargo.lock create mode 100644 tools/ci-cdk/canary-wasm/Cargo.toml create mode 100644 tools/ci-cdk/canary-wasm/src/lib.rs create mode 100644 tools/ci-cdk/canary-wasm/wit/component.wit diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 097e51e636..a3f0176ff0 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -16,3 +16,15 @@ message = "EKS Pod Identity is now supported as part of the default ECS credenti references = ["smithy-rs#3416"] meta = { "breaking" = false, "bug" = false, "tada" = true } author = "jackkleeman" + +[[aws-sdk-rust]] +message = "Added aws-smithy-wasm crate to enable SDK use in WASI compliant environments" +references = ["smithy-rs#2087", "smithy-rs#2520", "smithy-rs#3409", "aws-sdk-rust#59"] +meta = { "breaking" = false, "tada" = true, "bug" = false } +author = "landonxjames" + +[[smithy-rs]] +message = "Added aws-smithy-wasm crate to enable SDK use in WASI compliant environments" +references = ["smithy-rs#2087", "smithy-rs#2520", "smithy-rs#3409"] +meta = { "breaking" = false, "tada" = true, "bug" = false, "target" = "client"} +author = "landonxjames" diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index bd84fc4f9c..327723871b 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "aws-config" version = "0.0.0-smithy-rs-head" -authors = ["AWS Rust SDK Team ", "Russell Cohen "] +authors = [ + "AWS Rust SDK Team ", + "Russell Cohen ", +] description = "AWS SDK config and credential provider implementations." edition = "2021" exclude = ["test-data/*", "integration-tests/*"] diff --git a/aws/sdk/integration-tests/.gitignore b/aws/sdk/integration-tests/.gitignore index 1e7caa9ea8..5f5fe6daf2 100644 --- a/aws/sdk/integration-tests/.gitignore +++ b/aws/sdk/integration-tests/.gitignore @@ -1,2 +1,3 @@ Cargo.lock target/ +webassembly/src/bindings.rs diff --git a/aws/sdk/integration-tests/test.sh b/aws/sdk/integration-tests/test.sh index d26a34a116..88026f6d7c 100755 --- a/aws/sdk/integration-tests/test.sh +++ b/aws/sdk/integration-tests/test.sh @@ -11,6 +11,12 @@ for f in *; do echo echo "Testing ${f}..." echo "###############" - cargo test --manifest-path "${f}/Cargo.toml" --all-features + if [ "$f" != "webassembly" ]; then + cargo test --manifest-path "${f}/Cargo.toml" --all-features + else + # The webassembly tests use a custom runner set in config.toml that + # is not picked up when running the tests outside of the package + cd webassembly && cargo component test --all-features --all-targets && cd .. + fi fi done diff --git a/aws/sdk/integration-tests/webassembly/.cargo/config.toml b/aws/sdk/integration-tests/webassembly/.cargo/config.toml index 031dad7377..06b75c8e09 100644 --- a/aws/sdk/integration-tests/webassembly/.cargo/config.toml +++ b/aws/sdk/integration-tests/webassembly/.cargo/config.toml @@ -3,3 +3,9 @@ target = "wasm32-wasi" [target.wasm32-wasi] rustflags = ["-C", "opt-level=1"] +runner = [ + "wasmtime", + "-C", "cache=n", + "-S", "preview2=y", + "-S", "http=y" +] diff --git a/aws/sdk/integration-tests/webassembly/Cargo.toml b/aws/sdk/integration-tests/webassembly/Cargo.toml index 379f3d8e7d..66c06fd06e 100644 --- a/aws/sdk/integration-tests/webassembly/Cargo.toml +++ b/aws/sdk/integration-tests/webassembly/Cargo.toml @@ -2,7 +2,9 @@ [package] name = "webassembly" version = "0.1.0" -authors = ["Eduardo Rodrigues <16357187+eduardomourar@users.noreply.github.com>"] +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. @@ -12,20 +14,34 @@ license = "Apache-2.0" repository = "https://github.com/smithy-lang/smithy-rs" publish = false -[lib] -crate-type = ["cdylib"] +[features] +default = ["test-util"] +test-util = [] # 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", "behavior-version-latest"]} +[target.'cfg(target_family = "wasm")'.dependencies] +aws-config = { path = "../../build/aws-sdk/sdk/aws-config", default-features = false, features = [ + "rt-tokio", + "behavior-version-latest" +] } 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-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["client"] } aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["client"] } 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" +aws-smithy-wasm = { path = "../../build/aws-sdk/sdk/aws-smithy-wasm" } +http = "0.2.9" +tokio = { version = "1.32.0", features = ["macros", "rt"] } + +[target.'cfg(all(target_family = "wasm", target_os = "wasi"))'.dependencies] +wit-bindgen = { version = "0.16.0", features = ["macros", "realloc"] } + + +[lib] +crate-type = ["cdylib"] + +# metadata used by cargo-component to identify which wit world to embed in the binary +[package.metadata.component] +package = "aws:component" diff --git a/aws/sdk/integration-tests/webassembly/src/default_config.rs b/aws/sdk/integration-tests/webassembly/src/default_config.rs deleted file mode 100644 index c87a364b5e..0000000000 --- a/aws/sdk/integration-tests/webassembly/src/default_config.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use crate::http::WasmHttpConnector; -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; - -pub(crate) fn get_default_config() -> impl Future { - 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_client(WasmHttpConnector::new()) - .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.config().region().unwrap().to_string(), "us-west-2") -} diff --git a/aws/sdk/integration-tests/webassembly/src/http.rs b/aws/sdk/integration-tests/webassembly/src/http.rs deleted file mode 100644 index 19354d0606..0000000000 --- a/aws/sdk/integration-tests/webassembly/src/http.rs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_smithy_runtime_api::client::http::{ - HttpClient, HttpConnector, HttpConnectorFuture, HttpConnectorSettings, SharedHttpConnector, -}; -use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; -use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; -use aws_smithy_runtime_api::shared::IntoShared; -use aws_smithy_types::body::SdkBody; - -pub(crate) fn make_request(_req: HttpRequest) -> Result { - // Consumers here would pass the HTTP request to - // the Wasm host in order to get the response back - let body = " - - - - 2023-01-23T11:59:03.575496Z - doc-example-bucket - - - 2023-01-23T23:32:13.125238Z - doc-example-bucket2 - - - - account-name - a3a42310-42d0-46d1-9745-0cee9f4fb851 - - "; - Ok(HttpResponse::try_from(http::Response::new(SdkBody::from(body))).unwrap()) -} - -#[derive(Default, Debug, Clone)] -pub(crate) struct WasmHttpConnector; -impl WasmHttpConnector { - pub fn new() -> Self { - Self - } -} - -impl HttpConnector for WasmHttpConnector { - fn call(&self, request: HttpRequest) -> HttpConnectorFuture { - println!("Adapter: sending request..."); - let res = make_request(request).unwrap(); - println!("{:?}", res); - HttpConnectorFuture::new(async move { Ok(res) }) - } -} - -impl HttpClient for WasmHttpConnector { - fn http_connector( - &self, - _settings: &HttpConnectorSettings, - _components: &RuntimeComponents, - ) -> SharedHttpConnector { - self.clone().into_shared() - } -} diff --git a/aws/sdk/integration-tests/webassembly/src/http_client.rs b/aws/sdk/integration-tests/webassembly/src/http_client.rs new file mode 100644 index 0000000000..5737b0fa3a --- /dev/null +++ b/aws/sdk/integration-tests/webassembly/src/http_client.rs @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_config::retry::RetryConfig; +use aws_sdk_s3::operation::list_objects_v2::builders::ListObjectsV2FluentBuilder; +use aws_sdk_s3::Client; +use aws_smithy_types::timeout::TimeoutConfig; +use aws_smithy_wasm::wasi::WasiHttpClientBuilder; + +pub(crate) async fn get_default_wasi_config() -> aws_config::SdkConfig { + let http_client = WasiHttpClientBuilder::new().build(); + aws_config::from_env() + .region("us-east-2") + .timeout_config(TimeoutConfig::disabled()) + .retry_config(RetryConfig::disabled()) + .no_credentials() + .http_client(http_client) + .load() + .await +} + +#[tokio::test] +pub async fn test_default_config() { + let shared_config = get_default_wasi_config().await; + let client = aws_sdk_s3::Client::new(&shared_config); + assert_eq!(client.config().region().unwrap().to_string(), "us-east-2") +} + +async fn s3_list_objects_operation() -> ListObjectsV2FluentBuilder { + let shared_config = get_default_wasi_config().await; + let client = Client::new(&shared_config); + let operation = client + .list_objects_v2() + .bucket("nara-national-archives-catalog") + .delimiter("/") + .prefix("authority-records/organization/") + .max_keys(5); + + operation +} + +// Test constructing an operation using an SdkConfig with a WASI http client +// We do not send the request to keep these tests sandboxable, a full test of +// the client is in the SDK canary. +#[tokio::test] +pub async fn test_operation_construction() { + let operation = s3_list_objects_operation().await; + assert_eq!( + operation.get_bucket(), + &Some("nara-national-archives-catalog".to_string()) + ); +} diff --git a/aws/sdk/integration-tests/webassembly/src/lib.rs b/aws/sdk/integration-tests/webassembly/src/lib.rs index 1c4932afbb..3c29305c06 100644 --- a/aws/sdk/integration-tests/webassembly/src/lib.rs +++ b/aws/sdk/integration-tests/webassembly/src/lib.rs @@ -3,11 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -mod default_config; -mod http; -mod list_buckets; +#![allow(dead_code)] -#[tokio::main(flavor = "current_thread")] -pub async fn main() { - crate::list_buckets::s3_list_buckets().await -} +#[cfg(target_family = "wasm")] +mod http_client; +#[cfg(all(target_family = "wasm", target_os = "wasi"))] +mod wasi; diff --git a/aws/sdk/integration-tests/webassembly/src/list_buckets.rs b/aws/sdk/integration-tests/webassembly/src/list_buckets.rs deleted file mode 100644 index 692be0e071..0000000000 --- a/aws/sdk/integration-tests/webassembly/src/list_buckets.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -pub async fn s3_list_buckets() { - use aws_sdk_s3::Client; - - use crate::default_config::get_default_config; - - let shared_config = get_default_config().await; - let client = Client::new(&shared_config); - let result = client.list_buckets().send().await.unwrap(); - assert_eq!(result.buckets().len(), 2) -} - -#[tokio::test] -pub async fn test_s3_list_buckets() { - s3_list_buckets().await -} diff --git a/aws/sdk/integration-tests/webassembly/src/wasi.rs b/aws/sdk/integration-tests/webassembly/src/wasi.rs new file mode 100644 index 0000000000..fc131bc69b --- /dev/null +++ b/aws/sdk/integration-tests/webassembly/src/wasi.rs @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Needed for WASI-compliant environment as it expects specific functions +// to be exported such as `cabi_realloc`, `_start`, etc. + +wit_bindgen::generate!({ + inline: " + package aws:component; + + interface run { + run: func() -> result; + } + + world main { + export run; + } + ", + exports: { + "aws:component/run": Component + } +}); + +struct Component; + +impl exports::aws::component::run::Guest for Component { + fn run() -> Result<(), ()> { + Ok(()) + } +} diff --git a/buildSrc/src/main/kotlin/CrateSet.kt b/buildSrc/src/main/kotlin/CrateSet.kt index e3ad6e49ee..bf36433e17 100644 --- a/buildSrc/src/main/kotlin/CrateSet.kt +++ b/buildSrc/src/main/kotlin/CrateSet.kt @@ -70,6 +70,7 @@ object CrateSet { "aws-smithy-runtime-api", "aws-smithy-types", "aws-smithy-types-convert", + "aws-smithy-wasm", "aws-smithy-xml", ).map { Crate(it, version(it)) } diff --git a/rust-runtime/Cargo.toml b/rust-runtime/Cargo.toml index 142b137050..74d3258886 100644 --- a/rust-runtime/Cargo.toml +++ b/rust-runtime/Cargo.toml @@ -18,6 +18,7 @@ members = [ "aws-smithy-runtime-api", "aws-smithy-types", "aws-smithy-types-convert", + "aws-smithy-wasm", "aws-smithy-mocks-experimental", "aws-smithy-xml", ] diff --git a/rust-runtime/aws-smithy-wasm/Cargo.toml b/rust-runtime/aws-smithy-wasm/Cargo.toml new file mode 100644 index 0000000000..7dc1dd4cc1 --- /dev/null +++ b/rust-runtime/aws-smithy-wasm/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "aws-smithy-wasm" +version = "0.1.0" +authors = [ + "AWS Rust SDK Team ", + "Eduardo Rodrigues <16357187+eduardomourar@users.noreply.github.com>", +] +description = "Smithy WebAssembly configuration for smithy-rs." +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" + +[dependencies] +aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api", features = ["http-1x"]} +aws-smithy-http = { path = "../aws-smithy-http" } +aws-smithy-types = { path = "../aws-smithy-types" } +bytes = "1" +http = "1.0.0" +tracing = "0.1.40" +# Note the wasi crate will only build for target wasm32-wasi, but having a target +# statement here breaks some of the CI tests, so we leave it with the rest of the deps +wasi = "0.12.0+wasi-0.2.0" + +[package.metadata.docs.rs] +all-features = true +targets = ["x86_64-unknown-linux-gnu"] +cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] +rustdoc-args = ["--cfg", "docsrs"] +# End of docs.rs metadata diff --git a/rust-runtime/aws-smithy-wasm/LICENSE b/rust-runtime/aws-smithy-wasm/LICENSE new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/rust-runtime/aws-smithy-wasm/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/rust-runtime/aws-smithy-wasm/README.md b/rust-runtime/aws-smithy-wasm/README.md new file mode 100644 index 0000000000..5190145609 --- /dev/null +++ b/rust-runtime/aws-smithy-wasm/README.md @@ -0,0 +1,7 @@ +# aws-smithy-wasm + +WebAssembly and WASI related configuration for service clients generated by [smithy-rs](https://github.com/awslabs/smithy-rs). + + +This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/smithy-lang/smithy-rs) code generator. In most cases, it should not be used directly. + diff --git a/rust-runtime/aws-smithy-wasm/external-types.toml b/rust-runtime/aws-smithy-wasm/external-types.toml new file mode 100644 index 0000000000..25256eeaba --- /dev/null +++ b/rust-runtime/aws-smithy-wasm/external-types.toml @@ -0,0 +1,5 @@ +allowed_external_types = [ + "aws_smithy_runtime_api::client::http::HttpClient", + "aws_smithy_runtime_api::client::http::SharedHttpClient", + "aws_smithy_runtime_api::client::http::HttpConnector" +] diff --git a/rust-runtime/aws-smithy-wasm/src/lib.rs b/rust-runtime/aws-smithy-wasm/src/lib.rs new file mode 100644 index 0000000000..84e9b7dd9f --- /dev/null +++ b/rust-runtime/aws-smithy-wasm/src/lib.rs @@ -0,0 +1,20 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Automatically managed default lints */ +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +/* End of automatically managed default lints */ +#![allow(clippy::derive_partial_eq_without_eq)] +#![warn( + missing_docs, + rustdoc::missing_crate_level_docs, + unreachable_pub, + rust_2018_idioms +)] + +//! Smithy WebAssembly + +/// Tools for using Smithy SDKs in WASI environments +pub mod wasi; diff --git a/rust-runtime/aws-smithy-wasm/src/wasi.rs b/rust-runtime/aws-smithy-wasm/src/wasi.rs new file mode 100644 index 0000000000..1c32d3ee94 --- /dev/null +++ b/rust-runtime/aws-smithy-wasm/src/wasi.rs @@ -0,0 +1,350 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! WASI HTTP Adapter +use aws_smithy_http::header::ParseError; +use aws_smithy_runtime_api::{ + client::{ + http::{ + HttpClient, HttpConnector, HttpConnectorFuture, HttpConnectorSettings, + SharedHttpClient, SharedHttpConnector, + }, + orchestrator::HttpRequest, + result::ConnectorError, + runtime_components::RuntimeComponents, + }, + http::Response, + shared::IntoShared, +}; +use aws_smithy_types::body::SdkBody; +use bytes::{Bytes, BytesMut}; +use wasi::http::{ + outgoing_handler, + types::{self as wasi_http, OutgoingBody, RequestOptions}, +}; + +/// Builder for [`WasiHttpClient`]. Currently empty, but allows for future +/// config options to be added in a backwards compatible manner. +#[derive(Default, Debug)] +#[non_exhaustive] +pub struct WasiHttpClientBuilder {} + +impl WasiHttpClientBuilder { + /// Creates a new builder. + pub fn new() -> Self { + Default::default() + } + + /// Builds the [`WasiHttpClient`]. + pub fn build(self) -> SharedHttpClient { + let client = WasiHttpClient {}; + client.into_shared() + } +} + +/// An HTTP client that can be used during instantiation of the client SDK in +/// order to route the HTTP requests through the WebAssembly host. The host must +/// support the WASI HTTP proposal as defined in the Preview 2 specification. +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct WasiHttpClient {} + +impl HttpClient for WasiHttpClient { + fn http_connector( + &self, + settings: &HttpConnectorSettings, + _components: &RuntimeComponents, + ) -> SharedHttpConnector { + let options = WasiRequestOptions::from(settings); + let connector = WasiHttpConnector { options }; + + connector.into_shared() + } +} + +/// HTTP connector used in WASI environment +#[derive(Debug, Clone)] +struct WasiHttpConnector { + options: WasiRequestOptions, +} + +impl HttpConnector for WasiHttpConnector { + fn call(&self, request: HttpRequest) -> HttpConnectorFuture { + tracing::trace!("WasiHttpConnector: sending request {request:?}"); + + let client = WasiDefaultClient::new(self.options.clone()); + let http_req = request.try_into_http1x().expect("Http request invalid"); + let converted_req = http_req.map(|body| match body.bytes() { + Some(value) => Bytes::copy_from_slice(value), + None => Bytes::new(), + }); + + let fut_result = client.handle(converted_req); + + HttpConnectorFuture::new(async move { + let fut = fut_result?; + let response = fut.map(|body| { + if body.is_empty() { + SdkBody::empty() + } else { + SdkBody::from(body) + } + }); + tracing::trace!("WasiHttpConnector: response received {response:?}"); + + let sdk_res = Response::try_from(response) + .map_err(|err| ConnectorError::other(err.into(), None))?; + + Ok(sdk_res) + }) + } +} + +/// WASI HTTP client containing the options passed to the outgoing_handler +struct WasiDefaultClient { + options: WasiRequestOptions, +} + +impl WasiDefaultClient { + /// Create a new WASI HTTP client. + fn new(options: WasiRequestOptions) -> Self { + Self { options } + } + + /// Make outgoing HTTP request in a WASI environment + fn handle(&self, req: http::Request) -> Result, ConnectorError> { + let req = + WasiRequest::try_from(req).map_err(|err| ConnectorError::other(err.into(), None))?; + + let res = outgoing_handler::handle(req.0, self.options.clone().0) + .map_err(|err| ConnectorError::other(err.into(), None))?; + + // Right now only synchronous calls can be made through WASI, so we subscribe and + // block on the FutureIncomingResponse + let subscription = res.subscribe(); + subscription.block(); + + //The FutureIncomingResponse .get() method returns a + //Option, ()>>. + //The outer Option ensures readiness which we know is Some because we .block() waiting for it + //The outer Result is just a singleton enforcer so we can only get the response once + //The inner Result indicates whether the HTTP call was sent/received successfully (not the 200 succes of the call) + let incoming_res = res + .get() + .expect("Http response not ready") + .expect("Http response accessed more than once") + .map_err(|err| ConnectorError::other(err.into(), None))?; + + let response = http::Response::try_from(WasiResponse(incoming_res)) + .map_err(|err| ConnectorError::other(err.into(), None))?; + + Ok(response) + } +} + +/// Wrapper for the WASI RequestOptions type to allow us to impl Clone +#[derive(Debug)] +struct WasiRequestOptions(Option); + +impl From<&HttpConnectorSettings> for WasiRequestOptions { + fn from(value: &HttpConnectorSettings) -> Self { + //The WASI Duration is nanoseconds represented as u64 + //Note: that the HttpConnectorSettings provides nanoseconds as u128 + //so here we are clamping to u64::MAX if the value is above that + let connect_timeout = value + .connect_timeout() + .map(|dur| u64::try_from(dur.as_nanos()).unwrap_or(u64::MAX)); + let read_timeout = value + .read_timeout() + .map(|dur| u64::try_from(dur.as_nanos()).unwrap_or(u64::MAX)); + + //Note: these only fail if setting this particular type of timeout is not + //supported. Spec compliant runtimes should always support these so it is + //unlikely to be an issue. + let wasi_http_opts = wasi_http::RequestOptions::new(); + wasi_http_opts + .set_connect_timeout(connect_timeout) + .expect("Connect timeout not supported"); + wasi_http_opts + .set_first_byte_timeout(read_timeout) + .expect("Read timeout not supported"); + + WasiRequestOptions(Some(wasi_http_opts)) + } +} +//The WASI RequestOptions type doesn't impl copy or clone but the outgoing_handler::handle method +//takes ownership, so we impl it on this wrapper type +impl Clone for WasiRequestOptions { + fn clone(&self) -> Self { + //Note none of the expects here should ever trigger since all of the values passed in are from + //the existing RequestOptions that is being cloned and should be valid + let new_opts = if let Some(opts) = &self.0 { + let new_opts = RequestOptions::new(); + new_opts + .set_between_bytes_timeout(opts.between_bytes_timeout()) + .expect("Between bytes timeout"); + new_opts + .set_connect_timeout(opts.connect_timeout()) + .expect("Connect timeout"); + new_opts + .set_first_byte_timeout(opts.first_byte_timeout()) + .expect("First byte timeout"); + + Some(new_opts) + } else { + None + }; + + Self(new_opts) + } +} + +/// Wrapper to allow converting between HTTP Request types and WASI Request types +#[derive(Debug)] +struct WasiRequest(outgoing_handler::OutgoingRequest); + +impl TryFrom> for WasiRequest { + type Error = ParseError; + + fn try_from(value: http::Request) -> Result { + let (parts, body) = value.into_parts(); + let method = WasiMethod::try_from(parts.method)?; + let path_with_query = parts.uri.path_and_query().map(|path| path.as_str()); + let headers = WasiHeaders::try_from(parts.headers)?; + let scheme = match parts.uri.scheme_str().unwrap_or("") { + "http" => Some(&wasi_http::Scheme::Http), + "https" => Some(&wasi_http::Scheme::Https), + _ => None, + }; + let authority = parts.uri.authority().map(|auth| auth.as_str()); + + let request = wasi_http::OutgoingRequest::new(headers.0); + request + .set_scheme(scheme) + .map_err(|_| ParseError::new("Failed to set HTTP scheme"))?; + request + .set_method(&method.0) + .map_err(|_| ParseError::new("Failed to set HTTP method"))?; + request + .set_path_with_query(path_with_query) + .map_err(|_| ParseError::new("Failed to set HTTP path"))?; + request + .set_authority(authority) + .map_err(|_| ParseError::new("Failed to set HTTP authority"))?; + + let request_body = request.body().expect("Body accessed more than once"); + + let request_stream = request_body + .write() + .expect("Output stream accessed more than once"); + + request_stream + .blocking_write_and_flush(&body) + .map_err(|_| ParseError::new("Failed to write HTTP body"))?; + + //The OutputStream is a child resource: it must be dropped + //before the parent OutgoingBody resource is dropped (or finished), + //otherwise the OutgoingBody drop or finish will trap. + drop(request_stream); + + OutgoingBody::finish(request_body, None) + .map_err(|_| ParseError::new("Failed to finalize HTTP body"))?; + + Ok(WasiRequest(request)) + } +} + +/// Wrapper to allow converting between HTTP Methods and WASI Methods +struct WasiMethod(wasi_http::Method); + +impl TryFrom for WasiMethod { + type Error = ParseError; + + fn try_from(method: http::Method) -> Result { + Ok(Self(match method { + http::Method::GET => wasi_http::Method::Get, + http::Method::POST => wasi_http::Method::Post, + http::Method::PUT => wasi_http::Method::Put, + http::Method::DELETE => wasi_http::Method::Delete, + http::Method::PATCH => wasi_http::Method::Patch, + http::Method::CONNECT => wasi_http::Method::Connect, + http::Method::TRACE => wasi_http::Method::Trace, + http::Method::HEAD => wasi_http::Method::Head, + http::Method::OPTIONS => wasi_http::Method::Options, + _ => return Err(ParseError::new("failed due to unsupported method, currently supported methods are: GET, POST, PUT, DELETE, PATCH, CONNECT, TRACE, HEAD, and OPTIONS")), + })) + } +} + +/// Wrapper to allow converting between HTTP Response types and WASI Response types +struct WasiResponse(wasi_http::IncomingResponse); + +impl TryFrom for http::Response { + type Error = ParseError; + + fn try_from(value: WasiResponse) -> Result { + let response = value.0; + + let status = response.status(); + + //This headers resource is a child: it must be dropped before the parent incoming-response is dropped. + //The drop happens via the consuming iterator used below + let headers = response.headers().entries(); + + let res_build = headers + .into_iter() + .fold(http::Response::builder().status(status), |rb, header| { + rb.header(header.0, header.1) + }); + + let body_incoming = response.consume().expect("Consume called more than once"); + + //The input-stream resource is a child: it must be dropped before the parent + //incoming-body is dropped, or consumed by incoming-body.finish. + //That drop is done explicitly below + let body_stream = body_incoming + .stream() + .expect("Stream accessed more than once"); + + let mut body = BytesMut::new(); + + //blocking_read blocks until at least one byte is available + while let Ok(stream_bytes) = body_stream.blocking_read(u64::MAX) { + body.extend_from_slice(stream_bytes.as_slice()) + } + + drop(body_stream); + + let res = res_build + .body(body.freeze()) + .map_err(|err| ParseError::new(err.to_string()))?; + + Ok(res) + } +} + +/// Wrapper to allow converting between HTTP headers and WASI headers +struct WasiHeaders(wasi_http::Fields); + +impl TryFrom for WasiHeaders { + type Error = ParseError; + + fn try_from(headers: http::HeaderMap) -> Result { + let entries = headers + .iter() + .map(|(name, value)| { + ( + name.to_string(), + value.to_str().unwrap().as_bytes().to_vec(), + ) + }) + .collect::>(); + + let fields = wasi_http::Fields::from_list(&entries) + .map_err(|err| ParseError::new(err.to_string()))?; + + Ok(Self(fields)) + } +} diff --git a/tools/ci-build/Dockerfile b/tools/ci-build/Dockerfile index d6262863c7..a6bc44128d 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -15,9 +15,9 @@ RUN yum -y updateinfo FROM bare_base_image as musl_toolchain RUN yum -y install --allowerasing tar gzip gcc make RUN curl https://musl.libc.org/releases/musl-1.2.3.tar.gz -o musl-1.2.3.tar.gz \ - && ls \ - && tar xvzf musl-1.2.3.tar.gz \ - && (cd musl-1.2.3 && ./configure && make install) + && ls \ + && tar xvzf musl-1.2.3.tar.gz \ + && (cd musl-1.2.3 && ./configure && make install) # # Rust & Tools Installation Stage @@ -121,17 +121,14 @@ ARG wasm_pack_version=0.11.0 RUN cargo install wasm-pack --locked --version ${wasm_pack_version} FROM install_rust AS wasmtime -ARG wasmtime_precompiled_url=https://github.com/bytecodealliance/wasmtime/releases/download/v7.0.0/wasmtime-v7.0.0-x86_64-linux.tar.xz -ARG wasmtime_precompiled_sha256=b8a1c97f9107c885ea73a5c38677d0d340a7c26879d366e8a5f3dce84cffec99 -RUN set -eux; \ - curl "${wasmtime_precompiled_url}" -L -o wasmtime.xz; \ - echo "${wasmtime_precompiled_sha256} wasmtime.xz" | sha256sum --check; \ - tar xf wasmtime.xz; \ - mv wasmtime-v*/wasmtime /opt; +ARG cargo_wasmtime_version=18.0.1 +ARG rust_nightly_version +RUN cargo install wasmtime-cli --features="component-model" --locked --version ${cargo_wasmtime_version} -FROM install_rust AS cargo_wasi -ARG cargo_wasi_version=0.1.27 -RUN cargo install cargo-wasi --locked --version ${cargo_wasi_version} +FROM install_rust AS cargo_component +ARG cargo_component_version=0.7.1 +ARG rust_nightly_version +RUN cargo +${rust_nightly_version} install cargo-component --locked --version ${cargo_component_version} FROM install_rust AS cargo_semver_checks ARG cargo_semver_checks_version=0.24.1 @@ -182,8 +179,8 @@ COPY --chown=build:build --from=cargo_minimal_versions /opt/cargo/bin/cargo-mini COPY --chown=build:build --from=cargo_check_external_types /opt/cargo/bin/cargo-check-external-types /opt/cargo/bin/cargo-check-external-types COPY --chown=build:build --from=maturin /opt/cargo/bin/maturin /opt/cargo/bin/maturin COPY --chown=build:build --from=wasm_pack /opt/cargo/bin/wasm-pack /opt/cargo/bin/wasm-pack -COPY --chown=build:build --from=wasmtime /opt/wasmtime /opt/cargo/bin/wasmtime -COPY --chown=build:build --from=cargo_wasi /opt/cargo/bin/cargo-wasi /opt/cargo/bin/cargo-wasi +COPY --chown=build:build --from=wasmtime /opt/cargo/bin/wasmtime /opt/cargo/bin/wasmtime +COPY --chown=build:build --from=cargo_component /opt/cargo/bin/cargo-component /opt/cargo/bin/cargo-component COPY --chown=build:build --from=install_rust /opt/rustup /opt/rustup COPY --chown=build:build --from=cargo_semver_checks /opt/cargo/bin/cargo-semver-checks /opt/cargo/bin/cargo-semver-checks COPY --chown=build:build --from=cargo_mdbook /opt/cargo/bin/mdbook /opt/cargo/bin/mdbook diff --git a/tools/ci-cdk/.gitignore b/tools/ci-cdk/.gitignore index f6764db4c1..135a9d8508 100644 --- a/tools/ci-cdk/.gitignore +++ b/tools/ci-cdk/.gitignore @@ -8,3 +8,6 @@ build .cdk.staging cdk.out cdk-outputs.json + +# Generated Rust file +canary-wasm/src/bindings.rs diff --git a/tools/ci-cdk/README.md b/tools/ci-cdk/README.md index cbbd304293..34c78d6ea2 100644 --- a/tools/ci-cdk/README.md +++ b/tools/ci-cdk/README.md @@ -24,7 +24,7 @@ cd canary-runner cargo run -- run --sdk-release-tag --musl --cdk-output ../cdk-outputs.json ``` -__NOTE:__ You may want to add a `--profile` to the `deploy` command to select a specific credential +**NOTE:** You may want to add a `--profile` to the deploy command to select a specific credential profile to deploy to if you don't want to use the default. Also, if this is a new test AWS account, be sure it CDK bootstrap it before attempting to deploy. diff --git a/tools/ci-cdk/canary-lambda/src/canary.rs b/tools/ci-cdk/canary-lambda/src/canary.rs index 0f3a986a09..6b60811d4e 100644 --- a/tools/ci-cdk/canary-lambda/src/canary.rs +++ b/tools/ci-cdk/canary-lambda/src/canary.rs @@ -12,7 +12,7 @@ use aws_config::SdkConfig; use tracing::{info_span, Instrument}; use crate::current_canary::paginator_canary; -use crate::current_canary::{s3_canary, transcribe_canary}; +use crate::current_canary::{s3_canary, transcribe_canary, wasm_canary}; #[macro_export] macro_rules! mk_canary { @@ -35,6 +35,7 @@ pub fn get_canaries_to_run( paginator_canary::mk_canary(&sdk_config, &env), s3_canary::mk_canary(&sdk_config, &env), transcribe_canary::mk_canary(&sdk_config, &env), + wasm_canary::mk_canary(&sdk_config, &env), ]; canaries diff --git a/tools/ci-cdk/canary-lambda/src/latest.rs b/tools/ci-cdk/canary-lambda/src/latest.rs index 238c361166..f034a5578a 100644 --- a/tools/ci-cdk/canary-lambda/src/latest.rs +++ b/tools/ci-cdk/canary-lambda/src/latest.rs @@ -6,3 +6,4 @@ pub(crate) mod paginator_canary; pub(crate) mod s3_canary; pub(crate) mod transcribe_canary; +pub(crate) mod wasm_canary; diff --git a/tools/ci-cdk/canary-lambda/src/latest/wasm_canary.rs b/tools/ci-cdk/canary-lambda/src/latest/wasm_canary.rs new file mode 100644 index 0000000000..4832a58746 --- /dev/null +++ b/tools/ci-cdk/canary-lambda/src/latest/wasm_canary.rs @@ -0,0 +1,172 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::{mk_canary, CanaryEnv}; + +use aws_config::SdkConfig; +use wasmtime::component::{bindgen, Component, Linker}; +use wasmtime::{Engine, Store}; +use wasmtime_wasi::preview2::WasiCtxBuilder; + +mk_canary!("wasm", |_sdk_config: &SdkConfig, _env: &CanaryEnv| { + wasm_canary() +}); + +//This macro creates bindings to call the wasm functions in Rust +bindgen!({ + world: "canary-world", + path: "../canary-wasm/wit/component.wit", + async: true +}); + +struct WasiHostCtx { + preview2_ctx: wasmtime_wasi::preview2::WasiCtx, + preview2_table: wasmtime::component::ResourceTable, + wasi_http_ctx: wasmtime_wasi_http::WasiHttpCtx, +} + +impl wasmtime_wasi::preview2::WasiView for WasiHostCtx { + fn table(&self) -> &wasmtime::component::ResourceTable { + &self.preview2_table + } + + fn ctx(&self) -> &wasmtime_wasi::preview2::WasiCtx { + &self.preview2_ctx + } + + fn table_mut(&mut self) -> &mut wasmtime::component::ResourceTable { + &mut self.preview2_table + } + + fn ctx_mut(&mut self) -> &mut wasmtime_wasi::preview2::WasiCtx { + &mut self.preview2_ctx + } +} + +impl wasmtime_wasi_http::types::WasiHttpView for WasiHostCtx { + fn ctx(&mut self) -> &mut wasmtime_wasi_http::WasiHttpCtx { + &mut self.wasi_http_ctx + } + + fn table(&mut self) -> &mut wasmtime::component::ResourceTable { + &mut self.preview2_table + } +} + +pub async fn wasm_canary() -> anyhow::Result<()> { + let wasm_bin_path = std::env::current_dir() + .expect("Current dir") + .join("aws_sdk_rust_lambda_canary_wasm.wasm"); + + // Create a Wasmtime Engine configured to run Components + let engine = Engine::new( + wasmtime::Config::new() + .wasm_component_model(true) + .async_support(true), + )?; + + // Create our component from the wasm file + let component = Component::from_file(&engine, wasm_bin_path)?; + + // Create the linker and link in the necessary WASI bindings + let mut linker: Linker = Linker::new(&engine); + link_all_the_things(&mut linker); + + // Configure and create a `WasiCtx`, which WASI functions need access to + // through the host state of the store (which in this case is the host state + // of the store) + let wasi_ctx = WasiCtxBuilder::new() + .inherit_stderr() + .inherit_stdout() + .build(); + + let host_ctx = WasiHostCtx { + preview2_ctx: wasi_ctx, + preview2_table: wasmtime_wasi::preview2::ResourceTable::new(), + wasi_http_ctx: wasmtime_wasi_http::WasiHttpCtx {}, + }; + + let mut store: Store = Store::new(&engine, host_ctx); + + // Instantiate our module with the bindgen! bindings + let (bindings, _) = CanaryWorld::instantiate_async(&mut store, &component, &linker).await?; + + let canary_interface = bindings.aws_component_canary_interface(); + let api_result = canary_interface + .call_run_canary(store) + .await? + .map_err(anyhow::Error::msg)?; + + // Asserting on the post FFI result to confirm everything in the wasm module worked + assert!(!api_result.is_empty()); + + Ok(()) +} + +/// This function adds all of the WASI bindings to the linker +fn link_all_the_things(linker: &mut Linker) { + //IO + wasmtime_wasi::preview2::bindings::io::poll::add_to_linker(linker, |cx| cx) + .expect("Failed to link Poll"); + wasmtime_wasi::preview2::bindings::io::error::add_to_linker(linker, |cx| cx) + .expect("Failed to link Error"); + wasmtime_wasi::preview2::bindings::io::streams::add_to_linker(linker, |cx| cx) + .expect("Failed to link Streams"); + + //Random + wasmtime_wasi::preview2::bindings::random::random::add_to_linker(linker, |cx| cx) + .expect("Failed to link Random"); + + //Clocks + wasmtime_wasi::preview2::bindings::wasi::clocks::monotonic_clock::add_to_linker(linker, |cx| { + cx + }) + .expect("Failed to link Clock"); + wasmtime_wasi::preview2::bindings::wasi::clocks::wall_clock::add_to_linker(linker, |cx| cx) + .expect("Failed to link Wall Clock"); + + //Filesystem + wasmtime_wasi::preview2::bindings::filesystem::types::add_to_linker(linker, |cx| cx) + .expect("Failed to link Filesystem Types"); + wasmtime_wasi::preview2::bindings::filesystem::preopens::add_to_linker(linker, |cx| cx) + .expect("Failed to link Filesystem Preopen"); + + //CLI + wasmtime_wasi::preview2::bindings::wasi::cli::environment::add_to_linker(linker, |cx| cx) + .expect("Failed to link Environment"); + wasmtime_wasi::preview2::bindings::wasi::cli::exit::add_to_linker(linker, |cx| cx) + .expect("Failed to link Environment"); + wasmtime_wasi::preview2::bindings::wasi::cli::stdin::add_to_linker(linker, |cx| cx) + .expect("Failed to link Stdin"); + wasmtime_wasi::preview2::bindings::wasi::cli::stdout::add_to_linker(linker, |cx| cx) + .expect("Failed to link Stdout"); + wasmtime_wasi::preview2::bindings::wasi::cli::stderr::add_to_linker(linker, |cx| cx) + .expect("Failed to link Stderr"); + + // CLI Terminal + wasmtime_wasi::preview2::bindings::wasi::cli::terminal_input::add_to_linker(linker, |cx| cx) + .expect("Failed to link Terminal Input"); + wasmtime_wasi::preview2::bindings::wasi::cli::terminal_output::add_to_linker(linker, |cx| cx) + .expect("Failed to link Terminal Output"); + wasmtime_wasi::preview2::bindings::wasi::cli::terminal_stdin::add_to_linker(linker, |cx| cx) + .expect("Failed to link Terminal Stdin"); + wasmtime_wasi::preview2::bindings::wasi::cli::terminal_stdout::add_to_linker(linker, |cx| cx) + .expect("Failed to link Terminal Stdout"); + wasmtime_wasi::preview2::bindings::wasi::cli::terminal_stderr::add_to_linker(linker, |cx| cx) + .expect("Failed to link Terminal Stderr"); + + //HTTP + wasmtime_wasi_http::bindings::http::types::add_to_linker(linker, |cx| cx) + .expect("Failed to link HTTP Types"); + wasmtime_wasi_http::bindings::http::outgoing_handler::add_to_linker(linker, |cx| cx) + .expect("Failed to link HTTP Outgoing Handler"); +} + +// #[ignore] +#[cfg(test)] +#[tokio::test] +async fn test_wasm_canary() { + wasm_canary().await.expect("Wasm return") +} diff --git a/tools/ci-cdk/canary-lambda/src/main.rs b/tools/ci-cdk/canary-lambda/src/main.rs index df0b36c45a..df6a3d5c6a 100644 --- a/tools/ci-cdk/canary-lambda/src/main.rs +++ b/tools/ci-cdk/canary-lambda/src/main.rs @@ -123,7 +123,7 @@ async fn lambda_main(sdk_config: SdkConfig) -> Result { } async fn canary_result(handle: JoinHandle>) -> Result<(), String> { - match timeout(Duration::from_secs(20), handle).await { + match timeout(Duration::from_secs(180), handle).await { Err(_timeout) => Err("canary timed out".into()), Ok(Ok(result)) => match result { Ok(_) => Ok(()), diff --git a/tools/ci-cdk/canary-runner/src/build_bundle.rs b/tools/ci-cdk/canary-runner/src/build_bundle.rs index eaea69835e..15ec5c3cbf 100644 --- a/tools/ci-cdk/canary-runner/src/build_bundle.rs +++ b/tools/ci-cdk/canary-runner/src/build_bundle.rs @@ -55,6 +55,10 @@ tokio-stream = "0" tracing-texray = "0.1.1" reqwest = { version = "0.11.14", features = ["rustls-tls"], default-features = false } edit-distance = "2" +wit-bindgen = { version = "0.16.0", features = ["macros", "realloc"] } +wasmtime = { version = "17.0.1", features = ["component-model"] } +wasmtime-wasi = "17.0.1" +wasmtime-wasi-http = "17.0.1" "#; const REQUIRED_SDK_CRATES: &[&str] = &[ @@ -62,6 +66,7 @@ const REQUIRED_SDK_CRATES: &[&str] = &[ "aws-sdk-s3", "aws-sdk-ec2", "aws-sdk-transcribestreaming", + "aws-smithy-wasm", ]; // The elements in this `Vec` should be sorted in an ascending order by the release date. @@ -258,6 +263,10 @@ pub async fn build_bundle(opt: BuildBundleArgs) -> Result> { let crate_manifest_content = generate_crate_manifest(crate_source)?; fs::write(&manifest_path, crate_manifest_content).context("failed to write Cargo.toml")?; + let wasm_manifest_path = std::env::current_dir() + .expect("Current dir") + .join("../canary-wasm/Cargo.toml"); + if !opt.manifest_only { // Compile the canary Lambda let mut command = Command::new("cargo"); @@ -271,6 +280,16 @@ pub async fn build_bundle(opt: BuildBundleArgs) -> Result> { } handle_failure("cargo build", &command.output()?)?; + // Compile the wasm canary to a .wasm binary + let mut wasm_command = Command::new("cargo"); + wasm_command + .arg("component") + .arg("build") + .arg("--release") + .arg("--manifest-path") + .arg(&wasm_manifest_path); + handle_failure("cargo component build", &wasm_command.output()?)?; + // Bundle the Lambda let repository_root = find_git_repository_root("smithy-rs", canary_path)?; let target_path = { @@ -280,6 +299,14 @@ pub async fn build_bundle(opt: BuildBundleArgs) -> Result> { } path.join("release") }; + let wasm_bin_path = { + repository_root + .join("tools") + .join("target") + .join("wasm32-wasi") + .join("release") + .join("aws_sdk_rust_lambda_canary_wasm.wasm") + }; let bin_path = target_path.join("bootstrap"); let bundle_path = target_path.join(name_bundle( &bin_path, @@ -289,6 +316,7 @@ pub async fn build_bundle(opt: BuildBundleArgs) -> Result> { let zip_file = fs::File::create(&bundle_path).context(here!())?; let mut zip = zip::ZipWriter::new(zip_file); + //Write the canary bin to the zip zip.start_file( "bootstrap", zip::write::FileOptions::default().unix_permissions(0o755), @@ -296,6 +324,15 @@ pub async fn build_bundle(opt: BuildBundleArgs) -> Result> { .context(here!())?; zip.write_all(&fs::read(&bin_path).context(here!("read target"))?) .context(here!())?; + + // Write the wasm bin to the zip + zip.start_file( + "aws_sdk_rust_lambda_canary_wasm.wasm", + zip::write::FileOptions::default().unix_permissions(0o644), + ) + .context(here!())?; + zip.write_all(&fs::read(wasm_bin_path).context(here!("read wasm bin"))?) + .context(here!())?; zip.finish().context(here!())?; println!( @@ -453,10 +490,15 @@ tokio-stream = "0" tracing-texray = "0.1.1" reqwest = { version = "0.11.14", features = ["rustls-tls"], default-features = false } edit-distance = "2" +wit-bindgen = { version = "0.16.0", features = ["macros", "realloc"] } +wasmtime = { version = "17.0.1", features = ["component-model"] } +wasmtime-wasi = "17.0.1" +wasmtime-wasi-http = "17.0.1" aws-config = { path = "some/sdk/path/aws-config", features = ["behavior-version-latest"] } aws-sdk-s3 = { path = "some/sdk/path/s3" } aws-sdk-ec2 = { path = "some/sdk/path/ec2" } aws-sdk-transcribestreaming = { path = "some/sdk/path/transcribestreaming" } +aws-smithy-wasm = { path = "some/sdk/path/aws-smithy-wasm" } [features] latest = [] @@ -518,10 +560,15 @@ tokio-stream = "0" tracing-texray = "0.1.1" reqwest = { version = "0.11.14", features = ["rustls-tls"], default-features = false } edit-distance = "2" +wit-bindgen = { version = "0.16.0", features = ["macros", "realloc"] } +wasmtime = { version = "17.0.1", features = ["component-model"] } +wasmtime-wasi = "17.0.1" +wasmtime-wasi-http = "17.0.1" aws-config = { version = "0.46.0", features = ["behavior-version-latest"] } aws-sdk-s3 = "0.20.0" aws-sdk-ec2 = "0.19.0" aws-sdk-transcribestreaming = "0.16.0" +aws-smithy-wasm = "0.1.0" [features] latest = [] @@ -538,6 +585,7 @@ default = ["latest"] crate_version("aws-sdk-s3", "0.20.0"), crate_version("aws-sdk-ec2", "0.19.0"), crate_version("aws-sdk-transcribestreaming", "0.16.0"), + crate_version("aws-smithy-wasm", "0.1.0"), ] .into_iter() .collect(), diff --git a/tools/ci-cdk/canary-runner/src/run.rs b/tools/ci-cdk/canary-runner/src/run.rs index 8fd8cb4064..6d6d4e9f63 100644 --- a/tools/ci-cdk/canary-runner/src/run.rs +++ b/tools/ci-cdk/canary-runner/src/run.rs @@ -388,7 +388,7 @@ async fn create_lambda_fn( ) .publish(true) .environment(env_builder.build()) - .timeout(60) + .timeout(180) .send() .await .context(here!("failed to create canary Lambda function"))?; diff --git a/tools/ci-cdk/canary-wasm/Cargo.lock b/tools/ci-cdk/canary-wasm/Cargo.lock new file mode 100644 index 0000000000..112f72a1dc --- /dev/null +++ b/tools/ci-cdk/canary-wasm/Cargo.lock @@ -0,0 +1,1286 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "aws-config" +version = "1.1.7" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.11", + "hyper", + "time", + "tokio", + "tracing", +] + +[[package]] +name = "aws-credential-types" +version = "1.1.7" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.1.7" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.11", + "http-body", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-rust-lambda-canary-wasm" +version = "0.1.0" +dependencies = [ + "aws-config", + "aws-sdk-s3", + "aws-smithy-async", + "aws-smithy-wasm", + "tokio", + "wit-bindgen", +] + +[[package]] +name = "aws-sdk-s3" +version = "0.0.0-local" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "http 0.2.11", + "http-body", + "once_cell", + "percent-encoding", + "regex-lite", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sts" +version = "0.0.0-local" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http 0.2.11", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.1.7" +dependencies = [ + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.11", + "http 1.0.0", + "once_cell", + "percent-encoding", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.1.7" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.60.6" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http 0.2.11", + "http-body", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.4" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.6" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.11", + "http-body", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.60.6" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.6" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.1.7" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "http 0.2.11", + "http-body", + "once_cell", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.1.7" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.11", + "http 1.0.0", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-types" +version = "1.1.7" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.11", + "http-body", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", +] + +[[package]] +name = "aws-smithy-wasm" +version = "0.1.0" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "http 1.0.0", + "tokio", + "tower", + "tracing", + "wasi", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.6" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.1.7" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "http 0.2.11", + "rustc_version", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32c" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89254598aa9b9fa608de44b3ae54c810f0f06d755e24c50177f1f8f31ff50ce2" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.11", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 0.2.11", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex-lite" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "spdx" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bde1398b09b9f93fc2fc9b9da86e362693e999d3a54a8ac47a99a5a73f638b" +dependencies = [ + "smallvec", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "uuid" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.12.0+wasi-0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457dd7e321b36e7d9d275997825e17ff9dcba2f6c8c0ff656e2f46db6c8579ef" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-encoder" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "972f97a5d8318f908dded23594188a90bcd09365986b1163e66d70170e5287ae" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-metadata" +version = "0.10.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18ebaa7bd0f9e7a5e5dd29b9a998acf21c4abed74265524dd7e85934597bfb10" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder 0.41.2", + "wasmparser 0.121.2", +] + +[[package]] +name = "wasmparser" +version = "0.118.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77f1154f1ab868e2a01d9834a805faca7bf8b50d041b4ca714d005d0dab1c50c" +dependencies = [ + "indexmap", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.121.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b76f1d099678b4f69402a421e888bbe71bf20320c2f3f3565d0e7484dbe5bc20" +dependencies = [ + "bitflags", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75d55e1a488af2981fb0edac80d8d20a51ac36897a1bdef4abde33c29c1b6d0d" +dependencies = [ + "anyhow", + "wit-component", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01ff9cae7bf5736750d94d91eb8a49f5e3a04aff1d1a3218287d9b2964510f8" +dependencies = [ + "anyhow", + "heck", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804a98e2538393d47aa7da65a7348116d6ff403b426665152b70a168c0146d49" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", + "wit-component", +] + +[[package]] +name = "wit-component" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.38.1", + "wasm-metadata", + "wasmparser 0.118.2", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "316b36a9f0005f5aa4b03c39bc3728d045df136f8c13a73b7db4510dec725e08" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/tools/ci-cdk/canary-wasm/Cargo.toml b/tools/ci-cdk/canary-wasm/Cargo.toml new file mode 100644 index 0000000000..de883a420c --- /dev/null +++ b/tools/ci-cdk/canary-wasm/Cargo.toml @@ -0,0 +1,25 @@ + +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# IMPORTANT: Don't edit this file directly! Run `canary-runner build-bundle` to modify this file instead. +[package] +name = "aws-sdk-rust-lambda-canary-wasm" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[dependencies] +tokio = { version = "1.36.0", features = ["macros", "rt", "time"] } +wit-bindgen = { version = "0.16.0", features = ["macros", "realloc"] } +aws-config = { path = "../../../aws/sdk/build/aws-sdk/sdk/aws-config", default-features = false, features = ["behavior-version-latest"] } +aws-sdk-s3 = { path = "../../../aws/sdk/build/aws-sdk/sdk/s3", default-features = false } +aws-smithy-wasm = { path = "../../../aws/sdk/build/aws-sdk/sdk/aws-smithy-wasm" } +aws-smithy-async = { path = "../../../aws/sdk/build/aws-sdk/sdk/aws-smithy-async", default-features = false, features = ["rt-tokio"]} + +[lib] +crate-type = ["cdylib"] + +# metadata used by cargo-component to identify which wit world to embed in the binary +[package.metadata.component] +package = "aws:component" diff --git a/tools/ci-cdk/canary-wasm/src/lib.rs b/tools/ci-cdk/canary-wasm/src/lib.rs new file mode 100644 index 0000000000..6d90399ec0 --- /dev/null +++ b/tools/ci-cdk/canary-wasm/src/lib.rs @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_config::Region; +use aws_sdk_s3 as s3; +use aws_smithy_async::rt::sleep::TokioSleep; +use aws_smithy_wasm::wasi::WasiHttpClientBuilder; + +//Generates the Rust bindings from the wit file +wit_bindgen::generate!({ + world: "canary-world", + exports: { + "aws:component/canary-interface": Component + } +}); + +struct Component; + +impl exports::aws::component::canary_interface::Guest for Component { + fn run_canary() -> Result, String> { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_time() + .build() + .expect("Failed to generate runtime"); + let res = rt.block_on(run_canary())?; + Ok(res) + } +} + +async fn run_canary() -> Result, String> { + let http_client = WasiHttpClientBuilder::new().build(); + let sleep = TokioSleep::new(); + let config = aws_config::from_env() + .region(Region::new("us-east-2")) + .no_credentials() + .http_client(http_client) + .sleep_impl(sleep) + .load() + .await; + + let client = s3::Client::new(&config); + let result = client + .list_objects_v2() + .bucket("nara-national-archives-catalog") + .delimiter("/") + .prefix("authority-records/organization/") + .max_keys(5) + .send() + .await + .expect("Failed to ListObjects"); + + //For ease of modeling the return we just extract the keys from the objects + let object_names: Vec = result + .contents + .expect("No S3 Objects") + .iter() + .map(|obj| obj.key().expect("Object has no name").to_string()) + .collect(); + + Ok(object_names) +} diff --git a/tools/ci-cdk/canary-wasm/wit/component.wit b/tools/ci-cdk/canary-wasm/wit/component.wit new file mode 100644 index 0000000000..a23ee0aadf --- /dev/null +++ b/tools/ci-cdk/canary-wasm/wit/component.wit @@ -0,0 +1,15 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package aws:component; + +interface canary-interface { + run-canary: func() -> result, string>; +} + +world canary-world { + export canary-interface; +} + \ No newline at end of file diff --git a/tools/ci-scripts/check-aws-config b/tools/ci-scripts/check-aws-config index 135ab38eb1..49f1439027 100755 --- a/tools/ci-scripts/check-aws-config +++ b/tools/ci-scripts/check-aws-config @@ -55,6 +55,6 @@ cargo check --target wasm32-wasi --no-default-features # TODO(https://github.com/smithy-lang/smithy-rs/issues/2499): Uncomment the following once aws-config tests compile for WASM # echo "${C_YELLOW}## Testing the wasm32-unknown-unknown and wasm32-wasi targets${C_RESET}" # wasm-pack test --node -- --no-default-features -# cargo wasi test --no-default-features +# cargo test --target wasm32-wasi --no-default-features popd &>/dev/null diff --git a/tools/ci-scripts/check-aws-sdk-standalone-integration-tests b/tools/ci-scripts/check-aws-sdk-standalone-integration-tests index c331f9997b..9ba41631dc 100755 --- a/tools/ci-scripts/check-aws-sdk-standalone-integration-tests +++ b/tools/ci-scripts/check-aws-sdk-standalone-integration-tests @@ -39,4 +39,10 @@ find "${tmp_dir}" pushd "${tmp_dir}/aws/sdk/integration-tests" cargo check --tests --all-features + +# Running WebAssembly (WASI) specific integration tests +pushd "${tmp_dir}/aws/sdk/integration-tests/webassembly" &>/dev/null +cargo check --tests --all-features + +popd popd