Skip to content

Commit

Permalink
sources/skopeo: fetch index manifest for container
Browse files Browse the repository at this point in the history
With the local format being `dir` now, we can copy the manifest
specified in the source digest.  We can then merge the list manifest
into the image directory when requested by the user.  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.  Then, they can 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 14, 2023
1 parent 56ac5b8 commit c1be7a1
Showing 1 changed file with 28 additions and 4 deletions.
32 changes: 28 additions & 4 deletions sources/org.osbuild.skopeo
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ Buildhost commands used: `skopeo`.

import errno
import hashlib
import json
import os
import subprocess
import sys
import tempfile

from osbuild import sources
from osbuild.util import ctx
from osbuild.util import containers, ctx

SCHEMA = """
"additionalProperties": false,
Expand Down Expand Up @@ -86,6 +87,8 @@ class SkopeoSource(sources.SourceService):
# direct serialisation of the registry data.
dir_name = "image"
destination = f"dir:{archive_dir}/{dir_name}"
image_path = os.path.join(archive_dir, dir_name)
destination = f"dir:{image_path}"

extra_args = []
if not tls_verify:
Expand All @@ -104,13 +107,34 @@ class SkopeoSource(sources.SourceService):
raise RuntimeError(
f"Downloaded image {imagename}@{digest} has a id of {downloaded_id}, but expected {image_id}")

# Atomically move download archive into place on successful download
# Move image directory into place on successful download
with ctx.suppress_oserror(errno.ENOTEMPTY, errno.EEXIST):
os.makedirs(f"{self.cache}/{image_id}", exist_ok=True)
os.rename(f"{archive_dir}/{dir_name}", f"{self.cache}/{image_id}/{dir_name}")

def exists(self, checksum, _desc):
return os.path.exists(f"{self.cache}/{checksum}/image")
if self.is_manifest_list(source):
# the digest points to a manifest-list: download it and store it separately in the archive_dir, next to
# the image directory
manifest_path = os.path.join(archive_dir, digest)
subprocess.run(["skopeo", "copy", "--multi-arch=index-only", *extra_args,
source, f"dir:{manifest_path}"],
encoding="utf-8", check=True)

# Move manifest directory into place on successful download
with ctx.suppress_oserror(errno.ENOTEMPTY, errno.EEXIST):
os.rename(f"{archive_dir}/{digest}", f"{self.cache}/{image_id}/{digest}")

def exists(self, checksum, desc):
digest = desc["image"]["digest"]
# check that both the image and the digest exist
return (os.path.exists(f"{self.cache}/{checksum}/image") and
os.path.exists(f"{self.cache}/{checksum}/{digest}"))

@staticmethod
def is_manifest_list(source):
"""Inspect the manifest at the source and determine if it's a multi-image manifest-list."""
res = subprocess.check_output(["skopeo", "inspect", "--raw", source])
return containers.is_manifest_list(json.loads(res))


def main():
Expand Down

0 comments on commit c1be7a1

Please sign in to comment.