stages/skopeo: merge manifest into image directory

When a manifest list is matched with a container image, the skopeo
stage will merge the specified manifest into the container image dir
before copying it to the registry in the OS tree.

If there is no manifest to merge, we maintain the old behaviour of
symlinking the source to work around the ":" in filename issue.
Otherwise, we copy the container directory so that we can merge the
manifest in the new location.
This commit is contained in:
Achilleas Koutsou 2023-03-16 15:50:41 +01:00 committed by Ondřej Budai
parent dd902311c2
commit ac2f140d4c

View file

@ -121,6 +121,27 @@ def parse_input(inputs):
return res
def merge_manifest(list_manifest, destination):
"""
Merge the list manifest into the image directory. This preserves the manifest list with the image in the registry so
that users can run or inspect a container using the original manifest list digest used to pull the container.
See https://github.com/containers/skopeo/issues/1935
"""
# calculate the checksum of the manifest of the container image in the destination
dest_manifest = os.path.join(destination, "manifest.json")
manifest_checksum = subprocess.check_output(["skopeo", "manifest-digest", dest_manifest]).decode().strip()
parts = manifest_checksum.split(":")
assert len(parts) == 2, f"unexpected output for skopeo manifest-digest: {manifest_checksum}"
manifest_checksum = parts[1]
# rename the manifest to its checksum
os.rename(dest_manifest, os.path.join(destination, manifest_checksum + ".manifest.json"))
# copy the index manifest into the destination
subprocess.run(["cp", "--reflink=auto", "-a", list_manifest, dest_manifest], check=True)
def main(inputs, output, options):
images = parse_input(inputs)
@ -136,24 +157,25 @@ def main(inputs, output, options):
container_format = source_data["format"]
image_name = source_data["name"]
# We can't have special characters like ":" in the source names because containers/image
# treats them special, like e.g. /some/path:tag, so we make a symlink to the real name
# and pass the symlink name to skopeo to make it work with anything
with tempfile.TemporaryDirectory() as tmpdir:
linkname = os.path.join(tmpdir, "image")
os.symlink(source, linkname)
tmp_source = os.path.join(tmpdir, "image")
if container_format == "dir":
source = f"dir:{linkname}"
elif container_format == "oci-archive":
source = f"oci-archive:{linkname}"
if container_format == "dir" and image["manifest-list"]:
# copy the source container to the tmp source so we can merge the manifest into it
subprocess.run(["cp", "-a", "--reflink=auto", source, tmp_source], check=True)
merge_manifest(image["manifest-list"], tmp_source)
else:
# We can't have special characters like ":" in the source names because containers/image
# treats them special, like e.g. /some/path:tag, so we make a symlink to the real name
# and pass the symlink name to skopeo to make it work with anything
os.symlink(source, tmp_source)
if container_format not in ("dir", "oci-archive"):
raise RuntimeError(f"Unknown container format {container_format}")
source = f"{container_format}:{tmp_source}"
dest = f"containers-storage:[{storage_driver}@{output}{storage_root}+/run/containers/storage]{image_name}"
subprocess.run(["skopeo", "copy", source, dest],
check=True)
subprocess.run(["skopeo", "copy", source, dest], check=True)
if storage_driver == "overlay":
# Each time the overlay backend runs on an xfs fs it creates this file: