diff --git a/lib/src/install.rs b/lib/src/install.rs index 68a49d14..430d6705 100644 --- a/lib/src/install.rs +++ b/lib/src/install.rs @@ -40,6 +40,7 @@ use ostree_ext::ostree; use ostree_ext::prelude::Cast; use ostree_ext::sysroot::SysrootLock; use rustix::fs::{FileTypeExt, MetadataExt as _}; +use rustix::thread::Pid; use serde::{Deserialize, Serialize}; use self::baseline::InstallBlockDeviceOpts; @@ -56,6 +57,8 @@ use crate::utils::sigpolicy_from_opts; const BOOT: &str = "boot"; /// Directory for transient runtime state const RUN_BOOTC: &str = "/run/bootc"; +/// The default path for the host rootfs +const ALONGSIDE_ROOT_MOUNT: &str = "/target"; /// This is an ext4 special directory we need to ignore. const LOST_AND_FOUND: &str = "lost+found"; /// The filename of the composefs EROFS superblock; TODO move this into ostree @@ -316,9 +319,10 @@ pub(crate) struct InstallToExistingRootOpts { #[clap(long)] pub(crate) acknowledge_destructive: bool, - /// Path to the mounted root; it's expected to invoke podman with - /// `-v /:/target`, then supplying this argument is unnecessary. - #[clap(default_value = "/target")] + /// Path to the mounted root; this is now not necessary to provide. + /// Historically it was necessary to ensure the host rootfs was mounted at here + /// via e.g. `-v /:/target`. + #[clap(default_value = ALONGSIDE_ROOT_MOUNT)] pub(crate) root_path: Utf8PathBuf, } @@ -1631,6 +1635,22 @@ pub(crate) async fn install_to_filesystem( // And the last bit of state here is the fsopts, which we also destructure now. let mut fsopts = opts.filesystem_opts; + // If we're doing an alongside install, automatically set up the host rootfs + // mount if it wasn't done already. + if targeting_host_root + && fsopts.root_path.as_str() == ALONGSIDE_ROOT_MOUNT + && !fsopts.root_path.try_exists()? + { + std::fs::create_dir(ALONGSIDE_ROOT_MOUNT)?; + crate::mount::bind_mount_from_pidns( + Pid::from_raw(1).unwrap(), + "/".into(), + ALONGSIDE_ROOT_MOUNT.into(), + true, + ) + .context("Mounting host / to {ALONGSIDE_ROOT_MOUNT}")?; + } + // Check that the target is a directory { let root_path = &fsopts.root_path; diff --git a/ostree-ext/.github/workflows/bootc.yml b/ostree-ext/.github/workflows/bootc.yml index bba4cfe7..8e8dbe78 100644 --- a/ostree-ext/.github/workflows/bootc.yml +++ b/ostree-ext/.github/workflows/bootc.yml @@ -59,7 +59,7 @@ jobs: - name: Integration tests run: | set -xeuo pipefail - sudo podman run --rm -ti --privileged -v /:/target -v ./usr/bin/bootc:/usr/bin/bootc --pid=host --security-opt label=disable \ + sudo podman run --rm -ti --privileged -v ./usr/bin/bootc:/usr/bin/bootc --pid=host --security-opt label=disable \ quay.io/centos-bootc/centos-bootc-dev:stream9 bootc install to-filesystem \ --karg=foo=bar --disable-selinux --replace=alongside /target diff --git a/tests-integration/src/install.rs b/tests-integration/src/install.rs index 41866bca..ab175e09 100644 --- a/tests-integration/src/install.rs +++ b/tests-integration/src/install.rs @@ -85,7 +85,6 @@ pub(crate) fn run_alongside(image: &str, mut testargs: libtest_mimic::Arguments) let image: &'static str = String::from(image).leak(); // Handy defaults - let target_args = &["-v", "/:/target"]; // We always need this as we assume we're operating on a local image let generic_inst_args = ["--skip-fetch-check"]; @@ -110,7 +109,7 @@ pub(crate) fn run_alongside(image: &str, mut testargs: libtest_mimic::Arguments) let tmp_keys = tmpd.path().join("test_authorized_keys"); let tmp_keys = tmp_keys.to_str().unwrap(); std::fs::write(&tmp_keys, b"ssh-ed25519 ABC0123 testcase@example.com")?; - cmd!(sh, "sudo {BASE_ARGS...} {target_args...} -v {tmp_keys}:/test_authorized_keys {image} bootc install to-filesystem {generic_inst_args...} --acknowledge-destructive --karg=foo=bar --replace=alongside --root-ssh-authorized-keys=/test_authorized_keys /target").run()?; + cmd!(sh, "sudo {BASE_ARGS...} -v {tmp_keys}:/test_authorized_keys {image} bootc install to-filesystem {generic_inst_args...} --acknowledge-destructive --karg=foo=bar --replace=alongside --root-ssh-authorized-keys=/test_authorized_keys /target").run()?; generic_post_install_verification()?; @@ -145,7 +144,7 @@ pub(crate) fn run_alongside(image: &str, mut testargs: libtest_mimic::Arguments) Trial::test("Install and verify selinux state", move || { let sh = &xshell::Shell::new()?; reset_root(sh, image)?; - cmd!(sh, "sudo {BASE_ARGS...} {target_args...} {image} bootc install to-existing-root --acknowledge-destructive {generic_inst_args...}").run()?; + cmd!(sh, "sudo {BASE_ARGS...} {image} bootc install to-existing-root --acknowledge-destructive {generic_inst_args...}").run()?; generic_post_install_verification()?; let root = &Dir::open_ambient_dir("/ostree", cap_std::ambient_authority()).unwrap(); let mut path = PathBuf::from("."); @@ -155,7 +154,7 @@ pub(crate) fn run_alongside(image: &str, mut testargs: libtest_mimic::Arguments) Trial::test("Install to non-default stateroot", move || { let sh = &xshell::Shell::new()?; reset_root(sh, image)?; - cmd!(sh, "sudo {BASE_ARGS...} {target_args...} {image} bootc install to-existing-root --stateroot {NON_DEFAULT_STATEROOT} --acknowledge-destructive {generic_inst_args...}").run()?; + cmd!(sh, "sudo {BASE_ARGS...} {image} bootc install to-existing-root --stateroot {NON_DEFAULT_STATEROOT} --acknowledge-destructive {generic_inst_args...}").run()?; generic_post_install_verification()?; assert!( Utf8Path::new(&format!("/ostree/deploy/{NON_DEFAULT_STATEROOT}")).try_exists()? @@ -167,7 +166,7 @@ pub(crate) fn run_alongside(image: &str, mut testargs: libtest_mimic::Arguments) reset_root(sh, image)?; let empty = sh.create_temp_dir()?; let empty = empty.path().to_str().unwrap(); - cmd!(sh, "sudo {BASE_ARGS...} {target_args...} -v {empty}:/usr/lib/bootc/install {image} bootc install to-existing-root {generic_inst_args...}").run()?; + cmd!(sh, "sudo {BASE_ARGS...} -v {empty}:/usr/lib/bootc/install {image} bootc install to-existing-root {generic_inst_args...}").run()?; generic_post_install_verification()?; Ok(()) }),