mirror of https://github.com/smithy-lang/smithy-rs
fix: region provider now respects chained profiles (#1183)
* fix: region provider now respects chained profiles update: profile chain creation for load_credentials * update: CHANGELOG.next.toml * Update aws/rust-runtime/aws-config/src/profile/region.rs Co-authored-by: John DiSanti <jdisanti@amazon.com> Co-authored-by: John DiSanti <jdisanti@amazon.com>
This commit is contained in:
parent
6685d472d9
commit
bd6b65bd6b
|
@ -11,6 +11,12 @@
|
|||
# meta = { "breaking" = false, "tada" = false, "bug" = false }
|
||||
# author = "rcoh"
|
||||
|
||||
[[aws-sdk-rust]]
|
||||
message = "The `ProfileFileRegionProvider` will now respect regions set in chained profiles"
|
||||
references = ["aws-sdk-rust#443"]
|
||||
meta = { "breaking" = false, "tada" = false, "bug" = true }
|
||||
author = "Velfi"
|
||||
|
||||
[[aws-sdk-rust]]
|
||||
message = "Several modules defined in the `aws_config` crate that used to be declared within another module's file have been moved to their own files. The moved modules are `sts`, `connector`, and `default_providers`. They still have the exact same import paths."
|
||||
references = ["smithy-rs#1144"]
|
||||
|
|
|
@ -166,13 +166,13 @@ impl ProfileFileCredentialsProvider {
|
|||
}
|
||||
|
||||
async fn load_credentials(&self) -> credentials::Result {
|
||||
let profile = build_provider_chain(
|
||||
let inner_provider = build_provider_chain(
|
||||
&self.provider_config,
|
||||
&self.factory,
|
||||
self.profile_override.as_deref(),
|
||||
)
|
||||
.await;
|
||||
let inner_provider = profile.map_err(|err| match err {
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
ProfileFileError::NoProfilesDefined
|
||||
| ProfileFileError::ProfileDidNotContainCredentials { .. } => {
|
||||
CredentialsError::not_loaded(err)
|
||||
|
|
|
@ -10,6 +10,8 @@ use crate::provider_config::ProviderConfig;
|
|||
use aws_types::os_shim_internal::{Env, Fs};
|
||||
use aws_types::region::Region;
|
||||
|
||||
use super::ProfileSet;
|
||||
|
||||
/// Load a region from a profile file
|
||||
///
|
||||
/// This provider will attempt to load AWS shared configuration, then read the `region` property
|
||||
|
@ -88,18 +90,61 @@ impl ProfileFileRegionProvider {
|
|||
}
|
||||
|
||||
async fn region(&self) -> Option<Region> {
|
||||
let profile = super::parser::load(&self.fs, &self.env)
|
||||
let profile_set = super::parser::load(&self.fs, &self.env)
|
||||
.await
|
||||
.map_err(|err| tracing::warn!(err = %err, "failed to parse profile"))
|
||||
.ok()?;
|
||||
let selected_profile = self
|
||||
.profile_override
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| profile.selected_profile());
|
||||
let selected_profile = profile.get_profile(selected_profile)?;
|
||||
selected_profile
|
||||
|
||||
resolve_profile_chain_for_region(&profile_set, self.profile_override.as_deref())
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_profile_chain_for_region(
|
||||
profile_set: &'_ ProfileSet,
|
||||
profile_override: Option<&str>,
|
||||
) -> Option<Region> {
|
||||
if profile_set.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut selected_profile = profile_override.unwrap_or_else(|| profile_set.selected_profile());
|
||||
let mut visited_profiles = vec![];
|
||||
|
||||
loop {
|
||||
let profile = profile_set.get_profile(selected_profile)?;
|
||||
// Check to see if we're in a loop and return if that's true.
|
||||
// Else, add the profile we're currently checking to our list of visited profiles.
|
||||
if visited_profiles.contains(&selected_profile) {
|
||||
return None;
|
||||
} else {
|
||||
visited_profiles.push(selected_profile);
|
||||
}
|
||||
|
||||
// Attempt to get region and source_profile for current profile
|
||||
let selected_profile_region = profile
|
||||
.get("region")
|
||||
.map(|region| Region::new(region.to_owned()))
|
||||
.map(|region| Region::new(region.to_owned()));
|
||||
let source_profile = profile.get("source_profile");
|
||||
|
||||
// Check to see what we got
|
||||
match (selected_profile_region, source_profile) {
|
||||
// Profile had a region specified, return it :D
|
||||
(Some(region), _) => {
|
||||
return Some(region);
|
||||
}
|
||||
// No region specified, source_profile is self-referential so we return to avoid infinite loop
|
||||
(None, Some(source_profile)) if source_profile == selected_profile => {
|
||||
return None;
|
||||
}
|
||||
// No region specified, no source_profile specified so we return empty-handed
|
||||
(None, None) => {
|
||||
return None;
|
||||
}
|
||||
// No region specified, check source profile for a region in next loop iteration
|
||||
(None, Some(source_profile)) => {
|
||||
selected_profile = source_profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,4 +224,37 @@ mod test {
|
|||
Some(Region::from_static("us-east-1"))
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_region_from_source_profile() {
|
||||
let config = r#"
|
||||
[profile credentials]
|
||||
aws_access_key_id = test-access-key-id
|
||||
aws_secret_access_key = test-secret-access-key
|
||||
aws_session_token = test-session-token
|
||||
region = us-east-1
|
||||
|
||||
[profile needs-source]
|
||||
source_profile = credentials
|
||||
role_arn = arn:aws:iam::123456789012:role/test
|
||||
"#
|
||||
.trim();
|
||||
|
||||
let fs = Fs::from_slice(&[("test_config", config)]);
|
||||
let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]);
|
||||
let provider_config = ProviderConfig::empty()
|
||||
.with_fs(fs)
|
||||
.with_env(env)
|
||||
.with_http_connector(no_traffic_connector());
|
||||
|
||||
assert_eq!(
|
||||
Some(Region::new("us-east-1")),
|
||||
ProfileFileRegionProvider::builder()
|
||||
.profile_name("needs-source")
|
||||
.configure(&provider_config)
|
||||
.build()
|
||||
.region()
|
||||
.await
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue