|
| 1 | +//! # Helper APIs for interacting with bootc-image-builder |
| 2 | +//! |
| 3 | +//! See <https://github.com/osbuild/bootc-image-builder> |
| 4 | +//! |
| 5 | +
|
| 6 | +use anyhow::Result; |
| 7 | +use camino::Utf8Path; |
| 8 | +use cap_std_ext::{cap_std::fs::Dir, cmdext::CapStdExtCommandExt}; |
| 9 | +use fn_error_context::context; |
| 10 | + |
| 11 | +use crate::task::Task; |
| 12 | + |
| 13 | +/// Handle /etc/containers readonly mount. |
| 14 | +/// |
| 15 | +/// Ufortunately today podman requires that /etc be writable for |
| 16 | +/// `/etc/containers/networks`. bib today creates this as a readonly mount: |
| 17 | +/// https://github.com/osbuild/osbuild/blob/4edbe227d41c767441b9bf4390398afc6dc8f901/osbuild/buildroot.py#L243 |
| 18 | +/// |
| 19 | +/// Work around that by adding a transient, writable overlayfs. |
| 20 | +fn adjust_etc_containers(tempdir: &Dir) -> Result<()> { |
| 21 | + let etc_containers = Utf8Path::new("/etc/containers"); |
| 22 | + // If there's no /etc/containers, nothing to do |
| 23 | + if !etc_containers.try_exists()? { |
| 24 | + return Ok(()); |
| 25 | + } |
| 26 | + if rustix::fs::access(etc_containers.as_std_path(), rustix::fs::Access::WRITE_OK).is_ok() { |
| 27 | + return Ok(()); |
| 28 | + } |
| 29 | + // Create dirs for the overlayfs upper and work in the install-global tmpdir. |
| 30 | + tempdir.create_dir_all("etc-ovl/upper")?; |
| 31 | + tempdir.create_dir("etc-ovl/work")?; |
| 32 | + let opts = format!("lowerdir={etc_containers},workdir=etc-ovl/work,upperdir=etc-ovl/upper"); |
| 33 | + let mut t = Task::new( |
| 34 | + &format!("Mount transient overlayfs for {etc_containers}"), |
| 35 | + "mount", |
| 36 | + ) |
| 37 | + .args(["-t", "overlay", "overlay", "-o", opts.as_str()]) |
| 38 | + .arg(etc_containers); |
| 39 | + t.cmd.cwd_dir(tempdir.try_clone()?); |
| 40 | + t.run()?; |
| 41 | + Ok(()) |
| 42 | +} |
| 43 | + |
| 44 | +/// osbuild mounts the host's /var/lib/containers at /run/osbuild/containers; mount |
| 45 | +/// it back to /var/lib/containers where the default container stack expects to find it. |
| 46 | +fn propagate_run_osbuild_containers(root: &Dir) -> Result<()> { |
| 47 | + let osbuild_run_containers = Utf8Path::new("run/osbuild/containers"); |
| 48 | + // If we're not apparently running under osbuild, then we no-op. |
| 49 | + if !root.try_exists(osbuild_run_containers)? { |
| 50 | + return Ok(()); |
| 51 | + } |
| 52 | + // If we do seem to have a valid container store though, use that |
| 53 | + if crate::podman::storage_exists_default(root)? { |
| 54 | + return Ok(()); |
| 55 | + } |
| 56 | + let relative_storage = Utf8Path::new(crate::podman::CONTAINER_STORAGE.trim_start_matches('/')); |
| 57 | + root.create_dir_all(relative_storage)?; |
| 58 | + Task::new("Creating bind mount for run/osbuild/containers", "mount") |
| 59 | + .arg("--rbind") |
| 60 | + .args([osbuild_run_containers, relative_storage]) |
| 61 | + .cwd(root)? |
| 62 | + .run()?; |
| 63 | + Ok(()) |
| 64 | +} |
| 65 | + |
| 66 | +/// bootc-image-builder today does a few things that we need to |
| 67 | +/// deal with. |
| 68 | +#[context("bootc-image-builder adjustments")] |
| 69 | +pub(crate) fn adjust_for_bootc_image_builder(root: &Dir, tempdir: &Dir) -> Result<()> { |
| 70 | + adjust_etc_containers(tempdir)?; |
| 71 | + propagate_run_osbuild_containers(root)?; |
| 72 | + Ok(()) |
| 73 | +} |
0 commit comments