Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user agent #79

Merged
merged 20 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions aws-s3-transfer-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ aws-config = { version = "1.5.6", features = ["behavior-version-latest"] }
aws-sdk-s3 = { version = "1.51.0", features = ["behavior-version-latest"] }
aws-smithy-async = "1.2.1"
aws-smithy-experimental = { version = "0.1.3", features = ["crypto-aws-lc"] }
aws-smithy-runtime-api = "1.7.1"
aws-smithy-runtime-api = "1.7.3"
aws-runtime = "1.4.4"
aws-smithy-types = "1.2.6"
aws-types = "1.3.3"
blocking = "1.6.0"
Expand All @@ -32,7 +33,7 @@ walkdir = "2"
[dev-dependencies]
aws-sdk-s3 = { version = "1.51.0", features = ["behavior-version-latest", "test-util"] }
aws-smithy-mocks-experimental = "0.2.1"
aws-smithy-runtime = { version = "1.7.1", features = ["client", "connector-hyper-0-14-x", "test-util", "wire-mock"] }
aws-smithy-runtime = { version = "1.7.4", features = ["client", "connector-hyper-0-14-x", "test-util", "wire-mock"] }
clap = { version = "4.5.7", default-features = false, features = ["derive", "std", "help"] }
console-subscriber = "0.4.0"
http-02x = { package = "http", version = "0.2.9" }
Expand Down
9 changes: 9 additions & 0 deletions aws-s3-transfer-manager/examples/cp.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Cow;
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
Expand All @@ -7,6 +8,7 @@ use std::path::PathBuf;
use std::str::FromStr;
use std::time;

use aws_runtime::user_agent::FrameworkMetadata;
use aws_s3_transfer_manager::io::InputStream;
use aws_s3_transfer_manager::metrics::unit::ByteUnit;
use aws_s3_transfer_manager::metrics::Throughput;
Expand Down Expand Up @@ -151,6 +153,9 @@ async fn do_download(args: Args) -> Result<(), BoxError> {
let tm_config = aws_s3_transfer_manager::from_env()
.concurrency(ConcurrencySetting::Explicit(args.concurrency))
.part_size(PartSize::Target(args.part_size))
.frame_metadata(Some(
TingDaoK marked this conversation as resolved.
Show resolved Hide resolved
FrameworkMetadata::new("some-framework", Some(Cow::Borrowed("1.3"))).unwrap(),
))
.load()
.await;

Expand Down Expand Up @@ -228,6 +233,9 @@ async fn do_upload(args: Args) -> Result<(), BoxError> {
let tm_config = aws_s3_transfer_manager::from_env()
.concurrency(ConcurrencySetting::Explicit(args.concurrency))
.part_size(PartSize::Target(args.part_size))
.frame_metadata(Some(
FrameworkMetadata::new("some-framework", Some(Cow::Borrowed("1.3"))).unwrap(),
))
.load()
.await;

Expand Down Expand Up @@ -278,6 +286,7 @@ async fn main() -> Result<(), BoxError> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.with_thread_ids(true)
.with_ansi(false) // Disable ANSI colors
Copy link
Contributor

Choose a reason for hiding this comment

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

question: why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

with the ansi color, it's unreadable from pure text. I think it will be easier to read and parse.

.init();
}

Expand Down
3 changes: 2 additions & 1 deletion aws-s3-transfer-manager/external-types.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ allowed_external_types = [
"bytes::bytes::Bytes",
"bytes::buf::buf_impl::Buf",
"aws_types::request_id::RequestId",
"aws_types::request_id::RequestIdExt"
"aws_types::request_id::RequestIdExt",
"aws_types::app_name::AppName",
TingDaoK marked this conversation as resolved.
Show resolved Hide resolved
]
22 changes: 22 additions & 0 deletions aws-s3-transfer-manager/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

use aws_runtime::user_agent::FrameworkMetadata;

use crate::metrics::unit::ByteUnit;
use crate::types::{ConcurrencySetting, PartSize};
use std::cmp;
Expand All @@ -18,6 +20,7 @@ pub struct Config {
multipart_threshold: PartSize,
target_part_size: PartSize,
concurrency: ConcurrencySetting,
frame_metadata: Option<FrameworkMetadata>,
client: aws_sdk_s3::client::Client,
}

Expand All @@ -43,6 +46,11 @@ impl Config {
&self.concurrency
}

/// Returns the name of the app that is using the transfer manager, if it was provided.
pub fn frame_metadata(&self) -> Option<&FrameworkMetadata> {
self.frame_metadata.as_ref()
}

/// The Amazon S3 client instance that will be used to send requests to S3.
pub fn client(&self) -> &aws_sdk_s3::Client {
&self.client
Expand All @@ -55,6 +63,7 @@ pub struct Builder {
multipart_threshold_part_size: PartSize,
target_part_size: PartSize,
concurrency: ConcurrencySetting,
frame_metadata: Option<FrameworkMetadata>,
client: Option<aws_sdk_s3::Client>,
}

Expand Down Expand Up @@ -122,8 +131,20 @@ impl Builder {
self
}

/// Sets the name of the app that is using the client.
///
/// This _optional_ name is used to identify the application in the user agent that
/// gets sent along with requests.
pub fn frame_metadata(mut self, frame_metadata: Option<FrameworkMetadata>) -> Self {
TingDaoK marked this conversation as resolved.
Show resolved Hide resolved
self.frame_metadata = frame_metadata;
self
}

/// Set an explicit S3 client to use.
pub fn client(mut self, client: aws_sdk_s3::Client) -> Self {
// TODO - decide the approach here:
// - Convert the client to build to modify it based on other configs for transfer manager
// - Instead of taking the client, take sdk-config/s3-config/builder?
self.client = Some(client);
self
}
Expand All @@ -134,6 +155,7 @@ impl Builder {
multipart_threshold: self.multipart_threshold_part_size,
target_part_size: self.target_part_size,
concurrency: self.concurrency,
frame_metadata: self.frame_metadata,
client: self.client.expect("client set"),
}
}
Expand Down
84 changes: 81 additions & 3 deletions aws-s3-transfer-manager/src/config/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,52 @@
* SPDX-License-Identifier: Apache-2.0
*/

use aws_config::BehaviorVersion;
use aws_runtime::sdk_feature::AwsSdkFeature;
use aws_runtime::user_agent::{ApiMetadata, AwsUserAgent, FrameworkMetadata};
use aws_sdk_s3::config::{Intercept, IntoShared};
use aws_types::os_shim_internal::Env;

use crate::config::Builder;
use crate::{
http,
types::{ConcurrencySetting, PartSize},
Config,
};

#[derive(Debug)]
struct S3TransferManagerInterceptor {
frame_work_meta_data: Option<FrameworkMetadata>,
}

impl Intercept for S3TransferManagerInterceptor {
fn name(&self) -> &'static str {
"S3TransferManager"
}

fn read_before_execution(
&self,
_ctx: &aws_sdk_s3::config::interceptors::BeforeSerializationInterceptorContextRef<'_>,
cfg: &mut aws_sdk_s3::config::ConfigBag,
) -> Result<(), aws_sdk_s3::error::BoxError> {
// Assume the interceptor only be added to the client constructed by the loader.
// In this case, there should not be any user agent was sent before this interceptor starts.
// Create our own user agent with S3Transfer feature and user passed-in framework_meta_data if any.
cfg.interceptor_state()
.store_append(AwsSdkFeature::S3Transfer);
let api_metadata = cfg.load::<ApiMetadata>().unwrap();
// TODO: maybe APP Name someday
let mut ua = AwsUserAgent::new_from_environment(Env::real(), api_metadata.clone());
if let Some(framework_metadata) = self.frame_work_meta_data.clone() {
ua = ua.with_framework_metadata(framework_metadata);
}

cfg.interceptor_state().store_put(ua);

Ok(())
}
}

