Skip to content

Commit

Permalink
sources/skopeo: fetch index manifest for dir
Browse files Browse the repository at this point in the history
When the format is specified as "dir", copy the manifest specified in
the source digest and merge it into the final image directory.  The
effect of this is that the digest of the final image in the container
registry will match the manifest digest.  This enables users to specify
an image's manifest digest or a multi-image manifest list digest which
will be preserved in the container store on the final OS and allow them
to run the container using the same digest that was specified in the
input.

This may become a feature of Skopeo (or other tooling) in the future.
See containers/skopeo#1935
  • Loading branch information
achilleas-k committed Mar 10, 2023
1 parent 64417e8 commit f9e1438
Showing 1 changed file with 23 additions and 2 deletions.
25 changes: 23 additions & 2 deletions sources/org.osbuild.skopeo
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ class SkopeoSource(sources.SourceService):
os.chmod(archive_dir, 0o755)

source = f"docker://{imagename}@{digest}"

archive_name = containers.archive_name(archive_format)
destination = f"{archive_format}:{archive_dir}/{archive_name}"
dest_path = os.path.join(archive_dir, archive_name)
destination = f"{archive_format}:{dest_path}"

extra_args = []

Expand All @@ -119,6 +119,10 @@ class SkopeoSource(sources.SourceService):
raise RuntimeError(
f"Downloaded image {imagename}@{digest} has a id of {downloaded_id}, but expected {image_id}")

if archive_format == "dir":
# fetch the manifest and merge it into the archive
self.merge_manifest(source, dest_path)

# Atomically move download archive into place on successful download
with ctx.suppress_oserror(errno.ENOTEMPTY, errno.EEXIST):
os.makedirs(f"{self.cache}/{image_id}", exist_ok=True)
Expand All @@ -128,6 +132,23 @@ class SkopeoSource(sources.SourceService):
archive_name = containers.archive_name(desc["image"].get("format", "docker-archive"))
return os.path.exists(f"{self.cache}/{checksum}/{archive_name}")

def merge_manifest(self, source, destination):
with tempfile.TemporaryDirectory(prefix="tmp-download-", dir=self.cache) as indexdir:
# download the manifest(s) to a temporary directory
subprocess.run(["skopeo", "copy", "--multi-arch=index-only", source, f"dir:{indexdir}"],
encoding="utf-8", check=True)

# calculate the checksum of the manifest of the container image in the destination
manifest_path = os.path.join(destination, "manifest.json")
with open(manifest_path, encoding="utf-8") as manifest:
manifest_checksum = hashlib.sha256(manifest.read().encode()).hexdigest()

# rename the manifest to its checksum
os.rename(manifest_path, os.path.join(destination, manifest_checksum + ".manifest.json"))

# move the index manifest into the destination
os.rename(os.path.join(indexdir, "manifest.json"), manifest_path)


def main():
service = SkopeoSource.from_args(sys.argv[1:])
Expand Down

0 comments on commit f9e1438

Please sign in to comment.