Skip to content

Commit 1417126

Browse files
committed
install: Add cleanup option to install to-existing-root
When set, the bootc-destructive-cleanup flag is added to /sysroot/etc which enables the bootc-destructive-cleanup systemd service to remove the previous installation's rpm packages and podman containers/images. The service is only installed on fedora based systems. Signed-off-by: ckyrouac <[email protected]>
1 parent b1fb35c commit 1417126

File tree

5 files changed

+67
-7
lines changed

5 files changed

+67
-7
lines changed

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ install:
3737
# Copy dracut and systemd config files
3838
cp -Prf baseimage/dracut $(DESTDIR)$(prefix)/share/doc/bootc/baseimage/dracut
3939
cp -Prf baseimage/systemd $(DESTDIR)$(prefix)/share/doc/bootc/baseimage/systemd
40+
# Install fedora-bootc-destructive-cleanup in fedora derivatives
41+
ID=$$(. /usr/lib/os-release && echo $$ID); \
42+
ID_LIKE=$$(. /usr/lib/os-release && echo $$ID_LIKE); \
43+
if [ "$$ID" = "fedora" ] || [[ "$$ID_LIKE" == *"fedora"* ]]; then \
44+
ln -s ../bootc-destructive-cleanup.service $(DESTDIR)/$(prefix)/lib/systemd/system/multi-user.target.wants/bootc-destructive-cleanup.service; \
45+
install -D -m 0755 -t $(DESTDIR)/$(prefix)/lib/bootc contrib/scripts/fedora-bootc-destructive-cleanup; \
46+
fi
4047

4148
# Run this to also take over the functionality of `ostree container` for example.
4249
# Only needed for OS/distros that have callers invoking `ostree container` and not bootc.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
# An implementation of --cleanup for bootc installs on Fedora derivatives
3+
4+
set -xeuo pipefail
5+
6+
# Remove all RPMs installed in the physical root (i.e. the previous OS)
7+
mount -o remount,rw /sysroot
8+
rpm -qa --root=/sysroot --dbpath=/usr/lib/sysimage/rpm | xargs rpm -e --root=/sysroot --dbpath=/usr/lib/sysimage/rpm
9+
10+
# Remove all container images (including the one that was used to install)
11+
# Note that this does not remove stopped containers, and so some storage
12+
# may leak. This may change in the future.
13+
mount --bind -o rw /sysroot/var/lib/containers /var/lib/containers
14+
podman system prune --all -f

