Skip to content

Commit

Permalink
deploy: Add an API to prune undeployed images
Browse files Browse the repository at this point in the history
This is part of fixing coreos/rpm-ostree#4391
but is also in the general theme of making things less "stateful".

A huge huge mess today is `rpm-ostree rebase` and `bootc switch`
both have `--retain` options which keep the previous image.

But really what we want is to use the deployments as source-of-truth;
that way if e.g. an admin pins a deployment, it automatically pins
the image too.

And this will help strongly align with the bootc direction in
reconciling to desired state.
  • Loading branch information
cgwalters committed Jul 6, 2023
1 parent 060a3a4 commit 979b93a
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 6 deletions.
7 changes: 6 additions & 1 deletion ci/priv-integration.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ fi
if test '!' -d "${sysroot}/ostree/deploy/${stateroot}"; then
ostree admin os-init "${stateroot}" --sysroot "${sysroot}"
fi
# Should be no images pruned
ostree-ext-cli container image prune-images --sysroot "${sysroot}"
# Test the syntax which uses full imgrefs.
ostree-ext-cli container image deploy --sysroot "${sysroot}" \
--stateroot "${stateroot}" --imgref "${imgref}"
Expand All @@ -34,8 +36,11 @@ ostree admin --sysroot="${sysroot}" undeploy 0
ostree-ext-cli container image deploy --transport registry --sysroot "${sysroot}" \
--stateroot "${stateroot}" --image "${image}" --no-signature-verification
ostree admin --sysroot="${sysroot}" status
ostree-ext-cli container image remove --repo "${sysroot}/ostree/repo" registry:"${image}"
ostree admin --sysroot="${sysroot}" undeploy 0
# Now we should prune it
ostree-ext-cli container image prune-images --sysroot "${sysroot}"
ostree-ext-cli container image list --repo "${sysroot}/ostree/repo" > out.txt
test $(stat -c '%s' out.txt) = 0

for img in "${image}"; do
ostree-ext-cli container image deploy --sysroot "${sysroot}" \
Expand Down
37 changes: 37 additions & 0 deletions lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::commit::container_commit;
use crate::container::store::{ImportProgress, LayerProgress, PreparedImport};
use crate::container::{self as ostree_container};
use crate::container::{Config, ImageReference, OstreeImageReference};
use crate::sysroot::SysrootLock;
use ostree_container::store::{ImageImporter, PrepareResult};

/// Parse an [`OstreeImageReference`] from a CLI arguemnt.
Expand Down Expand Up @@ -273,6 +274,17 @@ pub(crate) enum ContainerImageOpts {
repo: Utf8PathBuf,
},

/// Garbage collect unreferenced image layer references.
PruneImages {
/// Path to the system root
#[clap(long)]
sysroot: Utf8PathBuf,

#[clap(long)]
/// Also prune layers
and_layers: bool,
},

/// Perform initial deployment for a container image
Deploy {
/// Path to the system root
Expand Down Expand Up @@ -825,6 +837,31 @@ where
println!("Removed layers: {nlayers}");
Ok(())
}
ContainerImageOpts::PruneImages {
sysroot,
and_layers,
} => {
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(());
}
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}");
}
Ok(())
}
ContainerImageOpts::Copy {
src_repo,
dest_repo,
Expand Down
52 changes: 51 additions & 1 deletion lib/src/container/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
//! Perform initial setup for a container image based system root

use std::collections::HashSet;

use super::store::LayeredImageState;
use super::OstreeImageReference;
use super::{ImageReference, OstreeImageReference};
use crate::container::store::PrepareResult;
use crate::keyfileext::KeyFileExt;
use crate::sysroot::SysrootLock;
use anyhow::Result;
use fn_error_context::context;
use ostree::glib;
Expand Down Expand Up @@ -112,3 +116,49 @@ pub async fn deploy(

Ok(state)
}

/// Query the container image reference for a deployment
fn deployment_origin_container(
deploy: &ostree::Deployment,
) -> Result<Option<OstreeImageReference>> {
let origin = deploy
.origin()
.map(|o| o.optional_string("origin", ORIGIN_CONTAINER))
.transpose()?
.flatten();
let r = origin
.map(|v| OstreeImageReference::try_from(v.as_str()))
.transpose()?;
Ok(r)
}

/// Remove all container images which are not the target of a deployment.
/// This acts equivalently to [`super::store::remove_images()`] - the underlying layers
/// are not pruned.
///
/// The set of removed images is returned.
pub fn remove_undeployed_images(sysroot: &SysrootLock) -> Result<Vec<ImageReference>> {
let repo = &sysroot.repo();
let deployment_origins: Result<HashSet<_>> = sysroot
.deployments()
.into_iter()
.filter_map(|deploy| {
deployment_origin_container(&deploy)
.map(|v| v.map(|v| v.imgref))
.transpose()
})
.collect();
let deployment_origins = deployment_origins?;
// TODO add an API that returns ImageReference instead
let all_images = super::store::list_images(&sysroot.repo())?
.into_iter()
.filter_map(|img| ImageReference::try_from(img.as_str()).ok());
let mut removed = Vec::new();
for image in all_images {
if !deployment_origins.contains(&image) {
super::store::remove_image(repo, &image)?;
removed.push(image);
}
}
Ok(removed)
}
8 changes: 4 additions & 4 deletions lib/src/container/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub(crate) const COMPONENT_SEPARATOR: char = ',';
type Result<T> = anyhow::Result<T>;

/// A backend/transport for OCI/Docker images.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq)]
pub enum Transport {
/// A remote Docker/OCI registry (`registry:` or `docker://`)
Registry,
Expand All @@ -63,7 +63,7 @@ pub enum Transport {
/// Combination of a remote image reference and transport.
///
/// For example,
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct ImageReference {
/// The storage and transport for the image
pub transport: Transport,
Expand All @@ -72,7 +72,7 @@ pub struct ImageReference {
}

/// Policy for signature verification.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SignatureSource {
/// Fetches will use the named ostree remote for signature verification of the ostree commit.
OstreeRemote(String),
Expand All @@ -87,7 +87,7 @@ pub const LABEL_VERSION: &str = "version";

/// Combination of a signature verification mechanism, and a standard container image reference.
///
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct OstreeImageReference {
/// The signature verification mechanism.
pub sigverify: SignatureSource,
Expand Down

0 comments on commit 979b93a

Please sign in to comment.