Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutable Prototype #389

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/7504.bugfix
@@ -0,0 +1 @@
Sync collection deprecation status
1 change: 1 addition & 0 deletions CHANGES/7504.removal
@@ -0,0 +1 @@
Removing `deprecated` field from Collection
35 changes: 34 additions & 1 deletion pulp_ansible/app/galaxy/v3/serializers.py
Expand Up @@ -13,7 +13,7 @@ class CollectionSerializer(serializers.ModelSerializer):

created_at = serializers.DateTimeField(source="collection.pulp_created")
updated_at = serializers.DateTimeField(source="collection.pulp_last_updated")
deprecated = serializers.BooleanField(source="collection.deprecated")
deprecated = serializers.SerializerMethodField()
href = serializers.SerializerMethodField()

versions_url = serializers.SerializerMethodField()
Expand Down Expand Up @@ -48,6 +48,15 @@ def get_href(self, obj):
kwargs={"path": self.context["path"], "namespace": obj.namespace, "name": obj.name},
)

def get_deprecated(self, obj):
"""Get deprecated."""
return (
models.AnsibleDistribution.objects.get(base_path=self.context["path"])
.mutable_data.filter(collection=obj.collection)
.latest("repository_version")
.deprecated
)

def get_versions_url(self, obj):
"""Get a link to a collection versions list."""
return reverse(
Expand Down Expand Up @@ -199,3 +208,27 @@ class CollectionVersionDocsSerializer(serializers.ModelSerializer):
class Meta:
fields = ("docs_blob",)
model = models.CollectionVersion


class DeprecatedCollectionSerializer(serializers.ModelSerializer):
"""A serializer to display the mutable metadata of a Collection."""

name = serializers.CharField(source="collection.name")
namespace = serializers.CharField(source="collection.namespace")

class Meta:
fields = ("name", "namespace")
model = models.MutableCollectionMetadata


class MutableCollectionMetadataSerializer(serializers.ModelSerializer):
"""A serializer to display the mutable metadata of a Collection."""

deprecated_collections = DeprecatedCollectionSerializer(
source="collection_memberships", many=True
)
last_updated = serializers.DateTimeField(source="pulp_last_updated")

class Meta:
fields = ("deprecated_collections", "last_updated")
model = models.RepositoryVersion
78 changes: 67 additions & 11 deletions pulp_ansible/app/galaxy/v3/views.py
Expand Up @@ -2,7 +2,7 @@
from gettext import gettext as _
import semantic_version

from django.db.models import Q
from django.db.models import Q, Prefetch
from django.shortcuts import get_object_or_404
from django.utils.dateparse import parse_datetime
from drf_spectacular.utils import OpenApiParameter, extend_schema
Expand All @@ -23,8 +23,15 @@
CollectionVersionSerializer,
CollectionVersionDocsSerializer,
CollectionVersionListSerializer,
MutableCollectionMetadataSerializer,
)
from pulp_ansible.app.models import (
AnsibleDistribution,
CollectionVersion,
CollectionImport,
MutableCollectionMetadata,
RepositoryVersion,
)
from pulp_ansible.app.models import AnsibleDistribution, CollectionVersion, CollectionImport
from pulp_ansible.app.serializers import (
CollectionOneShotSerializer,
CollectionImportDetailSerializer,
Expand All @@ -41,17 +48,22 @@ class AnsibleDistributionMixin:
"""

@staticmethod
def get_distro_content(path):
"""Returns distribution content."""
def get_repository_version(path):
"""Returns repository version."""
distro = get_object_or_404(AnsibleDistribution, base_path=path)
if distro.repository_version:
return distro.repository_version.content
else:
repo_version = distro.repository.latest_version()
if repo_version is None:
return Content.objects.none()
else:
return repo_version.content
return distro.repository_version

return distro.repository.latest_version()

@staticmethod
def get_distro_content(path):
"""Returns distribution content."""
repo_version = AnsibleDistributionMixin.get_repository_version(path)
if repo_version is None:
return Content.objects.none()

return repo_version.content

def get_serializer_context(self):
"""Inserts distribution path to a serializer context."""
Expand Down Expand Up @@ -108,6 +120,21 @@ class CollectionViewSet(
serializer_class = CollectionSerializer
pagination_class = LimitOffsetPagination

def filter_queryset(self, queryset):
"""
Filter Repository related fields.
"""
queryset = super().filter_queryset(queryset)
repo_version = self.get_repository_version(self.kwargs["path"])
if not repo_version:
return queryset

deprecated = False
if self.request.query_params.get("deprecated", "").lower() in ["true", "yes", "1"]:
deprecated = True

return queryset.filter(collection__mutablecollectionmetadata__deprecated=deprecated)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bring all collections by default, only filter deprecation with query params


def get_queryset(self):
"""
Returns a CollectionVersions queryset for specified distribution.
Expand Down Expand Up @@ -315,3 +342,32 @@ def retrieve(self, request, *args, **kwargs):
serializer = CollectionImportDetailSerializer(instance, context=context)

return Response(serializer.data)


class MutableCollectionMetadataViewset(
ExceptionHandlerMixin,
AnsibleDistributionMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
"""
ViewSet for MutableCollectionMetadata.
"""

authentication_classes = []
permission_classes = []
serializer_class = MutableCollectionMetadataSerializer
pagination_class = LimitOffsetPagination

def get_queryset(self):
"""
Returns a MutableCollectionMetadata queryset for specified distribution.
"""
return RepositoryVersion.objects.filter(
pk=self.get_repository_version(self.kwargs["path"]).pk
).prefetch_related(
Prefetch(
"collection_memberships",
queryset=MutableCollectionMetadata.objects.filter(deprecated=True),
)
)
Comment on lines +362 to +373
Copy link
Member Author

@fao89 fao89 Oct 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem: only displays deprecated for the last RepositoryVersion
Question: how to determine which content remains at the Repository, to get the correspondent MutableCollectionMetadata?

36 changes: 36 additions & 0 deletions pulp_ansible/app/migrations/0025_deprecation.py
@@ -0,0 +1,36 @@
# Generated by Django 2.2.16 on 2020-10-12 17:15

from django.db import migrations, models
import django.db.models.deletion
import django_lifecycle.mixins
import uuid


class Migration(migrations.Migration):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a step here to migrate the deprecation status over to MutableCollectionMetadata before the deprecated field is removed from the collections table.


dependencies = [
('core', '0048_fips_checksums'),
('ansible', '0024_remove_collectionversion_certification'),
]

operations = [
migrations.RemoveField(
model_name='collection',
name='deprecated',
),
migrations.CreateModel(
name='MutableCollectionMetadata',
fields=[
('pulp_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('pulp_created', models.DateTimeField(auto_now_add=True)),
('pulp_last_updated', models.DateTimeField(auto_now=True, null=True)),
('deprecated', models.BooleanField(default=False)),
('collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ansible.Collection')),
('repository_version', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='collection_memberships', to='core.RepositoryVersion')),
],
options={
'unique_together': {('collection', 'repository_version')},
},
bases=(django_lifecycle.mixins.LifecycleModelMixin, models.Model),
),
]
32 changes: 30 additions & 2 deletions pulp_ansible/app/models.py
Expand Up @@ -10,6 +10,7 @@
Content,
Remote,
Repository,
RepositoryVersion,
RepositoryVersionDistribution,
Task,
)
Expand Down Expand Up @@ -47,8 +48,6 @@ class Collection(BaseModel):
namespace = models.CharField(max_length=64, editable=False)
name = models.CharField(max_length=64, editable=False)