lib/src/cli.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
11541154
#[cfg(feature = "install-to-disk")]
11551155
InstallOpts::ToDisk(opts) => crate::install::install_to_disk(opts).await,
11561156
InstallOpts::ToFilesystem(opts) => {
1157-
crate::install::install_to_filesystem(opts, false).await
1157+
crate::install::install_to_filesystem(opts, false, crate::install::Cleanup::Skip).await
11581158
}
11591159
InstallOpts::ToExistingRoot(opts) => {
11601160
crate::install::install_to_existing_root(opts).await

lib/src/install.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ const BOOT: &str = "boot";
6969
const RUN_BOOTC: &str = "/run/bootc";
7070
/// The default path for the host rootfs
7171
const ALONGSIDE_ROOT_MOUNT: &str = "/target";
72+
/// Global flag to signal the booted system was provisioned via an alongside bootc install
73+
const DESTRUCTIVE_CLEANUP: &str = "bootc-destructive-cleanup";
7274
/// This is an ext4 special directory we need to ignore.
7375
const LOST_AND_FOUND: &str = "lost+found";
7476
/// The filename of the composefs EROFS superblock; TODO move this into ostree
@@ -335,6 +337,11 @@ pub(crate) struct InstallToExistingRootOpts {
335337
#[clap(long)]
336338
pub(crate) acknowledge_destructive: bool,
337339

340+
/// Add the bootc-destructive-cleanup systemd service to delete files from
341+
/// the previous install on first boot
342+
#[clap(long)]
343+
pub(crate) cleanup: bool,
344+
338345
/// Path to the mounted root; this is now not necessary to provide.
339346
/// Historically it was necessary to ensure the host rootfs was mounted at here
340347
/// via e.g. `-v /:/target`.
@@ -1460,7 +1467,7 @@ impl BoundImages {
14601467
}
14611468
}
14621469

1463-
async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Result<()> {
1470+
async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Result<Storage> {
14641471
if matches!(state.selinux_state, SELinuxFinalState::ForceTargetDisabled) {
14651472
rootfs.kargs.push("selinux=0".to_string());
14661473
}
@@ -1489,7 +1496,8 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
14891496
let bound_images = BoundImages::from_state(state).await?;
14901497

14911498
// Initialize the ostree sysroot (repo, stateroot, etc.)
1492-
{
1499+
1500+
let sysroot = {
14931501
let (sysroot, has_ostree, imgstore) = initialize_ostree_root(state, rootfs).await?;
14941502

14951503
install_with_sysroot(
@@ -1504,7 +1512,9 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
15041512
.await?;
15051513
// We must drop the sysroot here in order to close any open file
15061514
// descriptors.
1507-
}
1515+
//
1516+
sysroot
1517+
};
15081518

15091519
// Run this on every install as the penultimate step
15101520
install_finalize(&rootfs.physical_root_path).await?;
@@ -1517,7 +1527,7 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
15171527
}
15181528
}
15191529

1520-
Ok(())
1530+
Ok(sysroot)
15211531
}
15221532

15231533
fn installation_complete() {
@@ -1740,11 +1750,17 @@ fn warn_on_host_root(rootfs_fd: &Dir) -> Result<()> {
17401750
Ok(())
17411751
}
17421752

1753+
pub enum Cleanup {
1754+
Skip,
1755+
TriggerOnNextBoot,
1756+
}
1757+
17431758
/// Implementation of the `bootc install to-filsystem` CLI command.
17441759
#[context("Installing to filesystem")]
17451760
pub(crate) async fn install_to_filesystem(
17461761
opts: InstallToFilesystemOpts,
17471762
targeting_host_root: bool,
1763+
cleanup: Cleanup,
17481764
) -> Result<()> {
17491765
// Gather global state, destructuring the provided options.
17501766
// IMPORTANT: We might re-execute the current process in this function (for SELinux among other things)
@@ -1950,7 +1966,13 @@ pub(crate) async fn install_to_filesystem(
19501966
skip_finalize,
19511967
};
19521968

1953-
install_to_filesystem_impl(&state, &mut rootfs).await?;
1969+
let sysroot = install_to_filesystem_impl(&state, &mut rootfs).await?;
1970+
1971+
if matches!(cleanup, Cleanup::TriggerOnNextBoot) {
1972+
let sysroot_dir = crate::utils::sysroot_dir(&sysroot.sysroot)?;
1973+
tracing::debug!("Writing {DESTRUCTIVE_CLEANUP}");
1974+
sysroot_dir.atomic_write(format!("etc/{}", DESTRUCTIVE_CLEANUP), b"")?;
1975+
}
19541976

19551977
// Drop all data about the root except the path to ensure any file descriptors etc. are closed.
19561978
drop(rootfs);
@@ -1961,6 +1983,11 @@ pub(crate) async fn install_to_filesystem(
19611983
}
19621984

19631985
pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) -> Result<()> {
1986+
let cleanup = match opts.cleanup {
1987+
true => Cleanup::TriggerOnNextBoot,
1988+
false => Cleanup::Skip,
1989+
};
1990+
19641991
let opts = InstallToFilesystemOpts {
19651992
filesystem_opts: InstallTargetFilesystemOpts {
19661993
root_path: opts.root_path,
@@ -1975,7 +2002,7 @@ pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) ->
19752002
config_opts: opts.config_opts,
19762003
};
19772004

1978-
install_to_filesystem(opts, true).await
2005+
install_to_filesystem(opts, true, cleanup).await
19792006
}
19802007

19812008
/// Implementation of `bootc install finalize`.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[Unit]
2+
Description=Cleanup previous the installation after an alongside installation
3+
Documentation=man:bootc(8)
4+
ConditionPathExists=/sysroot/etc/bootc-destructive-cleanup
5+
6+
[Service]
7+
Type=oneshot
8+
ExecStart=/usr/lib/bootc/fedora-bootc-destructive-cleanup
9+
PrivateMounts=true
10+
11+
[Install]
12+
WantedBy=multi-user.target

0 commit comments

Comments
 (0)