Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: clear build folder before extracting #428

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 73 additions & 5 deletions deployer/src/deployment/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,8 @@ impl Queued {
info!("Extracting received data");

let project_path = builds_path.join(&self.service_name);
fs::create_dir_all(project_path.clone()).await?;

extract_tar_gz_data(self.data.as_slice(), &project_path)?;
extract_tar_gz_data(self.data.as_slice(), &project_path).await?;

let secrets = get_secrets(&project_path).await?;
set_secrets(secrets, &self.service_id, secret_recorder).await?;
Expand Down Expand Up @@ -224,11 +223,28 @@ async fn set_secrets(

/// Equivalent to the command: `tar -xzf --strip-components 1`
#[instrument(skip(data, dest))]
fn extract_tar_gz_data(data: impl Read, dest: impl AsRef<Path>) -> Result<()> {
async fn extract_tar_gz_data(data: impl Read, dest: impl AsRef<Path>) -> Result<()> {
let tar = GzDecoder::new(data);
let mut archive = Archive::new(tar);
archive.set_overwrite(true);

fs::create_dir_all(&dest).await?;

// Clear directory first
let mut entries = fs::read_dir(&dest).await?;
while let Some(entry) = entries.next_entry().await? {
// Ignore the build cache directory
if entry.file_name() == "target" {
continue;
}

if entry.metadata().await?.is_dir() {
fs::remove_dir_all(entry.path()).await?;
} else {
fs::remove_file(entry.path()).await?;
}
}

for entry in archive.entries()? {
let mut entry = entry?;
let path: PathBuf = entry.path()?.components().skip(1).collect();
Expand Down Expand Up @@ -337,6 +353,32 @@ mod tests {
let dir = TempDir::new("shuttle-extraction-test").unwrap();
let p = dir.path();

// Files whose content should be replaced with the archive
fs::write(p.join("world.txt"), b"original text")
.await
.unwrap();

// Extra files that should be deleted
fs::write(
p.join("extra.txt"),
b"extra file at top level that should be deleted",
)
.await
.unwrap();
fs::create_dir_all(p.join("subdir")).await.unwrap();
fs::write(
p.join("subdir/extra.txt"),
b"extra file in subdir that should be deleted",
)
.await
.unwrap();

// Build cache in `/target` should not be cleared/deleted
fs::create_dir_all(p.join("target")).await.unwrap();
fs::write(p.join("target/asset.txt"), b"some file in the build cache")
.await
.unwrap();

// Binary data for an archive in the following form:
//
// - temp
Expand All @@ -355,7 +397,9 @@ ff0e55bda1ff01000000000000000000e0079c01ff12a55500280000",
)
.unwrap();

super::extract_tar_gz_data(test_data.as_slice(), &p).unwrap();
super::extract_tar_gz_data(test_data.as_slice(), &p)
.await
.unwrap();
assert!(fs::read_to_string(p.join("world.txt"))
.await
.unwrap()
Expand All @@ -365,8 +409,32 @@ ff0e55bda1ff01000000000000000000e0079c01ff12a55500280000",
.unwrap()
.starts_with("def"));

assert_eq!(
fs::metadata(p.join("extra.txt")).await.unwrap_err().kind(),
std::io::ErrorKind::NotFound,
"extra file should be deleted"
);
assert_eq!(
fs::metadata(p.join("subdir/extra.txt"))
.await
.unwrap_err()
.kind(),
std::io::ErrorKind::NotFound,
"extra file in subdir should be deleted"
);

assert_eq!(
fs::read_to_string(p.join("target/asset.txt"))
.await
.unwrap(),
"some file in the build cache",
"build cache file should not be touched"
);

// Can we extract again without error?
super::extract_tar_gz_data(test_data.as_slice(), &p).unwrap();
super::extract_tar_gz_data(test_data.as_slice(), &p)
.await
.unwrap();
}

#[tokio::test(flavor = "multi_thread")]
Expand Down