Example #1
0
    async def run(self):
        """
        Build and emit `DeclarativeContent` from the Manifest data.

        If a cookbook specifier is set in the remote, cookbooks are filtered
        using this specifier.

        """
        with ProgressBar(message="Downloading Metadata", total=1) as pb:
            downloader = self.remote.get_downloader(url=urljoin(self.remote.url + "/", "universe"))
            result = await downloader.run()
            pb.increment()

        cookbook_names = self.remote.specifier_cookbook_names()

        with ProgressBar(message="Parsing Metadata") as pb:
            universe = Universe(result.path)
            for entry in universe.read():
                if cookbook_names and entry.name not in cookbook_names:
                    continue
                cookbook = CookbookPackageContent(
                    name=entry.name, version=entry.version, dependencies=entry.dependencies
                )
                artifact = Artifact()
                da = DeclarativeArtifact(
                    artifact=artifact,
                    url=entry.download_url,
                    relative_path=cookbook.relative_path(),
                    remote=self.remote,
                    deferred_download=not self.download_artifacts,
                )
                dc = DeclarativeContent(content=cookbook, d_artifacts=[da])
                pb.increment()
                await self.put(dc)
    def test_content_associated_using_repo_key(self):
        stage = QueryExistingRepoContentAndArtifacts(
            new_version=self.new_version_all_content())

        # c1: Existing content unit with Artifact
        c1 = CookbookPackageContent(name="c1",
                                    version="1.0.0",
                                    dependencies={})
        # c2: content unit does not exist in DB
        c2 = CookbookPackageContent(name="c2",
                                    version="1.0.0",
                                    dependencies={})
        # c3: content unit does exist, has a content_artifact association,
        # but no artifact (i.e. is a non-immediate content unit)
        c3 = CookbookPackageContent(name="c3",
                                    version="1.0.0",
                                    dependencies={})

        d_c1_d_a1 = DeclarativeArtifact(
            artifact=Artifact(),
            url="http://a1",
            relative_path=c1.relative_path(),
            remote=self.remote,
        )
        d_c2_d_a2 = DeclarativeArtifact(
            artifact=Artifact(),
            url="http://a2",
            relative_path=c2.relative_path(),
            remote=self.remote,
        )
        d_c3_d_a3 = DeclarativeArtifact(
            artifact=Artifact(),
            url="http://a3",
            relative_path=c3.relative_path(),
            remote=self.remote,
        )

        batch = [
            DeclarativeContent(content=c1, d_artifacts=[d_c1_d_a1]),
            DeclarativeContent(content=c2, d_artifacts=[d_c2_d_a2]),
            DeclarativeContent(content=c3, d_artifacts=[d_c3_d_a3]),
        ]

        stage._process_batch(batch)

        self.assertEqual(batch[0].content.content_id, "1")
        self.assertEqual(batch[0].content.pk, self.c1.pk)
        self.assertEqual(batch[0].d_artifacts[0].artifact.pk, self.a1.pk)

        self.assertIsNone(batch[1].content.pk)
        self.assertTrue(batch[1].d_artifacts[0].artifact._state.adding)

        self.assertEqual(batch[2].content.pk, self.c3.pk)
        self.assertTrue(batch[2].d_artifacts[0].artifact._state.adding)
    def test_content_associated_using_repo_key(self):
        dc_c1 = CookbookPackageContent(name="c1",
                                       version="1.0.0",
                                       dependencies={})
        dc_c2 = CookbookPackageContent(name="c2",
                                       version="1.0.0",
                                       dependencies={})

        batch = [
            DeclarativeContent(content=dc_c1, d_artifacts=[]),
            DeclarativeContent(content=dc_c2, d_artifacts=[]),
        ]
        stage = QueryExistingContentUnits(
            new_version=self.new_version_all_content())
        stage._process_batch(batch)
        self.assertEqual(batch[0].content.content_id, "1")
        self.assertEqual(batch[0].content.pk, self.c1.pk)
        self.assertIsNone(batch[1].content.pk)
    def test_content_associated_using_natural_key(self):
        dc_c1 = CookbookPackageContent(name="c1",
                                       version="1.0.0",
                                       content_id="1",
                                       dependencies={})
        dc_c1_other = CookbookPackageContent(name="c1",
                                             version="1.0.0",
                                             content_id="other",
                                             dependencies={})

        batch = [
            DeclarativeContent(content=dc_c1, d_artifacts=[]),
            DeclarativeContent(content=dc_c1_other, d_artifacts=[]),
        ]
        stage = QueryExistingContentUnits()
        stage._process_batch(batch)
        self.assertEqual(batch[0].content.content_id, "1")
        self.assertEqual(batch[0].content.pk, self.c1.pk)
        self.assertIsNone(batch[1].content.pk)
