@@ -15,10 +15,10 @@ use std::{
1515 io:: { self , Write } ,
1616 path:: { Path , PathBuf } ,
1717} ;
18+ use tempfile:: TempDir ;
1819use walkdir:: WalkDir ;
1920use zip:: { ZipWriter , write:: FileOptions } ;
2021
21- const SLASH_POST_COMPUTE_TMP : & str = "/post-compute-tmp" ;
2222const RESULT_FILE_NAME_MAX_LENGTH : usize = 31 ;
2323const IPFS_RESULT_STORAGE_PROVIDER : & str = "ipfs" ;
2424const 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 ! ( "{}/iexec_out.zip" , path ) ) ) ;
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 ! ( "{}/iexec_out.zip" , path ) ) ) ;
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 ! ( "{}/iexec_out.zip" , path ) ) ) ;
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