Skip to content

Commit 1f8bb00

Browse files
authored
Merge pull request #719 from cgwalters/bound-install-time
install: Fetch bound images from host's /var/lib/containers
2 parents 356943a + e2f833f commit 1f8bb00

File tree

2 files changed

+78
-19
lines changed

2 files changed

+78
-19
lines changed

lib/src/boundimage.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use camino::Utf8Path;
1111
use cap_std_ext::cap_std::fs::Dir;
1212
use cap_std_ext::dirext::CapStdExtDirExt;
1313
use fn_error_context::context;
14+
use ostree_ext::containers_image_proxy;
1415
use ostree_ext::ostree::Deployment;
1516
use ostree_ext::sysroot::SysrootLock;
1617

@@ -23,12 +24,18 @@ const BOUND_IMAGE_DIR: &str = "usr/lib/bootc/bound-images.d";
2324
///
2425
/// In the future this may be extended to include e.g. certificates or
2526
/// other pull options.
26-
#[derive(PartialEq, Eq)]
27+
#[derive(Debug, PartialEq, Eq)]
2728
pub(crate) struct BoundImage {
2829
image: String,
2930
auth_file: Option<String>,
3031
}
3132

33+
#[derive(Debug, PartialEq, Eq)]
34+
pub(crate) struct ResolvedBoundImage {
35+
pub(crate) image: String,
36+
pub(crate) digest: String,
37+
}
38+
3239
/// Given a deployment, pull all container images it references.
3340
pub(crate) fn pull_bound_images(sysroot: &SysrootLock, deployment: &Deployment) -> Result<()> {
3441
let deployment_root = &crate::utils::deployment_fd(sysroot, deployment)?;
@@ -86,6 +93,20 @@ pub(crate) fn query_bound_images(root: &Dir) -> Result<Vec<BoundImage>> {
8693
Ok(bound_images)
8794
}
8895

96+
impl ResolvedBoundImage {
97+
pub(crate) async fn from_image(src: &BoundImage) -> Result<Self> {
98+
let proxy = containers_image_proxy::ImageProxy::new().await?;
99+
let img = proxy
100+
.open_image(&format!("containers-storage:{}", src.image))
101+
.await?;
102+
let digest = proxy.fetch_manifest(&img).await?.0;
103+
Ok(Self {
104+
image: src.image.clone(),
105+
digest,
106+
})
107+
}
108+
}
109+
89110
fn parse_image_file(file_contents: &tini::Ini) -> Result<BoundImage> {
90111
let image: String = file_contents
91112
.get("Image", "Image")

lib/src/install.rs

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub(crate) mod config;
1111
pub(crate) mod osconfig;
1212

1313
use std::io::Write;
14-
use std::os::fd::AsFd;
14+
use std::os::fd::{AsFd, OwnedFd};
1515
use std::os::unix::process::CommandExt;
1616
use std::path::Path;
1717
use std::process::Command;
@@ -26,6 +26,7 @@ use camino::Utf8PathBuf;
2626
use cap_std::fs::{Dir, MetadataExt};
2727
use cap_std_ext::cap_std;
2828
use cap_std_ext::cap_std::fs_utf8::DirEntry as DirEntryUtf8;
29+
use cap_std_ext::cmdext::CapStdExtCommandExt;
2930
use cap_std_ext::prelude::CapStdExtDirExt;
3031
use chrono::prelude::*;
3132
use clap::ValueEnum;
@@ -1271,6 +1272,7 @@ async fn install_with_sysroot(
12711272
rootfs: &RootSetup,
12721273
sysroot: &ostree::Sysroot,
12731274
boot_uuid: &str,
1275+
bound_images: &[crate::boundimage::ResolvedBoundImage],
12741276
) -> Result<()> {
12751277
let sysroot = SysrootLock::new_from_sysroot(&sysroot).await?;
12761278
// And actually set up the container in that root, returning a deployment and
@@ -1299,33 +1301,51 @@ async fn install_with_sysroot(
12991301
tracing::debug!("Installed bootloader");
13001302

13011303
tracing::debug!("Perfoming post-deployment operations");
1302-
let deployment_root = crate::utils::deployment_fd(&sysroot, &deployment)?;
1303-
let bound_images = if state.config_opts.skip_bound_images {
1304-
Vec::new()
1305-
} else {
1306-
crate::boundimage::query_bound_images(&deployment_root)?
1307-
};
13081304
if !bound_images.is_empty() {
1305+
// TODO: We shouldn't hardcode the overlay driver for source or
1306+
// target, but we currently need to in order to reference the location.
1307+
// For this one, containers-storage: is actually the *host*'s /var/lib/containers
1308+
// which we are accessing directly.
1309+
let storage_src = "containers-storage:";
13091310
// TODO: We only do this dance to initialize `/var` at install time if
13101311
// there are bound images today; it minimizes side effects.
13111312
// However going forward we really do need to handle a separate /var partition...
13121313
// and to do that we may in the general case need to run the `var.mount`
13131314
// target from the new root.
1315+
// Probably the best fix is for us to switch bound images to use the bootc storage.
13141316
let varpath = format!("ostree/deploy/{stateroot}/var");
13151317
let var = rootfs
13161318
.rootfs_fd
13171319
.open_dir(&varpath)
13181320
.with_context(|| format!("Opening {varpath}"))?;
1319-
Task::new("Mounting deployment /var", "mount")
1320-
.args(["--bind", ".", "/var"])
1321-
.cwd(&var)?
1322-
.run()?;
1323-
// podman needs this
1324-
Task::new("Initializing /var/tmp", "systemd-tmpfiles")
1325-
.args(["--create", "--boot", "--prefix=/var/tmp"])
1326-
.verbose()
1327-
.run()?;
1328-
crate::boundimage::pull_images(&deployment_root, bound_images)?;
1321+
1322+
// The skopeo API expects absolute paths, so we make a temporary bind
1323+
let tmp_dest_var_abs = tempfile::tempdir()?;
1324+
let tmp_dest_var_abs: &Utf8Path = tmp_dest_var_abs.path().try_into()?;
1325+
let mut t = Task::new("Mounting deployment /var", "mount")
1326+
.args(["--bind", "/proc/self/fd/3"])
1327+
.arg(tmp_dest_var_abs);
1328+
t.cmd.take_fd_n(Arc::new(OwnedFd::from(var)), 3);
1329+
t.run()?;
1330+
1331+
// And an ephemeral place for the transient state
1332+
let tmp_runroot = tempfile::tempdir()?;
1333+
let tmp_runroot: &Utf8Path = tmp_runroot.path().try_into()?;
1334+
1335+
// The destination (target stateroot) + container storage dest
1336+
let storage_dest = &format!(
1337+
"containers-storage:[overlay@{tmp_dest_var_abs}/lib/containers/storage+{tmp_runroot}]"
1338+
);
1339+
1340+
// Now copy each bound image from the host's container storage into the target.
1341+
for image in bound_images {
1342+
let image = image.image.as_str();
1343+
Task::new(format!("Copying image to target: {}", image), "skopeo")
1344+
.arg("copy")
1345+
.arg(format!("{storage_src}{image}"))
1346+
.arg(format!("{storage_dest}{image}"))
1347+
.run()?;
1348+
}
13291349
}
13301350

13311351
Ok(())
@@ -1357,10 +1377,28 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
13571377
.ok_or_else(|| anyhow!("No uuid for boot/root"))?;
13581378
tracing::debug!("boot uuid={boot_uuid}");
13591379

1380+
let bound_images = if state.config_opts.skip_bound_images {
1381+
Vec::new()
1382+
} else {
1383+
crate::boundimage::query_bound_images(&state.container_root)?
1384+
};
1385+
tracing::debug!("bound images={bound_images:?}");
1386+
1387+
// Verify each bound image is present in the container storage
1388+
let bound_images = {
1389+
let mut r = Vec::with_capacity(bound_images.len());
1390+
for image in bound_images {
1391+
let resolved = crate::boundimage::ResolvedBoundImage::from_image(&image).await?;
1392+
tracing::debug!("Resolved {}: {}", resolved.image, resolved.digest);
1393+
r.push(resolved)
1394+
}
1395+
r
1396+
};
1397+
13601398
// Initialize the ostree sysroot (repo, stateroot, etc.)
13611399
{
13621400
let sysroot = initialize_ostree_root(state, rootfs).await?;
1363-
install_with_sysroot(state, rootfs, &sysroot, &boot_uuid).await?;
1401+
install_with_sysroot(state, rootfs, &sysroot, &boot_uuid, &bound_images).await?;
13641402
// We must drop the sysroot here in order to close any open file
13651403
// descriptors.
13661404
}

0 commit comments

Comments
 (0)