Use the config digest to compare images loaded/pulled using different methods

Historically, non-schema1 images had a deterministic image ID == config digest.
With zstd:chunked, we don't want to deduplicate layers pulled by consuming the
full tarball and layers partially pulled based on TOC, because we can't cheaply
ensure equivalence; so, image IDs for images where a TOC was used differ.

To accommodate that, compare images using their configs digests, not using image IDs.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
This commit is contained in:
Miloslav Trmač
2024-10-17 21:50:18 +02:00
parent bf8f2b5551
commit 1d7ec1ef5f
4 changed files with 43 additions and 26 deletions

View File

@ -383,9 +383,13 @@ EOF
assert "${lines[-2]}" =~ ".*$IMAGE false" "image from readwrite store" assert "${lines[-2]}" =~ ".*$IMAGE false" "image from readwrite store"
assert "${lines[-1]}" =~ ".*$IMAGE true" "image from readonly store" assert "${lines[-1]}" =~ ".*$IMAGE true" "image from readonly store"
id=${lines[-2]%% *} id=${lines[-2]%% *}
local config_digest; config_digest=$(image_config_digest "@$id") # Without $sconf, i.e. from the read-write store.
CONTAINERS_STORAGE_CONF=$sconf run_podman pull -q $IMAGE CONTAINERS_STORAGE_CONF=$sconf run_podman pull -q $IMAGE
is "$output" "$id" "pull -q $IMAGE, using storage.conf" # This is originally a regression test, (podman pull) used to output multiple image IDs. Ensure it only prints one.
assert "${#lines[*]}" -le 1 "Number of output lines from podman pull"
local config_digest2; config_digest2=$(image_config_digest "@$output")
assert "$config_digest2" = "$config_digest" "pull -q $IMAGE, using storage.conf"
run_podman --root $imstore/root rmi --all run_podman --root $imstore/root rmi --all
} }

View File

