From e4468f73d4b5a90aaa79238714fd7a597c3f45bd Mon Sep 17 00:00:00 2001 From: John Eckersberg Date: Mon, 16 Sep 2024 16:47:18 -0400 Subject: [PATCH] Port to ocidir 0.3 Update containers-image-proxy to 0.7.0 Signed-off-by: John Eckersberg --- lib/Cargo.toml | 4 +-- lib/src/chunking.rs | 13 ++++--- lib/src/cli.rs | 13 ++++--- lib/src/container/encapsulate.rs | 9 +++-- lib/src/container/mod.rs | 10 +++--- lib/src/container/skopeo.rs | 6 ++-- lib/src/container/store.rs | 16 ++++----- lib/src/container/unencapsulate.rs | 25 +++++++------ lib/src/container/update_detachedmeta.rs | 28 +++++++++------ lib/src/fixture.rs | 3 +- lib/src/integrationtest.rs | 18 +++++++--- lib/tests/it/main.rs | 46 ++++++++++++++---------- 12 files changed, 117 insertions(+), 74 deletions(-) diff --git a/lib/Cargo.toml b/lib/Cargo.toml index d66eda06..4614a6e7 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -13,7 +13,7 @@ rust-version = "1.74.0" # Note that we re-export the oci-spec types # that are exported by this crate, so when bumping # semver here you must also bump our semver. -containers-image-proxy = "0.6.0" +containers-image-proxy = "0.7.0" # We re-export this library too. ostree = { features = ["v2022_6"], version = "0.19.0" } @@ -36,7 +36,7 @@ once_cell = "1.9" libc = "0.2.92" libsystemd = "0.7.0" openssl = "0.10.33" -ocidir = "0.2.0" +ocidir = "0.3.0" pin-project = "1.0" regex = "1.5.4" rustix = { version = "0.38", features = ["fs", "process"] } diff --git a/lib/src/chunking.rs b/lib/src/chunking.rs index 251ef98b..da11a09e 100644 --- a/lib/src/chunking.rs +++ b/lib/src/chunking.rs @@ -785,7 +785,12 @@ fn basic_packing<'a>( mod test { use super::*; + use oci_spec::image as oci_image; + use std::str::FromStr; + const FCOS_CONTENTMETA: &[u8] = include_bytes!("fixtures/fedora-coreos-contentmeta.json.gz"); + const SHA256_EXAMPLE: &str = + "sha256:0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff"; #[test] fn test_packing_basics() -> Result<()> { @@ -838,8 +843,8 @@ mod test { let config = oci_spec::image::DescriptorBuilder::default() .media_type(oci_spec::image::MediaType::ImageConfig) - .size(7023) - .digest("sha256:imageconfig") + .size(7023_u64) + .digest(oci_image::Digest::from_str(SHA256_EXAMPLE).unwrap()) .build() .expect("build config descriptor"); @@ -850,8 +855,8 @@ mod test { let sep = COMPONENT_SEPARATOR.encode_utf8(&mut buf); oci_spec::image::DescriptorBuilder::default() .media_type(oci_spec::image::MediaType::ImageLayerGzip) - .size(100) - .digest(format!("sha256:{}", l.len())) + .size(100_u64) + .digest(oci_image::Digest::from_str(SHA256_EXAMPLE).unwrap()) .annotations(HashMap::from([( CONTENT_ANNOTATION.to_string(), l.join(sep), diff --git a/lib/src/cli.rs b/lib/src/cli.rs index 1605c56a..f5e9e078 100644 --- a/lib/src/cli.rs +++ b/lib/src/cli.rs @@ -594,9 +594,14 @@ pub fn layer_progress_format(p: &ImportProgress) -> String { ImportProgress::DerivedLayerCompleted(v) => (false, "layer", v), }; // podman outputs 12 characters of digest, let's add 7 for `sha256:`. - let short_digest = layer.digest().chars().take(12 + 7).collect::(); + let short_digest = layer + .digest() + .digest() + .chars() + .take(12 + 7) + .collect::(); if starting { - let size = glib::format_size(layer.size() as u64); + let size = glib::format_size(layer.size()); format!("Fetching {s} {short_digest} ({size})") } else { format!("Fetched {s} {short_digest}") @@ -934,13 +939,13 @@ async fn container_history(repo: &ostree::Repo, imgref: &ImageReference) -> Resu let mut remaining = width; - let digest = layer.digest().as_str(); + let digest = layer.digest().digest(); // Verify it's OK to slice, this should all be ASCII assert!(digest.is_ascii()); let digest_max = columns[0].1; let digest = &digest[0..digest_max as usize]; print_column(digest, digest_max, &mut remaining); - let size = glib::format_size(layer.size() as u64); + let size = glib::format_size(layer.size()); print_column(size.as_str(), columns[1].1, &mut remaining); print_column(created_by, columns[2].1, &mut remaining); println!(); diff --git a/lib/src/container/encapsulate.rs b/lib/src/container/encapsulate.rs index e7964fcf..d411f5e1 100644 --- a/lib/src/container/encapsulate.rs +++ b/lib/src/container/encapsulate.rs @@ -151,7 +151,10 @@ pub(crate) fn export_chunked( // This label (mentioned above) points to the last layer that is part of // the ostree commit. - labels.insert(DIFFID_LABEL.into(), format!("sha256:{}", last_digest)); + labels.insert( + DIFFID_LABEL.into(), + format!("sha256:{}", last_digest.digest()), + ); Ok(()) } @@ -300,7 +303,7 @@ async fn build_impl( config: &Config, opts: Option>, dest: &ImageReference, -) -> Result { +) -> Result { let mut opts = opts.unwrap_or_default(); if dest.transport == Transport::ContainerStorage { opts.skip_compression = true; @@ -407,7 +410,7 @@ pub async fn encapsulate>( config: &Config, opts: Option>, dest: &ImageReference, -) -> Result { +) -> Result { build_impl(repo, ostree_ref.as_ref(), config, opts, dest).await } diff --git a/lib/src/container/mod.rs b/lib/src/container/mod.rs index 3c7f2929..c1d31ee2 100644 --- a/lib/src/container/mod.rs +++ b/lib/src/container/mod.rs @@ -341,12 +341,12 @@ impl<'a> ManifestDiff<'a> { let src_layers = src .layers() .iter() - .map(|l| (l.digest(), l)) + .map(|l| (l.digest().digest(), l)) .collect::>(); let dest_layers = dest .layers() .iter() - .map(|l| (l.digest(), l)) + .map(|l| (l.digest().digest(), l)) .collect::>(); let mut removed = Vec::new(); let mut added = Vec::new(); @@ -355,16 +355,16 @@ impl<'a> ManifestDiff<'a> { removed.push(descriptor); } } - removed.sort_by(|a, b| a.digest().cmp(b.digest())); + removed.sort_by(|a, b| a.digest().digest().cmp(b.digest().digest())); for (blobid, &descriptor) in dest_layers.iter() { if !src_layers.contains_key(blobid) { added.push(descriptor); } } - added.sort_by(|a, b| a.digest().cmp(b.digest())); + added.sort_by(|a, b| a.digest().digest().cmp(b.digest().digest())); fn layersum<'a, I: Iterator>(layers: I) -> u64 { - layers.map(|layer| layer.size() as u64).sum() + layers.map(|layer| layer.size()).sum() } let total = dest_layers.len() as u64; let total_size = layersum(dest.layers().iter()); diff --git a/lib/src/container/skopeo.rs b/lib/src/container/skopeo.rs index 20537689..7ac46250 100644 --- a/lib/src/container/skopeo.rs +++ b/lib/src/container/skopeo.rs @@ -3,12 +3,14 @@ use super::ImageReference; use anyhow::{Context, Result}; use cap_std_ext::cmdext::CapStdExtCommandExt; +use containers_image_proxy::oci_spec::image as oci_image; use fn_error_context::context; use io_lifetimes::OwnedFd; use serde::Deserialize; use std::io::Read; use std::path::Path; use std::process::Stdio; +use std::str::FromStr; use tokio::process::Command; // See `man containers-policy.json` and @@ -68,7 +70,7 @@ pub(crate) async fn copy( authfile: Option<&Path>, add_fd: Option<(std::sync::Arc, i32)>, progress: bool, -) -> Result { +) -> Result { let digestfile = tempfile::NamedTempFile::new()?; let mut cmd = new_cmd(); cmd.arg("copy"); @@ -96,7 +98,7 @@ pub(crate) async fn copy( let mut digestfile = digestfile.into_file(); let mut r = String::new(); digestfile.read_to_string(&mut r)?; - Ok(r.trim().to_string()) + Ok(oci_image::Digest::from_str(r.trim())?) } #[cfg(test)] diff --git a/lib/src/container/store.rs b/lib/src/container/store.rs index 017b046d..2828b361 100644 --- a/lib/src/container/store.rs +++ b/lib/src/container/store.rs @@ -68,7 +68,7 @@ fn ref_for_blob_digest(d: &str) -> Result { /// Convert e.g. sha256:12345... into `/ostree/container/blob/sha256_2B12345...`. fn ref_for_layer(l: &oci_image::Descriptor) -> Result { - ref_for_blob_digest(l.digest().as_str()) + ref_for_blob_digest(l.digest().digest()) } /// Convert e.g. sha256:12345... into `/ostree/container/blob/sha256_2B12345...`. @@ -202,12 +202,12 @@ pub struct ManifestLayerState { impl ManifestLayerState { /// The cryptographic checksum. pub fn digest(&self) -> &str { - self.layer.digest().as_str() + self.layer.digest().digest() } /// The (possibly compressed) size. pub fn size(&self) -> u64 { - self.layer.size() as u64 + self.layer.size() } } @@ -638,7 +638,7 @@ impl ImageImporter { } let (manifest_digest, manifest) = self.proxy.fetch_manifest(&self.proxy_img).await?; - let new_imageid = manifest.config().digest().as_str(); + let new_imageid = manifest.config().digest().digest(); // Query for previous stored state @@ -649,7 +649,7 @@ impl ImageImporter { return Ok(PrepareResult::AlreadyPresent(previous_state)); } // Failing that, if they have the same imageID, we're also done. - let previous_imageid = previous_state.manifest.config().digest().as_str(); + let previous_imageid = previous_state.manifest.config().digest().digest(); if previous_imageid == new_imageid { return Ok(PrepareResult::AlreadyPresent(previous_state)); } @@ -1355,7 +1355,7 @@ pub(crate) fn export_to_oci( let new_config = dest_oci.write_config(new_config)?; new_manifest.set_config(new_config); - dest_oci.insert_manifest(new_manifest, tag, oci_image::Platform::default()) + Ok(dest_oci.insert_manifest(new_manifest, tag, oci_image::Platform::default())?) } /// Given a container image reference which is stored in `repo`, export it to the @@ -1366,7 +1366,7 @@ pub async fn export( src_imgref: &ImageReference, dest_imgref: &ImageReference, opts: Option, -) -> Result { +) -> Result { let opts = opts.unwrap_or_default(); let target_oci = dest_imgref.transport == Transport::OciDir; let tempdir = if !target_oci { @@ -1476,7 +1476,7 @@ fn gc_image_layers_impl( let mut referenced_layers = BTreeSet::new(); for m in all_manifests.iter() { for layer in m.layers() { - referenced_layers.insert(layer.digest().as_str()); + referenced_layers.insert(layer.digest().digest()); } } tracing::debug!("Referenced layers: {}", referenced_layers.len()); diff --git a/lib/src/container/unencapsulate.rs b/lib/src/container/unencapsulate.rs index 2f90e159..7b5112f0 100644 --- a/lib/src/container/unencapsulate.rs +++ b/lib/src/container/unencapsulate.rs @@ -101,18 +101,18 @@ impl AsyncRead for ProgressReader { async fn fetch_manifest_impl( proxy: &mut ImageProxy, imgref: &OstreeImageReference, -) -> Result<(oci_spec::image::ImageManifest, String)> { +) -> Result<(oci_image::ImageManifest, oci_image::Digest)> { let oi = &proxy.open_image(&imgref.imgref.to_string()).await?; let (digest, manifest) = proxy.fetch_manifest(oi).await?; proxy.close_image(oi).await?; - Ok((manifest, digest)) + Ok((manifest, oci_image::Digest::from_str(digest.as_str())?)) } /// Download the manifest for a target image and its sha256 digest. #[context("Fetching manifest")] pub async fn fetch_manifest( imgref: &OstreeImageReference, -) -> Result<(oci_spec::image::ImageManifest, String)> { +) -> Result<(oci_image::ImageManifest, oci_image::Digest)> { let mut proxy = ImageProxy::new().await?; fetch_manifest_impl(&mut proxy, imgref).await } @@ -122,13 +122,14 @@ pub async fn fetch_manifest( pub async fn fetch_manifest_and_config( imgref: &OstreeImageReference, ) -> Result<( - oci_spec::image::ImageManifest, - String, - oci_spec::image::ImageConfiguration, + oci_image::ImageManifest, + oci_image::Digest, + oci_image::ImageConfiguration, )> { let proxy = ImageProxy::new().await?; let oi = &proxy.open_image(&imgref.imgref.to_string()).await?; let (digest, manifest) = proxy.fetch_manifest(oi).await?; + let digest = oci_image::Digest::from_str(&digest)?; let config = proxy.fetch_config(oi).await?; Ok((manifest, digest, config)) } @@ -289,19 +290,17 @@ pub(crate) async fn fetch_layer_decompress<'a>( })?; size = layer_blob.size; media_type = &layer_blob.media_type; - (blob, driver) = proxy - .get_blob(img, layer_blob.digest.as_str(), size as u64) - .await?; + (blob, driver) = proxy.get_blob(img, &layer_blob.digest, size).await?; } _ => { size = layer.size(); media_type = layer.media_type(); - (blob, driver) = proxy - .get_blob(img, layer.digest().as_str(), size as u64) - .await?; + (blob, driver) = proxy.get_blob(img, layer.digest(), size).await?; } }; + let driver = async { driver.await.map_err(Into::into) }; + if let Some(progress) = progress { let (readprogress, mut readwatch) = ProgressReader::new(blob); let readprogress = tokio::io::BufReader::new(readprogress); @@ -311,7 +310,7 @@ pub(crate) async fn fetch_layer_decompress<'a>( let status = LayerProgress { layer_index, fetched: *fetched, - total: size as u64, + total: size, }; progress.send_replace(Some(status)); } diff --git a/lib/src/container/update_detachedmeta.rs b/lib/src/container/update_detachedmeta.rs index 8993680b..503bc3b7 100644 --- a/lib/src/container/update_detachedmeta.rs +++ b/lib/src/container/update_detachedmeta.rs @@ -5,7 +5,7 @@ use anyhow::{anyhow, Context, Result}; use camino::Utf8Path; use cap_std::fs::Dir; use cap_std_ext::cap_std; -use containers_image_proxy::oci_spec; +use containers_image_proxy::oci_spec::image as oci_image; use std::io::{BufReader, BufWriter}; /// Given an OSTree container image reference, update the detached metadata (e.g. GPG signature) @@ -16,7 +16,7 @@ pub async fn update_detached_metadata( src: &ImageReference, dest: &ImageReference, detached_buf: Option<&[u8]>, -) -> Result { +) -> Result { // For now, convert the source to a temporary OCI directory, so we can directly // parse and manipulate it. In the future this will be replaced by https://github.com/ostreedev/ostree-rs-ext/issues/153 // and other work to directly use the containers/image API via containers-image-proxy. @@ -29,7 +29,7 @@ pub async fn update_detached_metadata( }; // Full copy of the source image - let pulled_digest: String = skopeo::copy(src, &tempsrc_ref, None, None, false) + let pulled_digest = skopeo::copy(src, &tempsrc_ref, None, None, false) .await .context("Creating temporary copy to OCI dir")?; @@ -44,16 +44,24 @@ pub async fn update_detached_metadata( let tempsrc = ocidir::OciDir::open(&tempsrc)?; // Load the manifest, platform, and config - let (mut manifest, manifest_descriptor) = tempsrc - .read_manifest_and_descriptor() - .context("Reading manifest from source")?; - anyhow::ensure!(manifest_descriptor.digest().as_str() == pulled_digest.as_str()); + let idx = tempsrc + .read_index()? + .ok_or(anyhow!("Reading image index from source"))?; + let manifest_descriptor = idx + .manifests() + .first() + .ok_or(anyhow!("No manifests in index"))?; + let mut manifest: oci_image::ImageManifest = tempsrc + .read_json_blob(manifest_descriptor) + .context("Reading manifest json blob")?; + + anyhow::ensure!(manifest_descriptor.digest().digest() == pulled_digest.digest()); let platform = manifest_descriptor .platform() .as_ref() .cloned() .unwrap_or_default(); - let mut config: oci_spec::image::ImageConfiguration = + let mut config: oci_image::ImageConfiguration = tempsrc.read_json_blob(manifest.config())?; let mut ctrcfg = config .config() @@ -91,10 +99,10 @@ pub async fn update_detached_metadata( .complete()? }; // Get the diffid and descriptor for our new tar layer - let out_layer_diffid = format!("sha256:{}", out_layer.uncompressed_sha256); + let out_layer_diffid = format!("sha256:{}", out_layer.uncompressed_sha256.digest()); let out_layer_descriptor = out_layer .descriptor() - .media_type(oci_spec::image::MediaType::ImageLayerGzip) + .media_type(oci_image::MediaType::ImageLayerGzip) .build() .unwrap(); // SAFETY: We pass all required fields diff --git a/lib/src/fixture.rs b/lib/src/fixture.rs index 7a2ba921..99a2e384 100644 --- a/lib/src/fixture.rs +++ b/lib/src/fixture.rs @@ -15,6 +15,7 @@ use cap_std::fs::Dir; use cap_std_ext::cap_std; use cap_std_ext::prelude::CapStdExtCommandExt; use chrono::TimeZone; +use containers_image_proxy::oci_spec::image as oci_image; use fn_error_context::context; use gvariant::aligned_bytes::TryAsAligned; use gvariant::{Marker, Structure}; @@ -805,7 +806,7 @@ impl Fixture { /// Export the current ref as a container image. /// This defaults to using chunking. #[context("Exporting container")] - pub async fn export_container(&self) -> Result<(ImageReference, String)> { + pub async fn export_container(&self) -> Result<(ImageReference, oci_image::Digest)> { let name = "oci-v1"; let container_path = &self.path.join(name); if container_path.exists() { diff --git a/lib/src/integrationtest.rs b/lib/src/integrationtest.rs index 1ceadb63..d8166ab2 100644 --- a/lib/src/integrationtest.rs +++ b/lib/src/integrationtest.rs @@ -3,13 +3,14 @@ use std::path::Path; use crate::container_utils::is_ostree_container; -use anyhow::Result; +use anyhow::{anyhow, Context, Result}; use camino::Utf8Path; use cap_std::fs::Dir; use cap_std_ext::cap_std; use containers_image_proxy::oci_spec; use fn_error_context::context; use gio::prelude::*; +use oci_spec::image as oci_image; use ocidir::{ oci_spec::image::{Arch, Platform}, GzipLayerWriter, @@ -68,8 +69,17 @@ where let src = Dir::open_ambient_dir(src, cap_std::ambient_authority())?; let src = ocidir::OciDir::open(&src)?; - let mut manifest = src.read_manifest()?; - let mut config: oci_spec::image::ImageConfiguration = src.read_json_blob(manifest.config())?; + let idx = src + .read_index()? + .ok_or(anyhow!("Reading image index from source"))?; + let manifest_descriptor = idx + .manifests() + .first() + .ok_or(anyhow!("No manifests in index"))?; + let mut manifest: oci_image::ImageManifest = src + .read_json_blob(manifest_descriptor) + .context("Reading manifest json blob")?; + let mut config: oci_image::ImageConfiguration = src.read_json_blob(manifest.config())?; if let Some(arch) = arch.as_ref() { config.set_architecture(arch.clone()); @@ -96,7 +106,7 @@ where config .rootfs_mut() .diff_ids_mut() - .push(new_layer.uncompressed_sha256); + .push(new_layer.uncompressed_sha256.digest().to_string()); let new_config_desc = src.write_config(config)?; manifest.set_config(new_config_desc); diff --git a/lib/tests/it/main.rs b/lib/tests/it/main.rs index 2732217b..3a339a2b 100644 --- a/lib/tests/it/main.rs +++ b/lib/tests/it/main.rs @@ -3,7 +3,8 @@ use camino::Utf8Path; use cap_std::fs::{Dir, DirBuilder, DirBuilderExt}; use cap_std_ext::cap_std; use containers_image_proxy::oci_spec; -use containers_image_proxy::oci_spec::image::ImageManifest; +use oci_image::ImageManifest; +use oci_spec::image as oci_image; use ocidir::oci_spec::image::Arch; use once_cell::sync::Lazy; use ostree_ext::chunking::ObjectMetaSized; @@ -516,7 +517,7 @@ async fn impl_test_container_import_export(chunked: bool) -> Result<()> { transport: Transport::OciArchive, name: archivepath.as_str().to_string(), }; - let _: String = ostree_ext::container::encapsulate( + let _: oci_image::Digest = ostree_ext::container::encapsulate( fixture.srcrepo(), fixture.testref(), &config, @@ -577,10 +578,9 @@ async fn impl_test_container_import_export(chunked: bool) -> Result<()> { transport: Transport::OciDir, name: fixture.path.join("unsigned.ocidir").to_string(), }; - let _: String = - ostree_ext::container::update_detached_metadata(&srcoci_imgref, &temp_unsigned, None) - .await - .unwrap(); + let _ = ostree_ext::container::update_detached_metadata(&srcoci_imgref, &temp_unsigned, None) + .await + .unwrap(); let temp_unsigned = OstreeImageReference { sigverify: SignatureSource::OstreeRemote("myremote".to_string()), imgref: temp_unsigned, @@ -621,8 +621,12 @@ async fn test_export_as_container_nonderived() -> Result<()> { let exported = store::export(fixture.destrepo(), &src_imgref, &dest, None) .await .unwrap(); - let (new_manifest, desc) = ocidir.read_manifest_and_descriptor()?; - assert_eq!(desc.digest(), exported.as_str()); + + let idx = ocidir.read_index()?.unwrap(); + let desc = idx.manifests().first().unwrap(); + let new_manifest: oci_image::ImageManifest = ocidir.read_json_blob(desc).unwrap(); + + assert_eq!(desc.digest().digest(), exported.digest()); assert_eq!(new_manifest.layers().len(), fixture::LAYERS_V0_LEN); // Reset the destrepo @@ -666,8 +670,11 @@ async fn test_export_as_container_derived() -> Result<()> { .await .unwrap(); - let (new_manifest, desc) = ocidir.read_manifest_and_descriptor()?; - assert_eq!(desc.digest(), exported.as_str()); + let idx = ocidir.read_index()?.unwrap(); + let desc = idx.manifests().first().unwrap(); + let new_manifest: oci_image::ImageManifest = ocidir.read_json_blob(desc).unwrap(); + + assert_eq!(desc.digest().digest(), exported.digest()); assert_eq!(new_manifest.layers().len(), fixture::LAYERS_V0_LEN + 1); // Reset the destrepo @@ -737,7 +744,10 @@ fn validate_chunked_structure(oci_path: &Utf8Path) -> Result<()> { let d = Dir::open_ambient_dir(oci_path, cap_std::ambient_authority())?; let d = ocidir::OciDir::open(&d)?; - let manifest = d.read_manifest()?; + let idx = d.read_index()?.unwrap(); + let desc = idx.manifests().first().unwrap(); + let manifest: oci_image::ImageManifest = d.read_json_blob(desc).unwrap(); + assert_eq!(manifest.layers().len(), LAYERS_V0_LEN); let ostree_layer = manifest.layers().first().unwrap(); let mut ostree_layer_blob = d @@ -855,7 +865,7 @@ async fn test_container_chunked() -> Result<()> { for layer in prep.layers.iter() { assert!(layer.commit.is_none()); } - assert_eq!(digest, expected_digest); + assert_eq!(digest, expected_digest.to_string()); { let mut layer_history = prep.layers_with_history(); assert!(layer_history @@ -904,7 +914,7 @@ r usr/bin/bash bash-v0 .context("Failed to update")?; let expected_digest = fixture.export_container().await.unwrap().1; - assert_ne!(digest, expected_digest); + assert_ne!(digest, expected_digest.digest()); let mut imp = store::ImageImporter::new(fixture.destrepo(), &imgref, Default::default()).await?; @@ -928,7 +938,7 @@ r usr/bin/bash bash-v0 } let to_fetch = prep.layers_to_fetch().collect::>>()?; assert_eq!(to_fetch.len(), 2); - assert_eq!(expected_digest, prep.manifest_digest.as_str()); + assert_eq!(expected_digest.to_string(), prep.manifest_digest.as_str()); assert!(prep.ostree_commit_layer.commit.is_none()); assert_eq!(prep.ostree_layers.len(), nlayers); let (first, second) = (to_fetch[0], to_fetch[1]); @@ -1741,20 +1751,20 @@ fn test_manifest_diff() { assert_eq!(d.to, &b); assert_eq!(d.added.len(), 4); assert_eq!( - d.added[0].digest(), + d.added[0].digest().to_string(), "sha256:0b5d930ffc92d444b0a7b39beed322945a3038603fbe2a56415a6d02d598df1f" ); assert_eq!( - d.added[3].digest(), + d.added[3].digest().to_string(), "sha256:cb9b8a4ac4a8df62df79e6f0348a14b3ec239816d42985631c88e76d4e3ff815" ); assert_eq!(d.removed.len(), 4); assert_eq!( - d.removed[0].digest(), + d.removed[0].digest().to_string(), "sha256:0ff8b1fdd38e5cfb6390024de23ba4b947cd872055f62e70f2c21dad5c928925" ); assert_eq!( - d.removed[3].digest(), + d.removed[3].digest().to_string(), "sha256:76b83eea62b7b93200a056b5e0201ef486c67f1eeebcf2c7678ced4d614cece2" ); }