Loading CHANGELOG.next.toml +28 −0 Original line number Diff line number Diff line Loading @@ -53,3 +53,31 @@ of breaking changes and how to resolve them. references = ["smithy-rs#1740", "smithy-rs#256"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } author = "jdisanti" [[aws-sdk-rust]] message = """ It is now possible to programmatically customize the locations of the profile config/credentials files in `aws-config`: ```rust use aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider}; use aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind}; let profile_files = ProfileFiles::builder() .with_file(ProfileFileKind::Credentials, "some/path/to/credentials-file") .build(); let credentials_provider = ProfileFileCredentialsProvider::builder() .profile_files(profile_files.clone()) .build(); let region_provider = ProfileFileRegionProvider::builder() .profile_files(profile_files) .build(); let sdk_config = aws_config::from_env() .credentials_provider(credentials_provider) .region(region_provider) .load() .await; ``` """ references = ["aws-sdk-rust#237", "smithy-rs#1770"] meta = { "breaking" = false, "tada" = true, "bug" = false } author = "jdisanti" aws/rust-runtime/aws-config/src/imds/client.rs +3 −3 Original line number Diff line number Diff line Loading @@ -37,7 +37,7 @@ use tokio::sync::OnceCell; use crate::connector::expect_connector; use crate::imds::client::token::TokenMiddleware; use crate::profile::ProfileParseError; use crate::profile::credentials::ProfileFileError; use crate::provider_config::ProviderConfig; use crate::{profile, PKG_VERSION}; use aws_sdk_sso::config::timeout::TimeoutConfig; Loading Loading @@ -439,7 +439,7 @@ pub enum BuildError { InvalidEndpointMode(InvalidEndpointMode), /// The AWS Profile (e.g. `~/.aws/config`) was invalid InvalidProfile(ProfileParseError), InvalidProfile(ProfileFileError), /// The specified endpoint was not a valid URI InvalidEndpointUri(InvalidUri), Loading Loading @@ -626,7 +626,7 @@ impl EndpointSource { } EndpointSource::Env(env, fs) => { // load an endpoint override from the environment let profile = profile::load(fs, env) let profile = profile::load(fs, env, &Default::default()) .await .map_err(BuildError::InvalidProfile)?; let uri_override = if let Ok(uri) = env.get(env::ENDPOINT) { Loading aws/rust-runtime/aws-config/src/profile/app_name.rs +8 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ //! Load an app name from an AWS profile use super::profile_file::ProfileFiles; use crate::provider_config::ProviderConfig; use aws_types::app_name::AppName; use aws_types::os_shim_internal::{Env, Fs}; Loading @@ -14,6 +15,8 @@ use aws_types::os_shim_internal::{Env, Fs}; /// This provider will attempt to shared AWS shared configuration and then read the /// `sdk-ua-app-id` property from the active profile. /// #[doc = include_str!("location_of_profile_files.md")] /// /// # Examples /// /// **Loads "my-app" as the app name** Loading @@ -35,6 +38,7 @@ pub struct ProfileFileAppNameProvider { fs: Fs, env: Env, profile_override: Option<String>, profile_files: ProfileFiles, } impl ProfileFileAppNameProvider { Loading @@ -46,6 +50,7 @@ impl ProfileFileAppNameProvider { fs: Fs::real(), env: Env::real(), profile_override: None, profile_files: Default::default(), } } Loading @@ -56,7 +61,7 @@ impl ProfileFileAppNameProvider { /// Parses the profile config and attempts to find an app name. pub async fn app_name(&self) -> Option<AppName> { let profile = super::parser::load(&self.fs, &self.env) let profile = super::parser::load(&self.fs, &self.env, &self.profile_files) .await .map_err(|err| tracing::warn!(err = %err, "failed to parse profile")) .ok()?; Loading @@ -82,6 +87,7 @@ impl ProfileFileAppNameProvider { pub struct Builder { config: Option<ProviderConfig>, profile_override: Option<String>, profile_files: Option<ProfileFiles>, } impl Builder { Loading @@ -104,6 +110,7 @@ impl Builder { env: conf.env(), fs: conf.fs(), profile_override: self.profile_override, profile_files: self.profile_files.unwrap_or_default(), } } } Loading aws/rust-runtime/aws-config/src/profile/credentials.rs +47 −32 Original line number Diff line number Diff line Loading @@ -22,22 +22,21 @@ //! - `exec` which contains a chain representation of providers to implement passing bootstrapped credentials //! through a series of providers. use crate::profile::credentials::exec::named::NamedProviderFactory; use crate::profile::credentials::exec::{ClientConfiguration, ProviderChain}; use crate::profile::parser::ProfileParseError; use crate::profile::profile_file::ProfileFiles; use crate::profile::Profile; use crate::provider_config::ProviderConfig; use aws_types::credentials::{self, future, CredentialsError, ProvideCredentials}; use std::borrow::Cow; use std::collections::HashMap; use std::error::Error; use std::fmt::{Display, Formatter}; use std::path::PathBuf; use std::sync::Arc; use aws_types::credentials::{self, future, CredentialsError, ProvideCredentials}; use tracing::Instrument; use crate::profile::credentials::exec::named::NamedProviderFactory; use crate::profile::credentials::exec::{ClientConfiguration, ProviderChain}; use crate::profile::parser::ProfileParseError; use crate::profile::Profile; use crate::provider_config::ProviderConfig; mod exec; mod repr; Loading Loading @@ -142,29 +141,14 @@ impl ProvideCredentials for ProfileFileCredentialsProvider { /// /// SSO can also be used as a source profile for assume role chains. /// /// ## Location of Profile Files /// * The location of the config file will be loaded from the `AWS_CONFIG_FILE` environment variable /// with a fallback to `~/.aws/config` /// * The location of the credentials file will be loaded from the `AWS_SHARED_CREDENTIALS_FILE` /// environment variable with a fallback to `~/.aws/credentials` /// /// ## Home directory resolution /// Home directory resolution is implemented to match the behavior of the CLI & Python. `~` is only /// used for home directory resolution when it: /// - Starts the path /// - Is followed immediately by `/` or a platform specific separator. (On windows, `~/` and `~\` both /// resolve to the home directory. /// /// When determining the home directory, the following environment variables are checked: /// - `HOME` on all platforms /// - `USERPROFILE` on Windows /// - The concatenation of `HOMEDRIVE` and `HOMEPATH` on Windows (`$HOMEDRIVE$HOMEPATH`) #[doc = include_str!("location_of_profile_files.md")] #[derive(Debug)] pub struct ProfileFileCredentialsProvider { factory: NamedProviderFactory, client_config: ClientConfiguration, provider_config: ProviderConfig, profile_override: Option<String>, profile_files: ProfileFiles, } impl ProfileFileCredentialsProvider { Loading @@ -178,6 +162,7 @@ impl ProfileFileCredentialsProvider { &self.provider_config, &self.factory, self.profile_override.as_deref(), &self.profile_files, ) .await .map_err(|err| match err { Loading Loading @@ -225,6 +210,13 @@ impl ProfileFileCredentialsProvider { } } #[doc(hidden)] #[derive(Debug)] pub struct CouldNotReadProfileFile { pub(crate) path: PathBuf, pub(crate) cause: std::io::Error, } /// An Error building a Credential source from an AWS Profile #[derive(Debug)] #[non_exhaustive] Loading Loading @@ -283,6 +275,10 @@ pub enum ProfileFileError { /// The name of the provider name: String, }, /// A custom profile file location didn't exist or could not be read #[non_exhaustive] CouldNotReadProfileFile(CouldNotReadProfileFile), } impl ProfileFileError { Loading Loading @@ -326,6 +322,13 @@ impl Display for ProfileFileError { "profile `{}` did not contain credential information", profile ), ProfileFileError::CouldNotReadProfileFile(details) => { write!( f, "Failed to read custom profile file at {:?}", details.path ) } } } } Loading @@ -334,16 +337,24 @@ impl Error for ProfileFileError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { ProfileFileError::CouldNotParseProfile(err) => Some(err), ProfileFileError::CouldNotReadProfileFile(details) => Some(&details.cause), _ => None, } } } impl From<ProfileParseError> for ProfileFileError { fn from(err: ProfileParseError) -> Self { ProfileFileError::CouldNotParseProfile(err) } } /// Builder for [`ProfileFileCredentialsProvider`] #[derive(Debug, Default)] pub struct Builder { provider_config: Option<ProviderConfig>, profile_override: Option<String>, profile_files: Option<ProfileFiles>, custom_providers: HashMap<Cow<'static, str>, Arc<dyn ProvideCredentials>>, } Loading Loading @@ -409,6 +420,12 @@ impl Builder { self } /// Set the profile file that should be used by the [`ProfileFileCredentialsProvider`] pub fn profile_files(mut self, profile_files: ProfileFiles) -> Self { self.profile_files = Some(profile_files); self } /// Builds a [`ProfileFileCredentialsProvider`] pub fn build(self) -> ProfileFileCredentialsProvider { let build_span = tracing::debug_span!("build_profile_provider"); Loading Loading @@ -453,6 +470,7 @@ impl Builder { }, provider_config: conf, profile_override: self.profile_override, profile_files: self.profile_files.unwrap_or_default(), } } } Loading @@ -461,13 +479,10 @@ async fn build_provider_chain( provider_config: &ProviderConfig, factory: &NamedProviderFactory, profile_override: Option<&str>, profile_files: &ProfileFiles, ) -> Result<ProviderChain, ProfileFileError> { let profile_set = super::parser::load(&provider_config.fs(), &provider_config.env()) .await .map_err(|err| { tracing::warn!(err = %err, "failed to parse profile"); ProfileFileError::CouldNotParseProfile(err) })?; let profile_set = super::parser::load(&provider_config.fs(), &provider_config.env(), profile_files).await?; let repr = repr::resolve_chain(&profile_set, profile_override)?; tracing::info!(chain = ?repr, "constructed abstract provider from config file"); exec::ProviderChain::from_repr(provider_config, repr, factory) Loading aws/rust-runtime/aws-config/src/profile/location_of_profile_files.md 0 → 100644 +19 −0 Original line number Diff line number Diff line ## Location of Profile Files * The location of the config file will be loaded from the `AWS_CONFIG_FILE` environment variable with a fallback to `~/.aws/config` * The location of the credentials file will be loaded from the `AWS_SHARED_CREDENTIALS_FILE` environment variable with a fallback to `~/.aws/credentials` The location of these files can also be customized programmatically using [`ProfileFiles`](crate::profile::profile_file::ProfileFiles). ## Home directory resolution Home directory resolution is implemented to match the behavior of the CLI & Python. `~` is only used for home directory resolution when it: - Starts the path - Is followed immediately by `/` or a platform specific separator. (On windows, `~/` and `~\` both resolve to the home directory. When determining the home directory, the following environment variables are checked: - `HOME` on all platforms - `USERPROFILE` on Windows - The concatenation of `HOMEDRIVE` and `HOMEPATH` on Windows (`$HOMEDRIVE$HOMEPATH`) Loading
CHANGELOG.next.toml +28 −0 Original line number Diff line number Diff line Loading @@ -53,3 +53,31 @@ of breaking changes and how to resolve them. references = ["smithy-rs#1740", "smithy-rs#256"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } author = "jdisanti" [[aws-sdk-rust]] message = """ It is now possible to programmatically customize the locations of the profile config/credentials files in `aws-config`: ```rust use aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider}; use aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind}; let profile_files = ProfileFiles::builder() .with_file(ProfileFileKind::Credentials, "some/path/to/credentials-file") .build(); let credentials_provider = ProfileFileCredentialsProvider::builder() .profile_files(profile_files.clone()) .build(); let region_provider = ProfileFileRegionProvider::builder() .profile_files(profile_files) .build(); let sdk_config = aws_config::from_env() .credentials_provider(credentials_provider) .region(region_provider) .load() .await; ``` """ references = ["aws-sdk-rust#237", "smithy-rs#1770"] meta = { "breaking" = false, "tada" = true, "bug" = false } author = "jdisanti"
aws/rust-runtime/aws-config/src/imds/client.rs +3 −3 Original line number Diff line number Diff line Loading @@ -37,7 +37,7 @@ use tokio::sync::OnceCell; use crate::connector::expect_connector; use crate::imds::client::token::TokenMiddleware; use crate::profile::ProfileParseError; use crate::profile::credentials::ProfileFileError; use crate::provider_config::ProviderConfig; use crate::{profile, PKG_VERSION}; use aws_sdk_sso::config::timeout::TimeoutConfig; Loading Loading @@ -439,7 +439,7 @@ pub enum BuildError { InvalidEndpointMode(InvalidEndpointMode), /// The AWS Profile (e.g. `~/.aws/config`) was invalid InvalidProfile(ProfileParseError), InvalidProfile(ProfileFileError), /// The specified endpoint was not a valid URI InvalidEndpointUri(InvalidUri), Loading Loading @@ -626,7 +626,7 @@ impl EndpointSource { } EndpointSource::Env(env, fs) => { // load an endpoint override from the environment let profile = profile::load(fs, env) let profile = profile::load(fs, env, &Default::default()) .await .map_err(BuildError::InvalidProfile)?; let uri_override = if let Ok(uri) = env.get(env::ENDPOINT) { Loading
aws/rust-runtime/aws-config/src/profile/app_name.rs +8 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ //! Load an app name from an AWS profile use super::profile_file::ProfileFiles; use crate::provider_config::ProviderConfig; use aws_types::app_name::AppName; use aws_types::os_shim_internal::{Env, Fs}; Loading @@ -14,6 +15,8 @@ use aws_types::os_shim_internal::{Env, Fs}; /// This provider will attempt to shared AWS shared configuration and then read the /// `sdk-ua-app-id` property from the active profile. /// #[doc = include_str!("location_of_profile_files.md")] /// /// # Examples /// /// **Loads "my-app" as the app name** Loading @@ -35,6 +38,7 @@ pub struct ProfileFileAppNameProvider { fs: Fs, env: Env, profile_override: Option<String>, profile_files: ProfileFiles, } impl ProfileFileAppNameProvider { Loading @@ -46,6 +50,7 @@ impl ProfileFileAppNameProvider { fs: Fs::real(), env: Env::real(), profile_override: None, profile_files: Default::default(), } } Loading @@ -56,7 +61,7 @@ impl ProfileFileAppNameProvider { /// Parses the profile config and attempts to find an app name. pub async fn app_name(&self) -> Option<AppName> { let profile = super::parser::load(&self.fs, &self.env) let profile = super::parser::load(&self.fs, &self.env, &self.profile_files) .await .map_err(|err| tracing::warn!(err = %err, "failed to parse profile")) .ok()?; Loading @@ -82,6 +87,7 @@ impl ProfileFileAppNameProvider { pub struct Builder { config: Option<ProviderConfig>, profile_override: Option<String>, profile_files: Option<ProfileFiles>, } impl Builder { Loading @@ -104,6 +110,7 @@ impl Builder { env: conf.env(), fs: conf.fs(), profile_override: self.profile_override, profile_files: self.profile_files.unwrap_or_default(), } } } Loading
aws/rust-runtime/aws-config/src/profile/credentials.rs +47 −32 Original line number Diff line number Diff line Loading @@ -22,22 +22,21 @@ //! - `exec` which contains a chain representation of providers to implement passing bootstrapped credentials //! through a series of providers. use crate::profile::credentials::exec::named::NamedProviderFactory; use crate::profile::credentials::exec::{ClientConfiguration, ProviderChain}; use crate::profile::parser::ProfileParseError; use crate::profile::profile_file::ProfileFiles; use crate::profile::Profile; use crate::provider_config::ProviderConfig; use aws_types::credentials::{self, future, CredentialsError, ProvideCredentials}; use std::borrow::Cow; use std::collections::HashMap; use std::error::Error; use std::fmt::{Display, Formatter}; use std::path::PathBuf; use std::sync::Arc; use aws_types::credentials::{self, future, CredentialsError, ProvideCredentials}; use tracing::Instrument; use crate::profile::credentials::exec::named::NamedProviderFactory; use crate::profile::credentials::exec::{ClientConfiguration, ProviderChain}; use crate::profile::parser::ProfileParseError; use crate::profile::Profile; use crate::provider_config::ProviderConfig; mod exec; mod repr; Loading Loading @@ -142,29 +141,14 @@ impl ProvideCredentials for ProfileFileCredentialsProvider { /// /// SSO can also be used as a source profile for assume role chains. /// /// ## Location of Profile Files /// * The location of the config file will be loaded from the `AWS_CONFIG_FILE` environment variable /// with a fallback to `~/.aws/config` /// * The location of the credentials file will be loaded from the `AWS_SHARED_CREDENTIALS_FILE` /// environment variable with a fallback to `~/.aws/credentials` /// /// ## Home directory resolution /// Home directory resolution is implemented to match the behavior of the CLI & Python. `~` is only /// used for home directory resolution when it: /// - Starts the path /// - Is followed immediately by `/` or a platform specific separator. (On windows, `~/` and `~\` both /// resolve to the home directory. /// /// When determining the home directory, the following environment variables are checked: /// - `HOME` on all platforms /// - `USERPROFILE` on Windows /// - The concatenation of `HOMEDRIVE` and `HOMEPATH` on Windows (`$HOMEDRIVE$HOMEPATH`) #[doc = include_str!("location_of_profile_files.md")] #[derive(Debug)] pub struct ProfileFileCredentialsProvider { factory: NamedProviderFactory, client_config: ClientConfiguration, provider_config: ProviderConfig, profile_override: Option<String>, profile_files: ProfileFiles, } impl ProfileFileCredentialsProvider { Loading @@ -178,6 +162,7 @@ impl ProfileFileCredentialsProvider { &self.provider_config, &self.factory, self.profile_override.as_deref(), &self.profile_files, ) .await .map_err(|err| match err { Loading Loading @@ -225,6 +210,13 @@ impl ProfileFileCredentialsProvider { } } #[doc(hidden)] #[derive(Debug)] pub struct CouldNotReadProfileFile { pub(crate) path: PathBuf, pub(crate) cause: std::io::Error, } /// An Error building a Credential source from an AWS Profile #[derive(Debug)] #[non_exhaustive] Loading Loading @@ -283,6 +275,10 @@ pub enum ProfileFileError { /// The name of the provider name: String, }, /// A custom profile file location didn't exist or could not be read #[non_exhaustive] CouldNotReadProfileFile(CouldNotReadProfileFile), } impl ProfileFileError { Loading Loading @@ -326,6 +322,13 @@ impl Display for ProfileFileError { "profile `{}` did not contain credential information", profile ), ProfileFileError::CouldNotReadProfileFile(details) => { write!( f, "Failed to read custom profile file at {:?}", details.path ) } } } } Loading @@ -334,16 +337,24 @@ impl Error for ProfileFileError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { ProfileFileError::CouldNotParseProfile(err) => Some(err), ProfileFileError::CouldNotReadProfileFile(details) => Some(&details.cause), _ => None, } } } impl From<ProfileParseError> for ProfileFileError { fn from(err: ProfileParseError) -> Self { ProfileFileError::CouldNotParseProfile(err) } } /// Builder for [`ProfileFileCredentialsProvider`] #[derive(Debug, Default)] pub struct Builder { provider_config: Option<ProviderConfig>, profile_override: Option<String>, profile_files: Option<ProfileFiles>, custom_providers: HashMap<Cow<'static, str>, Arc<dyn ProvideCredentials>>, } Loading Loading @@ -409,6 +420,12 @@ impl Builder { self } /// Set the profile file that should be used by the [`ProfileFileCredentialsProvider`] pub fn profile_files(mut self, profile_files: ProfileFiles) -> Self { self.profile_files = Some(profile_files); self } /// Builds a [`ProfileFileCredentialsProvider`] pub fn build(self) -> ProfileFileCredentialsProvider { let build_span = tracing::debug_span!("build_profile_provider"); Loading Loading @@ -453,6 +470,7 @@ impl Builder { }, provider_config: conf, profile_override: self.profile_override, profile_files: self.profile_files.unwrap_or_default(), } } } Loading @@ -461,13 +479,10 @@ async fn build_provider_chain( provider_config: &ProviderConfig, factory: &NamedProviderFactory, profile_override: Option<&str>, profile_files: &ProfileFiles, ) -> Result<ProviderChain, ProfileFileError> { let profile_set = super::parser::load(&provider_config.fs(), &provider_config.env()) .await .map_err(|err| { tracing::warn!(err = %err, "failed to parse profile"); ProfileFileError::CouldNotParseProfile(err) })?; let profile_set = super::parser::load(&provider_config.fs(), &provider_config.env(), profile_files).await?; let repr = repr::resolve_chain(&profile_set, profile_override)?; tracing::info!(chain = ?repr, "constructed abstract provider from config file"); exec::ProviderChain::from_repr(provider_config, repr, factory) Loading
aws/rust-runtime/aws-config/src/profile/location_of_profile_files.md 0 → 100644 +19 −0 Original line number Diff line number Diff line ## Location of Profile Files * The location of the config file will be loaded from the `AWS_CONFIG_FILE` environment variable with a fallback to `~/.aws/config` * The location of the credentials file will be loaded from the `AWS_SHARED_CREDENTIALS_FILE` environment variable with a fallback to `~/.aws/credentials` The location of these files can also be customized programmatically using [`ProfileFiles`](crate::profile::profile_file::ProfileFiles). ## Home directory resolution Home directory resolution is implemented to match the behavior of the CLI & Python. `~` is only used for home directory resolution when it: - Starts the path - Is followed immediately by `/` or a platform specific separator. (On windows, `~/` and `~\` both resolve to the home directory. When determining the home directory, the following environment variables are checked: - `HOME` on all platforms - `USERPROFILE` on Windows - The concatenation of `HOMEDRIVE` and `HOMEPATH` on Windows (`$HOMEDRIVE$HOMEPATH`)