Esempio n. 1
0
    def create(self, request):
        """
        Create a Content Artifact
        """
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        artifacts = serializer.validated_data.pop('artifacts')
        content = serializer.save()

        for relative_path, artifact in artifacts.items():
            ca = ContentArtifact(artifact=artifact, content=content, relative_path=relative_path)
            ca.save()

        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Esempio n. 2
0
    def create(self, request):
        """
        Create a Content Artifact
        """
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        artifacts = serializer.validated_data.pop('artifacts')
        content = serializer.save()

        for relative_path, artifact in artifacts.items():
            ca = ContentArtifact(artifact=artifact, content=content, relative_path=relative_path)
            ca.save()

        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Esempio n. 3
0
    async def _match_and_stream(self, path, request):
        """
        Match the path and stream results either from the filesystem or by downloading new data.

        After deciding the client can access the distribution at ``path``, this function calls
        :meth:`BaseDistribution.content_handler`. If that function returns a not-None result,
        it is returned to the client.

        Then the publication linked to the Distribution is used to determine what content should
        be served. If ``path`` is a directory entry (i.e. not a file), the directory contents
        are served to the client. This method calls
        :meth:`BaseDistribution.content_handler_list_directory` to acquire any additional entries
        the Distribution's content_handler might serve in that directory. If there is an actifact
        to be served, it is served to the client.

        If there's no publication, the above paragraph is applied to the lastest repository linked
        to the matched Distribution.

        Finally, when nothing is served to client yet, we check if there is a remote for the
        Distribution. If so, the artifact is pulled from the remote and streamed to the client.

        Args:
            path (str): The path component of the URL.
            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.
        """
        distro = self._match_distribution(path)
        self._permit(request, distro)

        rel_path = path.lstrip("/")
        rel_path = rel_path[len(distro.base_path) :]
        rel_path = rel_path.lstrip("/")

        content_handler_result = distro.content_handler(rel_path)
        if content_handler_result is not None:
            return content_handler_result

        headers = self.response_headers(rel_path)

        publication = getattr(distro, "publication", None)

        if publication:
            if rel_path == "" or rel_path[-1] == "/":
                try:
                    index_path = "{}index.html".format(rel_path)
                    publication.published_artifact.get(relative_path=index_path)
                    rel_path = index_path
                    headers = self.response_headers(rel_path)
                except ObjectDoesNotExist:
                    dir_list = await self.list_directory(None, publication, rel_path)
                    dir_list.update(distro.content_handler_list_directory(rel_path))
                    return HTTPOk(
                        headers={"Content-Type": "text/html"}, body=self.render_html(dir_list)
                    )

            # published artifact
            try:
                pa = publication.published_artifact.get(relative_path=rel_path)
                ca = pa.content_artifact
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return self._serve_content_artifact(ca, headers)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca
                    )

            # pass-through
            if publication.pass_through:
                try:
                    ca = ContentArtifact.objects.get(
                        content__in=publication.repository_version.content, relative_path=rel_path
                    )
                except MultipleObjectsReturned:
                    log.error(
                        _("Multiple (pass-through) matches for {b}/{p}"),
                        {"b": distro.base_path, "p": rel_path},
                    )
                    raise
                except ObjectDoesNotExist:
                    pass
                else:
                    if ca.artifact:
                        return self._serve_content_artifact(ca, headers)
                    else:
                        return await self._stream_content_artifact(
                            request, StreamResponse(headers=headers), ca
                        )

        repo_version = getattr(distro, "repository_version", None)
        repository = getattr(distro, "repository", None)

        if repository or repo_version:
            if repository:
                repo_version = distro.repository.latest_version()

            if rel_path == "" or rel_path[-1] == "/":
                try:
                    index_path = "{}index.html".format(rel_path)
                    ContentArtifact.objects.get(
                        content__in=repo_version.content, relative_path=index_path
                    )
                    rel_path = index_path
                except ObjectDoesNotExist:
                    dir_list = await self.list_directory(repo_version, None, rel_path)
                    dir_list.update(distro.content_handler_list_directory(rel_path))
                    return HTTPOk(
                        headers={"Content-Type": "text/html"}, body=self.render_html(dir_list)
                    )

            try:
                ca = ContentArtifact.objects.get(
                    content__in=repo_version.content, relative_path=rel_path
                )
            except MultipleObjectsReturned:
                log.error(
                    _("Multiple (pass-through) matches for {b}/{p}"),
                    {"b": distro.base_path, "p": rel_path},
                )
                raise
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return self._serve_content_artifact(ca, headers)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca
                    )

        if distro.remote:
            remote = distro.remote.cast()
            try:
                url = remote.get_remote_artifact_url(rel_path)
                ra = RemoteArtifact.objects.get(remote=remote, url=url)
                ca = ra.content_artifact
                if ca.artifact:
                    return self._serve_content_artifact(ca, headers)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca
                    )
            except ObjectDoesNotExist:
                ca = ContentArtifact(relative_path=rel_path)
                ra = RemoteArtifact(remote=remote, url=url, content_artifact=ca)
                return await self._stream_remote_artifact(
                    request, StreamResponse(headers=headers), ra
                )

        raise PathNotResolved(path)
