From dc63b00b18f4c9dcd4a3c1f4d1001750577b871d Mon Sep 17 00:00:00 2001 From: Bruno Tavares Date: Thu, 17 Aug 2023 19:44:53 -0300 Subject: [PATCH] Safely handle missing credentials.d folder The `$HOME/.aws/credentials.d` folder is not required to exist, as all credential configurations can be defined on `$HOME/.aws/credentials` instead. The early return with the `?` operator caused the binary to fail if the optional folder did not exist, but worked if the folder existed but was empty. This commit refactors the code the avoid crashing when the `$HOME/.aws/credentials.d` does not exist. It is collecting into a Vector instead of chaining the streams as it would required Boxed Streams to switch between `ReadDirStream` and `stream::empty` in case of an `Err`. --- src/loader.rs | 80 +++++++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/src/loader.rs b/src/loader.rs index 95ff12b..ab0ceaa 100644 --- a/src/loader.rs +++ b/src/loader.rs @@ -91,44 +91,50 @@ impl AwsCredentials { let (plain_sync, encrypted_sync) = (Arc::new(Semaphore::new(32)), Arc::new(Semaphore::new(4))); - let handles = ReadDirStream::new(fs::read_dir(&creds_d).await.map_err(|e| { - log::error!("Unable to read directory: {}", e); - e - })?) - .filter_map(|e| { - // NOTE according to docs, this will only fail if there is an intermittent I/O error, not worth handling - e.ok() - }) - .map(|e| e.path()) - .chain(stream::iter(vec![aws_config_dir.join("credentials")])) - .filter(|p| p.is_file()) - .map(|p| { - let (plain_permit, encrypted_permit) = (plain_sync.clone(), encrypted_sync.clone()); - - tokio::spawn(async { - let name = p.file_name().unwrap().to_string_lossy(); - - if name.ends_with(".asc") || name.ends_with(".gpg") || name.ends_with(".pgp") { - Some({ - let work = encrypted_permit.acquire_owned().await.unwrap(); - let r = AwsCredentialsFile::load_encrypted(p).await; - drop(work); - r - }) - } else if name.ends_with(".ini") || name.ends_with("") { - Some({ - let work = plain_permit.acquire_owned().await.unwrap(); - let r = AwsCredentialsFile::load(p).await; - drop(work); - r - }) - } else { - log::debug!("Skipping file with unknown extension {}", p.display()); - None - } + let creds_files: Vec = match fs::read_dir(&creds_d).await { + Ok(creds) => { + ReadDirStream::new(creds) + .filter_map(|f| f.ok()) + .map(|f| f.path()) + .collect() + .await + } + Err(_) => { + log::debug!("No $HOME/.aws/credentials.d directory found"); + vec![] + } + }; + + let handles = stream::iter(creds_files) + .chain(stream::iter(vec![aws_config_dir.join("credentials")])) + .filter(|p| p.is_file()) + .map(|p| { + let (plain_permit, encrypted_permit) = (plain_sync.clone(), encrypted_sync.clone()); + + tokio::spawn(async { + let name = p.file_name().unwrap().to_string_lossy(); + + if name.ends_with(".asc") || name.ends_with(".gpg") || name.ends_with(".pgp") { + Some({ + let work = encrypted_permit.acquire_owned().await.unwrap(); + let r = AwsCredentialsFile::load_encrypted(p).await; + drop(work); + r + }) + } else if name.ends_with(".ini") || name.ends_with("") { + Some({ + let work = plain_permit.acquire_owned().await.unwrap(); + let r = AwsCredentialsFile::load(p).await; + drop(work); + r + }) + } else { + log::debug!("Skipping file with unknown extension {}", p.display()); + None + } + }) }) - }) - .collect::>>(); + .collect::>>(); let mut credentials = BTreeSet::new();