diff --git a/ci/priv-integration.sh b/ci/priv-integration.sh index 6aa25d28..699d1b2f 100755 --- a/ci/priv-integration.sh +++ b/ci/priv-integration.sh @@ -58,6 +58,9 @@ for img in "${image}"; do test "$(($initial_refs - 1))" = "$pruned_refs" ostree admin --sysroot="${sysroot}" undeploy 0 # TODO: when we fold together ostree and ostree-ext, automatically prune layers + n_commits=$(find ${sysroot}/ostree/repo -name '*.commit') + test "${n_commits}" -gt 0 + # But right now this still doesn't prune *content* ostree-ext-cli container image prune-layers --repo="${sysroot}/ostree/repo" ostree --repo="${sysroot}/ostree/repo" refs > refs.txt if test "$(wc -l < refs.txt)" -ne 0; then @@ -65,6 +68,10 @@ for img in "${image}"; do cat refs.txt exit 1 fi + # And this one should GC the objects too + ostree-ext-cli container image prune-images --full --repo="${sysroot}/ostree/repo" > out.txt + n_commits=$(find ${sysroot}/ostree/repo -name '*.commit') + test "${n_commits}" -eq 0 done # Verify we have systemd journal messages diff --git a/lib/src/cli.rs b/lib/src/cli.rs index b031d0a7..0012195b 100644 --- a/lib/src/cli.rs +++ b/lib/src/cli.rs @@ -325,6 +325,10 @@ pub(crate) enum ContainerImageOpts { #[clap(long)] /// Also prune layers and_layers: bool, + + #[clap(long, conflicts_with = "and_layers")] + /// Also prune layers and OSTree objects + full: bool, }, /// Perform initial deployment for a container image @@ -1005,25 +1009,40 @@ async fn run_from_opt(opt: Opt) -> Result<()> { ContainerImageOpts::PruneImages { sysroot, and_layers, + full, } => { let sysroot = &ostree::Sysroot::new(Some(&gio::File::for_path(&sysroot))); sysroot.load(gio::Cancellable::NONE)?; let sysroot = &SysrootLock::new_from_sysroot(sysroot).await?; - let removed = crate::container::deploy::remove_undeployed_images(sysroot)?; - match removed.as_slice() { - [] => { - println!("No unreferenced images."); - return Ok(()); + if full { + let res = crate::container::deploy::prune(sysroot)?; + if res.is_empty() { + println!("No content was pruned."); + } else { + println!("Removed images: {}", res.n_images); + println!("Removed layers: {}", res.n_layers); + println!("Removed objects: {}", res.n_objects_pruned); + let objsize = glib::format_size(res.objsize); + println!("Freed: {objsize}"); } - o => { - for imgref in o { - println!("Removed: {imgref}"); + } else { + let removed = crate::container::deploy::remove_undeployed_images(sysroot)?; + match removed.as_slice() { + [] => { + println!("No unreferenced images."); + return Ok(()); + } + o => { + for imgref in o { + println!("Removed: {imgref}"); + } } } - } - if and_layers { - let nlayers = crate::container::store::gc_image_layers(&sysroot.repo())?; - println!("Removed layers: {nlayers}"); + if and_layers { + let nlayers = + crate::container::store::gc_image_layers(&sysroot.repo())?; + println!("Removed layers: {nlayers}"); + } } Ok(()) } diff --git a/lib/src/container/deploy.rs b/lib/src/container/deploy.rs index fcd750ae..52b247de 100644 --- a/lib/src/container/deploy.rs +++ b/lib/src/container/deploy.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; -use super::store::LayeredImageState; +use super::store::{gc_image_layers, LayeredImageState}; use super::{ImageReference, OstreeImageReference}; use crate::container::store::PrepareResult; use crate::keyfileext::KeyFileExt; @@ -174,3 +174,47 @@ pub fn remove_undeployed_images(sysroot: &SysrootLock) -> Result bool { + self.n_images == 0 && self.n_layers == 0 && self.n_objects_pruned == 0 + } +} + +/// This combines the functionality of [`remove_undeployed_images()`] with [`super::store::gc_image_layers()`]. +pub fn prune(sysroot: &SysrootLock) -> Result { + let repo = &sysroot.repo(); + // Prune container images which are not deployed. + // SAFETY: There should never be more than u32 images + let n_images = remove_undeployed_images(sysroot)?.len().try_into().unwrap(); + // Prune unreferenced layer branches. + let n_layers = gc_image_layers(repo)?; + // Prune the objects in the repo; the above just removed refs (branches). + let (_, n_objects_pruned, objsize) = repo.prune( + ostree::RepoPruneFlags::REFS_ONLY, + 0, + ostree::gio::Cancellable::NONE, + )?; + // SAFETY: The number of pruned objects should never be negative + let n_objects_pruned = u32::try_from(n_objects_pruned).unwrap(); + Ok(Pruned { + n_images, + n_layers, + n_objects_pruned, + objsize, + }) +}