Esempio n. 4
0
    async def _match_and_stream(self, path, request):
        """
        Match the path and stream results either from the filesystem or by downloading new data.

        After deciding the client can access the distribution at ``path``, this function calls
        :meth:`Distribution.content_handler`. If that function returns a not-None result, it is
        returned to the client.

        Then the publication linked to the Distribution is used to determine what content should
        be served. If ``path`` is a directory entry (i.e. not a file), the directory contents
        are served to the client. This method calls
        :meth:`Distribution.content_handler_list_directory` to acquire any additional entries the
        Distribution's content_handler might serve in that directory. If there is an Artifact to be
        served, it is served to the client.

        If there's no publication, the above paragraph is applied to the latest repository linked
        to the matched Distribution.

        Finally, when nothing is served to client yet, we check if there is a remote for the
        Distribution. If so, the Artifact is pulled from the remote and streamed to the client.

        Args:
            path (str): The path component of the URL.
            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.
        """

        distro = await sync_to_async(self._match_distribution)(path)

        await sync_to_async(self._permit)(request, distro)

        rel_path = path.lstrip("/")
        rel_path = rel_path[len(distro.base_path):]
        rel_path = rel_path.lstrip("/")

        content_handler_result = await sync_to_async(distro.content_handler
                                                     )(rel_path)
        if content_handler_result is not None:
            return content_handler_result

        headers = self.response_headers(rel_path)

        repository = distro.repository
        publication = distro.publication
        repo_version = distro.repository_version

        if repository:

            def get_latest_publication_or_version_blocking():
                nonlocal repo_version
                nonlocal publication

                # Search for publication serving the closest latest version
                if not publication:
                    try:
                        versions = repository.versions.all()
                        publications = Publication.objects.filter(
                            repository_version__in=versions, complete=True)
                        publication = publications.select_related(
                            "repository_version").latest(
                                "repository_version", "pulp_created")
                        repo_version = publication.repository_version
                    except ObjectDoesNotExist:
                        pass

                if not repo_version:
                    repo_version = repository.latest_version()

            await sync_to_async(get_latest_publication_or_version_blocking)()

        if publication:
            if rel_path == "" or rel_path[-1] == "/":
                try:
                    index_path = "{}index.html".format(rel_path)

                    await sync_to_async(publication.published_artifact.get
                                        )(relative_path=index_path)

                    rel_path = index_path
                    headers = self.response_headers(rel_path)
                except ObjectDoesNotExist:
                    dir_list = await self.list_directory(
                        None, publication, rel_path)
                    dir_list.update(await sync_to_async(
                        distro.content_handler_list_directory)(rel_path))
                    return HTTPOk(headers={"Content-Type": "text/html"},
                                  body=self.render_html(dir_list))

            # published artifact
            try:

                def get_contentartifact_blocking():
                    return (publication.published_artifact.select_related(
                        "content_artifact",
                        "content_artifact__artifact",
                    ).get(relative_path=rel_path).content_artifact)

                ca = await sync_to_async(get_contentartifact_blocking)()
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return await self._serve_content_artifact(
                        ca, headers, request)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca)

            # pass-through
            if publication.pass_through:
                try:

                    def get_contentartifact_blocking():
                        ca = ContentArtifact.objects.select_related(
                            "artifact").get(
                                content__in=publication.repository_version.
                                content,
                                relative_path=rel_path,
                            )
                        return ca

                    ca = await sync_to_async(get_contentartifact_blocking)()
                except MultipleObjectsReturned:
                    log.error(
                        _("Multiple (pass-through) matches for {b}/{p}"),
                        {
                            "b": distro.base_path,
                            "p": rel_path
                        },
                    )
                    raise
                except ObjectDoesNotExist:
                    pass
                else:
                    if ca.artifact:
                        return await self._serve_content_artifact(
                            ca, headers, request)
                    else:
                        return await self._stream_content_artifact(
                            request, StreamResponse(headers=headers), ca)

        if repo_version and not publication and not distro.SERVE_FROM_PUBLICATION:
            if rel_path == "" or rel_path[-1] == "/":
                index_path = "{}index.html".format(rel_path)

                def contentartifact_exists_blocking():
                    return ContentArtifact.objects.filter(
                        content__in=repo_version.content,
                        relative_path=index_path).exists()

                contentartifact_exists = await sync_to_async(
                    contentartifact_exists_blocking)()
                if contentartifact_exists:
                    rel_path = index_path
                else:
                    dir_list = await self.list_directory(
                        repo_version, None, rel_path)
                    dir_list.update(await sync_to_async(
                        distro.content_handler_list_directory)(rel_path))
                    return HTTPOk(headers={"Content-Type": "text/html"},
                                  body=self.render_html(dir_list))

            try:

                def get_contentartifact_blocking():
                    ca = ContentArtifact.objects.select_related(
                        "artifact").get(content__in=repo_version.content,
                                        relative_path=rel_path)
                    return ca

                ca = await sync_to_async(get_contentartifact_blocking)()
            except MultipleObjectsReturned:
                log.error(
                    _("Multiple (pass-through) matches for {b}/{p}"),
                    {
                        "b": distro.base_path,
                        "p": rel_path
                    },
                )
                raise
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return await self._serve_content_artifact(
                        ca, headers, request)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca)

        if distro.remote:

            def cast_remote_blocking():
                return distro.remote.cast()

            remote = await sync_to_async(cast_remote_blocking)()

            try:
                url = remote.get_remote_artifact_url(rel_path, request=request)

                def get_remote_artifact_blocking():
                    ra = RemoteArtifact.objects.select_related(
                        "content_artifact",
                        "content_artifact__artifact",
                        "remote",
                    ).get(remote=remote, url=url)
                    return ra

                ra = await sync_to_async(get_remote_artifact_blocking)()
                ca = ra.content_artifact
                if ca.artifact:
                    return await self._serve_content_artifact(
                        ca, headers, request)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca)
            except ObjectDoesNotExist:
                ca = ContentArtifact(relative_path=rel_path)
                ra = RemoteArtifact(remote=remote,
                                    url=url,
                                    content_artifact=ca)
                return await self._stream_remote_artifact(
                    request, StreamResponse(headers=headers), ra)

        raise PathNotResolved(path)
