Skip to content

Commit

Permalink
Merge pull request #1069 from adrianabedon/fix1038
Browse files Browse the repository at this point in the history
Create architectures and components when creating a release
  • Loading branch information
quba42 authored Jun 5, 2024
2 parents 32b604a + 2172893 commit 85bc12a
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGES/1038.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Added the ``architectures`` and ``components`` parameters to the release API in order to create releases with accompanying architectures and components in a single API call.
Also added retrieve functionality to the release API.
3 changes: 3 additions & 0 deletions CHANGES/1038.removal
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
When creating a release, the API now returns a task instead of the release being created.
In addition, attempting to create a release that already exists will no longer return an error.
Instead the resulting task will simply list the existing content in its ``created_resources`` field.
12 changes: 5 additions & 7 deletions docs/_scripts/structured_repo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ REPO_HREF=$(http ${PULP_URL}/pulp/api/v3/repositories/deb/apt/ name=${NAME} | jq
TASK_HREF=$(http ${PULP_URL}/pulp/api/v3/distributions/deb/apt/ name=${NAME} base_path=${NAME} repository=${REPO_HREF} | jq -r .task)
wait_until_task_finished ${PULP_URL}${TASK_HREF}

# upload the package to create Package, ReleaseComponent, PackageReleaseComponent, and Architecture content and add it to the repo in a single action
TASK_HREF=$(http --form ${PULP_URL}/pulp/api/v3/content/deb/packages/ file@frigg_1.0_ppc64.deb repository=${REPO_HREF} distribution=${DIST} component=${COMP} | jq -r .task)
# Create a Release, ReleaseArchitecture, and ReleaseComponent content to set various release file fields and add them to the repo in a single action
TASK_HREF=$(http ${PULP_URL}/pulp/api/v3/content/deb/releases/ repository=${REPO_HREF} distribution=${DIST} ${RELEASE_FILE_FIELDS[@]} architectures:='["amd64", "ppc64"]' components:=['"'${COMP}'"'] | jq -r .task)
wait_until_task_finished ${PULP_URL}${TASK_HREF}

# Also create a Release content to set various release file fields
RELEASE_HREF=$(http ${PULP_URL}/pulp/api/v3/content/deb/releases/ distribution=${DIST} ${RELEASE_FILE_FIELDS[@]} | jq -r .pulp_href)

# add our content to the repository
TASK_HREF=$(http ${PULP_URL}${REPO_HREF}modify/ add_content_units:="[\"${RELEASE_HREF}\"]" | jq -r .task)
# upload the package to create Package and PackageReleaseComponent content and add it to the repo in a single action
# the ReleaseComponent and ReleaseArchitecture were created in the previous step but could have been created in this step
TASK_HREF=$(http --form ${PULP_URL}/pulp/api/v3/content/deb/packages/ file@frigg_1.0_ppc64.deb repository=${REPO_HREF} distribution=${DIST} component=${COMP} | jq -r .task)
wait_until_task_finished ${PULP_URL}${TASK_HREF}

# publish our repo
Expand Down
72 changes: 70 additions & 2 deletions pulp_deb/app/serializers/content_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@

from debian import deb822, debfile

from rest_framework.serializers import CharField, DictField, Field, ValidationError, Serializer
from pulpcore.plugin.models import Artifact, CreatedResource, RemoteArtifact
from rest_framework.serializers import (
CharField,
DictField,
ListField,
Field,
ValidationError,
Serializer,
)
from pulpcore.plugin.models import Artifact, Content, CreatedResource, RemoteArtifact
from pulpcore.plugin.serializers import (
ContentChecksumSerializer,
MultipleArtifactContentSerializer,
Expand Down Expand Up @@ -721,13 +728,72 @@ class ReleaseSerializer(NoArtifactContentSerializer):
A Serializer for Release.
"""

def get_unique_together_validators(self):
"""
We do not want UniqueTogetherValidator since we have retrieve logic!
"""
return []

codename = CharField()
suite = CharField()
distribution = CharField()
version = NullableCharField(required=False, allow_null=True, default=None)
origin = NullableCharField(required=False, allow_null=True, default=None)
label = NullableCharField(required=False, allow_null=True, default=None)
description = NullableCharField(required=False, allow_null=True, default=None)
architectures = ListField(child=CharField(), required=False)
components = ListField(child=CharField(), required=False)

@staticmethod
def _get_or_create_content_pk(model, **data):
content, created = model.objects.get_or_create(**data)
CreatedResource(content_object=content).save()
if not created:
content.touch()
return content.pk

def retrieve(self, validated_data):
"""
If the Release already exists, retrieve it!
"""
return Release.objects.filter(
codename=validated_data["codename"],
suite=validated_data["suite"],
distribution=validated_data["distribution"],
version=validated_data.get("version", NULL_VALUE),
origin=validated_data.get("origin", NULL_VALUE),
label=validated_data.get("label", NULL_VALUE),
description=validated_data.get("description", NULL_VALUE),
).first()

def create(self, validated_data):
architectures = validated_data.pop("architectures", [])
components = validated_data.pop("components", [])
repository = validated_data.pop("repository", None)

content_pks = []

release = super().create(validated_data)
content_pks.append(release.pk)

for arch in architectures:
architecture_pk = self._get_or_create_content_pk(
ReleaseArchitecture, distribution=release.distribution, architecture=arch
)
content_pks.append(architecture_pk)

for comp in components:
component_pk = self._get_or_create_content_pk(
ReleaseComponent, distribution=release.distribution, component=comp
)
content_pks.append(component_pk)

if repository:
repository.cast()
with repository.new_version() as new_version:
new_version.add_content(Content.objects.filter(pk__in=content_pks))

return release

class Meta(NoArtifactContentSerializer.Meta):
model = Release
Expand All @@ -739,6 +805,8 @@ class Meta(NoArtifactContentSerializer.Meta):
"origin",
"label",
"description",
"architectures",
"components",
)


Expand Down
2 changes: 1 addition & 1 deletion pulp_deb/app/viewsets/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ class Meta:
fields = ["codename", "suite", "distribution", "version", "label", "origin"]


class ReleaseViewSet(ContentViewSet):
class ReleaseViewSet(NoArtifactContentViewSet):
# The doc string is a top level element of the user facing REST API documentation:
"""
The Release contains release file fields, that are not relevant to the APT repo structure.
Expand Down
58 changes: 58 additions & 0 deletions pulp_deb/tests/functional/api/test_crud_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,61 @@ def test_release_architecture_upload(

assert repository.latest_version_href.endswith("/1/")
assert architecture.pulp_href == architecture2.pulp_href


def test_release_comp_arch_upload(
deb_get_repository_by_href,
deb_repository_factory,
deb_release_factory,
apt_release_api,
apt_release_component_api,
apt_release_architecture_api,
):
"""Test creating a Release with Components and Architectures directly in a repository."""
repository = deb_repository_factory()
assert repository.latest_version_href.endswith("/0/")

distribution = str(uuid4())
architectures = [str(uuid4()), str(uuid4())]
components = [str(uuid4()), str(uuid4())]

release_attr = {
"repository": repository.pulp_href,
"distribution": distribution,
"codename": distribution,
"suite": distribution,
"architectures": architectures,
"components": components,
}

release = deb_release_factory(**release_attr)
repository = deb_get_repository_by_href(repository.pulp_href)
assert repository.latest_version_href.endswith("/1/")

repo_version_releases = apt_release_api.list(repository_version=repository.latest_version_href)
assert len(repo_version_releases.results) == 1
assert repo_version_releases.results[0].pulp_href == release.pulp_href

repo_version_components = apt_release_component_api.list(
repository_version=repository.latest_version_href
)

assert len(repo_version_components.results) == 2
for component in repo_version_components.results:
assert component.distribution == distribution
assert component.component in components

repo_version_architectures = apt_release_architecture_api.list(
repository_version=repository.latest_version_href
)

assert len(repo_version_architectures.results) == 2
for architecture in repo_version_architectures.results:
assert architecture.distribution == distribution
assert architecture.architecture in architectures

release2 = deb_release_factory(**release_attr)
repository = deb_get_repository_by_href(repository.pulp_href)

assert repository.latest_version_href.endswith("/1/")
assert release.pulp_href == release2.pulp_href
18 changes: 18 additions & 0 deletions pulp_deb/tests/functional/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
Copy,
DebAptPublication,
DebCopyApi,
DebRelease,
DebReleaseArchitecture,
DebReleaseComponent,
DebSourcePackageReleaseComponent,
Expand Down Expand Up @@ -163,6 +164,23 @@ def _deb_source_release_component_factory(source_package, release_component, **k
return _deb_source_release_component_factory


@pytest.fixture(scope="class")
def deb_release_factory(apt_release_api, gen_object_with_cleanup):
"""Fixture that generates deb release with cleanup."""

def _deb_release_factory(codename, suite, distribution, **kwargs):
"""Create a deb release.
:returns: The created release.
"""
release_object = DebRelease(
codename=codename, suite=suite, distribution=distribution, **kwargs
)
return gen_object_with_cleanup(apt_release_api, release_object)

return _deb_release_factory


@pytest.fixture(scope="class")
def deb_release_component_factory(apt_release_component_api, gen_object_with_cleanup):
"""Fixture that generates deb package with cleanup."""
Expand Down

0 comments on commit 85bc12a

Please sign in to comment.