/// Load transfer manager [`Config`] from the environment.
#[derive(Default, Debug)]
pub struct ConfigLoader {
Expand Down Expand Up @@ -52,17 +91,56 @@ impl ConfigLoader {
self
}

/// Sets the name of the app that is using the client.
///
/// This _optional_ name is used to identify the application in the user agent that
/// gets sent along with requests.
pub fn frame_metadata(mut self, frame_metadata: Option<FrameworkMetadata>) -> Self {
self.builder = self.builder.frame_metadata(frame_metadata);
self
}

/// Load the default configuration
///
/// If fields have been overridden during builder construction, the override values will be
/// used. Otherwise, the default values for each field will be provided.
pub async fn load(self) -> Config {
let shared_config = aws_config::from_env()
let shared_config = aws_config::defaults(BehaviorVersion::latest())
.http_client(http::default_client())
.load()
.await;
let s3_client = aws_sdk_s3::Client::new(&shared_config);
let builder = self.builder.client(s3_client);

let mut sdk_client_builder = aws_sdk_s3::config::Builder::from(&shared_config);

let interceptor = S3TransferManagerInterceptor {
frame_work_meta_data: self.builder.frame_metadata.clone(),
};
sdk_client_builder.push_interceptor(S3TransferManagerInterceptor::into_shared(interceptor));
let builder = self
.builder
.client(aws_sdk_s3::Client::from_conf(sdk_client_builder.build()));
builder.build()
}
}

#[cfg(test)]
mod tests {
use crate::types::{ConcurrencySetting, PartSize};
use aws_sdk_s3::config::Intercept;

#[tokio::test]
async fn load_with_frame_metadata_and_interceptor() {
let config = crate::from_env()
.concurrency(ConcurrencySetting::Explicit(123))
.part_size(PartSize::Target(8))
.load()
.await;
let sdk_s3_config = config.client().config();
let tm_interceptor_exists = sdk_s3_config
.interceptors()
.any(|item| item.name() == "S3TransferManager");
assert!(tm_interceptor_exists);
}

/* TODO: test the framework metadata added correctly */
}
Loading