#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
rustdoc::missing_crate_level_docs,
unreachable_pub
)]
#[allow(dead_code)]
const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
pub mod default_provider;
pub mod environment;
pub mod meta;
pub mod profile;
pub mod sts;
#[cfg(test)]
mod test_case;
pub mod web_identity_token;
pub mod ecs;
pub mod provider_config;
mod cache;
pub mod imds;
mod json_credentials;
mod fs_util;
mod http_credential_provider;
pub mod sso;
pub mod connector;
pub mod credential_process;
pub use aws_smithy_types::retry;
pub use aws_smithy_types::timeout;
pub use aws_types::{
app_name::{AppName, InvalidAppName},
SdkConfig,
};
pub fn from_env() -> ConfigLoader {
ConfigLoader::default()
}
pub async fn load_from_env() -> aws_types::SdkConfig {
from_env().load().await
}
pub use loader::ConfigLoader;
mod loader {
use std::sync::Arc;
use crate::connector::default_connector;
use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep};
use aws_smithy_client::http_connector::{ConnectorSettings, HttpConnector};
use aws_smithy_types::retry::RetryConfig;
use aws_smithy_types::timeout::TimeoutConfig;
use aws_types::app_name::AppName;
use aws_types::credentials::{ProvideCredentials, SharedCredentialsProvider};
use aws_types::endpoint::ResolveAwsEndpoint;
use aws_types::SdkConfig;
use crate::default_provider::{app_name, credentials, region, retry_config, timeout_config};
use crate::meta::region::ProvideRegion;
use crate::provider_config::ProviderConfig;
#[derive(Default, Debug)]
pub struct ConfigLoader {
app_name: Option<AppName>,
credentials_provider: Option<SharedCredentialsProvider>,
endpoint_resolver: Option<Arc<dyn ResolveAwsEndpoint>>,
region: Option<Box<dyn ProvideRegion>>,
retry_config: Option<RetryConfig>,
sleep: Option<Arc<dyn AsyncSleep>>,
timeout_config: Option<TimeoutConfig>,
provider_config: Option<ProviderConfig>,
http_connector: Option<HttpConnector>,
}
impl ConfigLoader {
pub fn region(mut self, region: impl ProvideRegion + 'static) -> Self {
self.region = Some(Box::new(region));
self
}
pub fn retry_config(mut self, retry_config: RetryConfig) -> Self {
self.retry_config = Some(retry_config);
self
}
pub fn timeout_config(mut self, timeout_config: TimeoutConfig) -> Self {
self.timeout_config = Some(timeout_config);
self
}
pub fn sleep_impl(mut self, sleep: impl AsyncSleep + 'static) -> Self {
self.sleep = Some(Arc::new(sleep));
self
}
pub fn http_connector(mut self, http_connector: HttpConnector) -> Self {
self.http_connector = Some(http_connector);
self
}
pub fn credentials_provider(
mut self,
credentials_provider: impl ProvideCredentials + 'static,
) -> Self {
self.credentials_provider = Some(SharedCredentialsProvider::new(credentials_provider));
self
}
pub fn endpoint_resolver(
mut self,
endpoint_resolver: impl ResolveAwsEndpoint + 'static,
) -> Self {
self.endpoint_resolver = Some(Arc::new(endpoint_resolver));
self
}
pub fn configure(mut self, provider_config: ProviderConfig) -> Self {
self.provider_config = Some(provider_config);
self
}
pub async fn load(self) -> SdkConfig {
let conf = self.provider_config.unwrap_or_default();
let region = if let Some(provider) = self.region {
provider.region().await
} else {
region::Builder::default()
.configure(&conf)
.build()
.region()
.await
};
let retry_config = if let Some(retry_config) = self.retry_config {
retry_config
} else {
retry_config::default_provider()
.configure(&conf)
.retry_config()
.await
};
let app_name = if self.app_name.is_some() {
self.app_name
} else {
app_name::default_provider()
.configure(&conf)
.app_name()
.await
};
let sleep_impl = if self.sleep.is_none() {
if default_async_sleep().is_none() {
tracing::warn!(
"An implementation of AsyncSleep was requested by calling default_async_sleep \
but no default was set.
This happened when ConfigLoader::load was called during Config construction. \
You can fix this by setting a sleep_impl on the ConfigLoader before calling \
load or by enabling the rt-tokio feature"
);
}
default_async_sleep()
} else {
self.sleep
};
let timeout_config = if let Some(timeout_config) = self.timeout_config {
timeout_config
} else {
timeout_config::default_provider()
.configure(&conf)
.timeout_config()
.await
};
let http_connector = if let Some(http_connector) = self.http_connector {
http_connector
} else {
HttpConnector::Prebuilt(default_connector(
&ConnectorSettings::from_timeout_config(&timeout_config),
sleep_impl.clone(),
))
};
let credentials_provider = if let Some(provider) = self.credentials_provider {
provider
} else {
let mut builder = credentials::DefaultCredentialsChain::builder().configure(conf);
builder.set_region(region.clone());
SharedCredentialsProvider::new(builder.build().await)
};
let endpoint_resolver = self.endpoint_resolver;
let mut builder = SdkConfig::builder()
.region(region)
.retry_config(retry_config)
.timeout_config(timeout_config)
.credentials_provider(credentials_provider)
.http_connector(http_connector);
builder.set_endpoint_resolver(endpoint_resolver);
builder.set_app_name(app_name);
builder.set_sleep_impl(sleep_impl);
builder.build()
}
}
#[cfg(test)]
mod test {
use crate::from_env;
use crate::provider_config::ProviderConfig;
use aws_smithy_async::rt::sleep::TokioSleep;
use aws_smithy_client::erase::DynConnector;
use aws_smithy_client::never::NeverConnector;
use aws_types::credentials::ProvideCredentials;
use aws_types::os_shim_internal::Env;
#[tokio::test]
async fn provider_config_used() {
let env = Env::from_slice(&[
("AWS_MAX_ATTEMPTS", "10"),
("AWS_REGION", "us-west-4"),
("AWS_ACCESS_KEY_ID", "akid"),
("AWS_SECRET_ACCESS_KEY", "secret"),
]);
let loader = from_env()
.configure(
ProviderConfig::empty()
.with_sleep(TokioSleep::new())
.with_env(env)
.with_http_connector(DynConnector::new(NeverConnector::new())),
)
.load()
.await;
assert_eq!(loader.retry_config().unwrap().max_attempts(), 10);
assert_eq!(loader.region().unwrap().as_ref(), "us-west-4");
assert_eq!(
loader
.credentials_provider()
.unwrap()
.provide_credentials()
.await
.unwrap()
.access_key_id(),
"akid"
);
}
}
}