Beispiel #1
0
    async def dispatch_converted_schema(tag, accepted_media_types, path):
        """
        Convert a manifest from the format schema 2 to the format schema 1.

        The format is converted on-the-go and created resources are not stored for further uses.
        The conversion is made after each request which does not accept the format for schema 2.

        Args:
            tag: A tag object which contains reference to tagged manifests and config blobs.
            accepted_media_types: Accepted media types declared in the accept header.
            path: A path of a repository.

        Raises:
            PathNotResolved: There was not found a valid conversion for the specified tag.

        Returns:
            :class:`aiohttp.web.StreamResponse` or :class:`aiohttp.web.Response`: The response
                streamed back to the client.

        """
        schema1_converter = Schema2toSchema1ConverterWrapper(
            tag, accepted_media_types, path)
        try:
            schema, converted, digest = schema1_converter.convert()
        except RuntimeError:
            raise PathNotResolved(tag.name)
        response_headers = {
            "Docker-Content-Digest": digest,
            "Content-Type": MEDIA_TYPE.MANIFEST_V1_SIGNED,
            "Docker-Distribution-API-Version": "registry/2.0",
        }
        if not converted:
            raise PathNotResolved(tag.name)

        return web.Response(text=schema, headers=response_headers)
Beispiel #2
0
    async def handle_universe(self, request):
        """
        Serve the '__universe__' metadata object at '/universe'.

        Since the '/universe' endpoint contains absolute URLs, translate
        the content of '__universe__' on the fly.
        """
        path = request.match_info["path"] + "/universe"
        distribution = Handler._match_distribution(path)
        Handler._permit(request, distribution)
        publication = distribution.publication
        if not publication:
            raise PathNotResolved(path)
        pa = publication.published_artifact.get(relative_path="__universe__")
        ca = pa.content_artifact
        try:
            with ca.artifact.file.open("rb") as u:
                content = u.read().decode("utf-8")
        except FileNotFoundError:
            return PathNotResolved(path)
        except PermissionError:
            return aiohttp.web_exceptions.HTTPForbidden
        content = replace_all_paths(
            content,
            self._get_content_base_url(request) + distribution.base_path)
        return aiohttp.web.Response(body=content,
                                    content_type="application/json",
                                    charset="utf-8")
Beispiel #3
0
    async def get_tag(self, request):
        """
        Match the path and stream either Manifest or ManifestList.

        Args:
            request(:class:`~aiohttp.web.Request`): The request to prepare a response for.

        Raises:
            PathNotResolved: The path could not be matched to a published file.
            PermissionError: When not permitted.

        Returns:
            :class:`aiohttp.web.StreamResponse` or :class:`aiohttp.web.FileResponse`: The response
                streamed back to the client.

        """
        path = request.match_info["path"]
        tag_name = request.match_info["tag_name"]
        distribution = self._match_distribution(path)
        self._permit(request, distribution)
        repository_version = distribution.get_repository_version()
        accepted_media_types = get_accepted_media_types(request.headers)

        try:
            tag = Tag.objects.get(pk__in=repository_version.content,
                                  name=tag_name)
        except ObjectDoesNotExist:
            raise PathNotResolved(tag_name)

        # we do not convert OCI to docker
        oci_mediatypes = [MEDIA_TYPE.MANIFEST_OCI, MEDIA_TYPE.INDEX_OCI]
        if (tag.tagged_manifest.media_type in oci_mediatypes and
                tag.tagged_manifest.media_type not in accepted_media_types):
            log.warn(
                "OCI format found, but the client only accepts {accepted_media_types}."
                .format(accepted_media_types=accepted_media_types))
            raise PathNotResolved(tag_name)

        # return schema1 (even in case only oci is requested)
        if tag.tagged_manifest.media_type == MEDIA_TYPE.MANIFEST_V1:
            return_media_type = MEDIA_TYPE.MANIFEST_V1_SIGNED
            response_headers = {
                "Content-Type": return_media_type,
                "Docker-Content-Digest": tag.tagged_manifest.digest,
            }
            return await Registry.dispatch_tag(tag, response_headers)

        # return what was found in case media_type is accepted header (docker, oci)
        if tag.tagged_manifest.media_type in accepted_media_types:
            return_media_type = tag.tagged_manifest.media_type
            response_headers = {
                "Content-Type": return_media_type,
                "Docker-Content-Digest": tag.tagged_manifest.digest,
            }
            return await Registry.dispatch_tag(tag, response_headers)

        # convert if necessary
        return await Registry.dispatch_converted_schema(
            tag, accepted_media_types, path)
Beispiel #4
0
    async def get_tag(self, request):
        """
        Match the path and stream either Manifest or ManifestList.

        Args:
            request(:class:`~aiohttp.web.Request`): The request to prepare a response for.

        Raises:
            PathNotResolved: The path could not be matched to a published file.
            PermissionError: When not permitted.

        Returns:
            :class:`aiohttp.web.StreamResponse` or :class:`aiohttp.web.FileResponse`: The response
                streamed back to the client.

        """
        path = request.match_info['path']
        tag_name = request.match_info['tag_name']
        distribution = self._match_distribution(path)
        repository_version = distribution.get_repository_version()
        accepted_media_types = await Registry.get_accepted_media_types(request)

        try:
            tag = Tag.objects.get(
                pk__in=repository_version.content,
                name=tag_name,
            )
        except ObjectDoesNotExist:
            raise PathNotResolved(tag_name)

        if tag.tagged_manifest.media_type == MEDIA_TYPE.MANIFEST_V1:
            return_media_type = MEDIA_TYPE.MANIFEST_V1_SIGNED

        elif tag.tagged_manifest.media_type in accepted_media_types:
            return_media_type = tag.tagged_manifest.media_type
        else:
            # This is where we could eventually support on-the-fly conversion to schema 1.
            log.warn(
                "The requested tag `{name}` is of type {media_type}, but the client only accepts "
                "{accepted_media_types}.".format(
                    name=tag.name,
                    media_type=tag.tagged_manifest.media_type,
                    accepted_media_types=accepted_media_types))
            raise PathNotResolved(tag_name)

        response_headers = {
            'Content-Type': return_media_type,
            'Docker-Content-Digest': tag.tagged_manifest.digest
        }
        return await Registry.dispatch_tag(tag, response_headers)