Esempio n. 5
0
    async def _match_and_stream(self, path, request):
        """
        Match the path and stream results either from the filesystem or by downloading new data.

        Args:
            path (str): The path component of the URL.
            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.
        """
        distro = self._match_distribution(path)
        self._permit(request, distro)

        rel_path = path.lstrip('/')
        rel_path = rel_path[len(distro.base_path):]
        rel_path = rel_path.lstrip('/')

        headers = self.response_headers(rel_path)

        publication = getattr(distro, 'publication', None)

        if publication:
            if rel_path == '' or rel_path[-1] == '/':
                try:
                    index_path = '{}index.html'.format(rel_path)
                    publication.published_artifact.get(
                        relative_path=index_path)
                    rel_path = index_path
                except ObjectDoesNotExist:
                    dir_list = await self.list_directory(
                        None, publication, rel_path)
                    return HTTPOk(headers={"Content-Type": "text/html"},
                                  body=dir_list)

            # published artifact
            try:
                pa = publication.published_artifact.get(relative_path=rel_path)
                ca = pa.content_artifact
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return self._handle_file_response(ca.artifact.file,
                                                      headers)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca)

            # pass-through
            if publication.pass_through:
                try:
                    ca = ContentArtifact.objects.get(
                        content__in=publication.repository_version.content,
                        relative_path=rel_path)
                except MultipleObjectsReturned:
                    log.error(_('Multiple (pass-through) matches for {b}/{p}'),
                              {
                                  'b': distro.base_path,
                                  'p': rel_path,
                              })
                    raise
                except ObjectDoesNotExist:
                    pass
                else:
                    if ca.artifact:
                        return self._handle_file_response(
                            ca.artifact.file, headers)
                    else:
                        return await self._stream_content_artifact(
                            request, StreamResponse(headers=headers), ca)

        repo_version = getattr(distro, 'repository_version', None)
        repository = getattr(distro, 'repository', None)

        if repository or repo_version:
            if repository:
                repo_version = distro.repository.latest_version()

            if rel_path == '' or rel_path[-1] == '/':
                try:
                    index_path = '{}index.html'.format(rel_path)
                    ContentArtifact.objects.get(
                        content__in=repo_version.content,
                        relative_path=index_path)
                    rel_path = index_path
                except ObjectDoesNotExist:
                    dir_list = await self.list_directory(
                        repo_version, None, rel_path)
                    return HTTPOk(headers={"Content-Type": "text/html"},
                                  body=dir_list)

            try:
                ca = ContentArtifact.objects.get(
                    content__in=repo_version.content, relative_path=rel_path)
            except MultipleObjectsReturned:
                log.error(_('Multiple (pass-through) matches for {b}/{p}'), {
                    'b': distro.base_path,
                    'p': rel_path,
                })
                raise
            except ObjectDoesNotExist:
                pass
            else:
                return self._handle_file_response(ca.artifact.file, headers)

        if distro.remote:
            remote = distro.remote.cast()
            try:
                url = remote.get_remote_artifact_url(rel_path)
                ra = RemoteArtifact.objects.get(remote=remote, url=url)
                ca = ra.content_artifact
                if ca.artifact:
                    return self._handle_file_response(ca.artifact.file,
                                                      headers)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca)
            except ObjectDoesNotExist:
                ca = ContentArtifact(relative_path=rel_path)
                ra = RemoteArtifact(remote=remote,
                                    url=url,
                                    content_artifact=ca)
                return await self._stream_remote_artifact(
                    request, StreamResponse(headers=headers), ra)

        raise PathNotResolved(path)
