Skip to content

Commit 7c8121a

Browse files
authored
Merge pull request #860 from omertuc/pull_lbi
install: Add support for pulling LBIs during install
2 parents 9d10a78 + 33bd095 commit 7c8121a

File tree

3 files changed

+94
-31
lines changed

3 files changed

+94
-31
lines changed

lib/src/boundimage.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ pub(crate) fn query_bound_images(root: &Dir) -> Result<Vec<BoundImage>> {
108108

109109
#[cfg(feature = "install")]
110110
impl ResolvedBoundImage {
111+
#[context("resolving bound image {}", src.image)]
111112
pub(crate) async fn from_image(src: &BoundImage) -> Result<Self> {
112113
let proxy = containers_image_proxy::ImageProxy::new().await?;
113114
let img = proxy
@@ -148,7 +149,10 @@ fn parse_container_file(file_contents: &tini::Ini) -> Result<BoundImage> {
148149
}
149150

150151
#[context("Pulling bound images")]
151-
pub(crate) async fn pull_images(sysroot: &Storage, bound_images: Vec<BoundImage>) -> Result<()> {
152+
pub(crate) async fn pull_images(
153+
sysroot: &Storage,
154+
bound_images: Vec<crate::boundimage::BoundImage>,
155+
) -> Result<()> {
152156
tracing::debug!("Pulling bound images: {}", bound_images.len());
153157
// Yes, the usage of NonZeroUsize here is...maybe odd looking, but I find
154158
// it an elegant way to divide (empty vector, non empty vector) since

lib/src/cli.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,11 @@ fn test_parse_install_args() {
10391039
};
10401040
assert!(o.target_opts.target_no_signature_verification);
10411041
assert_eq!(o.filesystem_opts.root_path.as_str(), "/target");
1042+
// Ensure we default to old bound images behavior
1043+
assert_eq!(
1044+
o.config_opts.bound_images,
1045+
crate::install::BoundImagesOpt::Stored
1046+
);
10421047
}
10431048

10441049
#[test]

lib/src/install.rs

+84-30
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ use std::str::FromStr;
2020
use std::sync::Arc;
2121
use std::time::Duration;
2222

23-
use anyhow::{anyhow, Context, Result};
24-
use anyhow::{ensure, Ok};
23+
use anyhow::{anyhow, ensure, Context, Result};
2524
use bootc_utils::CommandRunExt;
2625
use camino::Utf8Path;
2726
use camino::Utf8PathBuf;
@@ -44,6 +43,7 @@ use rustix::fs::{FileTypeExt, MetadataExt as _};
4443
use serde::{Deserialize, Serialize};
4544

4645
use self::baseline::InstallBlockDeviceOpts;
46+
use crate::boundimage::{BoundImage, ResolvedBoundImage};
4747
use crate::containerenv::ContainerExecutionInfo;
4848
use crate::lsm;
4949
use crate::mount::Filesystem;
@@ -131,6 +131,27 @@ pub(crate) struct InstallSourceOpts {
131131
pub(crate) source_imgref: Option<String>,
132132
}
133133

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+
134155
#[derive(clap::Args, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
135156
pub(crate) struct InstallConfigOpts {
136157
/// Disable SELinux in the target (installed) system.
@@ -166,10 +187,11 @@ pub(crate) struct InstallConfigOpts {
166187
#[serde(default)]
167188
pub(crate) generic_image: bool,
168189

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)]
171192
#[serde(default)]
172-
pub(crate) skip_bound_images: bool,
193+
#[arg(default_value_t)]
194+
pub(crate) bound_images: BoundImagesOpt,
173195

174196
/// The stateroot name to use. Defaults to `default`.
175197
#[clap(long)]
@@ -1318,7 +1340,7 @@ async fn install_with_sysroot(
13181340
rootfs: &RootSetup,
13191341
sysroot: &Storage,
13201342
boot_uuid: &str,
1321-
bound_images: &[crate::boundimage::ResolvedBoundImage],
1343+
bound_images: BoundImages,
13221344
has_ostree: bool,
13231345
) -> Result<()> {
13241346
// And actually set up the container in that root, returning a deployment and
@@ -1346,18 +1368,66 @@ async fn install_with_sysroot(
13461368
tracing::debug!("Installed bootloader");
13471369

13481370
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.
13511374
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+
}
13561390
}
13571391

13581392
Ok(())
13591393
}
13601394

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+
13611431
async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Result<()> {
13621432
if matches!(state.selinux_state, SELinuxFinalState::ForceTargetDisabled) {
13631433
rootfs.kargs.push("selinux=0".to_string());
@@ -1390,23 +1460,7 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
13901460
"Missing /dev mount to host /dev"
13911461
);
13921462

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?;
14101464

14111465
// Initialize the ostree sysroot (repo, stateroot, etc.)
14121466
{
@@ -1416,7 +1470,7 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
14161470
rootfs,
14171471
&sysroot,
14181472
&boot_uuid,
1419-
&bound_images,
1473+
bound_images,
14201474
has_ostree,
14211475
)
14221476
.await?;

0 commit comments

Comments
 (0)