Skip to content

Commit 1c3aa85

Browse files
committed
install: Add support for pulling LBIs during install
Partially solves #846 This adds a new `--pull` option to `bootc install` which will pull all LBIs into the target's container storage, even if they are not available in the source root container storage. Signed-off-by: Omer Tuchfeld <[email protected]>
1 parent b469332 commit 1c3aa85

File tree

2 files changed

+69
-27
lines changed

2 files changed

+69
-27
lines changed

lib/src/boundimage.rs

Lines changed: 5 additions & 1 deletion
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/install.rs

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use rustix::fs::{FileTypeExt, MetadataExt as _};
4444
use serde::{Deserialize, Serialize};
4545

4646
use self::baseline::InstallBlockDeviceOpts;
47+
use crate::boundimage::{BoundImage, ResolvedBoundImage};
4748
use crate::containerenv::ContainerExecutionInfo;
4849
use crate::mount::Filesystem;
4950
use 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+
13131367
async 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

Comments
 (0)