Skip to content

Commit f7624d2

Browse files
authored
feat: use temporary directory for zip file handling (#26)
1 parent 2d55149 commit f7624d2

File tree

2 files changed

+46
-31
lines changed

2 files changed

+46
-31
lines changed

post-compute/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ sha256 = "1.6.0"
2424
sha3 = "0.10.8"
2525
strum = "0.27.2"
2626
strum_macros = "0.27.2"
27+
tempfile = "3.20.0"
2728
thiserror = "2.0.12"
2829
walkdir = "2.5.0"
2930
zip = "4.0.0"
@@ -34,6 +35,5 @@ mockall = "0.13.1"
3435
once_cell = "1.21.3"
3536
serial_test = "3.2.0"
3637
temp-env = "0.3.6"
37-
tempfile = "3.20.0"
3838
tokio = "1.45.0"
3939
wiremock = "0.6.3"

post-compute/src/compute/web2_result.rs

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ use std::{
1515
io::{self, Write},
1616
path::{Path, PathBuf},
1717
};
18+
use tempfile::TempDir;
1819
use walkdir::WalkDir;
1920
use zip::{ZipWriter, write::FileOptions};
2021

21-
const SLASH_POST_COMPUTE_TMP: &str = "/post-compute-tmp";
2222
const RESULT_FILE_NAME_MAX_LENGTH: usize = 31;
2323
const IPFS_RESULT_STORAGE_PROVIDER: &str = "ipfs";
2424
const DROPBOX_RESULT_STORAGE_PROVIDER: &str = "dropbox";
@@ -225,6 +225,10 @@ impl Web2ResultInterface for Web2ResultService {
225225
/// The method name maintains compatibility with the Java implementation, though
226226
/// encryption is not yet implemented.
227227
///
228+
/// The method creates a temporary directory for intermediate files (zip archive and
229+
/// encrypted files if encryption is enabled). The temporary directory is automatically
230+
/// cleaned up when the function completes, whether successfully or with an error.
231+
///
228232
/// # Arguments
229233
///
230234
/// * `computed_file` - The [`ComputedFile`] containing task information and metadata
@@ -247,24 +251,29 @@ impl Web2ResultInterface for Web2ResultService {
247251
// check result file names are not too long
248252
self.check_result_files_name(computed_file.task_id.as_ref().unwrap(), "/iexec_out")?;
249253

250-
// save zip file to the protected region /post-compute-tmp (temporarily)
251-
let zip_path = match self.zip_iexec_out("/iexec_out", SLASH_POST_COMPUTE_TMP) {
252-
Ok(path) => path,
253-
Err(..) => {
254-
error!("zipIexecOut stage failed");
255-
return Err(ReplicateStatusCause::PostComputeOutFolderZipFailed);
256-
}
257-
};
254+
// Create a temporary directory for the zip file
255+
let temp_dir = TempDir::new().map_err(|e| {
256+
error!("Failed to create temporary directory: {e}");
257+
ReplicateStatusCause::PostComputeOutFolderZipFailed
258+
})?;
259+
260+
// Get the path as a string - to_str() returns Option<&str>
261+
let temp_dir_path = temp_dir.path().to_str().ok_or_else(|| {
262+
error!("Failed to convert temporary directory path to string");
263+
ReplicateStatusCause::PostComputeOutFolderZipFailed
264+
})?;
265+
266+
// save zip file to the temporary directory
267+
let zip_path = self
268+
.zip_iexec_out("/iexec_out", temp_dir_path)
269+
.map_err(|e| {
270+
error!("zipIexecOut stage failed: {e}");
271+
ReplicateStatusCause::PostComputeOutFolderZipFailed
272+
})?;
258273

259274
let result_path = self.eventually_encrypt_result(&zip_path)?;
260275
self.upload_result(computed_file, &result_path)?; //TODO Share result link to beneficiary
261276

262-
// Clean up the temporary zip file
263-
if let Err(e) = fs::remove_file(&zip_path) {
264-
error!("Failed to remove temporary zip file {zip_path}: {e}");
265-
// We don't return an error here as the upload was successful
266-
};
267-
268277
Ok(())
269278
}
270279

@@ -640,7 +649,12 @@ mod tests {
640649
computed_file: &ComputedFile,
641650
) -> Result<(), ReplicateStatusCause> {
642651
service.check_result_files_name(computed_file.task_id.as_ref().unwrap(), "/iexec_out")?;
643-
let zip_path = match service.zip_iexec_out("/iexec_out", SLASH_POST_COMPUTE_TMP) {
652+
let temp_dir = TempDir::new().map_err(|e| {
653+
error!("Failed to create temporary directory: {e}");
654+
ReplicateStatusCause::PostComputeOutFolderZipFailed
655+
})?;
656+
let zip_path = match service.zip_iexec_out("/iexec_out", temp_dir.path().to_str().unwrap())
657+
{
644658
Ok(path) => path,
645659
Err(..) => {
646660
error!("zipIexecOut stage failed");
@@ -649,14 +663,14 @@ mod tests {
649663
};
650664
let result_path = service.eventually_encrypt_result(&zip_path)?;
651665
service.upload_result(computed_file, &result_path)?;
666+
drop(temp_dir);
652667
Ok(())
653668
}
654669

655670
#[test]
656671
fn encrypt_and_upload_result_completes_successfully_when_all_operations_succeed() {
657672
let mut web2_result_mock = MockWeb2ResultInterface::new();
658673
let computed_file = create_test_computed_file("0x123");
659-
let zip_path = "/post-compute-tmp/iexec_out.zip";
660674

661675
web2_result_mock
662676
.expect_check_result_files_name()
@@ -666,19 +680,22 @@ mod tests {
666680

667681
web2_result_mock
668682
.expect_zip_iexec_out()
669-
.with(eq("/iexec_out"), eq(SLASH_POST_COMPUTE_TMP))
683+
.with(eq("/iexec_out"), function(|path: &str| !path.is_empty()))
670684
.times(1)
671-
.returning(move |_, _| Ok(String::from(zip_path)));
685+
.returning(|_, path| Ok(format!("{path}/iexec_out.zip")));
672686

673687
web2_result_mock
674688
.expect_eventually_encrypt_result()
675-
.with(eq(zip_path))
689+
.with(function(|path: &str| path.ends_with("iexec_out.zip")))
676690
.times(1)
677-
.returning(|_| Ok(String::from("/post-compute-tmp/iexec_out.zip")));
691+
.returning(|path| Ok(path.to_string()));
678692

679693
web2_result_mock
680694
.expect_upload_result()
681-
.with(eq(computed_file.clone()), eq(zip_path))
695+
.with(
696+
eq(computed_file.clone()),
697+
function(|path: &str| path.ends_with("iexec_out.zip")),
698+
)
682699
.times(1)
683700
.returning(|_, _| Ok(String::from("https://ipfs.io/ipfs/QmHash")));
684701

@@ -726,7 +743,6 @@ mod tests {
726743
fn encrypt_and_upload_result_returns_error_when_encryption_returns_error() {
727744
let mut web2_result_mock = MockWeb2ResultInterface::new();
728745
let computed_file = create_test_computed_file("0x123");
729-
let zip_path = "/post-compute-tmp/iexec_out.zip";
730746

731747
web2_result_mock
732748
.expect_check_result_files_name()
@@ -736,13 +752,13 @@ mod tests {
736752

737753
web2_result_mock
738754
.expect_zip_iexec_out()
739-
.with(eq("/iexec_out"), eq(SLASH_POST_COMPUTE_TMP))
755+
.with(eq("/iexec_out"), function(|path: &str| !path.is_empty()))
740756
.times(1)
741-
.returning(move |_, _| Ok(String::from(zip_path)));
757+
.returning(|_, path| Ok(format!("{path}/iexec_out.zip")));
742758

743759
web2_result_mock
744760
.expect_eventually_encrypt_result()
745-
.with(eq(zip_path))
761+
.with(function(|path: &str| path.ends_with("iexec_out.zip")))
746762
.times(1)
747763
.returning(|_| Err(ReplicateStatusCause::PostComputeEncryptionFailed));
748764

@@ -757,21 +773,20 @@ mod tests {
757773
fn encrypt_and_upload_result_returns_error_when_upload_fails() {
758774
let mut web2_result_mock = MockWeb2ResultInterface::new();
759775
let computed_file = create_test_computed_file("0x123");
760-
let zip_path = "/post-compute-tmp/iexec_out.zip";
761776

762777
web2_result_mock
763778
.expect_check_result_files_name()
764779
.returning(|_, _| Ok(()));
765780

766781
web2_result_mock
767782
.expect_zip_iexec_out()
768-
.returning(move |_, _| Ok(String::from(zip_path)));
783+
.returning(|_, path| Ok(format!("{path}/iexec_out.zip")));
769784

770785
web2_result_mock
771786
.expect_eventually_encrypt_result()
772-
.with(eq(zip_path))
787+
.with(function(|path: &str| path.ends_with("iexec_out.zip")))
773788
.times(1)
774-
.returning(move |_| Ok(String::from("/post-compute-tmp/iexec_out.zip")));
789+
.returning(|path| Ok(path.to_string()));
775790

776791
web2_result_mock
777792
.expect_upload_result()

0 commit comments

Comments
 (0)