@@ -20,8 +20,7 @@ use std::str::FromStr;
20
20
use std:: sync:: Arc ;
21
21
use std:: time:: Duration ;
22
22
23
- use anyhow:: { anyhow, Context , Result } ;
24
- use anyhow:: { ensure, Ok } ;
23
+ use anyhow:: { anyhow, ensure, Context , Result } ;
25
24
use bootc_utils:: CommandRunExt ;
26
25
use camino:: Utf8Path ;
27
26
use camino:: Utf8PathBuf ;
@@ -44,6 +43,7 @@ use rustix::fs::{FileTypeExt, MetadataExt as _};
44
43
use serde:: { Deserialize , Serialize } ;
45
44
46
45
use self :: baseline:: InstallBlockDeviceOpts ;
46
+ use crate :: boundimage:: { BoundImage , ResolvedBoundImage } ;
47
47
use crate :: containerenv:: ContainerExecutionInfo ;
48
48
use crate :: lsm;
49
49
use crate :: mount:: Filesystem ;
@@ -131,6 +131,27 @@ pub(crate) struct InstallSourceOpts {
131
131
pub ( crate ) source_imgref : Option < String > ,
132
132
}
133
133
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
+
134
155
#[ derive( clap:: Args , Debug , Clone , Serialize , Deserialize , PartialEq , Eq ) ]
135
156
pub ( crate ) struct InstallConfigOpts {
136
157
/// Disable SELinux in the target (installed) system.
@@ -166,10 +187,11 @@ pub(crate) struct InstallConfigOpts {
166
187
#[ serde( default ) ]
167
188
pub ( crate ) generic_image : bool ,
168
189
169
- /// Do not pull any " logically bound" images at install time .
170
- #[ clap( long, hide = true ) ]
190
+ /// How should logically bound images be retrieved .
191
+ #[ clap( long) ]
171
192
#[ serde( default ) ]
172
- pub ( crate ) skip_bound_images : bool ,
193
+ #[ arg( default_value_t) ]
194
+ pub ( crate ) bound_images : BoundImagesOpt ,
173
195
174
196
/// The stateroot name to use. Defaults to `default`.
175
197
#[ clap( long) ]
@@ -1318,7 +1340,7 @@ async fn install_with_sysroot(
1318
1340
rootfs : & RootSetup ,
1319
1341
sysroot : & Storage ,
1320
1342
boot_uuid : & str ,
1321
- bound_images : & [ crate :: boundimage :: ResolvedBoundImage ] ,
1343
+ bound_images : BoundImages ,
1322
1344
has_ostree : bool ,
1323
1345
) -> Result < ( ) > {
1324
1346
// And actually set up the container in that root, returning a deployment and
@@ -1346,18 +1368,66 @@ async fn install_with_sysroot(
1346
1368
tracing:: debug!( "Installed bootloader" ) ;
1347
1369
1348
1370
tracing:: debug!( "Perfoming post-deployment operations" ) ;
1349
- // Note that we *always* initialize this container storage, even
1350
- // if there are no bound images today.
1371
+
1372
+ // Note that we *always* initialize this container storage, even if there are no bound images
1373
+ // today.
1351
1374
let imgstore = sysroot. get_ensure_imgstore ( ) ?;
1352
- // Now copy each bound image from the host's container storage into the target.
1353
- for image in bound_images {
1354
- let image = image. image . as_str ( ) ;
1355
- imgstore. pull_from_host_storage ( image) . await ?;
1375
+
1376
+ match bound_images {
1377
+ BoundImages :: Skip => { }
1378
+ BoundImages :: Resolved ( resolved_bound_images) => {
1379
+ // Now copy each bound image from the host's container storage into the target.
1380
+ for image in resolved_bound_images {
1381
+ let image = image. image . as_str ( ) ;
1382
+ imgstore. pull_from_host_storage ( image) . await ?;
1383
+ }
1384
+ }
1385
+ BoundImages :: Unresolved ( bound_images) => {
1386
+ crate :: boundimage:: pull_images ( sysroot, bound_images)
1387
+ . await
1388
+ . context ( "pulling bound images" ) ?;
1389
+ }
1356
1390
}
1357
1391
1358
1392
Ok ( ( ) )
1359
1393
}
1360
1394
1395
+ enum BoundImages {
1396
+ Skip ,
1397
+ Resolved ( Vec < ResolvedBoundImage > ) ,
1398
+ Unresolved ( Vec < BoundImage > ) ,
1399
+ }
1400
+
1401
+ impl BoundImages {
1402
+ async fn from_state ( state : & State ) -> Result < Self > {
1403
+ let bound_images = match state. config_opts . bound_images {
1404
+ BoundImagesOpt :: Skip => BoundImages :: Skip ,
1405
+ others => {
1406
+ let queried_images = crate :: boundimage:: query_bound_images ( & state. container_root ) ?;
1407
+ match others {
1408
+ BoundImagesOpt :: Stored => {
1409
+ // Verify each bound image is present in the container storage
1410
+ let mut r = Vec :: with_capacity ( queried_images. len ( ) ) ;
1411
+ for image in queried_images {
1412
+ let resolved = ResolvedBoundImage :: from_image ( & image) . await ?;
1413
+ tracing:: debug!( "Resolved {}: {}" , resolved. image, resolved. digest) ;
1414
+ r. push ( resolved)
1415
+ }
1416
+ BoundImages :: Resolved ( r)
1417
+ }
1418
+ BoundImagesOpt :: Pull => {
1419
+ // No need to resolve the images, we will pull them into the target later
1420
+ BoundImages :: Unresolved ( queried_images)
1421
+ }
1422
+ BoundImagesOpt :: Skip => anyhow:: bail!( "unreachable error" ) ,
1423
+ }
1424
+ }
1425
+ } ;
1426
+
1427
+ Ok ( bound_images)
1428
+ }
1429
+ }
1430
+
1361
1431
async fn install_to_filesystem_impl ( state : & State , rootfs : & mut RootSetup ) -> Result < ( ) > {
1362
1432
if matches ! ( state. selinux_state, SELinuxFinalState :: ForceTargetDisabled ) {
1363
1433
rootfs. kargs . push ( "selinux=0" . to_string ( ) ) ;
@@ -1390,23 +1460,7 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
1390
1460
"Missing /dev mount to host /dev"
1391
1461
) ;
1392
1462
1393
- let bound_images = if state. config_opts . skip_bound_images {
1394
- Vec :: new ( )
1395
- } else {
1396
- crate :: boundimage:: query_bound_images ( & state. container_root ) ?
1397
- } ;
1398
- tracing:: debug!( "bound images={bound_images:?}" ) ;
1399
-
1400
- // Verify each bound image is present in the container storage
1401
- let bound_images = {
1402
- let mut r = Vec :: with_capacity ( bound_images. len ( ) ) ;
1403
- for image in bound_images {
1404
- let resolved = crate :: boundimage:: ResolvedBoundImage :: from_image ( & image) . await ?;
1405
- tracing:: debug!( "Resolved {}: {}" , resolved. image, resolved. digest) ;
1406
- r. push ( resolved)
1407
- }
1408
- r
1409
- } ;
1463
+ let bound_images = BoundImages :: from_state ( state) . await ?;
1410
1464
1411
1465
// Initialize the ostree sysroot (repo, stateroot, etc.)
1412
1466
{
@@ -1416,7 +1470,7 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
1416
1470
rootfs,
1417
1471
& sysroot,
1418
1472
& boot_uuid,
1419
- & bound_images,
1473
+ bound_images,
1420
1474
has_ostree,
1421
1475
)
1422
1476
. await ?;
0 commit comments