@ -23,21 +23,23 @@ function teardown() {
# Custom helpers for this test only. These just save us having to duplicate # Custom helpers for this test only. These just save us having to duplicate
# the same thing four times (two tests, each with -i and stdin). # the same thing four times (two tests, each with -i and stdin).
# #
# initialize, read image ID and name # initialize, read image ID, image config digest, and name
get_iid_and_name() { get_img_ids_and_name() {
run_podman images -a --format '{{.ID}} {{.Repository}}:{{.Tag}}' run_podman images -a --format '{{.ID}} {{.Repository}}:{{.Tag}}'
read iid img_name <<<"$output" read iid img_name <<<"$output"
img_config_digest=$(image_config_digest "@$iid")
archive=$PODMAN_TMPDIR/myimage-$(random_string 8).tar archive=$PODMAN_TMPDIR/myimage-$(random_string 8).tar
} }
# Simple verification of image ID and name # Simple verification of image config digest and name
verify_iid_and_name() { verify_img_config_digest_and_name() {
run_podman images -a --format '{{.ID}} {{.Repository}}:{{.Tag}}' run_podman images -a --format '{{.ID}} {{.Repository}}:{{.Tag}}'
read new_iid new_img_name < <(echo "$output") read new_iid new_img_name < <(echo "$output")
new_img_config_digest=$(image_config_digest "@$new_iid")
# Verify # Verify
is "$new_iid" "$iid" "Image ID of loaded image == original" is "$new_img_config_digest" "$img_config_digest" "Image config digest of loaded image == original"
is "$new_img_name" "$1" "Name & tag of restored image" is "$new_img_name" "$1" "Name & tag of restored image"
} }
@ -178,7 +180,7 @@ verify_iid_and_name() {
@test "podman load - by image ID" { @test "podman load - by image ID" {
# FIXME: how to build a simple archive instead? # FIXME: how to build a simple archive instead?
get_iid_and_name get_img_ids_and_name
# Save image by ID, and remove it. # Save image by ID, and remove it.
run_podman save $iid -o $archive run_podman save $iid -o $archive
@ -186,41 +188,41 @@ verify_iid_and_name() {
# Load using -i; IID should be preserved, but name is not. # Load using -i; IID should be preserved, but name is not.
run_podman load -i $archive run_podman load -i $archive
verify_iid_and_name "<none>:<none>" verify_img_config_digest_and_name "<none>:<none>"
# Same as above, using stdin # Same as above, using stdin
run_podman rmi $iid run_podman rmi $iid
run_podman load < $archive run_podman load < $archive
verify_iid_and_name "<none>:<none>" verify_img_config_digest_and_name "<none>:<none>"
# Same as above, using stdin but with `podman image load` # Same as above, using stdin but with `podman image load`
run_podman rmi $iid run_podman rmi $iid
run_podman image load < $archive run_podman image load < $archive
verify_iid_and_name "<none>:<none>" verify_img_config_digest_and_name "<none>:<none>"
} }
@test "podman load - by image name" { @test "podman load - by image name" {
get_iid_and_name get_img_ids_and_name
run_podman save $img_name -o $archive run_podman save $img_name -o $archive
run_podman rmi $iid run_podman rmi $iid
# Load using -i; this time the image should be tagged. # Load using -i; this time the image should be tagged.
run_podman load -i $archive run_podman load -i $archive
verify_iid_and_name $img_name verify_img_config_digest_and_name $img_name
run_podman rmi $iid run_podman rmi $iid
# Also make sure that `image load` behaves the same. # Also make sure that `image load` behaves the same.
run_podman image load -i $archive run_podman image load -i $archive
verify_iid_and_name $img_name verify_img_config_digest_and_name $img_name
run_podman rmi $iid run_podman rmi $iid
# Same as above, using stdin # Same as above, using stdin
run_podman load < $archive run_podman load < $archive
verify_iid_and_name $img_name verify_img_config_digest_and_name $img_name
} }
@test "podman load - from URL" { @test "podman load - from URL" {
get_iid_and_name get_img_ids_and_name
run_podman save $img_name -o $archive run_podman save $img_name -o $archive
run_podman rmi $iid run_podman rmi $iid
@ -234,7 +236,7 @@ verify_iid_and_name() {
$IMAGE /bin/busybox-extras httpd -f -p 80 $IMAGE /bin/busybox-extras httpd -f -p 80
run_podman load -i $SERVER/image.tar run_podman load -i $SERVER/image.tar
verify_iid_and_name $img_name verify_img_config_digest_and_name $img_name
run_podman rm -f -t0 myweb run_podman rm -f -t0 myweb
} }

View File

@ -149,9 +149,8 @@ EOF
} }
function _push_search_test() { function _push_search_test() {
# Preserve image ID for later comparison against push/pulled image # Look up image config digest for later comparison against push/pulled image
run_podman inspect --format '{{.Id}}' $IMAGE local img_config_digest; img_config_digest=$(image_config_digest $IMAGE)
iid=$output
destname=ok-$(random_string 10 | tr A-Z a-z)-ok destname=ok-$(random_string 10 | tr A-Z a-z)-ok
# Use command-line credentials # Use command-line credentials
@ -188,8 +187,8 @@ function _push_search_test() {
localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$destname localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$destname
# Compare to original image # Compare to original image
run_podman inspect --format '{{.Id}}' $destname local img_config_digest2; img_config_digest2=$(image_config_digest localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$destname)
is "$output" "$iid" "Image ID of pulled image == original IID" assert "$img_config_digest2" = "$img_config_digest" "config digest of pulled image == original digest"
run_podman rmi $destname run_podman rmi $destname
} }
@ -345,12 +344,12 @@ function _test_skopeo_credential_sharing() {
$image1 $image1
run_podman rmi $image1 run_podman rmi $image1
run_podman images $IMAGE --format {{.ID}} local podman_image_config_digest=$(image_config_digest $IMAGE)
local podman_image_id=$output
run_podman pull -q --retry 4 --retry-delay "0s" --authfile=$authfile \ run_podman pull -q --retry 4 --retry-delay "0s" --authfile=$authfile \
--tls-verify=false $image1 --tls-verify=false $image1
assert "${output:0:12}" = "$podman_image_id" "First pull (before stopping registry)" local pulled_image_config_digest; pulled_image_config_digest=$(image_config_digest @$output)
assert "$pulled_image_config_digest" = "$podman_image_config_digest" "First pull (before stopping registry)"
run_podman rmi $image1 run_podman rmi $image1
# This actually STOPs the registry, so the port is unbound... # This actually STOPs the registry, so the port is unbound...
@ -360,7 +359,8 @@ function _test_skopeo_credential_sharing() {
run_podman 0+w pull -q --retry 4 --retry-delay "5s" --authfile=$authfile \ run_podman 0+w pull -q --retry 4 --retry-delay "5s" --authfile=$authfile \
--tls-verify=false $image1 --tls-verify=false $image1
assert "$output" =~ "Failed, retrying in 5s.*Error: initializing.* connection refused" assert "$output" =~ "Failed, retrying in 5s.*Error: initializing.* connection refused"
assert "${lines[-1]:0:12}" = "$podman_image_id" "push should succeed via retry" local pulled_image_config_digest2; pulled_image_config_digest2=$(image_config_digest "@${lines[-1]}")
assert "$pulled_image_config_digest2" = "$podman_image_config_digest" "push should succeed via retry"
unpause_registry unpause_registry
run_podman rmi $image1 run_podman rmi $image1

View File

@ -847,6 +847,17 @@ function _ensure_container_running() {
die "Timed out waiting for container $1 to enter state running=$2" die "Timed out waiting for container $1 to enter state running=$2"
} }
# Return the config digest of an image in containers-storage.
# The input can be a named reference, or an @imageID (including shorter imageID prefixes)
# Historically, the image ID was a good indicator of “the same” image;
# with zstd:chunked, the same image might have different IDs depending on whether
# creating layers happened based on the TOC (and per-file operations) or the full layer tarball
function image_config_digest() {
local sha_output; sha_output=$(skopeo inspect --raw --config "containers-storage:$1" | sha256sum)
# strip filename ("-") from sha output
echo "${sha_output%% *}"
}
########################### ###########################
# _add_label_if_missing # make sure skip messages include rootless/remote # _add_label_if_missing # make sure skip messages include rootless/remote
########################### ###########################