From 46285d2b9dee45a89679f25b7cf41e409307206f Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 8 Mar 2024 15:01:02 -0500 Subject: [PATCH] install: Add `to-existing-root` This is just mild sugar on top of the existing `to-filesystem` right now. But, I think we can do more with this later. Partially addresses https://github.com/containers/bootc/issues/380 Signed-off-by: Colin Walters --- .github/workflows/ci.yml | 3 +- docs/src/bootc-install.md | 20 +++++++++---- lib/src/cli.rs | 4 +++ lib/src/install.rs | 38 ++++++++++++++++++++++++ tests/integration/playbooks/install.yaml | 2 +- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e3c5ec92..933060c8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -143,6 +143,5 @@ jobs: sudo chattr -i /ostree/deploy/default/deploy/* sudo rm /ostree/deploy/default -rf sudo podman run --rm -ti --privileged --env BOOTC_SKIP_SELINUX_HOST_CHECK=1 --env RUST_LOG=debug -v /:/target -v /var/lib/containers:/var/lib/containers -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 \ - --replace=alongside /target + quay.io/centos-bootc/centos-bootc-dev:stream9 bootc install to-existing-root sudo ls -ldZ / /ostree/deploy/default/deploy/* /ostree/deploy/default/deploy/*/etc diff --git a/docs/src/bootc-install.md b/docs/src/bootc-install.md index 00af7a6b4..482a28d5e 100644 --- a/docs/src/bootc-install.md +++ b/docs/src/bootc-install.md @@ -225,7 +225,7 @@ podman run --rm --privileged --pid=host --security-opt label=type:unconfined_t Notice that we use `--generic-image` for this use case. -### Using `bootc install to-filesystem --replace=alongside` +### Using `bootc install to-existing-root` This is a variant of `install to-filesystem`, which maximizes convenience for using an existing Linux system, converting it into the target container image. Note that @@ -241,13 +241,23 @@ The core command should look like this (root/elevated permission required): podman run --rm --privileged -v /var/lib/containers:/var/lib/containers -v /:/target \ --pid=host --security-opt label=type:unconfined_t \ \ - bootc install to-filesystem --replace=alongside /target + bootc install to-existing-root ``` -At the current time, leftover data in `/` is **NOT** automatically cleaned up. This can +It is assumed in this command that the target rootfs is pased via `-v /:/target` at this time. + +As noted above, the data in `/boot` will be wiped, but everything else in the existing +operating `/` is **NOT** automatically cleaned up. This can be useful, because it allows the new image to automatically import data from the previous -host system! For example, things like SSH keys or container images can be copied -and then deleted from the original. +host system! For example, container images, database, user home directory data, config +files in `/etc` are all available after the subsequent reboot in `/sysroot` (which +is the "physical root"). + +A special case of this trick is using the `--root-ssh-authorized-keys` flag to inherit +root's SSH keys (which may have been injected from e.g. cloud instance userdata +via a tool like `cloud-init`). To do this, just add +`--root-ssh-authorized-keys /target/root/.ssh/authorized_keys` +to the above. ### Using `bootc install to-filesystem --source-imgref ` diff --git a/lib/src/cli.rs b/lib/src/cli.rs index 7d0fbea62..18e58cf2e 100644 --- a/lib/src/cli.rs +++ b/lib/src/cli.rs @@ -118,6 +118,7 @@ pub(crate) enum InstallOpts { ToDisk(crate::install::InstallToDiskOpts), /// Install to the target filesystem ToFilesystem(crate::install::InstallToFilesystemOpts), + ToExistingRoot(crate::install::InstallToExistingRootOpts), /// Output JSON to stdout that contains the merged installation configuration /// as it may be relevant to calling processes using `install to-filesystem` /// that want to honor e.g. `root-fs-type`. @@ -533,6 +534,9 @@ async fn run_from_opt(opt: Opt) -> Result<()> { Opt::Install(opts) => match opts { InstallOpts::ToDisk(opts) => crate::install::install_to_disk(opts).await, InstallOpts::ToFilesystem(opts) => crate::install::install_to_filesystem(opts).await, + InstallOpts::ToExistingRoot(opts) => { + crate::install::install_to_existing_root(opts).await + } InstallOpts::PrintConfiguration => crate::install::print_configuration(), }, #[cfg(feature = "install")] diff --git a/lib/src/install.rs b/lib/src/install.rs index c0178217e..e2287914c 100644 --- a/lib/src/install.rs +++ b/lib/src/install.rs @@ -249,6 +249,28 @@ pub(crate) struct InstallToFilesystemOpts { pub(crate) config_opts: InstallConfigOpts, } +/// Perform an installation to the host root filesystem. +#[derive(Debug, Clone, clap::Parser)] +pub(crate) struct InstallToExistingRootOpts { + /// Configure how existing data is treated. + #[clap(long, default_value = "alongside")] + pub(crate) replace: Option, + + #[clap(flatten)] + pub(crate) source_opts: InstallSourceOpts, + + #[clap(flatten)] + pub(crate) target_opts: InstallTargetOpts, + + #[clap(flatten)] + pub(crate) config_opts: InstallConfigOpts, + + /// Path to the mounted root; it's expected to invoke podman with + /// `-v /:/target`, then supplying this argument is unnecessary. + #[clap(default_value = "/target")] + pub(crate) root_path: Utf8PathBuf, +} + /// Global state captured from the container. #[derive(Debug, Clone)] pub(crate) struct SourceInfo { @@ -1355,6 +1377,22 @@ pub(crate) async fn install_to_filesystem(opts: InstallToFilesystemOpts) -> Resu Ok(()) } +pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) -> Result<()> { + let opts = InstallToFilesystemOpts { + filesystem_opts: InstallTargetFilesystemOpts { + root_path: opts.root_path, + root_mount_spec: None, + boot_mount_spec: None, + replace: opts.replace, + }, + source_opts: opts.source_opts, + target_opts: opts.target_opts, + config_opts: opts.config_opts, + }; + + install_to_filesystem(opts).await +} + #[test] fn install_opts_serializable() { let c: InstallToDiskOpts = serde_json::from_value(serde_json::json!({ diff --git a/tests/integration/playbooks/install.yaml b/tests/integration/playbooks/install.yaml index cb213be4f..1e64714e9 100644 --- a/tests/integration/playbooks/install.yaml +++ b/tests/integration/playbooks/install.yaml @@ -72,7 +72,7 @@ -v /var/lib/containers:/var/lib/containers \ --security-opt label=type:unconfined_t \ {{ test_image_url }} \ - bootc install to-filesystem --replace=alongside /target" + bootc install to-existing-root" become: true - name: Reboot to deploy new system