11use anyhow:: { anyhow, Context , Error , Result } ;
2- use futures:: future:: try_join_all;
32use futures:: FutureExt ;
43use itertools:: Itertools ;
54use simplelog:: { debug, trace} ;
65use std:: fs:: File ;
76use std:: io:: { BufReader , Read , Write } ;
7+ use std:: iter:: repeat_with;
88use std:: path:: { Path , PathBuf } ;
99use tempfile:: tempdir_in;
1010use zip;
1111
1212use crate :: builder:: docker;
1313use crate :: clients:: docker;
1414use crate :: configparser:: challenge:: { ChallengeConfig , ProvideConfig } ;
15+ use crate :: utils:: TryJoinAll ;
1516
1617/// extract assets from provide config and possible container to challenge directory, return file path(s) extracted
17- #[ tokio:: main( flavor = "current_thread" ) ] // make this a sync function
1818pub async fn extract_asset (
1919 chal : & ChallengeConfig ,
2020 provide : & ProvideConfig ,
@@ -62,73 +62,76 @@ pub async fn extract_asset(
6262 Ok ( vec ! [ archive_name. clone( ) ] )
6363 }
6464
65+ // handle all container events together to manage container, then match again
6566 ProvideConfig :: FromContainer {
6667 container : container_name,
67- files,
68- } => {
69- let tag = chal. container_tag_for_pod ( profile_name, container_name) ?;
70-
71- let name = format ! (
72- "asset-container-{}-{}" ,
73- chal. directory. to_string_lossy( ) . replace( "/" , "-" ) ,
74- container_name
75- ) ;
76- let container = docker:: create_container ( & tag, & name) . await ?;
77-
78- let files = extract_files ( chal, & container, files) . await ;
79-
80- docker:: remove_container ( container) . await ?;
81-
82- files
68+ ..
8369 }
84- . with_context ( || format ! ( "could not copy files {files:?} from container {container_name}" ) ) ,
85-
86- ProvideConfig :: FromContainerRename {
70+ | ProvideConfig :: FromContainerRename {
8771 container : container_name,
88- from,
89- to,
90- } => {
91- let tag = chal. container_tag_for_pod ( profile_name, container_name) ?;
92-
93- let name = format ! (
94- "asset-container-{}-{}" ,
95- chal. directory. to_string_lossy( ) . replace( "/" , "-" ) ,
96- container_name
97- ) ;
98- let container = docker:: create_container ( & tag, & name) . await ?;
99-
100- let files = extract_rename ( chal, & container, from, & chal. directory . join ( to) ) . await ;
101-
102- docker:: remove_container ( container) . await ?;
103-
104- files
72+ ..
10573 }
106- . with_context ( || format ! ( "could not copy file {from:?} from container {container_name}" ) ) ,
107-
108- ProvideConfig :: FromContainerArchive {
74+ | ProvideConfig :: FromContainerArchive {
10975 container : container_name,
110- files,
111- archive_name,
76+ ..
11277 } => {
11378 let tag = chal. container_tag_for_pod ( profile_name, container_name) ?;
11479
11580 let name = format ! (
116- "asset-container-{}-{}" ,
81+ "asset-container-{}-{}-{} " ,
11782 chal. directory. to_string_lossy( ) . replace( "/" , "-" ) ,
118- container_name
83+ container_name,
84+ // include random discriminator to avoid name collisions
85+ repeat_with( fastrand:: alphanumeric)
86+ . take( 6 )
87+ . collect:: <String >( )
11988 ) ;
89+
12090 let container = docker:: create_container ( & tag, & name) . await ?;
12191
122- let files =
123- extract_archive ( chal, & container, files, & chal. directory . join ( archive_name) ) . await ;
92+ // match on `provide` enum again to handle each container type
93+ let files = match provide {
94+ ProvideConfig :: FromContainer {
95+ container : container_name,
96+ files,
97+ } => extract_files ( chal, & container, files)
98+ . await
99+ . with_context ( || {
100+ format ! ( "could not copy files {files:?} from container {container_name}" )
101+ } ) ,
102+
103+ ProvideConfig :: FromContainerRename {
104+ container : container_name,
105+ from,
106+ to,
107+ } => extract_rename ( chal, & container, from, & chal. directory . join ( to) )
108+ . await
109+ . with_context ( || {
110+ format ! ( "could not copy file {from:?} from container {container_name}" )
111+ } ) ,
112+
113+ ProvideConfig :: FromContainerArchive {
114+ container : container_name,
115+ files,
116+ archive_name,
117+ } => extract_archive ( chal, & container, files, & chal. directory . join ( archive_name) )
118+ . await
119+ . with_context ( || {
120+ // rustfmt chokes silently if these format args are inlined... ???
121+ format ! (
122+ "could not create archive {:?} with files {:?} from container {}" ,
123+ archive_name, files, container_name
124+ )
125+ } ) ,
126+
127+ // non-container variants handled by outer match
128+ _ => unreachable ! ( ) ,
129+ } ;
124130
125131 docker:: remove_container ( container) . await ?;
126132
127133 files
128134 }
129- . with_context ( || {
130- format ! ( "could not create archive {archive_name:?} from container {container_name}" )
131- } ) ,
132135 }
133136}
134137
@@ -144,12 +147,15 @@ async fn extract_files(
144147 files
145148 ) ;
146149
147- try_join_all ( files. iter ( ) . map ( |from| async {
148- // use basename of source file as target name
149- let to = chal. directory . join ( from. file_name ( ) . unwrap ( ) ) ;
150- docker:: copy_file ( container, from, & to) . await
151- } ) )
152- . await
150+ files
151+ . iter ( )
152+ . map ( |from| async {
153+ // use basename of source file as target name
154+ let to = chal. directory . join ( from. file_name ( ) . unwrap ( ) ) ;
155+ docker:: copy_file ( container, from, & to) . await
156+ } )
157+ . try_join_all ( )
158+ . await
153159}
154160
155161/// Extract one file from container and rename
@@ -170,7 +176,6 @@ async fn extract_rename(
170176async fn extract_archive (
171177 chal : & ChallengeConfig ,
172178 container : & docker:: ContainerInfo ,
173- // files: &Vec<PathBuf>,
174179 files : & [ PathBuf ] ,
175180 archive_name : & Path ,
176181) -> Result < Vec < PathBuf > > {
@@ -185,16 +190,19 @@ async fn extract_archive(
185190 let tempdir = tempfile:: Builder :: new ( )
186191 . prefix ( ".beavercds-archive-" )
187192 . tempdir_in ( "." ) ?;
188- let copied_files = try_join_all ( files. iter ( ) . map ( |from| async {
189- let to = tempdir. path ( ) . join ( from. file_name ( ) . unwrap ( ) ) ;
190- docker:: copy_file ( container, from, & to) . await
191- } ) )
192- . await ?;
193+ let copied_files = files
194+ . iter ( )
195+ . map ( |from| async {
196+ let to = tempdir. path ( ) . join ( from. file_name ( ) . unwrap ( ) ) ;
197+ docker:: copy_file ( container, from, & to) . await
198+ } )
199+ . try_join_all ( )
200+ . await ?;
193201
194202 // archive_name already has the chal dir prepended
195203 zip_files ( archive_name, & copied_files) ?;
196204
197- Ok ( vec ! [ chal . directory . join ( archive_name ) ] )
205+ Ok ( vec ! [ archive_name . to_path_buf ( ) ] )
198206}
199207
200208/// Add multiple local `files` to a zipfile at `zip_name`
0 commit comments