@@ -45,22 +45,19 @@ impl BootstrapOptions {
4545 ///
4646 /// Takes all the configuration options specified in BootstrapOptions and packages them into
4747 /// a disk image that can be mounted by the GuestOS. The image contains a FAT filesystem with
48- /// a single file named 'ic-bootstrap.tar' that includes all configuration files.
48+ /// the configuration files.
4949 pub fn build_bootstrap_config_image ( & self , out_file : & Path ) -> Result < ( ) > {
5050 let tmp_dir = tempfile:: tempdir ( ) . context ( "Failed to create temporary directory" ) ?;
5151
52- // Create bootstrap tar
53- let tar_path = tmp_dir. path ( ) . join ( "ic-bootstrap.tar" ) ;
54- self . build_bootstrap_tar ( & tar_path) ?;
52+ // Create bootstrap directory with all files
53+ let bootstrap_dir = tmp_dir. path ( ) . join ( "bootstrap" ) ;
54+ fs:: create_dir ( & bootstrap_dir) . context ( "Failed to create bootstrap directory" ) ?;
55+ self . populate_bootstrap_dir ( & bootstrap_dir) ?;
5556
56- let tar_size = fs :: metadata ( & tar_path )
57- . context ( "Failed to get tar file metadata" ) ?
58- . len ( ) ;
57+ let dir_size = fs_extra :: dir :: get_size ( & bootstrap_dir ) ? ;
58+ // image size = 2 * directory size + 1 MB
59+ let image_size = dir_size * 2 + 1024 * 1024 ;
5960
60- // Calculate the disk image size (2 * tar_size + 1MB)
61- let image_size = 2 * tar_size + 1_048_576 ;
62-
63- // Create an empty file of the calculated size
6461 let file = File :: create ( out_file) . context ( "Failed to create output file" ) ?;
6562 file. set_len ( image_size)
6663 . context ( "Failed to set output file size" ) ?;
@@ -77,57 +74,43 @@ impl BootstrapOptions {
7774 bail ! ( "Failed to format disk image" ) ;
7875 }
7976
80- // Copy the tar file to the disk image
81- if !Command :: new ( "mcopy" )
82- . arg ( "-i" )
83- . arg ( out_file)
84- . arg ( "-o" )
85- . arg ( & tar_path)
86- . arg ( "::" )
87- . status ( )
88- . context ( "Failed to execute mcopy command" ) ?
89- . success ( )
90- {
91- bail ! ( "Failed to copy tar to disk image" ) ;
92- }
77+ // Copy all files from bootstrap directory to the disk image
78+ Self :: copy_dir_to_vfat ( & bootstrap_dir, out_file) ?;
9379
9480 Ok ( ( ) )
9581 }
9682
97- /// Build a bootstrap tar file with this configuration.
98- fn build_bootstrap_tar ( & self , out_file : & Path ) -> Result < ( ) > {
99- // Create temporary directory for bootstrap files
100- let bootstrap_dir = tempfile:: tempdir ( ) . context ( "Failed to create temporary directory" ) ?;
101-
83+ /// Populate a directory with bootstrap files.
84+ fn populate_bootstrap_dir ( & self , bootstrap_dir : & Path ) -> Result < ( ) > {
10285 if let Some ( guestos_config) = & self . guestos_config {
103- serialize_and_write_config ( & bootstrap_dir. path ( ) . join ( "config.json" ) , guestos_config)
86+ serialize_and_write_config ( & bootstrap_dir. join ( "config.json" ) , guestos_config)
10487 . context ( "Failed to write guestos config to config.json" ) ?;
10588 }
10689
10790 if let Some ( node_operator_private_key) = & self . node_operator_private_key {
10891 fs:: copy (
10992 node_operator_private_key,
110- bootstrap_dir. path ( ) . join ( "node_operator_private_key.pem" ) ,
93+ bootstrap_dir. join ( "node_operator_private_key.pem" ) ,
11194 )
11295 . context ( "Failed to copy node operator private key" ) ?;
11396 }
11497
11598 if let Some ( ic_crypto) = & self . ic_crypto {
116- Self :: copy_dir_recursively ( ic_crypto, & bootstrap_dir. path ( ) . join ( "ic_crypto" ) )
99+ Self :: copy_dir_recursively ( ic_crypto, & bootstrap_dir. join ( "ic_crypto" ) )
117100 . context ( "Failed to copy IC crypto directory" ) ?;
118101 }
119102
120103 if let Some ( ic_state) = & self . ic_state
121104 && ic_state. exists ( )
122105 {
123- Self :: copy_dir_recursively ( ic_state, & bootstrap_dir. path ( ) . join ( "ic_state" ) )
106+ Self :: copy_dir_recursively ( ic_state, & bootstrap_dir. join ( "ic_state" ) )
124107 . context ( "Failed to copy IC state directory" ) ?;
125108 }
126109
127110 if let Some ( ic_registry_local_store) = & self . ic_registry_local_store {
128111 Self :: copy_dir_recursively (
129112 ic_registry_local_store,
130- & bootstrap_dir. path ( ) . join ( "ic_registry_local_store" ) ,
113+ & bootstrap_dir. join ( "ic_registry_local_store" ) ,
131114 )
132115 . context ( "Failed to copy registry local store" ) ?;
133116 }
@@ -137,33 +120,39 @@ impl BootstrapOptions {
137120 if let Some ( nns_public_key_override) = & self . nns_public_key_override {
138121 fs:: copy (
139122 nns_public_key_override,
140- bootstrap_dir. path ( ) . join ( "nns_public_key_override.pem" ) ,
123+ bootstrap_dir. join ( "nns_public_key_override.pem" ) ,
141124 )
142125 . context ( "Failed to copy NNS public key override" ) ?;
143126 }
144127
145128 if let Some ( accounts_ssh_authorized_keys) = & self . accounts_ssh_authorized_keys {
146- let target_dir = bootstrap_dir. path ( ) . join ( "accounts_ssh_authorized_keys" ) ;
129+ let target_dir = bootstrap_dir. join ( "accounts_ssh_authorized_keys" ) ;
147130 Self :: copy_dir_recursively ( accounts_ssh_authorized_keys, & target_dir)
148131 . context ( "Failed to copy SSH authorized keys" ) ?;
149132 }
150133 }
151134
152- if !Command :: new ( "tar" )
153- . arg ( "cf" )
154- . arg ( out_file)
155- . arg ( "--sort=name" )
156- . arg ( "--owner=root:0" )
157- . arg ( "--group=root:0" )
158- . arg ( "--mtime=UTC 1970-01-01 00:00:00" )
159- . arg ( "-C" )
160- . arg ( bootstrap_dir. path ( ) )
161- . arg ( "." )
162- . status ( )
163- . context ( "Failed to execute tar command" ) ?
164- . success ( )
165- {
166- bail ! ( "Failed to create tar file" ) ;
135+ Ok ( ( ) )
136+ }
137+
138+ /// Copy all files from a directory to a vfat image using mcopy.
139+ fn copy_dir_to_vfat ( src_dir : & Path , vfat_image : & Path ) -> Result < ( ) > {
140+ let all_files = fs:: read_dir ( src_dir) ?
141+ . map ( |entry| Ok ( entry?. path ( ) ) )
142+ . collect :: < Result < Vec < _ > > > ( )
143+ . context ( "Failed to collect config directory entries" ) ?;
144+
145+ let output = Command :: new ( "/usr/bin/mcopy" )
146+ . arg ( "-i" )
147+ . arg ( vfat_image)
148+ . arg ( "-s" )
149+ . args ( all_files)
150+ . arg ( "::/" )
151+ . output ( )
152+ . context ( "Failed to execute mcopy" ) ?;
153+
154+ if !output. status . success ( ) {
155+ bail ! ( "Failed to copy directory contents to vfat image. {output:?}" ) ;
167156 }
168157
169158 Ok ( ( ) )
@@ -191,28 +180,14 @@ impl BootstrapOptions {
191180#[ cfg( test) ]
192181mod tests {
193182 use super :: * ;
194-
195- #[ test]
196- fn test_build_bootstrap_config_image_succeeds_with_default_options ( ) {
197- let tmp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
198- let out_file = tmp_dir. path ( ) . join ( "bootstrap.tar" ) ;
199-
200- assert ! (
201- BootstrapOptions :: default ( )
202- . build_bootstrap_config_image( & out_file)
203- . is_ok( )
204- ) ;
205- }
183+ use config_types:: { DeploymentEnvironment , ICOSSettings , Ipv6Config , NetworkSettings } ;
206184
207185 #[ test]
208186 fn test_build_bootstrap_image ( ) -> Result < ( ) > {
209187 let tmp_dir = tempfile:: tempdir ( ) ?;
210188 let out_file = tmp_dir. path ( ) . join ( "bootstrap.img" ) ;
211189
212- BootstrapOptions {
213- ..Default :: default ( )
214- }
215- . build_bootstrap_config_image ( & out_file) ?;
190+ BootstrapOptions :: default ( ) . build_bootstrap_config_image ( & out_file) ?;
216191
217192 assert ! ( out_file. exists( ) ) ;
218193 assert ! ( fs:: metadata( & out_file) ?. len( ) > 0 ) ;
@@ -222,7 +197,7 @@ mod tests {
222197
223198 #[ test]
224199 #[ cfg( feature = "dev" ) ]
225- fn test_build_bootstrap_tar_with_all_options ( ) -> Result < ( ) > {
200+ fn test_populate_bootstrap_dir_with_all_options ( ) -> Result < ( ) > {
226201 use config_types:: {
227202 DeploymentEnvironment , GuestOSUpgradeConfig , GuestVMType , ICOSSettings , Ipv6Config ,
228203 NetworkSettings ,
@@ -231,7 +206,8 @@ mod tests {
231206 use std:: str:: FromStr ;
232207
233208 let tmp_dir = tempfile:: tempdir ( ) ?;
234- let out_file = tmp_dir. path ( ) . join ( "bootstrap.tar" ) ;
209+ let bootstrap_dir = tmp_dir. path ( ) . join ( "bootstrap" ) ;
210+ fs:: create_dir ( & bootstrap_dir) ?;
235211
236212 // Create test files and directories
237213 let test_files_dir = tmp_dir. path ( ) . join ( "test_files" ) ;
@@ -297,44 +273,36 @@ mod tests {
297273 ic_registry_local_store : Some ( registry_dir) ,
298274 } ;
299275
300- // Build and extract tar
301- bootstrap_options. build_bootstrap_tar ( & out_file) ?;
302- let extract_dir = tmp_dir. path ( ) . join ( "extract" ) ;
303- fs:: create_dir ( & extract_dir) ?;
304- Command :: new ( "tar" )
305- . arg ( "xf" )
306- . arg ( & out_file)
307- . arg ( "-C" )
308- . arg ( & extract_dir)
309- . status ( ) ?;
276+ // Populate bootstrap directory
277+ bootstrap_options. populate_bootstrap_dir ( & bootstrap_dir) ?;
310278
311279 // Verify all copied files and directories
312280 assert_eq ! (
313- fs:: read_to_string( extract_dir . join( "config.json" ) ) ?,
281+ fs:: read_to_string( bootstrap_dir . join( "config.json" ) ) ?,
314282 serde_json:: to_string_pretty( & guestos_config) ?
315283 ) ;
316284 assert_eq ! (
317- fs:: read_to_string( extract_dir . join( "nns_public_key_override.pem" ) ) ?,
285+ fs:: read_to_string( bootstrap_dir . join( "nns_public_key_override.pem" ) ) ?,
318286 "test_nns_key"
319287 ) ;
320288 assert_eq ! (
321- fs:: read_to_string( extract_dir . join( "node_operator_private_key.pem" ) ) ?,
289+ fs:: read_to_string( bootstrap_dir . join( "node_operator_private_key.pem" ) ) ?,
322290 "test_node_key"
323291 ) ;
324292 assert_eq ! (
325- fs:: read_to_string( extract_dir . join( "accounts_ssh_authorized_keys/key1" ) ) ?,
293+ fs:: read_to_string( bootstrap_dir . join( "accounts_ssh_authorized_keys/key1" ) ) ?,
326294 "ssh_key1"
327295 ) ;
328296 assert_eq ! (
329- fs:: read_to_string( extract_dir . join( "ic_crypto/test" ) ) ?,
297+ fs:: read_to_string( bootstrap_dir . join( "ic_crypto/test" ) ) ?,
330298 "crypto_data"
331299 ) ;
332300 assert_eq ! (
333- fs:: read_to_string( extract_dir . join( "ic_state/test" ) ) ?,
301+ fs:: read_to_string( bootstrap_dir . join( "ic_state/test" ) ) ?,
334302 "state_data"
335303 ) ;
336304 assert_eq ! (
337- fs:: read_to_string( extract_dir . join( "ic_registry_local_store/test" ) ) ?,
305+ fs:: read_to_string( bootstrap_dir . join( "ic_registry_local_store/test" ) ) ?,
338306 "registry_data"
339307 ) ;
340308
0 commit comments