@@ -44,6 +44,7 @@ use rustix::fs::{FileTypeExt, MetadataExt as _};
4444use serde:: { Deserialize , Serialize } ;
4545
4646use self :: baseline:: InstallBlockDeviceOpts ;
47+ use crate :: boundimage:: { BoundImage , ResolvedBoundImage } ;
4748use crate :: containerenv:: ContainerExecutionInfo ;
4849use crate :: mount:: Filesystem ;
4950use crate :: spec:: ImageReference ;
@@ -165,11 +166,16 @@ pub(crate) struct InstallConfigOpts {
165166 #[ serde( default ) ]
166167 pub ( crate ) generic_image : bool ,
167168
168- /// Do not pull any "logically bound" images at install time.
169+ /// Do not resolve any "logically bound" images at install time.
169170 #[ clap( long, hide = true ) ]
170171 #[ serde( default ) ]
171172 pub ( crate ) skip_bound_images : bool ,
172173
174+ /// Pull "logically bound" images at install time.
175+ #[ clap( long) ]
176+ #[ serde( default ) ]
177+ pub ( crate ) pull : bool ,
178+
173179 /// The stateroot name to use. Defaults to `default`.
174180 #[ clap( long) ]
175181 pub ( crate ) stateroot : Option < String > ,
@@ -1271,7 +1277,7 @@ async fn install_with_sysroot(
12711277 rootfs : & RootSetup ,
12721278 sysroot : & Storage ,
12731279 boot_uuid : & str ,
1274- bound_images : & [ crate :: boundimage :: ResolvedBoundImage ] ,
1280+ bound_images : BoundImages ,
12751281) -> Result < ( ) > {
12761282 // And actually set up the container in that root, returning a deployment and
12771283 // the aleph state (see below).
@@ -1298,18 +1304,66 @@ async fn install_with_sysroot(
12981304 tracing:: debug!( "Installed bootloader" ) ;
12991305
13001306 tracing:: debug!( "Perfoming post-deployment operations" ) ;
1301- // Note that we *always* initialize this container storage, even
1302- // if there are no bound images today.
1307+
1308+ // Note that we *always* initialize this container storage, even if there are no bound images
1309+ // today.
13031310 let imgstore = sysroot. get_ensure_imgstore ( ) ?;
1304- // Now copy each bound image from the host's container storage into the target.
1305- for image in bound_images {
1306- let image = image. image . as_str ( ) ;
1307- imgstore. pull_from_host_storage ( image) . await ?;
1311+
1312+ match bound_images {
1313+ BoundImages :: Skip => { }
1314+ BoundImages :: Resolved ( resolved_bound_images) => {
1315+ // Now copy each bound image from the host's container storage into the target.
1316+ for image in resolved_bound_images {
1317+ let image = image. image . as_str ( ) ;
1318+ imgstore. pull_from_host_storage ( image) . await ?;
1319+ }
1320+ }
1321+ BoundImages :: Unresolved ( bound_images) => {
1322+ crate :: boundimage:: pull_images ( sysroot, bound_images)
1323+ . await
1324+ . context ( "pulling bound images" ) ?;
1325+ }
13081326 }
13091327
13101328 Ok ( ( ) )
13111329}
13121330
1331+ enum BoundImages {
1332+ Skip ,
1333+ Resolved ( Vec < ResolvedBoundImage > ) ,
1334+ Unresolved ( Vec < BoundImage > ) ,
1335+ }
1336+
1337+ impl BoundImages {
1338+ async fn from_state ( state : & State ) -> Result < Self > {
1339+ let bound_images = ( !state. config_opts . skip_bound_images )
1340+ . then ( || crate :: boundimage:: query_bound_images ( & state. container_root ) )
1341+ . transpose ( ) ?;
1342+
1343+ let bound_images = match bound_images {
1344+ Some ( bound_images) => {
1345+ tracing:: debug!( "bound images={bound_images:?}" ) ;
1346+ if state. config_opts . pull {
1347+ // No need to resolve the images, we will pull them into the target later
1348+ BoundImages :: Unresolved ( bound_images)
1349+ } else {
1350+ // Verify each bound image is present in the container storage
1351+ let mut r = Vec :: with_capacity ( bound_images. len ( ) ) ;
1352+ for image in bound_images {
1353+ let resolved = ResolvedBoundImage :: from_image ( & image) . await ?;
1354+ tracing:: debug!( "Resolved {}: {}" , resolved. image, resolved. digest) ;
1355+ r. push ( resolved)
1356+ }
1357+ BoundImages :: Resolved ( r)
1358+ }
1359+ }
1360+ None => BoundImages :: Skip ,
1361+ } ;
1362+
1363+ Ok ( bound_images)
1364+ }
1365+ }
1366+
13131367async fn install_to_filesystem_impl ( state : & State , rootfs : & mut RootSetup ) -> Result < ( ) > {
13141368 if matches ! ( state. selinux_state, SELinuxFinalState :: ForceTargetDisabled ) {
13151369 rootfs. kargs . push ( "selinux=0" . to_string ( ) ) ;
@@ -1336,28 +1390,12 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
13361390 . ok_or_else ( || anyhow ! ( "No uuid for boot/root" ) ) ?;
13371391 tracing:: debug!( "boot uuid={boot_uuid}" ) ;
13381392
1339- let bound_images = if state. config_opts . skip_bound_images {
1340- Vec :: new ( )
1341- } else {
1342- crate :: boundimage:: query_bound_images ( & state. container_root ) ?
1343- } ;
1344- tracing:: debug!( "bound images={bound_images:?}" ) ;
1345-
1346- // Verify each bound image is present in the container storage
1347- let bound_images = {
1348- let mut r = Vec :: with_capacity ( bound_images. len ( ) ) ;
1349- for image in bound_images {
1350- let resolved = crate :: boundimage:: ResolvedBoundImage :: from_image ( & image) . await ?;
1351- tracing:: debug!( "Resolved {}: {}" , resolved. image, resolved. digest) ;
1352- r. push ( resolved)
1353- }
1354- r
1355- } ;
1393+ let bound_images = BoundImages :: from_state ( state) . await ?;
13561394
13571395 // Initialize the ostree sysroot (repo, stateroot, etc.)
13581396 {
13591397 let sysroot = initialize_ostree_root ( state, rootfs) . await ?;
1360- install_with_sysroot ( state, rootfs, & sysroot, & boot_uuid, & bound_images) . await ?;
1398+ install_with_sysroot ( state, rootfs, & sysroot, & boot_uuid, bound_images) . await ?;
13611399 // We must drop the sysroot here in order to close any open file
13621400 // descriptors.
13631401 }
0 commit comments