Esempio n. 6
0
    async def _match_and_stream(self, path, request):
        """
        Match the path and stream results either from the filesystem or by downloading new data.

        Args:
            path (str): The path component of the URL.
            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.
        """
        distribution = Handler._match_distribution(path)
        self._permit(request, distribution)
        publication = distribution.publication
        remote = distribution.remote
        if not publication and not remote:
            raise PathNotResolved(path)
        rel_path = path.lstrip('/')
        rel_path = rel_path[len(distribution.base_path):]
        rel_path = rel_path.lstrip('/')

        if publication:
            # published artifact
            try:
                pa = publication.published_artifact.get(relative_path=rel_path)
                ca = pa.content_artifact
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return self._handle_file_response(ca.artifact.file)
                else:
                    return await self._stream_content_artifact(request, StreamResponse(), ca)

            # published metadata
            try:
                pm = publication.published_metadata.get(relative_path=rel_path)
            except ObjectDoesNotExist:
                pass
            else:
                return self._handle_file_response(pm.file)

            # pass-through
            if publication.pass_through:
                try:
                    ca = ContentArtifact.objects.get(
                        content__in=publication.repository_version.content,
                        relative_path=rel_path)
                except MultipleObjectsReturned:
                    log.error(
                        _('Multiple (pass-through) matches for {b}/{p}'),
                        {
                            'b': distribution.base_path,
                            'p': rel_path,
                        }
                    )
                    raise
                except ObjectDoesNotExist:
                    pass
                else:
                    if ca.artifact:
                        return self._handle_file_response(ca.artifact.file)
                    else:
                        return await self._stream_content_artifact(request, StreamResponse(), ca)
        if remote:
            remote = remote.cast()
            try:
                url = remote.get_remote_artifact_url(rel_path)
                ra = RemoteArtifact.objects.get(remote=remote, url=url)
                ca = ra.content_artifact
                if ca.artifact:
                    return self._handle_file_response(ca.artifact.file)
                else:
                    return await self._stream_content_artifact(request, StreamResponse(), ca)
            except ObjectDoesNotExist:
                ca = ContentArtifact(relative_path=rel_path)
                ra = RemoteArtifact(remote=remote, url=url, content_artifact=ca)
                return await self._stream_remote_artifact(request, StreamResponse(), ra)

        raise PathNotResolved(path)