From a6374a6c50280f5970c5add4b5660d51d3b6c8e7 Mon Sep 17 00:00:00 2001 From: Omer Tuchfeld Date: Thu, 31 Oct 2024 13:49:25 +0100 Subject: [PATCH] install: Add support for pulling LBIs during install 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 --- lib/src/boundimage.rs | 8 ++++++-- lib/src/install.rs | 42 +++++++++++++++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/lib/src/boundimage.rs b/lib/src/boundimage.rs index f5b552c3c..6290c8e0a 100644 --- a/lib/src/boundimage.rs +++ b/lib/src/boundimage.rs @@ -44,7 +44,7 @@ pub(crate) struct ResolvedBoundImage { /// Given a deployment, pull all container images it references. pub(crate) async fn pull_bound_images(sysroot: &Storage, deployment: &Deployment) -> Result<()> { let bound_images = query_bound_images_for_deployment(sysroot, deployment)?; - pull_images(sysroot, bound_images).await + pull_images(sysroot, &bound_images).await } #[context("Querying bound images")] @@ -108,6 +108,7 @@ pub(crate) fn query_bound_images(root: &Dir) -> Result> { #[cfg(feature = "install")] impl ResolvedBoundImage { + #[context("resolving bound image {}", src.image)] pub(crate) async fn from_image(src: &BoundImage) -> Result { let proxy = containers_image_proxy::ImageProxy::new().await?; let img = proxy @@ -148,7 +149,10 @@ fn parse_container_file(file_contents: &tini::Ini) -> Result { } #[context("Pulling bound images")] -pub(crate) async fn pull_images(sysroot: &Storage, bound_images: Vec) -> Result<()> { +pub(crate) async fn pull_images( + sysroot: &Storage, + bound_images: &[crate::boundimage::BoundImage], +) -> Result<()> { tracing::debug!("Pulling bound images: {}", bound_images.len()); // Yes, the usage of NonZeroUsize here is...maybe odd looking, but I find // it an elegant way to divide (empty vector, non empty vector) since diff --git a/lib/src/install.rs b/lib/src/install.rs index 29e747081..0d6130fd3 100644 --- a/lib/src/install.rs +++ b/lib/src/install.rs @@ -165,11 +165,16 @@ pub(crate) struct InstallConfigOpts { #[serde(default)] pub(crate) generic_image: bool, - /// Do not pull any "logically bound" images at install time. + /// Do not resolve any "logically bound" images at install time. #[clap(long, hide = true)] #[serde(default)] pub(crate) skip_bound_images: bool, + /// Pull "logically bound" images at install time. + #[clap(long)] + #[serde(default)] + pub(crate) pull: bool, + /// The stateroot name to use. Defaults to `default`. #[clap(long)] pub(crate) stateroot: Option, @@ -1271,7 +1276,8 @@ async fn install_with_sysroot( rootfs: &RootSetup, sysroot: &Storage, boot_uuid: &str, - bound_images: &[crate::boundimage::ResolvedBoundImage], + bound_images: &[crate::boundimage::BoundImage], + resolved_bound_images: &[crate::boundimage::ResolvedBoundImage], ) -> Result<()> { // And actually set up the container in that root, returning a deployment and // the aleph state (see below). @@ -1298,13 +1304,21 @@ async fn install_with_sysroot( tracing::debug!("Installed bootloader"); tracing::debug!("Perfoming post-deployment operations"); + // Note that we *always* initialize this container storage, even // if there are no bound images today. let imgstore = sysroot.get_ensure_imgstore()?; - // Now copy each bound image from the host's container storage into the target. - for image in bound_images { - let image = image.image.as_str(); - imgstore.pull_from_host_storage(image).await?; + + if state.config_opts.pull { + crate::boundimage::pull_images(sysroot, bound_images) + .await + .context("pulling bound images")?; + } else { + // Now copy each bound image from the host's container storage into the target. + for image in resolved_bound_images { + let image = image.image.as_str(); + imgstore.pull_from_host_storage(image).await?; + } } Ok(()) @@ -1344,9 +1358,11 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re tracing::debug!("bound images={bound_images:?}"); // Verify each bound image is present in the container storage - let bound_images = { + let resolved_bound_images = if state.config_opts.pull { + Vec::new() + } else { let mut r = Vec::with_capacity(bound_images.len()); - for image in bound_images { + for image in &bound_images { let resolved = crate::boundimage::ResolvedBoundImage::from_image(&image).await?; tracing::debug!("Resolved {}: {}", resolved.image, resolved.digest); r.push(resolved) @@ -1357,7 +1373,15 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re // Initialize the ostree sysroot (repo, stateroot, etc.) { let sysroot = initialize_ostree_root(state, rootfs).await?; - install_with_sysroot(state, rootfs, &sysroot, &boot_uuid, &bound_images).await?; + install_with_sysroot( + state, + rootfs, + &sysroot, + &boot_uuid, + &bound_images, + &resolved_bound_images, + ) + .await?; // We must drop the sysroot here in order to close any open file // descriptors. }