From 2f5c4d77d616148ecfab59cc5e3a9d1839a50bce Mon Sep 17 00:00:00 2001 From: Adam Willden Date: Fri, 18 Sep 2026 16:15:09 +0100 Subject: [PATCH 1/2] Improve error handling for existing directories Handle the case where a directory is created concurrently by another deployment without treating it as an error. With the move to CodeDeploy Agent v2 and its support for concurrent deployments, we are experiencing failures during the Install step when multiple deployments attempt to create the same directory at the same time. In our case, three deployments run simultaneously and copy files into separate destinations beneath a shared nested directory structure that does not initially exist. Each deployment determines that the shared parent directory needs to be created and queues a directory creation command. If another deployment creates that directory before the command is executed, fs::create_dir returns AlreadyExists, causing the deployment to fail. This change treats AlreadyExists as successful when the existing path is a directory. The directory is only written to the cleanup file when it was created by the current deployment, ensuring that a deployment does not subsequently attempt to clean up a directory created by another deployment. --- src/installer/commands/make_directory_command.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/installer/commands/make_directory_command.rs b/src/installer/commands/make_directory_command.rs index 2cf751a6..ae6a097b 100644 --- a/src/installer/commands/make_directory_command.rs +++ b/src/installer/commands/make_directory_command.rs @@ -17,10 +17,18 @@ impl MakeDirectoryCommand { } /// # Errors - /// Returns an error if the command execution fails. + /// Returns an error if the command execution fails, unless the path already exists pub fn execute(&self, cleanup_file: &mut dyn Write) -> Result<()> { - fs::create_dir(&self.directory)?; - writeln!(cleanup_file, "{}", self.directory.display())?; + match fs::create_dir(&self.directory) { + Ok(()) => { + writeln!(cleanup_file, "{}", self.directory.display())?; + } + Err(e) + if e.kind() == std::io::ErrorKind::AlreadyExists + && self.directory.is_dir() => {} + Err(e) => return Err(e.into()), + } + Ok(()) } From a6e6d2cf7b3d0fe8719c69764aa33be54c279d08 Mon Sep 17 00:00:00 2001 From: Giorgos Miliaras Date: Mon, 26 Oct 2026 14:54:59 +0000 Subject: [PATCH 2/2] Update already-exists tests and format --- .../commands/make_directory_command.rs | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/installer/commands/make_directory_command.rs b/src/installer/commands/make_directory_command.rs index ae6a097b..e861b671 100644 --- a/src/installer/commands/make_directory_command.rs +++ b/src/installer/commands/make_directory_command.rs @@ -17,18 +17,18 @@ impl MakeDirectoryCommand { } /// # Errors - /// Returns an error if the command execution fails, unless the path already exists + /// Returns an error if the directory cannot be created, unless a directory + /// already exists at the path. pub fn execute(&self, cleanup_file: &mut dyn Write) -> Result<()> { match fs::create_dir(&self.directory) { Ok(()) => { writeln!(cleanup_file, "{}", self.directory.display())?; - } - Err(e) - if e.kind() == std::io::ErrorKind::AlreadyExists - && self.directory.is_dir() => {} + }, + Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists && self.directory.is_dir() => { + }, Err(e) => return Err(e.into()), } - + Ok(()) } @@ -70,11 +70,46 @@ mod tests { let cmd = MakeDirectoryCommand::new(dir.clone()); let mut cleanup = Vec::new(); - assert!(cmd.execute(&mut cleanup).is_err()); + cmd.execute(&mut cleanup).unwrap(); + + assert!(dir.is_dir()); + assert!(cleanup.is_empty()); fs::remove_dir(&dir).ok(); } + #[test] + fn execute_file_exists() { + let path = std::env::temp_dir().join("test_mkdir_file_exists"); + let _ = fs::remove_dir_all(&path); + fs::write(&path, b"").unwrap(); + + let cmd = MakeDirectoryCommand::new(path.clone()); + let mut cleanup = Vec::new(); + assert!(cmd.execute(&mut cleanup).is_err()); + assert!(cleanup.is_empty()); + + fs::remove_file(&path).ok(); + } + + #[cfg(unix)] + #[test] + fn execute_symlink_to_dir_exists() { + let target = std::env::temp_dir().join("test_mkdir_symlink_target"); + let link = std::env::temp_dir().join("test_mkdir_symlink_link"); + let _ = fs::remove_file(&link); + fs::create_dir_all(&target).unwrap(); + std::os::unix::fs::symlink(&target, &link).unwrap(); + + let cmd = MakeDirectoryCommand::new(link.clone()); + let mut cleanup = Vec::new(); + cmd.execute(&mut cleanup).unwrap(); + assert!(cleanup.is_empty()); + + fs::remove_file(&link).ok(); + fs::remove_dir(&target).ok(); + } + #[test] fn execute_parent_not_exists() { let cmd = MakeDirectoryCommand::new("/nonexistent/parent/dir".into());