Skip to content

Commit

Permalink
Added upload support
Browse files Browse the repository at this point in the history
closes: #52
  • Loading branch information
dkliban committed Feb 27, 2023
1 parent 68e0d44 commit d7b03bb
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 41 deletions.
1 change: 1 addition & 0 deletions CHANGES/52.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added ability to upload Maven Artifacts to repositories.
1 change: 1 addition & 0 deletions docs/workflows/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ set is the hostname and port::
:maxdepth: 2

cache
upload
49 changes: 49 additions & 0 deletions docs/workflows/upload.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
Upload a Jar to a Maven Repository
==================================

Create a maven Repository for the

Create a Maven Repository
-------------------------

``$ http POST http://localhost:24817/pulp/api/v3/repositories/maven/maven/ name='my-snapshot-repository'``

Create a Maven Distribution for the Maven Repository
----------------------------------------------------

``$ http POST http://localhost:24817/pulp/api/v3/distributions/maven/maven/ name='my-snapshot-repository' base_path='my/local/snapshots' repository=$REPO_HREF``


.. code:: json
{
"pulp_href": "/pulp/api/v3/distributions/67baa17e-0a9f-4302-b04a-dbf324d139de/"
}
Upload a Jar to the Repository
------------------------------

``$ http --form POST http://localhost:24817/pulp/api/v3/content/maven/artifact/ group_id='org.openapitools' artifact_id='openapi-generator-cli' version='6.4.0-SNAPSHOT' filename='openapi-generator-cli-6.4.0.jar' file@./openapi-generator-cli.jar repository=$REPO_HREF``


.. code:: json
{
"task": "/pulp/api/v3/tasks/03d5a40b-4bda-4ee7-96cb-f0639b6c5d6a/"
}
Add Pulp as mirror for Maven
----------------------------

.. code:: xml
<settings>
<mirrors>
<mirror>
<id>pulp-maven-central</id>
<name>Local Maven Central mirror </name>
<url>http://localhost:24816/pulp/content/my/local/my/local/snapshots</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>
70 changes: 46 additions & 24 deletions pulp_maven/app/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,70 @@

from rest_framework import serializers

from pulpcore.plugin import serializers as platform
from pulpcore.plugin.serializers import (
ContentChecksumSerializer,
DetailRelatedField,
DistributionSerializer,
RemoteSerializer,
RepositorySerializer,
SingleArtifactContentUploadSerializer,
)

from . import models


class MavenRepositorySerializer(platform.RepositorySerializer):
class MavenRepositorySerializer(RepositorySerializer):
"""
Serializer for Maven Repositories.
"""

class Meta:
fields = platform.RepositorySerializer.Meta.fields
fields = RepositorySerializer.Meta.fields
model = models.MavenRepository


class MavenArtifactSerializer(platform.SingleArtifactContentSerializer):
class MavenArtifactSerializer(SingleArtifactContentUploadSerializer, ContentChecksumSerializer):
"""
A Serializer for MavenArtifact.
"""

group_id = serializers.CharField(
help_text=_("Group Id of the artifact's package."), read_only=True
)
artifact_id = serializers.CharField(
help_text=_("Artifact Id of the artifact's package."), read_only=True
)
version = serializers.CharField(
help_text=_("Version of the artifact's package."), read_only=True
)
filename = serializers.CharField(help_text=_("Filename of the artifact."), read_only=True)
group_id = serializers.CharField(help_text=_("Group Id of the artifact's package."))
artifact_id = serializers.CharField(help_text=_("Artifact Id of the artifact's package."))
version = serializers.CharField(help_text=_("Version of the artifact's package."))
filename = serializers.CharField(help_text=_("Filename of the artifact."))

def deferred_validate(self, data):
"""Validate the FileContent data."""
data = super().deferred_validate(data)
data["relative_path"] = (
f"{data['group_id'].replace('.', '/')}/{data['artifact_id']}/{data['version']}/"
"{data['filename']}"
)
return data

def retrieve(self, validated_data):
content = models.MavenArtifact.objects.filter(
group_id=validated_data["group_id"],
artifact_id=validated_data["artifact_id"],
version=validated_data["version"],
filename=validated_data["filename"],
)
return content.first()

