From cecae0934930f9c4e78c2a094c6ece62ad892fa2 Mon Sep 17 00:00:00 2001 From: Gerrod Ubben Date: Mon, 3 Feb 2025 12:34:30 -0500 Subject: [PATCH] Add labels to domains fixes: #6236 --- CHANGES/6236.feature | 1 + .../app/migrations/0128_domain_pulp_labels.py | 19 ++++++++++ pulpcore/app/models/domain.py | 3 ++ pulpcore/app/serializers/domain.py | 9 ++++- pulpcore/app/viewsets/domain.py | 6 +++- .../tests/functional/api/test_crud_domains.py | 35 +++++++++++++++++++ 6 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 CHANGES/6236.feature create mode 100644 pulpcore/app/migrations/0128_domain_pulp_labels.py diff --git a/CHANGES/6236.feature b/CHANGES/6236.feature new file mode 100644 index 0000000000..60fd4bcf88 --- /dev/null +++ b/CHANGES/6236.feature @@ -0,0 +1 @@ +Added support for labels on domains. \ No newline at end of file diff --git a/pulpcore/app/migrations/0128_domain_pulp_labels.py b/pulpcore/app/migrations/0128_domain_pulp_labels.py new file mode 100644 index 0000000000..d8a654ddc7 --- /dev/null +++ b/pulpcore/app/migrations/0128_domain_pulp_labels.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.16 on 2025-02-03 16:41 + +import django.contrib.postgres.fields.hstore +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0127_remove_upstreampulp_pulp_label_select"), + ] + + operations = [ + migrations.AddField( + model_name="domain", + name="pulp_labels", + field=django.contrib.postgres.fields.hstore.HStoreField(default=dict), + ), + ] diff --git a/pulpcore/app/models/domain.py b/pulpcore/app/models/domain.py index aee7ba81d0..18dd3e8921 100644 --- a/pulpcore/app/models/domain.py +++ b/pulpcore/app/models/domain.py @@ -1,5 +1,6 @@ from django.core.files.storage import default_storage from django.db import models +from django.contrib.postgres.fields import HStoreField from django_lifecycle import hook, BEFORE_DELETE, BEFORE_UPDATE from pulpcore.app.models import BaseModel, AutoAddObjPermsMixin @@ -25,6 +26,7 @@ class Domain(BaseModel, AutoAddObjPermsMixin): Fields: name (models.SlugField): Unique name of domain + pulp_labels (HStoreField): Dictionary of string values description (models.TextField): Optional description of domain storage_class (models.TextField): Required storage class for backend storage_settings (EncryptedJSONField): Settings needed to configure storage backend @@ -33,6 +35,7 @@ class Domain(BaseModel, AutoAddObjPermsMixin): """ name = models.SlugField(null=False, unique=True) + pulp_labels = HStoreField(default=dict) description = models.TextField(null=True) # Storage class is required, optional settings are validated by serializer storage_class = models.TextField(null=False) diff --git a/pulpcore/app/serializers/domain.py b/pulpcore/app/serializers/domain.py index cd38598a8e..2fff76bc60 100644 --- a/pulpcore/app/serializers/domain.py +++ b/pulpcore/app/serializers/domain.py @@ -11,7 +11,12 @@ from rest_framework.validators import UniqueValidator from pulpcore.app.models import Domain -from pulpcore.app.serializers import IdentityField, ModelSerializer, HiddenFieldsMixin +from pulpcore.app.serializers import ( + IdentityField, + ModelSerializer, + HiddenFieldsMixin, + pulp_labels_validator, +) BACKEND_CHOICES = ( @@ -433,6 +438,7 @@ class DomainSerializer(BackendSettingsValidator, ModelSerializer): description = serializers.CharField( help_text=_("An optional description."), required=False, allow_null=True ) + pulp_labels = serializers.HStoreField(required=False, validators=[pulp_labels_validator]) storage_class = serializers.ChoiceField( help_text=_("Backend storage class for domain."), choices=BACKEND_CHOICES, @@ -484,6 +490,7 @@ class Meta: fields = ModelSerializer.Meta.fields + ( "name", "description", + "pulp_labels", "storage_class", "storage_settings", "redirect_to_object_storage", diff --git a/pulpcore/app/viewsets/domain.py b/pulpcore/app/viewsets/domain.py index 40ef1efd61..f35caf284b 100644 --- a/pulpcore/app/viewsets/domain.py +++ b/pulpcore/app/viewsets/domain.py @@ -14,14 +14,17 @@ AsyncOperationResponseSerializer, ) from pulpcore.app.tasks import migrate_backend -from pulpcore.app.viewsets import NamedModelViewSet, AsyncRemoveMixin, AsyncUpdateMixin +from pulpcore.app.viewsets import NamedModelViewSet, AsyncRemoveMixin, AsyncUpdateMixin, LabelsMixin from pulpcore.app.viewsets.base import NAME_FILTER_OPTIONS +from pulpcore.app.viewsets.custom_filters import LabelFilter from pulpcore.tasking.tasks import dispatch class DomainFilter(BaseFilterSet): """FilterSet for Domain.""" + pulp_label_select = LabelFilter() + class Meta: model = Domain fields = {"name": NAME_FILTER_OPTIONS} @@ -34,6 +37,7 @@ class DomainViewSet( mixins.RetrieveModelMixin, AsyncUpdateMixin, AsyncRemoveMixin, + LabelsMixin, ): """ ViewSet for Domain. diff --git a/pulpcore/tests/functional/api/test_crud_domains.py b/pulpcore/tests/functional/api/test_crud_domains.py index e645240613..1e1325a668 100644 --- a/pulpcore/tests/functional/api/test_crud_domains.py +++ b/pulpcore/tests/functional/api/test_crud_domains.py @@ -308,3 +308,38 @@ def test_special_domain_creation(pulpcore_bindings, gen_object_with_cleanup, pul domain = gen_object_with_cleanup(pulpcore_bindings.DomainsApi, body, pulp_domain=random_name) assert "default/api/v3/" in domain.pulp_href assert random_name not in domain.pulp_href + + +@pytest.mark.parallel +def test_filter_domains_by_label(pulpcore_bindings, gen_object_with_cleanup): + """Test filtering domains by label.""" + # Create domains with different labels + body_a = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + "pulp_labels": {"key_a": "label_a"} + } + domain_a = gen_object_with_cleanup(pulpcore_bindings.DomainsApi, body_a) + + body_b = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + "pulp_labels": {"key_b": "label_b"} + } + domain_b = gen_object_with_cleanup(pulpcore_bindings.DomainsApi, body_b) + + # Filter by label key + domains = pulpcore_bindings.DomainsApi.list(pulp_label_select="key_a").results + assert len(domains) == 1 + assert domains[0].pulp_href == domain_a.pulp_href + + # Filter by label value + domains = pulpcore_bindings.DomainsApi.list(pulp_label_select="key_b=label_b").results + assert len(domains) == 1 + assert domains[0].pulp_href == domain_b.pulp_href + + # Filter by non-existent label + domains = pulpcore_bindings.DomainsApi.list(pulp_label_select="key_c").results + assert len(domains) == 0