# This file implements the OCI Registry API
# https://github.com/opencontainers/distribution-spec/blob/main/spec.md#api
-import itertools
+import hashlib
import json
import tornado.web
"""
A base handler for any registry stuff
"""
+ def hash(self, data):
+ if isinstance(data, str):
+ data = data.encode()
+
+ h = hashlib.new("sha256")
+ h.update(data)
+
+ return "sha256:%s" % h.hexdigest()
+
async def get_blob(self, distro, digest):
"""
Returns a blob if we have it.
pass
+# Docker is pulling the same content more than once (god knows why). After the
+# ManifestLabelHandler has responded, the client will ask for the same file again
+# by its digest. Therefore we simply cache them and will send them again upon request.
+CACHE = {}
+
class ManifestLabelHandler(BaseHandler):
async def head(self, *args, **kwargs):
return await self.get(*args, **kwargs, send_body=False)
self.set_header("Content-Type", "application/vnd.oci.image.index.v1+json")
self.set_header("Content-Length", len(index))
+ # Hash and cache
+ CACHE[self.hash(index)] = index
+
# Send the response
if send_body:
self.finish(index)
return await self.get(*args, **kwargs, send_body=False)
async def get(self, distro_slug, digest, send_body=True):
+ # If we have this item in the cache, we serve it straight away
+ if digest in CACHE:
+ return self._get_cached_manifest(digest, send_body=send_body)
+
# Fetch the distribution
distro = await self.backend.distros.get_by_slug(distro_slug)
if not distro:
if send_body:
await self.stream_blob(blob)
+ def _get_cached_manifest(self, digest, send_body=True):
+ try:
+ manifest = CACHE[digest]
+ except KeyError:
+ raise ManifestUnknownError
+
+ # Set Content-Type
+ self.set_header("Content-Type", "application/vnd.oci.image.index.v1+json")
+ self.set_header("Content-Length", len(index))
+
+ if send_body:
+ self.finish(manifest)
+
class BlobHandler(BaseHandler):
async def head(self, *args, **kwargs):