@@ -20,8 +20,8 @@ use std::str::FromStr;
2020use std:: sync:: Arc ;
2121use std:: time:: Duration ;
2222
23- use anyhow:: Ok ;
2423use anyhow:: { anyhow, Context , Result } ;
24+ use anyhow:: { bail, Ok } ;
2525use bootc_utils:: CommandRunExt ;
2626use camino:: Utf8Path ;
2727use camino:: Utf8PathBuf ;
@@ -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 ;
@@ -130,6 +131,27 @@ pub(crate) struct InstallSourceOpts {
130131 pub ( crate ) source_imgref : Option < String > ,
131132}
132133
134+ #[ derive( ValueEnum , Debug , Copy , Clone , PartialEq , Eq , Serialize , Deserialize , Default ) ]
135+ #[ serde( rename_all = "kebab-case" ) ]
136+ pub ( crate ) enum BoundImagesOpt {
137+ /// Bound images must exist in the source's root container storage (default)
138+ #[ default]
139+ Stored ,
140+ #[ clap( hide = true ) ]
141+ /// Do not resolve any "logically bound" images at install time.
142+ Skip ,
143+ // TODO: Once we implement https://github.com/containers/bootc/issues/863 update this comment
144+ // to mention source's root container storage being used as lookaside cache
145+ /// Bound images will be pulled and stored directly in the target's bootc container storage
146+ Pull ,
147+ }
148+
149+ impl std:: fmt:: Display for BoundImagesOpt {
150+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
151+ self . to_possible_value ( ) . unwrap ( ) . get_name ( ) . fmt ( f)
152+ }
153+ }
154+
133155#[ derive( clap:: Args , Debug , Clone , Serialize , Deserialize , PartialEq , Eq ) ]
134156pub ( crate ) struct InstallConfigOpts {
135157 /// Disable SELinux in the target (installed) system.
@@ -165,10 +187,10 @@ pub(crate) struct InstallConfigOpts {
165187 #[ serde( default ) ]
166188 pub ( crate ) generic_image : bool ,
167189
168- /// Do not pull any " logically bound" images at install time .
169- #[ clap( long, hide = true ) ]
190+ /// How should logically bound images be retrieved .
191+ #[ clap( long) ]
170192 #[ serde( default ) ]
171- pub ( crate ) skip_bound_images : bool ,
193+ pub ( crate ) bound_images : BoundImagesOpt ,
172194
173195 /// The stateroot name to use. Defaults to `default`.
174196 #[ clap( long) ]
@@ -1271,7 +1293,7 @@ async fn install_with_sysroot(
12711293 rootfs : & RootSetup ,
12721294 sysroot : & Storage ,
12731295 boot_uuid : & str ,
1274- bound_images : & [ crate :: boundimage :: ResolvedBoundImage ] ,
1296+ bound_images : BoundImages ,
12751297) -> Result < ( ) > {
12761298 // And actually set up the container in that root, returning a deployment and
12771299 // the aleph state (see below).
@@ -1298,18 +1320,66 @@ async fn install_with_sysroot(
12981320 tracing:: debug!( "Installed bootloader" ) ;
12991321
13001322 tracing:: debug!( "Perfoming post-deployment operations" ) ;
1301- // Note that we *always* initialize this container storage, even
1302- // if there are no bound images today.
1323+
1324+ // Note that we *always* initialize this container storage, even if there are no bound images
1325+ // today.
13031326 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 ?;
1327+
1328+ match bound_images {
1329+ BoundImages :: Skip => { }
1330+ BoundImages :: Resolved ( resolved_bound_images) => {
1331+ // Now copy each bound image from the host's container storage into the target.
1332+ for image in resolved_bound_images {
1333+ let image = image. image . as_str ( ) ;
1334+ imgstore. pull_from_host_storage ( image) . await ?;
1335+ }
1336+ }
1337+ BoundImages :: Unresolved ( bound_images) => {
1338+ crate :: boundimage:: pull_images ( sysroot, bound_images)
1339+ . await
1340+ . context ( "pulling bound images" ) ?;
1341+ }
13081342 }
13091343
13101344 Ok ( ( ) )
13111345}
13121346
1347+ enum BoundImages {
1348+ Skip ,
1349+ Resolved ( Vec < ResolvedBoundImage > ) ,
1350+ Unresolved ( Vec < BoundImage > ) ,
1351+ }
1352+
1353+ impl BoundImages {
1354+ async fn from_state ( state : & State ) -> Result < Self > {
1355+ let bound_images = match state. config_opts . bound_images {
1356+ BoundImagesOpt :: Skip => BoundImages :: Skip ,
1357+ others => {
1358+ let queried_images = crate :: boundimage:: query_bound_images ( & state. container_root ) ?;
1359+ match others {
1360+ BoundImagesOpt :: Stored => {
1361+ // Verify each bound image is present in the container storage
1362+ let mut r = Vec :: with_capacity ( queried_images. len ( ) ) ;
1363+ for image in queried_images {
1364+ let resolved = ResolvedBoundImage :: from_image ( & image) . await ?;
1365+ tracing:: debug!( "Resolved {}: {}" , resolved. image, resolved. digest) ;
1366+ r. push ( resolved)
1367+ }
1368+ BoundImages :: Resolved ( r)
1369+ }
1370+ BoundImagesOpt :: Pull => {
1371+ // No need to resolve the images, we will pull them into the target later
1372+ BoundImages :: Unresolved ( queried_images)
1373+ }
1374+ BoundImagesOpt :: Skip => bail ! ( "unreachable error" ) ,
1375+ }
1376+ }
1377+ } ;
1378+
1379+ Ok ( bound_images)
1380+ }
1381+ }
1382+
13131383async fn install_to_filesystem_impl ( state : & State , rootfs : & mut RootSetup ) -> Result < ( ) > {
13141384 if matches ! ( state. selinux_state, SELinuxFinalState :: ForceTargetDisabled ) {
13151385 rootfs. kargs . push ( "selinux=0" . to_string ( ) ) ;
@@ -1336,28 +1406,12 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
13361406 . ok_or_else ( || anyhow ! ( "No uuid for boot/root" ) ) ?;
13371407 tracing:: debug!( "boot uuid={boot_uuid}" ) ;
13381408
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- } ;
1409+ let bound_images = BoundImages :: from_state ( state) . await ?;
13561410
13571411 // Initialize the ostree sysroot (repo, stateroot, etc.)
13581412 {
13591413 let sysroot = initialize_ostree_root ( state, rootfs) . await ?;
1360- install_with_sysroot ( state, rootfs, & sysroot, & boot_uuid, & bound_images) . await ?;
1414+ install_with_sysroot ( state, rootfs, & sysroot, & boot_uuid, bound_images) . await ?;
13611415 // We must drop the sysroot here in order to close any open file
13621416 // descriptors.
13631417 }
0 commit comments