diff --git a/Cargo.lock b/Cargo.lock index 408f7fc131..aa87e0baee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,19 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +[[package]] +name = "async-compression" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443ccbb270374a2b1055fc72da40e1f237809cd6bb0e97e66d264cd138473a6" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + [[package]] name = "atty" version = "0.2.14" @@ -186,7 +199,7 @@ dependencies = [ "ansi_term 0.11.0", "atty", "bitflags", - "strsim", + "strsim 0.8.0", "textwrap", "unicode-width", "vec_map", @@ -224,6 +237,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "containers-image-proxy" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d50088ba8ca7ac24a15bad68cf240aee9ef399021c866bc2b79d5d92b6cf0d" +dependencies = [ + "anyhow", + "fn-error-context", + "futures-util", + "lazy_static", + "nix 0.22.0", + "semver", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + [[package]] name = "core-foundation" version = "0.9.1" @@ -377,6 +410,72 @@ dependencies = [ "syn", ] +[[package]] +name = "darling" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_builder" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73" +dependencies = [ + "derive_builder_core", + "syn", +] + [[package]] name = "dlv-list" version = "0.2.3" @@ -656,6 +755,18 @@ dependencies = [ "wasi 0.10.0+wasi-snapshot-preview1", ] +[[package]] +name = "getset" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "gio" version = "0.14.3" @@ -890,6 +1001,12 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.2.3" @@ -1240,6 +1357,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "oci-spec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63f0f82a50257e72a6f13616dc093f3d0874294629bf8b7d0bc2a098e3db324f" +dependencies = [ + "derive_builder", + "getset", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "once_cell" version = "1.8.0" @@ -1321,9 +1451,9 @@ dependencies = [ [[package]] name = "ostree" -version = "0.13.0" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9358905170a49b82e5baecb879bb944c42736a4b02074a40286a7b20665af381" +checksum = "ed97148d69c9822e948d9c1d2e288e3efd11592814981f46fce6f47cb7837d4e" dependencies = [ "bitflags", "gio", @@ -1338,30 +1468,36 @@ dependencies = [ [[package]] name = "ostree-ext" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e40c1a1b52ed5164b0fda610952438afff5162ff2dafa49e55c0a78698228d" +version = "0.4.0-alpha.0" +source = "git+https://github.com/ostreedev/ostree-rs-ext?branch=main#5a1e9fde1b2d5602d1ad75cc8f60e474cb7ccb76" dependencies = [ "anyhow", + "async-compression", + "bitflags", "bytes", "camino", "cjson", + "containers-image-proxy", "flate2", "fn-error-context", "futures-util", "gvariant", "hex", "indicatif", + "lazy_static", "libc", "maplit", "nix 0.22.0", + "oci-spec", "openat", "openat-ext", "openssl", "ostree", "phf 0.9.0", + "pin-project", "serde", "serde_json", + "serde_plain", "structopt", "tar", "tempfile", @@ -1373,9 +1509,9 @@ dependencies = [ [[package]] name = "ostree-sys" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46755b75b81ac66623ee86d6eee779a82acf495a8a6a94ab458a96ad3e6880d1" +checksum = "6f21a33d095678ef9b32503ce042d4101477ab9a20c971d5f27e7f42d5a2bc79" dependencies = [ "gio-sys", "glib-sys", @@ -1509,6 +1645,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.7" @@ -1933,6 +2089,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" + [[package]] name = "serde" version = "1.0.130" @@ -1964,6 +2126,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_plain" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625fb0da2b006092b426a94acc1611bec52f2ec27bb27b266a9f93c29ee38eda" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.0" @@ -2040,6 +2211,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "structopt" version = "0.3.24" @@ -2204,18 +2381,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 504fefd86f..e29bc7e5af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,8 @@ nix = "0.23.0" openat = "0.1.21" openat-ext = "^0.2.2" os-release = "0.1.0" -ostree-ext = "0.3.0" +# To allow us to ship the new container code as experimental +ostree-ext = { git = "https://github.com/ostreedev/ostree-rs-ext", branch = "main" } paste = "1.0" phf = { version = "0.10", features = ["macros"] } rand = "0.8.4" diff --git a/packaging/rpm-ostree.spec.in b/packaging/rpm-ostree.spec.in index f77ee56c72..16a93c3c93 100644 --- a/packaging/rpm-ostree.spec.in +++ b/packaging/rpm-ostree.spec.in @@ -48,7 +48,7 @@ BuildRequires: gnome-common BuildRequires: /usr/bin/g-ir-scanner # Core requirements # One way to check this: `objdump -p /path/to/rpm-ostree | grep LIBOSTREE` and pick the highest (though that might miss e.g. new struct members) -BuildRequires: pkgconfig(ostree-1) >= 2021.1 +BuildRequires: pkgconfig(ostree-1) >= 2021.5 BuildRequires: pkgconfig(polkit-gobject-1) BuildRequires: pkgconfig(json-glib-1.0) BuildRequires: pkgconfig(rpm) >= 4.14.0 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 837db99ee1..dfa724c7d5 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -137,19 +137,19 @@ pub mod ffi { /// cxx.rs currently requires types used as extern Rust types to be defined by the same crate /// that contains the bridge using them, so we redefine an `ContainerImport` struct here. pub(crate) struct ContainerImport { + pub changed: bool, pub ostree_commit: String, pub image_digest: String, } // sysroot_upgrade.rs extern "Rust" { - fn import_container( + fn pull_container( repo: Pin<&mut OstreeRepo>, cancellable: Pin<&mut GCancellable>, - imgref: String, + imgref: &str, ) -> Result>; - - fn fetch_digest(imgref: String, cancellable: Pin<&mut GCancellable>) -> Result; + fn get_commit_manifest_digest(commit_v: Pin<&mut GVariant>) -> Result; } // core.rs diff --git a/rust/src/origin.rs b/rust/src/origin.rs index 80073f0702..91baa82c1f 100644 --- a/rust/src/origin.rs +++ b/rust/src/origin.rs @@ -37,12 +37,14 @@ pub(crate) fn origin_to_treefile_inner(kf: &KeyFile) -> Result> { Some(r) } else if let Some(r) = keyfile_get_optional_string(kf, ORIGIN, "baserefspec")? { Some(r) + } else if let Some(r) = keyfile_get_optional_string(kf, ORIGIN, "container-image-reference")? { + // TODO - consider this key deprecated and remove it + Some(r) } else { - keyfile_get_optional_string(kf, ORIGIN, "container-image-reference")? + keyfile_get_optional_string(kf, ORIGIN, ostree_ext::container::deploy::ORIGIN_CONTAINER)? }; - let refspec_str = refspec_str.ok_or_else(|| { - anyhow::anyhow!("Failed to find refspec/baserefspec/container-image-reference in origin") - })?; + let refspec_str = refspec_str + .ok_or_else(|| anyhow::anyhow!("Failed to find refspec/baserefspec/container in origin"))?; cfg.derive.base_refspec = Some(refspec_str); cfg.packages = parse_stringlist(kf, PACKAGES, "requested")?; cfg.derive.packages_local = parse_localpkglist(kf, PACKAGES, "requested-local")?; diff --git a/rust/src/sysroot_upgrade.rs b/rust/src/sysroot_upgrade.rs index a46468d5c4..2c812f82d5 100644 --- a/rust/src/sysroot_upgrade.rs +++ b/rust/src/sysroot_upgrade.rs @@ -3,48 +3,87 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::cxxrsutil::*; -use crate::ffi::ContainerImport; -use std::convert::TryInto; +use crate::ffi::{output_message, ContainerImport}; +use anyhow::Result; +use ostree::glib; +use ostree_container::store::LayeredImageImporter; +use ostree_container::store::PrepareResult; +use ostree_container::OstreeImageReference; +use ostree_ext::container as ostree_container; +use ostree_ext::ostree; +use std::convert::TryFrom; use std::pin::Pin; use tokio::runtime::Handle; +async fn pull_container_async( + repo: &ostree::Repo, + imgref: &OstreeImageReference, +) -> Result { + let unchanged = || ContainerImport { + changed: false, + ostree_commit: "".to_string(), + image_digest: "".to_string(), + }; + output_message(&format!("Pulling manifest: {}", &imgref)); + let mut imp = LayeredImageImporter::new(repo, imgref).await?; + let prep = match imp.prepare().await? { + PrepareResult::AlreadyPresent(_) => return Ok(unchanged()), + PrepareResult::Ready(r) => r, + }; + let digest = prep.manifest_digest.clone(); + output_message(&format!("Importing: {} (digest: {})", &imgref, &digest)); + if prep.base_layer.commit.is_none() { + let size = glib::format_size(prep.base_layer.size()); + output_message(&format!( + "Downloading base layer: {} ({})", + prep.base_layer.digest(), + size + )); + } else { + output_message(&format!("Using base: {}", prep.base_layer.digest())); + } + // TODO add nice download progress + for layer in prep.layers.iter() { + if layer.commit.is_some() { + output_message(&format!("Using layer: {}", layer.digest())); + } else { + let size = glib::format_size(layer.size()); + output_message(&format!("Downloading layer: {} ({})", layer.digest(), size)); + } + } + let import = imp.import(prep).await?; + Ok(ContainerImport { + changed: true, + ostree_commit: import.commit, + image_digest: digest, + }) +} + /// Import ostree commit in container image using ostree-rs-ext's API. -pub(crate) fn import_container( +pub(crate) fn pull_container( mut repo: Pin<&mut crate::FFIOstreeRepo>, mut cancellable: Pin<&mut crate::FFIGCancellable>, - imgref: String, + imgref: &str, ) -> CxxResult> { - let repo = repo.gobj_wrap(); + let repo = &repo.gobj_wrap(); let cancellable = cancellable.gobj_wrap(); - let imgref = imgref.as_str().try_into()?; + let imgref = &OstreeImageReference::try_from(imgref)?; - let imported = Handle::current().block_on(async { + let r = Handle::current().block_on(async { crate::utils::run_with_cancellable( - async { ostree_ext::container::import(&repo, &imgref, None).await }, + async { pull_container_async(repo, imgref).await }, &cancellable, ) .await })?; - Ok(Box::new(ContainerImport { - ostree_commit: imported.ostree_commit, - image_digest: imported.image_digest, - })) + Ok(Box::new(r)) } -/// Fetch the image digest for `imgref` using ostree-rs-ext's API. -pub(crate) fn fetch_digest( - imgref: String, - mut cancellable: Pin<&mut crate::FFIGCancellable>, +pub(crate) fn get_commit_manifest_digest( + mut commit_v: Pin<&mut crate::FFIGVariant>, ) -> CxxResult { - let imgref = imgref.as_str().try_into()?; - let cancellable = cancellable.gobj_wrap(); - - let digest = Handle::current().block_on(async { - crate::utils::run_with_cancellable( - async { ostree_ext::container::fetch_manifest_info(&imgref).await }, - &cancellable, - ) - .await - })?; - Ok(digest.manifest_digest) + let commit = &commit_v.gobj_wrap(); + Ok(ostree_container::store::manifest_digest_from_commit( + commit, + )?) } diff --git a/src/daemon/rpmostree-sysroot-upgrader.cxx b/src/daemon/rpmostree-sysroot-upgrader.cxx index 30a539537a..60cb876a8c 100644 --- a/src/daemon/rpmostree-sysroot-upgrader.cxx +++ b/src/daemon/rpmostree-sysroot-upgrader.cxx @@ -450,27 +450,14 @@ rpmostree_sysroot_upgrader_pull_base (RpmOstreeSysrootUpgrader *self, if (override_commit) return glnx_throw (error, "Specifying commit overrides for container-image-reference type refspecs is not supported"); - if (strstr (refspec, "@")) - return glnx_throw (error, "Pinned to specific container image digest, cannot upgrade"); - - /* Check to see if the digest that `refspec` points to is the same before pulling a new container image. */ - const char *cur_digest = rpmostree_origin_get_container_image_reference_digest (self->original_origin); - if (cur_digest) + auto refspec_s = std::string(refspec); + auto import = rpmostreecxx::pull_container(*self->repo, *cancellable, refspec_s); + if (!import->changed) { - rpmostree_output_message ("Pulling manifest: %s", refspec); - auto new_digest = CXX_TRY_VAL(fetch_digest(std::string(refspec), *cancellable), error); - if (strcmp (new_digest.c_str(), cur_digest) == 0) - { - /* No new digest. */ - *out_changed = FALSE; - return TRUE; - } + /* No new digest. */ + *out_changed = FALSE; + return TRUE; } - - rpmostree_output_message ("Pulling: %s", refspec); - auto import = CXX_TRY_VAL(import_container(*self->repo, *cancellable, std::string(refspec)), error); - - rpmostree_origin_set_container_image_reference_digest (self->original_origin, import->image_digest.c_str()); new_base_rev = strdup (import->ostree_commit.c_str()); break; } diff --git a/src/daemon/rpmostreed-deployment-utils.cxx b/src/daemon/rpmostreed-deployment-utils.cxx index 7e496e1365..c079636e33 100644 --- a/src/daemon/rpmostreed-deployment-utils.cxx +++ b/src/daemon/rpmostreed-deployment-utils.cxx @@ -302,8 +302,8 @@ rpmostreed_deployment_generate_variant (OstreeSysroot *sysroot, case RPMOSTREE_REFSPEC_TYPE_CONTAINER: { g_variant_dict_insert (dict, "container-image-reference", "s", refspec); - auto digest = rpmostree_origin_get_container_image_reference_digest (origin); - g_variant_dict_insert (dict, "container-image-reference-digest", "s", digest); + auto digest = rpmostreecxx::get_commit_manifest_digest (*commit); + g_variant_dict_insert (dict, "container-image-reference-digest", "s", digest.c_str()); } break; case RPMOSTREE_REFSPEC_TYPE_CHECKSUM: diff --git a/src/libpriv/rpmostree-origin.cxx b/src/libpriv/rpmostree-origin.cxx index 0e727bb5eb..9cefbad3c9 100644 --- a/src/libpriv/rpmostree-origin.cxx +++ b/src/libpriv/rpmostree-origin.cxx @@ -234,11 +234,6 @@ rpmostree_origin_remove_transient_state (RpmOstreeOrigin *origin) rpmostree_origin_set_override_commit (origin, NULL, NULL); } -const char * -rpmostree_origin_get_container_image_reference_digest (RpmOstreeOrigin *origin) -{ - return origin->cached_digest; -} const char * rpmostree_origin_get_refspec (RpmOstreeOrigin *origin) @@ -523,23 +518,6 @@ rpmostree_origin_set_regenerate_initramfs (RpmOstreeOrigin *origin, g_key_file_get_string_list (origin->kf, "rpmostree", "initramfs-args", NULL, NULL); } -void -rpmostree_origin_set_container_image_reference_digest (RpmOstreeOrigin *origin, - const char *digest) -{ - if (digest != NULL) - { - g_key_file_set_string (origin->kf, "origin", "container-image-reference-digest", digest); - } - else - { - g_key_file_remove_key (origin->kf, "origin", "container-image-reference-digest", NULL); - } - - g_free (origin->cached_digest); - origin->cached_digest = g_strdup (digest); -} - void rpmostree_origin_set_override_commit (RpmOstreeOrigin *origin, const char *checksum, diff --git a/src/libpriv/rpmostree-origin.h b/src/libpriv/rpmostree-origin.h index 42575400da..e7d0a7bf5b 100644 --- a/src/libpriv/rpmostree-origin.h +++ b/src/libpriv/rpmostree-origin.h @@ -57,9 +57,6 @@ rpmostree_origin_dup (RpmOstreeOrigin *origin); void rpmostree_origin_remove_transient_state (RpmOstreeOrigin *origin); -const char * -rpmostree_origin_get_container_image_reference_digest (RpmOstreeOrigin *origin); - const char * rpmostree_origin_get_refspec (RpmOstreeOrigin *origin); @@ -146,10 +143,6 @@ rpmostree_origin_set_regenerate_initramfs (RpmOstreeOrigin *origin, gboolean regenerate, char **args); -void -rpmostree_origin_set_container_image_reference_digest (RpmOstreeOrigin *origin, - const char *digest); - void rpmostree_origin_set_override_commit (RpmOstreeOrigin *origin, const char *checksum, diff --git a/tests/kolainst/destructive/container-image b/tests/kolainst/destructive/container-image index d679fe6f21..0a7ac5f879 100755 --- a/tests/kolainst/destructive/container-image +++ b/tests/kolainst/destructive/container-image @@ -26,18 +26,23 @@ set -x libtest_prepare_offline cd $(mktemp -d) -image=containers-storage:localhost/fcos:latest +basedir=/var/tmp/fcos +# TODO: It'd be much better to test this via a registry +image=oci:$basedir image_pull=ostree-unverified-image:$image case "${AUTOPKGTEST_REBOOT_MARK:-}" in "") # Test rebase checksum=$(rpm-ostree status --json | jq -r '.deployments[0].checksum') + content_checksum=$(ostree show "${checksum}" | grep ContentChecksum) rpm-ostree ex-container export --repo=/ostree/repo ${checksum} "${image}" rpm-ostree rebase "$image_pull" --experimental | tee out.txt - assert_file_has_content_literal out.txt 'Pulling: '"$image_pull" - rpmostree_assert_status ".deployments[0][\"container-image-reference\"] == \"ostree-unverified-image:containers-storage:localhost/fcos:latest\"" - rpmostree_assert_status ".deployments[0][\"checksum\"] == \"${checksum}\"" + assert_file_has_content out.txt 'Importing.*'"$image_pull" + rpmostree_assert_status ".deployments[0][\"container-image-reference\"] == \"ostree-unverified-image:$image\"" + new_checksum=$(rpm-ostree status --json | jq -r '.deployments[0].checksum') + new_content_checksum=$(ostree show "${new_checksum}" | grep ContentChecksum) + assert_streq "${content_checksum}" "${new_content_checksum}" echo "ok rebase to container image reference" rpm-ostree status | tee out.txt @@ -69,11 +74,15 @@ case "${AUTOPKGTEST_REBOOT_MARK:-}" in with_foo_commit=$(rpm-ostree status --json | jq -r '.deployments[0].checksum') ostree refs ${with_foo_commit} --create vmcheck_tmp/new_update new_commit=$(ostree commit -b vmcheck --tree=ref=vmcheck_tmp/new_update) + new_content_checksum=$(ostree show "${new_commit}" | grep ContentChecksum) + rm "$basedir" -rf rpm-ostree ex-container export --repo=/ostree/repo ${new_commit} "$image" rpm-ostree uninstall foo rpm-ostree upgrade - rpmostree_assert_status ".deployments[0][\"checksum\"] == \"${new_commit}\"" + new_client_checksum=$(rpm-ostree status --json | jq -r '.deployments[0].checksum') + new_client_content_checksum=$(ostree show "${new_client_checksum}" | grep ContentChecksum) + assert_streq "${new_content_checksum}" "${new_client_content_checksum}" /tmp/autopkgtest-reboot 2 ;;