Skip to content

Commit

Permalink
install: Add support for pulling LBIs during install
Browse files Browse the repository at this point in the history
Partially solves containers#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]>
  • Loading branch information
omertuc committed Nov 1, 2024
1 parent b469332 commit 1c3aa85
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 27 deletions.
6 changes: 5 additions & 1 deletion lib/src/boundimage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ pub(crate) fn query_bound_images(root: &Dir) -> Result<Vec<BoundImage>> {

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

#[context("Pulling bound images")]
pub(crate) async fn pull_images(sysroot: &Storage, bound_images: Vec<BoundImage>) -> Result<()> {
pub(crate) async fn pull_images(
sysroot: &Storage,
bound_images: Vec<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
Expand Down
90 changes: 64 additions & 26 deletions lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ use rustix::fs::{FileTypeExt, MetadataExt as _};
use serde::{Deserialize, Serialize};

use self::baseline::InstallBlockDeviceOpts;
use crate::boundimage::{BoundImage, ResolvedBoundImage};
use crate::containerenv::ContainerExecutionInfo;
use crate::mount::Filesystem;
use crate::spec::ImageReference;
Expand Down Expand Up @@ -165,11 +166,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<String>,
Expand Down Expand Up @@ -1271,7 +1277,7 @@ async fn install_with_sysroot(
rootfs: &RootSetup,
sysroot: &Storage,
boot_uuid: &str,
bound_images: &[crate::boundimage::ResolvedBoundImage],
bound_images: BoundImages,
) -> Result<()> {
// And actually set up the container in that root, returning a deployment and
// the aleph state (see below).
Expand All @@ -1298,18 +1304,66 @@ 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.

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

match bound_images {
BoundImages::Skip => {}
BoundImages::Resolved(resolved_bound_images) => {
// 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?;
}
}
BoundImages::Unresolved(bound_images) => {
crate::boundimage::pull_images(sysroot, bound_images)
.await
.context("pulling bound images")?;
}
}

Ok(())
}

enum BoundImages {
Skip,
Resolved(Vec<ResolvedBoundImage>),
Unresolved(Vec<BoundImage>),
}

impl BoundImages {
async fn from_state(state: &State) -> Result<Self> {
let bound_images = (!state.config_opts.skip_bound_images)
.then(|| crate::boundimage::query_bound_images(&state.container_root))
.transpose()?;

let bound_images = match bound_images {
Some(bound_images) => {
tracing::debug!("bound images={bound_images:?}");
if state.config_opts.pull {
// No need to resolve the images, we will pull them into the target later
BoundImages::Unresolved(bound_images)
} else {
// Verify each bound image is present in the container storage
let mut r = Vec::with_capacity(bound_images.len());
for image in bound_images {
let resolved = ResolvedBoundImage::from_image(&image).await?;
tracing::debug!("Resolved {}: {}", resolved.image, resolved.digest);
r.push(resolved)
}
BoundImages::Resolved(r)
}
}
None => BoundImages::Skip,
};

Ok(bound_images)
}
}

async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Result<()> {
if matches!(state.selinux_state, SELinuxFinalState::ForceTargetDisabled) {
rootfs.kargs.push("selinux=0".to_string());
Expand All @@ -1336,28 +1390,12 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
.ok_or_else(|| anyhow!("No uuid for boot/root"))?;
tracing::debug!("boot uuid={boot_uuid}");

let bound_images = if state.config_opts.skip_bound_images {
Vec::new()
} else {
crate::boundimage::query_bound_images(&state.container_root)?
};
tracing::debug!("bound images={bound_images:?}");

// Verify each bound image is present in the container storage
let bound_images = {
let mut r = Vec::with_capacity(bound_images.len());
for image in bound_images {
let resolved = crate::boundimage::ResolvedBoundImage::from_image(&image).await?;
tracing::debug!("Resolved {}: {}", resolved.image, resolved.digest);
r.push(resolved)
}
r
};
let bound_images = BoundImages::from_state(state).await?;

// 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).await?;
// We must drop the sysroot here in order to close any open file
// descriptors.
}
Expand Down

0 comments on commit 1c3aa85

Please sign in to comment.