From b4a0145e33b94f5fee9093b15f184c3dbcff90b3 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 2 Jan 2024 18:07:06 -0500 Subject: [PATCH] spec: Add cached update metadata In https://github.com/ostreedev/ostree-rs-ext/pull/537 we added a facility to cache metadata (manifest+config) for container image updates with an eye for use in rpm-ostree. The `bootc update --check` code here in bootc was also updated to use it; but we didn't really expose it back in the status. This closes the gap, just bridging the cached update metadata into status. We want to do this as opposed to having `bootc update --check` grow its own API for example. Closes: https://github.com/containers/bootc/issues/247 Signed-off-by: Colin Walters --- lib/src/spec.rs | 2 ++ lib/src/status.rs | 68 +++++++++++++++++++++++++++++++---------------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/lib/src/spec.rs b/lib/src/spec.rs index 78e73c2a..b018671f 100644 --- a/lib/src/spec.rs +++ b/lib/src/spec.rs @@ -88,6 +88,8 @@ pub struct BootEntryOstree { pub struct BootEntry { /// The image reference pub image: Option, + /// The last fetched cached update metadata + pub cached_update: Option, /// Whether this boot entry is not compatible (has origin changes bootc does not understand) pub incompatible: bool, /// Whether this entry will be subject to garbage collection diff --git a/lib/src/status.rs b/lib/src/status.rs index f882c491..1b113e3f 100644 --- a/lib/src/status.rs +++ b/lib/src/status.rs @@ -10,6 +10,7 @@ use ostree_container::OstreeImageReference; use ostree_ext::container as ostree_container; use ostree_ext::keyfileext::KeyFileExt; use ostree_ext::oci_spec; +use ostree_ext::oci_spec::image::ImageConfiguration; use ostree_ext::ostree; use ostree_ext::sysroot::SysrootLock; @@ -112,48 +113,69 @@ pub(crate) fn labels_of_config( config.config().as_ref().and_then(|c| c.labels().as_ref()) } +/// Convert between a subset of ostree-ext metadata and the exposed spec API. +pub(crate) fn create_imagestatus( + image: ImageReference, + manifest_digest: &str, + config: Option<&ImageConfiguration>, +) -> ImageStatus { + let labels = config.and_then(labels_of_config); + let timestamp = labels + .and_then(|l| { + l.get(oci_spec::image::ANNOTATION_CREATED) + .map(|s| s.as_str()) + }) + .and_then(try_deserialize_timestamp); + + let version = config + .and_then(ostree_container::version_for_config) + .map(ToOwned::to_owned); + ImageStatus { + image, + version, + timestamp, + image_digest: manifest_digest.to_owned(), + } +} + +/// Given an OSTree deployment, parse out metadata into our spec. #[context("Reading deployment metadata")] fn boot_entry_from_deployment( sysroot: &SysrootLock, deployment: &ostree::Deployment, ) -> Result { let repo = &sysroot.repo(); - let (image, incompatible) = if let Some(origin) = deployment.origin().as_ref() { + let (image, cached_update, incompatible) = if let Some(origin) = deployment.origin().as_ref() { let incompatible = crate::utils::origin_has_rpmostree_stuff(origin); - let image = if incompatible { + let (image, cached) = if incompatible { // If there are local changes, we can't represent it as a bootc compatible image. - None + (None, None) } else if let Some(image) = get_image_origin(origin)? { let image = ImageReference::from(image); let csum = deployment.csum(); let imgstate = ostree_container::store::query_image_commit(repo, &csum)?; - let config = imgstate.configuration.as_ref(); - let labels = config.and_then(labels_of_config); - let timestamp = labels - .and_then(|l| { - l.get(oci_spec::image::ANNOTATION_CREATED) - .map(|s| s.as_str()) - }) - .and_then(try_deserialize_timestamp); - - let version = config - .and_then(ostree_container::version_for_config) - .map(ToOwned::to_owned); - Some(ImageStatus { + let cached = imgstate.cached_update.map(|cached| { + create_imagestatus(image.clone(), &cached.manifest_digest, Some(&cached.config)) + }); + let imagestatus = create_imagestatus( image, - version, - timestamp, - image_digest: imgstate.manifest_digest, - }) + &imgstate.manifest_digest, + imgstate.configuration.as_ref(), + ); + // We found a container-image based deployment + (Some(imagestatus), cached) } else { - None + // The deployment isn't using a container image + (None, None) }; - (image, incompatible) + (image, cached, incompatible) } else { - (None, false) + // The deployment has no origin at all (this generally shouldn't happen) + (None, None, false) }; let r = BootEntry { image, + cached_update, incompatible, pinned: deployment.is_pinned(), ostree: Some(crate::spec::BootEntryOstree {