Beispiel #5
0
 async def get_by_digest(self, request):
     """
     Return a response to the "GET" action.
     """
     path = request.match_info['path']
     digest = "sha256:{digest}".format(digest=request.match_info['digest'])
     distribution = self._match_distribution(path)
     repository_version = distribution.get_repository_version()
     log.info(digest)
     try:
         ca = ContentArtifact.objects.get(
             content__in=repository_version.content, relative_path=digest)
         headers = {
             'Content-Type': ca.content.cast().media_type,
             'Docker-Content-Digest': ca.content.cast().digest
         }
     except ObjectDoesNotExist:
         raise PathNotResolved(path)
     else:
         artifact = ca.artifact
         if artifact:
             return await Registry._dispatch(
                 os.path.join(settings.MEDIA_ROOT, artifact.file.name),
                 headers)
         else:
             return await self._stream_content_artifact(
                 request, web.StreamResponse(), ca)
Beispiel #6
0
    async def get_by_digest(self, request):
        """
        Return a response to the "GET" action.
        """

        path = request.match_info["path"]
        digest = "sha256:{digest}".format(digest=request.match_info["digest"])
        distribution = self._match_distribution(path)
        self._permit(request, distribution)
        repository_version = distribution.get_repository_version()
        try:
            ca = ContentArtifact.objects.get(
                content__in=repository_version.content, relative_path=digest)
            headers = {
                "Content-Type": ca.content.cast().media_type,
                "Docker-Content-Digest": ca.content.cast().digest,
            }
        except ObjectDoesNotExist:
            raise PathNotResolved(path)
        else:
            artifact = ca.artifact
            if artifact:
                return await Registry._dispatch(artifact.file, headers)
            else:
                return await self._stream_content_artifact(
                    request, web.StreamResponse(), ca)
Beispiel #7
0
    async def get_by_digest(self, request):
        """
        Return a response to the "GET" action.
        """

        path = request.match_info["path"]
        digest = "sha256:{digest}".format(digest=request.match_info["digest"])
        distribution = await sync_to_async(self._match_distribution)(path)
        await sync_to_async(self._permit)(request, distribution)
        repository_version = await sync_to_async(
            distribution.get_repository_version)()
        if not repository_version:
            raise PathNotResolved(path)
        if digest == EMPTY_BLOB:
            return await Registry._empty_blob()
        try:
            ca = await sync_to_async(
                ContentArtifact.objects.select_related("artifact",
                                                       "content").get
            )(
                content__in=await sync_to_async(repository_version.get_content)
                (),
                relative_path=digest,
            )
            ca_content = await sync_to_async(ca.content.cast)()
            if isinstance(ca_content, Blob):
                media_type = BLOB_CONTENT_TYPE
            else:
                media_type = ca_content.media_type
            headers = {
                "Content-Type": media_type,
                "Docker-Content-Digest": ca_content.digest,
            }
        except ObjectDoesNotExist:
            raise PathNotResolved(path)
        else:
            artifact = ca.artifact
            if artifact:
                return await Registry._dispatch(artifact.file, headers)
            else:
                return await self._stream_content_artifact(
                    request, web.StreamResponse(), ca)
Beispiel #8
0
    async def get_tag(self, request):
        """
        Match the path and stream either Manifest or ManifestList.

        Args:
            request(:class:`~aiohttp.web.Request`): The request to prepare a response for.

        Raises:
            PathNotResolved: The path could not be matched to a published file.
            PermissionError: When not permitted.

        Returns:
            :class:`aiohttp.web.StreamResponse` or :class:`aiohttp.web.FileResponse`: The response
                streamed back to the client.

        """
        Registry.verify_token(request, 'pull')

        path = request.match_info['path']
        tag_name = request.match_info['tag_name']
        distribution = self._match_distribution(path)
        repository_version = distribution.get_repository_version()
        accepted_media_types = await Registry.get_accepted_media_types(request)

        try:
            tag = Tag.objects.get(
                pk__in=repository_version.content,
                name=tag_name,
            )
        except ObjectDoesNotExist:
            raise PathNotResolved(tag_name)

        if tag.tagged_manifest.media_type == MEDIA_TYPE.MANIFEST_V1:
            return_media_type = MEDIA_TYPE.MANIFEST_V1_SIGNED
            response_headers = {
                'Content-Type': return_media_type,
                'Docker-Content-Digest': tag.tagged_manifest.digest
            }
            return await Registry.dispatch_tag(tag, response_headers)

        if tag.tagged_manifest.media_type in accepted_media_types:
            return_media_type = tag.tagged_manifest.media_type
            response_headers = {
                'Content-Type': return_media_type,
                'Docker-Content-Digest': tag.tagged_manifest.digest
            }
            return await Registry.dispatch_tag(tag, response_headers)

        return await Registry.dispatch_converted_schema(
            tag, accepted_media_types, path)