class Meta:
fields = platform.SingleArtifactContentSerializer.Meta.fields + (
"group_id",
"artifact_id",
"version",
"filename",
fields = (
SingleArtifactContentUploadSerializer.Meta.fields
+ ContentChecksumSerializer.Meta.fields
+ ("group_id", "artifact_id", "version", "filename")
)
# Remove relative_path
fields = tuple(field for field in fields if field != "relative_path")
model = models.MavenArtifact
# Validation occurs in the task.
validators = []


class MavenRemoteSerializer(platform.RemoteSerializer):
class MavenRemoteSerializer(RemoteSerializer):
"""
A Serializer for MavenRemote.
Expand All @@ -58,16 +80,16 @@ class Meta:
"""

class Meta:
fields = platform.RemoteSerializer.Meta.fields
fields = RemoteSerializer.Meta.fields
model = models.MavenRemote


class MavenDistributionSerializer(platform.DistributionSerializer):
class MavenDistributionSerializer(DistributionSerializer):
"""
Serializer for Maven Distributions.
"""

remote = platform.DetailRelatedField(
remote = DetailRelatedField(
required=False,
help_text=_("Remote that can be used to fetch content when using pull-through caching."),
queryset=models.MavenRemote.objects.all(),
Expand All @@ -76,5 +98,5 @@ class MavenDistributionSerializer(platform.DistributionSerializer):
)

class Meta:
fields = platform.DistributionSerializer.Meta.fields + ("remote",)
fields = DistributionSerializer.Meta.fields + ("remote",)
model = models.MavenDistribution
50 changes: 33 additions & 17 deletions pulp_maven/app/viewsets.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,78 @@
from pulpcore.plugin import viewsets as core
from pulpcore.plugin.viewsets import (
ContentFilter,
DistributionViewSet,
RemoteViewSet,
RepositoryVersionViewSet,
RepositoryViewSet,
SingleArtifactContentUploadViewSet,
)
from pulpcore.plugin.actions import ModifyRepositoryActionMixin

from . import models, serializers

from pulp_maven.app.models import MavenArtifact, MavenRemote, MavenRepository, MavenDistribution

class MavenArtifactFilter(core.ContentFilter):
from pulp_maven.app.serializers import (
MavenArtifactSerializer,
MavenRemoteSerializer,
MavenRepositorySerializer,
MavenDistributionSerializer,
)


class MavenArtifactFilter(ContentFilter):
"""
FilterSet for MavenArtifact.
"""

class Meta:
model = models.MavenArtifact
model = MavenArtifact
fields = ["group_id", "artifact_id", "version", "filename"]


class MavenArtifactViewSet(core.ContentViewSet):
class MavenArtifactViewSet(SingleArtifactContentUploadViewSet):
"""
A ViewSet for MavenArtifact.
"""

endpoint_name = "artifact"
queryset = models.MavenArtifact.objects.all()
serializer_class = serializers.MavenArtifactSerializer
queryset = MavenArtifact.objects.prefetch_related("_artifacts").all()
serializer_class = MavenArtifactSerializer
filterset_class = MavenArtifactFilter


class MavenRemoteViewSet(core.RemoteViewSet):
class MavenRemoteViewSet(RemoteViewSet):
"""
A ViewSet for MavenRemote.
"""

endpoint_name = "maven"
queryset = models.MavenRemote.objects.all()
serializer_class = serializers.MavenRemoteSerializer
queryset = MavenRemote.objects.all()
serializer_class = MavenRemoteSerializer


class MavenRepositoryViewSet(core.RepositoryViewSet):
class MavenRepositoryViewSet(RepositoryViewSet, ModifyRepositoryActionMixin):
"""
A ViewSet for MavenRemote.
"""

endpoint_name = "maven"
queryset = models.MavenRepository.objects.all()
serializer_class = serializers.MavenRepositorySerializer
queryset = MavenRepository.objects.all()
serializer_class = MavenRepositorySerializer


class MavenRepositoryVersionViewSet(core.RepositoryVersionViewSet):
class MavenRepositoryVersionViewSet(RepositoryVersionViewSet):
"""
MavenRepositoryVersion represents a single Maven repository version.
"""

parent_viewset = MavenRepositoryViewSet


class MavenDistributionViewSet(core.DistributionViewSet):
class MavenDistributionViewSet(DistributionViewSet):
"""
ViewSet for Maven Distributions.
"""

endpoint_name = "maven"
queryset = models.MavenDistribution.objects.all()
serializer_class = serializers.MavenDistributionSerializer
queryset = MavenDistribution.objects.all()
serializer_class = MavenDistributionSerializer
83 changes: 83 additions & 0 deletions pulp_maven/tests/functional/api/test_upload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import hashlib
from urllib.parse import urljoin

from pulp_maven.tests.functional.utils import download_file


def test_upload_workflow(
maven_repo_api_client,
maven_repo_factory,
random_maven_artifact_factory,
maven_artifact_api_client,
maven_distribution_factory,
gen_object_with_cleanup,
):
# Create a repository and assert that the latest version is 0
repo = maven_repo_factory()
assert repo.latest_version_href.endswith("/versions/0/")

# Create a random jar
jar_file = random_maven_artifact_factory()

# Upload the jar into the repository
artifact_kwargs = dict(
group_id=jar_file["group_id"],
artifact_id=jar_file["artifact_id"],
version=jar_file["version"],
filename=jar_file["filename"],
file=jar_file["full_path"],
repository=repo.pulp_href,
)
maven_artifact = gen_object_with_cleanup(maven_artifact_api_client, **artifact_kwargs)

# Assert that a Maven Artifact was created
assert maven_artifact.group_id == jar_file["group_id"]
assert maven_artifact.artifact_id == jar_file["artifact_id"]
assert maven_artifact.version == jar_file["version"]
assert maven_artifact.filename == jar_file["filename"]

# Assert that a repository version was created
repo = maven_repo_api_client.read(repo.pulp_href)
assert repo.latest_version_href.endswith("/versions/1/")

# Assert that this Maven Artifact is in the repository version
content_in_repo_version = maven_artifact_api_client.list(
repository_version=repo.latest_version_href
)
assert content_in_repo_version.results[0].pulp_href == maven_artifact.pulp_href

# Create a second repository and assert that latest version is 0
repo2 = maven_repo_factory()
assert repo2.latest_version_href.endswith("/versions/0/")

# Assert that the same content unit can be uploaded again
artifact_kwargs["repository"] = repo2.pulp_href
maven_artifact2 = gen_object_with_cleanup(maven_artifact_api_client, **artifact_kwargs)

# Assert that the existing artifact was identified by the upload API.
assert maven_artifact.pulp_href == maven_artifact2.pulp_href

# Assert that a new repository version was created.
repo2 = maven_repo_api_client.read(repo2.pulp_href)
assert repo2.latest_version_href.endswith("/versions/1/")

# Assert that this Maven Artifact is in the repository version
content_in_repo2_version = maven_artifact_api_client.list(
repository_version=repo2.latest_version_href
)
assert content_in_repo2_version.results[0].pulp_href == maven_artifact2.pulp_href

# Create a distribution and serve repo
distribution = maven_distribution_factory(repository=repo.pulp_href)

# Download the jar from the distribution
unit_path = (
f"{jar_file['group_id'].replace('.', '/')}/{jar_file['artifact_id']}/"
"{jar_file['version']}/{jar_file['filename']}"
)
pulp_unit_url = urljoin(distribution.base_url, unit_path)
downloaded_file = download_file(pulp_unit_url)
downloaded_file_checksum = hashlib.sha256(downloaded_file.body).hexdigest()

# Assert that the downloaded file's checksum matches the original
assert jar_file["sha256"] == downloaded_file_checksum
20 changes: 20 additions & 0 deletions pulp_maven/tests/functional/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
RepositoriesMavenApi,
)

from pulp_maven.tests.functional.utils import generate_jar


@pytest.fixture(scope="session")
def maven_client(_api_client_set, bindings_cfg):
Expand Down Expand Up @@ -69,3 +71,21 @@ def _maven_remote_factory(**kwargs):
return gen_object_with_cleanup(maven_remote_api_client, kwargs)

yield _maven_remote_factory


@pytest.fixture
def random_maven_artifact_factory(tmp_path):
"""A factory to generate a random maven artifact."""

def _random_maven_artifact_factory(**kwargs):
kwargs.setdefault("group_id", f"org.{str(uuid.uuid4())}")
kwargs.setdefault("artifact_id", str(uuid.uuid4()))
kwargs.setdefault("version", str(uuid.uuid4()))
kwargs.setdefault("filename", f"{str(uuid.uuid4())}.jar")
full_path = tmp_path / kwargs["filename"]
_, checksum = generate_jar(full_path)
kwargs["full_path"] = full_path
kwargs["sha256"] = checksum
return kwargs

return _random_maven_artifact_factory
Loading

0 comments on commit d7b03bb

Please sign in to comment.