Skip to content

Commit

Permalink
Safely handle missing credentials.d folder
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
bltavares committed Aug 17, 2023
1 parent 68c307d commit dc63b00
Showing 1 changed file with 43 additions and 37 deletions.
80 changes: 43 additions & 37 deletions src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf> = 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::<Vec<JoinHandle<_>>>();
.collect::<Vec<JoinHandle<_>>>();

let mut credentials = BTreeSet::new();

Expand Down

0 comments on commit dc63b00

Please sign in to comment.