deprecated = models.BooleanField(default=False)

class Meta:
unique_together = ("namespace", "name")

Expand Down Expand Up @@ -236,12 +235,41 @@ class Meta:
permissions = (("modify_ansible_repo_content", "Can modify ansible repository content"),)


class MutableCollectionMetadata(BaseModel):
"""
A model that represents Collection mutable metadata for a given RepositoryVersion.
"""

repository_version = models.ForeignKey(
RepositoryVersion, on_delete=models.CASCADE, related_name="collection_memberships"
)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
deprecated = models.BooleanField(default=False)

class Meta:
unique_together = ("collection", "repository_version")
Comment on lines +238 to +250
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Should we make it sparse?
  • What about CollectionVersion metadata?



class AnsibleDistribution(RepositoryVersionDistribution):
"""
A Distribution for Ansible content.
"""

TYPE = "ansible"

@property
def mutable_data(self):
"""
Return the relative path of the ContentArtifact.
"""
if self.repository_version:
return MutableCollectionMetadata.objects.filter(
repository_version=self.repository_version
)

return MutableCollectionMetadata.objects.filter(
repository_version__repository=self.repository
)

class Meta:
default_related_name = "%(app_label)s_%(model_name)s"
7 changes: 0 additions & 7 deletions pulp_ansible/app/serializers.py
Expand Up @@ -331,12 +331,6 @@ class CollectionVersionSerializer(SingleArtifactContentSerializer, ContentChecks

version = serializers.CharField(help_text=_("The version of the collection."), max_length=32)

deprecated = serializers.BooleanField(
source="collection.deprecated",
help_text=_("Whether or not the collection has been deprecated."),
read_only=True,
)

class Meta:
fields = (
tuple(set(SingleArtifactContentSerializer.Meta.fields) - {"relative_path"})
Expand All @@ -357,7 +351,6 @@ class Meta:
"repository",
"tags",
"version",
"deprecated",
)
)
model = CollectionVersion
Expand Down