Example #5
0
    async def __call__(self, in_q, out_q):
        """
        Build and emit `DeclarativeContent` from the Manifest data.

        If a cookbook specifier is set in the remote, cookbooks are filtered
        using this specifier.

        Args: in_q (asyncio.Queue): Unused because the first stage doesn't read
            from an input queue. out_q (asyncio.Queue): The out_q to send
            `DeclarativeContent` objects to

        """
        with ProgressBar(message='Downloading Metadata', total=1) as pb:
            downloader = self.remote.get_downloader(
                url=urljoin(self.remote.url + '/', 'universe'))
            result = await downloader.run()
            pb.increment()

        cookbook_names = self.remote.specifier_cookbook_names()

        with ProgressBar(message='Parsing Metadata') as pb:
            universe = Universe(result.path)
            for entry in universe.read():
                if cookbook_names and entry.name not in cookbook_names:
                    continue
                cookbook = CookbookPackageContent(
                    name=entry.name,
                    version=entry.version,
                    dependencies=entry.dependencies)
                artifact = Artifact()
                da = DeclarativeArtifact(artifact, entry.download_url,
                                         cookbook.relative_path(), self.remote)
                dc = DeclarativeContent(content=cookbook, d_artifacts=[da])
                pb.increment()
                await out_q.put(dc)
        await out_q.put(None)
Example #6
0
def check_repo_version_constraint(publication):
    """
    Ensure that repo version to publish fulfills repo_key_fields() uniqueness.

    Raises:
        ValueError: When constraint is violated

    """
    fields = CookbookPackageContent.repo_key_fields()
    qs_content = CookbookPackageContent.objects.filter(
        pk__in=publication.repository_version.content)
    qs = qs_content.values(*fields).annotate(num_cookbooks=Count("pk")).filter(
        num_cookbooks__gt=1)
    duplicates = [f"{res['name']} {res['version']}" for res in qs]
    if duplicates:
        raise ValueError(
            f"Publication would contain multiple versions of cookbooks: {', '.join(duplicates)}"
        )
    def test_existing_content_is_ok(self):
        # dc_c1 is a duplicate of c1 existing in the DB
        dc_c1 = CookbookPackageContent(name="c1",
                                       version="1.0.0",
                                       content_id="1",
                                       dependencies={})
        c2 = CookbookPackageContent.objects.create(name="c2",
                                                   version="1.0.0",
                                                   content_id="2",
                                                   dependencies={})

        batch = [
            DeclarativeContent(content=dc_c1, d_artifacts=[]),
            DeclarativeContent(content=c2, d_artifacts=[]),
        ]
        stage = QueryExistingContentUnits()
        stage._process_batch(batch)
        self.assertEqual(batch[0].content.pk, self.c1.pk)
        self.assertEqual(batch[1].content.pk, c2.pk)
Example #8
0
    def deferred_validate(self, data):
        """Validate that the artifact is a cookbook and extract it's meta-data."""
        data = super().deferred_validate(data)

        try:
            metadata = CookbookMetadata.from_cookbook_file(
                fileobj=data["artifact"].file, name=data["name"])
        except FileNotFoundError:
            raise serializers.ValidationError(
                detail={
                    "artifact": _("No metadata.json found in cookbook tar")
                })

        try:
            if data["version"] != metadata.version:
                raise serializers.ValidationError(
                    detail={
                        "version":
                        _("version does not correspond to version in cookbook tar"
                          )
                    })
        except KeyError:
            pass
        data["version"] = metadata.version
        data["dependencies"] = metadata.dependencies
        data["content_id_type"] = self.Meta.model.SHA256
        data["content_id"] = data["artifact"].sha256
        data["relative_path"] = CookbookPackageContent.relative_path_from_data(
            data)

        content = self.Meta.model.objects.filter(
            content_id_type=data["content_id_type"],
            content_id=data["content_id"],
            name=data["name"],
            version=data["version"],
        )
        if content.exists():
            raise serializers.ValidationError(
                _("There is already a cookbook package '{name}-{version}'"
                  " with sha256 '{content_id}'.").format(**data))

        return data