diff --git a/.github/workflows/scripts/install.sh b/.github/workflows/scripts/install.sh index 9c586de21..72f561ca8 100755 --- a/.github/workflows/scripts/install.sh +++ b/.github/workflows/scripts/install.sh @@ -115,7 +115,7 @@ if [ "$TEST" = "azure" ]; then - ./azurite:/etc/pulp\ command: "azurite-blob --blobHost 0.0.0.0 --cert /etc/pulp/azcert.pem --key /etc/pulp/azkey.pem"' vars/main.yaml sed -i -e '$a azure_test: true\ -pulp_scenario_settings: null\ +pulp_scenario_settings: {"domain_enabled": true}\ ' vars/main.yaml fi diff --git a/CHANGES/3008.feature b/CHANGES/3008.feature new file mode 100644 index 000000000..56c7c5eba --- /dev/null +++ b/CHANGES/3008.feature @@ -0,0 +1 @@ +Added support for Domains. diff --git a/pulp_rpm/app/__init__.py b/pulp_rpm/app/__init__.py index 6a0c7a8c6..9398de533 100644 --- a/pulp_rpm/app/__init__.py +++ b/pulp_rpm/app/__init__.py @@ -10,3 +10,4 @@ class PulpRpmPluginAppConfig(PulpPluginAppConfig): label = "rpm" version = "3.21.0.dev" python_package_name = "pulp-rpm" + domain_compatible = True diff --git a/pulp_rpm/app/migrations/0051_alter_distributiontree_unique_together_and_more.py b/pulp_rpm/app/migrations/0051_alter_distributiontree_unique_together_and_more.py new file mode 100644 index 000000000..5cc11680e --- /dev/null +++ b/pulp_rpm/app/migrations/0051_alter_distributiontree_unique_together_and_more.py @@ -0,0 +1,170 @@ +# Generated by Django 4.2.1 on 2023-05-15 16:51 + +from django.db import migrations, models +import django.db.models.deletion +import pulpcore.app.util + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0106_alter_artifactdistribution_distribution_ptr_and_more'), + ('rpm', '0050_alter_addon_pulp_id_alter_checksum_pulp_id_and_more'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='distributiontree', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='modulemd', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='modulemdobsolete', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='package', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='repometadatafile', + unique_together=set(), + ), + migrations.AddField( + model_name='distributiontree', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AddField( + model_name='modulemd', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AddField( + model_name='modulemddefaults', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AddField( + model_name='modulemdobsolete', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AddField( + model_name='package', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AddField( + model_name='packagecategory', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AddField( + model_name='packageenvironment', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AddField( + model_name='packagegroup', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AddField( + model_name='packagelangpacks', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AddField( + model_name='repometadatafile', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AddField( + model_name='updaterecord', + name='_pulp_domain', + field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'), + ), + migrations.AlterField( + model_name='modulemddefaults', + name='digest', + field=models.TextField(db_index=True), + ), + migrations.AlterField( + model_name='package', + name='pkgId', + field=models.TextField(db_index=True), + ), + migrations.AlterField( + model_name='packagecategory', + name='digest', + field=models.TextField(db_index=True), + ), + migrations.AlterField( + model_name='packageenvironment', + name='digest', + field=models.TextField(db_index=True), + ), + migrations.AlterField( + model_name='packagegroup', + name='digest', + field=models.TextField(db_index=True), + ), + migrations.AlterField( + model_name='packagelangpacks', + name='digest', + field=models.TextField(db_index=True), + ), + migrations.AlterField( + model_name='updaterecord', + name='digest', + field=models.TextField(db_index=True), + ), + migrations.AlterUniqueTogether( + name='distributiontree', + unique_together={('_pulp_domain', 'digest')}, + ), + migrations.AlterUniqueTogether( + name='modulemd', + unique_together={('_pulp_domain', 'name', 'stream', 'version', 'context', 'arch')}, + ), + migrations.AlterUniqueTogether( + name='modulemddefaults', + unique_together={('_pulp_domain', 'digest')}, + ), + migrations.AlterUniqueTogether( + name='modulemdobsolete', + unique_together={('_pulp_domain', 'modified', 'module_name', 'module_stream')}, + ), + migrations.AlterUniqueTogether( + name='package', + unique_together={('_pulp_domain', 'name', 'epoch', 'version', 'release', 'arch', 'checksum_type', 'pkgId')}, + ), + migrations.AlterUniqueTogether( + name='packagecategory', + unique_together={('_pulp_domain', 'digest')}, + ), + migrations.AlterUniqueTogether( + name='packageenvironment', + unique_together={('_pulp_domain', 'digest')}, + ), + migrations.AlterUniqueTogether( + name='packagegroup', + unique_together={('_pulp_domain', 'digest')}, + ), + migrations.AlterUniqueTogether( + name='packagelangpacks', + unique_together={('_pulp_domain', 'digest')}, + ), + migrations.AlterUniqueTogether( + name='repometadatafile', + unique_together={('_pulp_domain', 'data_type', 'checksum', 'relative_path')}, + ), + migrations.AlterUniqueTogether( + name='updaterecord', + unique_together={('_pulp_domain', 'digest')}, + ), + ] diff --git a/pulp_rpm/app/models/advisory.py b/pulp_rpm/app/models/advisory.py index ac87d5f5e..f32376a21 100644 --- a/pulp_rpm/app/models/advisory.py +++ b/pulp_rpm/app/models/advisory.py @@ -8,6 +8,7 @@ BaseModel, Content, ) +from pulpcore.plugin.util import get_domain_pk from pulp_rpm.app.constants import ( CR_UPDATE_COLLECTION_ATTRS, @@ -100,14 +101,9 @@ class UpdateRecord(Content): # A field that represents the hash digest of the update record. Used to track differences # between two UpdateRecord objects without having to examine the associations like # UpdateCollection or UpdateCollectionPackage. - digest = models.TextField(unique=True) + digest = models.TextField(db_index=True) - @classmethod - def natural_key_fields(cls): - """ - Digest is used as a natural key for UpdateRecords. - """ - return ("digest",) + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) @classmethod def createrepo_to_dict(cls, update): @@ -228,6 +224,7 @@ def get_module_list(self): class Meta: default_related_name = "%(app_label)s_%(model_name)s" + unique_together = ("_pulp_domain", "digest") class UpdateCollection(BaseModel): diff --git a/pulp_rpm/app/models/comps.py b/pulp_rpm/app/models/comps.py index 0641498f3..6c4da7dde 100644 --- a/pulp_rpm/app/models/comps.py +++ b/pulp_rpm/app/models/comps.py @@ -5,6 +5,7 @@ from django.db import models from pulpcore.plugin.models import Content +from pulpcore.plugin.util import get_domain_pk from pulp_rpm.app.constants import ( LIBCOMPS_CATEGORY_ATTRS, @@ -73,19 +74,15 @@ class PackageGroup(Content): desc_by_lang = models.JSONField(default=dict) name_by_lang = models.JSONField(default=dict) - digest = models.TextField(unique=True) + digest = models.TextField(db_index=True) repo_key_fields = ("id",) + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) + class Meta: default_related_name = "%(app_label)s_%(model_name)s" - - @classmethod - def natural_key_fields(cls): - """ - Digest is used as a natural key for PackageGroups. - """ - return ("digest",) + unique_together = ("_pulp_domain", "digest") @classmethod def pkglist_to_list(cls, value): @@ -230,19 +227,15 @@ class PackageCategory(Content): desc_by_lang = models.JSONField(default=dict) name_by_lang = models.JSONField(default=dict) - digest = models.TextField(unique=True) + digest = models.TextField(db_index=True) repo_key_fields = ("id",) + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) + class Meta: default_related_name = "%(app_label)s_%(model_name)s" - - @classmethod - def natural_key_fields(cls): - """ - Digest is used as a natural key for PackageCategory. - """ - return ("digest",) + unique_together = ("_pulp_domain", "digest") @classmethod def grplist_to_lst(cls, value): @@ -357,19 +350,15 @@ class PackageEnvironment(Content): desc_by_lang = models.JSONField(default=dict) name_by_lang = models.JSONField(default=dict) - digest = models.TextField(unique=True) + digest = models.TextField(db_index=True) repo_key_fields = ("id",) + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) + class Meta: default_related_name = "%(app_label)s_%(model_name)s" - - @classmethod - def natural_key_fields(cls): - """ - Digest is used as a natural key for PackageEnvironment. - """ - return ("digest",) + unique_together = ("_pulp_domain", "digest") @classmethod def grplist_to_lst(cls, value): @@ -463,17 +452,13 @@ class PackageLangpacks(Content): matches = models.JSONField(default=dict) - digest = models.TextField(unique=True) + digest = models.TextField(db_index=True) + + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) class Meta: default_related_name = "%(app_label)s_%(model_name)s" - - @classmethod - def natural_key_fields(cls): - """ - Digest is used as a natural key for PackageLangpacks. - """ - return ("digest",) + unique_together = ("_pulp_domain", "digest") @classmethod def libcomps_to_dict(cls, langpacks): diff --git a/pulp_rpm/app/models/custom_metadata.py b/pulp_rpm/app/models/custom_metadata.py index f1263aaa2..dd317008d 100644 --- a/pulp_rpm/app/models/custom_metadata.py +++ b/pulp_rpm/app/models/custom_metadata.py @@ -3,6 +3,7 @@ from django.db import models from pulpcore.plugin.models import Content +from pulpcore.plugin.util import get_domain_pk from pulp_rpm.app.constants import CHECKSUM_CHOICES log = getLogger(__name__) @@ -32,9 +33,11 @@ class RepoMetadataFile(Content): repo_key_fields = ("data_type",) + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) + class Meta: default_related_name = "%(app_label)s_%(model_name)s" - unique_together = ("data_type", "checksum", "relative_path") + unique_together = ("_pulp_domain", "data_type", "checksum", "relative_path") @property def unsupported_metadata_type(self): diff --git a/pulp_rpm/app/models/distribution.py b/pulp_rpm/app/models/distribution.py index b322d0df5..cf222e7da 100644 --- a/pulp_rpm/app/models/distribution.py +++ b/pulp_rpm/app/models/distribution.py @@ -12,6 +12,7 @@ ContentArtifact, Repository, ) +from pulpcore.plugin.util import get_domain_pk log = getLogger(__name__) @@ -94,6 +95,8 @@ class DistributionTree(Content): digest = models.TextField(null=False) + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) + def repositories(self): """ Return the subrepos in this DistributionTree. @@ -134,7 +137,7 @@ def artifacts(self): class Meta: default_related_name = "%(app_label)s_%(model_name)s" - unique_together = ("digest",) + unique_together = ("_pulp_domain", "digest") class Checksum(BaseModel): diff --git a/pulp_rpm/app/models/modulemd.py b/pulp_rpm/app/models/modulemd.py index eda89a44e..7eae9c955 100644 --- a/pulp_rpm/app/models/modulemd.py +++ b/pulp_rpm/app/models/modulemd.py @@ -3,6 +3,7 @@ from django.db import models from pulpcore.plugin.models import Content +from pulpcore.plugin.util import get_domain_pk from pulp_rpm.app.models.package import Package @@ -65,9 +66,11 @@ class Modulemd(Content): snippet = models.TextField() + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) + class Meta: default_related_name = "%(app_label)s_%(model_name)s" - unique_together = ("name", "stream", "version", "context", "arch") + unique_together = ("_pulp_domain", "name", "stream", "version", "context", "arch") class ModulemdDefaults(Content): @@ -92,21 +95,17 @@ class ModulemdDefaults(Content): module = models.TextField() stream = models.TextField() profiles = models.JSONField(default=list) - digest = models.TextField(unique=True) + digest = models.TextField(db_index=True) snippet = models.TextField() repo_key_fields = ("module",) - @classmethod - def natural_key_fields(cls): - """ - Digest is used as a natural key for ModulemdDefaults. - """ - return ("digest",) + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) class Meta: default_related_name = "%(app_label)s_%(model_name)s" + unique_together = ("_pulp_domain", "digest") class ModulemdObsolete(Content): @@ -151,6 +150,8 @@ class ModulemdObsolete(Content): snippet = models.TextField() + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) + class Meta: default_related_name = "%(app_label)s_%(model_name)s" - unique_together = ("modified", "module_name", "module_stream") + unique_together = ("_pulp_domain", "modified", "module_name", "module_stream") diff --git a/pulp_rpm/app/models/package.py b/pulp_rpm/app/models/package.py index 3f82a8985..aeab1d27a 100644 --- a/pulp_rpm/app/models/package.py +++ b/pulp_rpm/app/models/package.py @@ -8,6 +8,7 @@ from django.db.models.functions import RowNumber from pulpcore.plugin.models import Content, ContentManager +from pulpcore.plugin.util import get_domain_pk from pulp_rpm.app.constants import ( CHECKSUM_CHOICES, @@ -167,7 +168,7 @@ class Package(Content): # Currently filled by a database trigger - consider eventually switching to generated column evr = RpmVersionField() - pkgId = models.TextField(unique=True) # formerly "checksum" in Pulp 2 + pkgId = models.TextField(db_index=True) # formerly "checksum" in Pulp 2 checksum_type = models.TextField(choices=CHECKSUM_CHOICES) # Optional metadata @@ -236,6 +237,8 @@ class Package(Content): # E.g. glibc-2.26.11.3.2.nosrc.rpm vs glibc-2.26.11.3.2.src.rpm repo_key_fields = ("name", "epoch", "version", "release", "arch", "location_href") + _pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT) + @property def filename(self): """ @@ -283,7 +286,16 @@ def nevra_short(self): class Meta: default_related_name = "%(app_label)s_%(model_name)s" - unique_together = ("name", "epoch", "version", "release", "arch", "checksum_type", "pkgId") + unique_together = ( + "_pulp_domain", + "name", + "epoch", + "version", + "release", + "arch", + "checksum_type", + "pkgId", + ) class ReadonlyMeta: readonly = ["evr"] diff --git a/pulp_rpm/app/models/repository.py b/pulp_rpm/app/models/repository.py index 85ee6da3f..d9b6ed347 100644 --- a/pulp_rpm/app/models/repository.py +++ b/pulp_rpm/app/models/repository.py @@ -452,11 +452,23 @@ def content_handler(self, path): repository, publication = self.get_repository_and_publication() if not publication: return - base_url = "{}/".format( - urlpath_sanitize( - settings.CONTENT_ORIGIN, settings.CONTENT_PATH_PREFIX, self.base_path + if settings.DOMAIN_ENABLED: + base_url = "{}/".format( + urlpath_sanitize( + settings.CONTENT_ORIGIN, + settings.CONTENT_PATH_PREFIX, + self.pulp_domain.name, + self.base_path, + ) + ) + else: + base_url = "{}/".format( + urlpath_sanitize( + settings.CONTENT_ORIGIN, + settings.CONTENT_PATH_PREFIX, + self.base_path, + ) ) - ) val = textwrap.dedent( f"""\ [{self.name}] diff --git a/pulp_rpm/app/serializers/advisory.py b/pulp_rpm/app/serializers/advisory.py index d06034344..bc6147f4e 100644 --- a/pulp_rpm/app/serializers/advisory.py +++ b/pulp_rpm/app/serializers/advisory.py @@ -10,6 +10,7 @@ ModelSerializer, NoArtifactContentUploadSerializer, ) +from pulpcore.plugin.util import get_domain_pk from pulp_rpm.app.advisory import hash_update_record from pulp_rpm.app.fields import ( @@ -231,6 +232,16 @@ def validate(self, data): validated_data = super().validate(update_record_data) return validated_data + # pulpcore 3.22 feature + # TODO upload has to be able to "find" a unique advisory, and id is Not It? + # when uploading content second time I don't get error but already existing content + def retrieve(self, validated_data): + content = UpdateRecord.objects.filter( + id=validated_data["id"], + pulp_domain=get_domain_pk(), + ) + return content.first() + class Meta: fields = NoArtifactContentUploadSerializer.Meta.fields + ( "id", diff --git a/pulp_rpm/app/serializers/custom_metadata.py b/pulp_rpm/app/serializers/custom_metadata.py index fbe5014e7..0bdda0951 100644 --- a/pulp_rpm/app/serializers/custom_metadata.py +++ b/pulp_rpm/app/serializers/custom_metadata.py @@ -5,6 +5,7 @@ ContentChecksumSerializer, SingleArtifactContentUploadSerializer, ) +from pulpcore.plugin.util import get_domain_pk from pulp_rpm.app.models import RepoMetadataFile @@ -19,6 +20,16 @@ class RepoMetadataFileSerializer(SingleArtifactContentUploadSerializer, ContentC checksum = serializers.CharField(help_text=_("Checksum for the file.")) relative_path = serializers.CharField(help_text=_("Relative path of the file.")) + # Mirror unique_together from the Model + def retrieve(self, validated_data): + content = RepoMetadataFile.objects.filter( + data_type=validated_data["data_type"], + checksum=validated_data["checksum"], + relative_path=validated_data["relative_path"], + pulp_domain=get_domain_pk(), + ) + return content.first() + class Meta: fields = ( ContentChecksumSerializer.Meta.fields diff --git a/pulp_rpm/app/serializers/package.py b/pulp_rpm/app/serializers/package.py index cbf841d67..ff3ae9f42 100644 --- a/pulp_rpm/app/serializers/package.py +++ b/pulp_rpm/app/serializers/package.py @@ -10,6 +10,7 @@ ContentChecksumSerializer, SingleArtifactContentUploadSerializer, ) +from pulpcore.plugin.util import get_domain_pk from pulp_rpm.app.models import Package from pulp_rpm.app.shared_utils import read_crpackage_from_artifact, format_nvra @@ -278,8 +279,7 @@ def retrieve(self, data): raise NotAcceptable(detail="RPM file cannot be parsed for metadata") package = Package(**pkg) - - return Package.objects.filter(package.q()).first() + return Package.objects.filter(pkgId=package.pkgId, pulp_domain=get_domain_pk()).first() class Meta: fields = ( diff --git a/pulp_rpm/app/serializers/repository.py b/pulp_rpm/app/serializers/repository.py index aa19ec033..4f2cafcd4 100644 --- a/pulp_rpm/app/serializers/repository.py +++ b/pulp_rpm/app/serializers/repository.py @@ -4,6 +4,7 @@ from jsonschema import Draft7Validator from rest_framework import serializers +from pulpcore.plugin.util import get_domain from pulpcore.plugin.models import ( AsciiArmoredDetachedSigningService, Remote, @@ -17,7 +18,7 @@ RemoteSerializer, RepositorySerializer, RepositorySyncURLSerializer, - validate_unknown_fields, + ValidateFieldsMixin, ) from pulp_rpm.app.constants import ( @@ -341,7 +342,7 @@ def validate(self, data): return data -class CopySerializer(serializers.Serializer): +class CopySerializer(ValidateFieldsMixin, serializers.Serializer): """ A serializer for Content Copy API. """ @@ -358,17 +359,37 @@ def validate(self, data): """ Validate that the Serializer contains valid data. - Set the RpmRepository based on the RepositoryVersion if only the latter is provided. - Set the RepositoryVersion based on the RpmRepository if only the latter is provided. - Convert the human-friendly names of the content types into what Pulp needs to query on. - + Make sure the config-JSON matches the config-schema. + Check for cross-domain references (if domain-enabled). """ - super().validate(data) - if hasattr(self, "initial_data"): - validate_unknown_fields(self.initial_data, self.fields) + def check_domain(domain, href, name): + # We're doing just a string-check here rather than checking objects + # because there can be A LOT of objects, and this is happening in the view-layer + # where we have strictly-limited timescales to work with + if href and domain not in href: + raise serializers.ValidationError( + _("{} must be a part of the {} domain.").format(name, domain) + ) + + def check_cross_domain_config(cfg): + """Check that all config-elts are in 'our' domain.""" + # copy-cfg is a list of dictionaries. + # source_repo_version and dest_repo are required fields. + # Insure curr-domain exists in src/dest/dest_base_version/content-list hrefs + curr_domain_name = get_domain().name + for entry in cfg: + check_domain(curr_domain_name, entry["source_repo_version"], "dest_repo") + check_domain(curr_domain_name, entry["dest_repo"], "dest_repo") + check_domain( + curr_domain_name, entry.get("dest_base_version", None), "dest_base_version" + ) + for content_href in entry.get("content", []): + check_domain(curr_domain_name, content_href, "content") + super().validate(data) if "config" in data: + # Make sure config is valid JSON validator = Draft7Validator(COPY_CONFIG_SCHEMA) err = [] @@ -379,4 +400,7 @@ def validate(self, data): _("Provided copy criteria is invalid:'{}'".format(err)) ) + if settings.DOMAIN_ENABLED: + check_cross_domain_config(data["config"]) + return data diff --git a/pulp_rpm/app/shared_utils.py b/pulp_rpm/app/shared_utils.py index 9efb47bcd..3d1105c25 100644 --- a/pulp_rpm/app/shared_utils.py +++ b/pulp_rpm/app/shared_utils.py @@ -4,7 +4,6 @@ from hashlib import sha256 from django.conf import settings -from django.core.files.storage import default_storage as storage from django.utils.dateparse import parse_datetime @@ -44,7 +43,7 @@ def read_crpackage_from_artifact(artifact): artifact: inited and validated artifact to save """ filename = f"{artifact.pulp_id}.rpm" - artifact_file = storage.open(artifact.file.name) + artifact_file = artifact.pulp_domain.get_storage().open(artifact.file.name) with tempfile.NamedTemporaryFile("wb", dir=".", suffix=filename) as temp_file: shutil.copyfileobj(artifact_file, temp_file) temp_file.flush() diff --git a/pulp_rpm/app/tasks/comps.py b/pulp_rpm/app/tasks/comps.py index 57ef437a1..2f3bbb5d9 100644 --- a/pulp_rpm/app/tasks/comps.py +++ b/pulp_rpm/app/tasks/comps.py @@ -8,6 +8,8 @@ from pulpcore.plugin.models import PulpTemporaryFile, CreatedResource from pulpcore.plugin.models import Content +from pulpcore.plugin.util import get_domain + from pulp_rpm.app.comps import strdict_to_dict, dict_digest from pulp_rpm.app.models import ( @@ -27,6 +29,7 @@ def parse_comps_components(comps_file): created_objects = [] all_objects = [] comps = libcomps.Comps() + curr_domain = get_domain() # Read the file and pass the string along because comps.fromxml_f() will only take a # path-string that doesn't work on things like S3 storage with comps_file.file.open("rb") as comps_uploaded: @@ -42,7 +45,9 @@ def parse_comps_components(comps_file): if comps.langpacks: langpack_dict = PackageLangpacks.libcomps_to_dict(comps.langpacks) langpack, created = PackageLangpacks.objects.get_or_create( - matches=strdict_to_dict(comps.langpacks), digest=dict_digest(langpack_dict) + matches=strdict_to_dict(comps.langpacks), + digest=dict_digest(langpack_dict), + _pulp_domain=curr_domain, ) if created: created_objects.append(langpack) @@ -52,6 +57,7 @@ def parse_comps_components(comps_file): for category in comps.categories: category_dict = PackageCategory.libcomps_to_dict(category) category_dict["digest"] = dict_digest(category_dict) + category_dict["_pulp_domain"] = curr_domain packagecategory, created = PackageCategory.objects.get_or_create(**category_dict) if created: created_objects.append(packagecategory) @@ -61,6 +67,7 @@ def parse_comps_components(comps_file): for environment in comps.environments: environment_dict = PackageEnvironment.libcomps_to_dict(environment) environment_dict["digest"] = dict_digest(environment_dict) + environment_dict["_pulp_domain"] = curr_domain packageenvironment, created = PackageEnvironment.objects.get_or_create( **environment_dict ) @@ -72,6 +79,7 @@ def parse_comps_components(comps_file): for group in comps.groups: group_dict = PackageGroup.libcomps_to_dict(group) group_dict["digest"] = dict_digest(group_dict) + group_dict["_pulp_domain"] = curr_domain packagegroup, created = PackageGroup.objects.get_or_create(**group_dict) if created: created_objects.append(packagegroup) diff --git a/pulp_rpm/app/tasks/copy.py b/pulp_rpm/app/tasks/copy.py index e8902ae29..397981bd0 100644 --- a/pulp_rpm/app/tasks/copy.py +++ b/pulp_rpm/app/tasks/copy.py @@ -2,6 +2,7 @@ from django.db.models import Q from pulpcore.plugin.models import Content, RepositoryVersion +from pulpcore.plugin.util import get_domain_pk from pulp_rpm.app.depsolving import Solver from pulp_rpm.app.models import ( @@ -52,7 +53,12 @@ def find_children_of_content(content, src_repo_version): for nevra in package_nevras: (name, epoch, version, release, arch) = nevra advisory_package_q |= Q( - name=name, epoch=epoch, version=version, release=release, arch=arch + name=name, + epoch=epoch, + version=version, + release=release, + arch=arch, + pulp_domain=get_domain_pk(), ) children.update(packages.filter(advisory_package_q).values_list("pk", flat=True)) @@ -61,7 +67,12 @@ def find_children_of_content(content, src_repo_version): for nsvca in module_nsvcas: (name, stream, version, context, arch) = nsvca advisory_module_q |= Q( - name=name, stream=stream, version=version, context=context, arch=arch + name=name, + stream=stream, + version=version, + context=context, + arch=arch, + pulp_domain=get_domain_pk(), ) children.update(modules.filter(advisory_module_q).values_list("pk", flat=True)) @@ -145,6 +156,8 @@ def process_entry(entry): else: content_filter = Q() + content_filter &= Q(pulp_domain=get_domain_pk()) + return ( source_repo_version, dest_repo_version, diff --git a/pulp_rpm/app/tasks/publishing.py b/pulp_rpm/app/tasks/publishing.py index 942d7a928..862d95a6c 100644 --- a/pulp_rpm/app/tasks/publishing.py +++ b/pulp_rpm/app/tasks/publishing.py @@ -10,7 +10,6 @@ from django.conf import settings from django.core.files import File -from django.core.files.storage import default_storage as storage from django.db.models import Q from pulpcore.plugin.models import ( @@ -218,7 +217,8 @@ def handle_sub_repos(self, distribution_tree): original_treeinfo_content_artifact = distribution_tree.contentartifact_set.get( relative_path__in=[".treeinfo", "treeinfo"] ) - artifact_file = storage.open(original_treeinfo_content_artifact.artifact.file.name) + orig_artifact = original_treeinfo_content_artifact.artifact + artifact_file = orig_artifact.pulp_domain.get_storage().open(orig_artifact.file.name) with tempfile.NamedTemporaryFile("wb", dir=".") as temp_file: shutil.copyfileobj(artifact_file, temp_file) temp_file.flush() diff --git a/pulp_rpm/app/tasks/synchronizing.py b/pulp_rpm/app/tasks/synchronizing.py index 147454ff3..0ca2ee270 100644 --- a/pulp_rpm/app/tasks/synchronizing.py +++ b/pulp_rpm/app/tasks/synchronizing.py @@ -25,6 +25,7 @@ import createrepo_c as cr import libcomps +from pulpcore.plugin.util import get_domain from pulpcore.plugin.models import ( Artifact, ContentArtifact, @@ -1347,6 +1348,7 @@ async def parse_advisories(self, result): async with ProgressReport(**progress_data) as advisories_pb: for update in updates: update_record = UpdateRecord(**UpdateRecord.createrepo_to_dict(update)) + update_record.pulp_domain = get_domain() update_record.digest = hash_update_record(update) future_relations = {"collections": defaultdict(list), "references": []} @@ -1355,6 +1357,7 @@ async def parse_advisories(self, result): if coll_dict["name"] is None: coll_dict["name"] = "collection-autofill-" + uuid.uuid4().hex[:12] coll = UpdateCollection(**coll_dict) + coll.pulp_domain = get_domain() for package in collection.packages: pkg_dict = UpdateCollectionPackage.createrepo_to_dict(package) @@ -1364,6 +1367,7 @@ async def parse_advisories(self, result): for reference in update.references: reference_dict = UpdateReference.createrepo_to_dict(reference) ref = UpdateReference(**reference_dict) + coll.pulp_domain = get_domain() future_relations["references"].append(ref) await advisories_pb.aincrement() diff --git a/pulp_rpm/app/urls.py b/pulp_rpm/app/urls.py index f4686280a..e90889e2d 100644 --- a/pulp_rpm/app/urls.py +++ b/pulp_rpm/app/urls.py @@ -3,7 +3,10 @@ from .viewsets import CopyViewSet, CompsXmlViewSet -V3_API_ROOT = settings.V3_API_ROOT_NO_FRONT_SLASH +if settings.DOMAIN_ENABLED: + V3_API_ROOT = settings.V3_DOMAIN_API_ROOT_NO_FRONT_SLASH +else: + V3_API_ROOT = settings.V3_API_ROOT_NO_FRONT_SLASH urlpatterns = [ path(f"{V3_API_ROOT}rpm/copy/", CopyViewSet.as_view({"post": "create"})), diff --git a/pulp_rpm/app/viewsets/acs.py b/pulp_rpm/app/viewsets/acs.py index 88b9b7918..2411e369f 100644 --- a/pulp_rpm/app/viewsets/acs.py +++ b/pulp_rpm/app/viewsets/acs.py @@ -49,15 +49,15 @@ class RpmAlternateContentSourceViewSet(AlternateContentSourceViewSet, RolesMixin "action": ["retrieve"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.view_rpmalternatecontentsource", + "condition": "has_model_or_domain_or_obj_perms:rpm.view_rpmalternatecontentsource", }, { "action": ["create"], "principal": "authenticated", "effect": "allow", "condition": [ - "has_remote_param_model_or_obj_perms:rpm.view_rpmremote", - "has_model_perms:rpm.add_rpmalternatecontentsource", + "has_remote_param_model_or_domain_or_obj_perms:rpm.view_rpmremote", + "has_model_or_domain_perms:rpm.add_rpmalternatecontentsource", ], }, { @@ -65,8 +65,8 @@ class RpmAlternateContentSourceViewSet(AlternateContentSourceViewSet, RolesMixin "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.view_rpmalternatecontentsource", - "has_model_perms:rpm.refresh_rpmalternatecontentsource", + "has_model_or_domain_or_obj_perms:rpm.view_rpmalternatecontentsource", + "has_model_or_domain_perms:rpm.refresh_rpmalternatecontentsource", ], }, { @@ -74,9 +74,9 @@ class RpmAlternateContentSourceViewSet(AlternateContentSourceViewSet, RolesMixin "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.change_rpmalternatecontentsource", - "has_model_or_obj_perms:rpm.view_rpmalternatecontentsource", - "has_remote_param_model_or_obj_perms:rpm.view_rpmremote", + "has_model_or_domain_or_obj_perms:rpm.change_rpmalternatecontentsource", + "has_model_or_domain_or_obj_perms:rpm.view_rpmalternatecontentsource", + "has_remote_param_model_or_domain_or_obj_perms:rpm.view_rpmremote", ], }, { @@ -84,15 +84,16 @@ class RpmAlternateContentSourceViewSet(AlternateContentSourceViewSet, RolesMixin "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.delete_rpmalternatecontentsource", - "has_model_or_obj_perms:rpm.view_rpmalternatecontentsource", + "has_model_or_domain_or_obj_perms:rpm.delete_rpmalternatecontentsource", + "has_model_or_domain_or_obj_perms:rpm.view_rpmalternatecontentsource", ], }, { "action": ["list_roles", "add_role", "remove_role"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.manage_roles_rpmalternatecontentsource", + "condition": "has_model_or_domain_or_obj_perms:" + "rpm.manage_roles_rpmalternatecontentsource", }, ], "queryset_scoping": {"function": "scope_queryset"}, diff --git a/pulp_rpm/app/viewsets/repository.py b/pulp_rpm/app/viewsets/repository.py index 0bb401a65..74edf00ab 100644 --- a/pulp_rpm/app/viewsets/repository.py +++ b/pulp_rpm/app/viewsets/repository.py @@ -65,15 +65,15 @@ class RpmRepositoryViewSet(RepositoryViewSet, ModifyRepositoryActionMixin, Roles "action": ["retrieve"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.view_rpmrepository", + "condition": "has_model_or_domain_or_obj_perms:rpm.view_rpmrepository", }, { "action": ["create"], "principal": "authenticated", "effect": "allow", "condition": [ - "has_remote_param_model_or_obj_perms:rpm.view_rpmremote", - "has_model_perms:rpm.add_rpmrepository", + "has_remote_param_model_or_domain_or_obj_perms:rpm.view_rpmremote", + "has_model_or_domain_perms:rpm.add_rpmrepository", ], }, { @@ -81,9 +81,9 @@ class RpmRepositoryViewSet(RepositoryViewSet, ModifyRepositoryActionMixin, Roles "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.change_rpmrepository", - "has_model_or_obj_perms:rpm.view_rpmrepository", - "has_remote_param_model_or_obj_perms:rpm.view_rpmremote", + "has_model_or_domain_or_obj_perms:rpm.change_rpmrepository", + "has_model_or_domain_or_obj_perms:rpm.view_rpmrepository", + "has_remote_param_model_or_domain_or_obj_perms:rpm.view_rpmremote", ], }, { @@ -91,8 +91,8 @@ class RpmRepositoryViewSet(RepositoryViewSet, ModifyRepositoryActionMixin, Roles "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.modify_content_rpmrepository", - "has_model_or_obj_perms:rpm.view_rpmrepository", + "has_model_or_domain_or_obj_perms:rpm.modify_content_rpmrepository", + "has_model_or_domain_or_obj_perms:rpm.view_rpmrepository", ], }, { @@ -100,8 +100,8 @@ class RpmRepositoryViewSet(RepositoryViewSet, ModifyRepositoryActionMixin, Roles "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.delete_rpmrepository", - "has_model_or_obj_perms:rpm.view_rpmrepository", + "has_model_or_domain_or_obj_perms:rpm.delete_rpmrepository", + "has_model_or_domain_or_obj_perms:rpm.view_rpmrepository", ], }, { @@ -109,16 +109,16 @@ class RpmRepositoryViewSet(RepositoryViewSet, ModifyRepositoryActionMixin, Roles "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.sync_rpmrepository", - "has_model_or_obj_perms:rpm.view_rpmrepository", - "has_remote_param_model_or_obj_perms:rpm.view_rpmremote", + "has_model_or_domain_or_obj_perms:rpm.sync_rpmrepository", + "has_model_or_domain_or_obj_perms:rpm.view_rpmrepository", + "has_remote_param_model_or_domain_or_obj_perms:rpm.view_rpmremote", ], }, { "action": ["list_roles", "add_role", "remove_role"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.manage_roles_rpmrepository", + "condition": "has_model_or_domain_or_obj_perms:rpm.manage_roles_rpmrepository", }, ], "queryset_scoping": {"function": "scope_queryset"}, @@ -262,15 +262,15 @@ class RpmRepositoryVersionViewSet(RepositoryVersionViewSet): "action": ["list", "retrieve"], "principal": "authenticated", "effect": "allow", - "condition": "has_repository_model_or_obj_perms:rpm.view_rpmrepository", + "condition": "has_repository_model_or_domain_or_obj_perms:rpm.view_rpmrepository", }, { "action": ["destroy"], "principal": "authenticated", "effect": "allow", "condition": [ - "has_repository_model_or_obj_perms:rpm.delete_rpmrepository", - "has_repository_model_or_obj_perms:rpm.view_rpmrepository", + "has_repository_model_or_domain_or_obj_perms:rpm.delete_rpmrepository", + "has_repository_model_or_domain_or_obj_perms:rpm.view_rpmrepository", ], }, { @@ -278,8 +278,8 @@ class RpmRepositoryVersionViewSet(RepositoryVersionViewSet): "principal": "authenticated", "effect": "allow", "condition": [ - "has_repository_model_or_obj_perms:rpm.delete_rpmrepository_version", - "has_repository_model_or_obj_perms:rpm.view_rpmrepository", + "has_repository_model_or_domain_or_obj_perms:rpm.delete_rpmrepository_version", + "has_repository_model_or_domain_or_obj_perms:rpm.view_rpmrepository", ], }, { @@ -287,8 +287,8 @@ class RpmRepositoryVersionViewSet(RepositoryVersionViewSet): "principal": "authenticated", "effect": "allow", "condition": [ - "has_repository_model_or_obj_perms:rpm.repair_rpmrepository", - "has_repository_model_or_obj_perms:rpm.view_rpmrepository", + "has_repository_model_or_domain_or_obj_perms:rpm.repair_rpmrepository", + "has_repository_model_or_domain_or_obj_perms:rpm.view_rpmrepository", ], }, ], @@ -316,21 +316,21 @@ class RpmRemoteViewSet(RemoteViewSet, RolesMixin): "action": ["retrieve"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.view_rpmremote", + "condition": "has_model_or_domain_or_obj_perms:rpm.view_rpmremote", }, { "action": ["create"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_perms:rpm.add_rpmremote", + "condition": "has_model_or_domain_perms:rpm.add_rpmremote", }, { "action": ["update", "partial_update"], "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.change_rpmremote", - "has_model_or_obj_perms:rpm.view_rpmremote", + "has_model_or_domain_or_obj_perms:rpm.change_rpmremote", + "has_model_or_domain_or_obj_perms:rpm.view_rpmremote", ], }, { @@ -338,15 +338,15 @@ class RpmRemoteViewSet(RemoteViewSet, RolesMixin): "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.delete_rpmremote", - "has_model_or_obj_perms:rpm.view_rpmremote", + "has_model_or_domain_or_obj_perms:rpm.delete_rpmremote", + "has_model_or_domain_or_obj_perms:rpm.view_rpmremote", ], }, { "action": ["list_roles", "add_role", "remove_role"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.manage_roles_rpmremote", + "condition": "has_model_or_domain_or_obj_perms:rpm.manage_roles_rpmremote", }, ], "queryset_scoping": {"function": "scope_queryset"}, @@ -395,21 +395,21 @@ class UlnRemoteViewSet(RemoteViewSet, RolesMixin): "action": ["retrieve"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.view_ulnremote", + "condition": "has_model_or_domain_or_obj_perms:rpm.view_ulnremote", }, { "action": ["create"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_perms:rpm.add_ulnremote", + "condition": "has_model_or_domain_perms:rpm.add_ulnremote", }, { "action": ["update", "partial_update"], "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.change_ulnremote", - "has_model_or_obj_perms:rpm.view_ulnremote", + "has_model_or_domain_or_obj_perms:rpm.change_ulnremote", + "has_model_or_domain_or_obj_perms:rpm.view_ulnremote", ], }, { @@ -417,15 +417,15 @@ class UlnRemoteViewSet(RemoteViewSet, RolesMixin): "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.delete_ulnremote", - "has_model_or_obj_perms:rpm.view_ulnremote", + "has_model_or_domain_or_obj_perms:rpm.delete_ulnremote", + "has_model_or_domain_or_obj_perms:rpm.view_ulnremote", ], }, { "action": ["list_roles", "add_role", "remove_role"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.manage_roles_ulnremote", + "condition": "has_model_or_domain_or_obj_perms:rpm.manage_roles_ulnremote", }, ], "queryset_scoping": {"function": "scope_queryset"}, @@ -474,15 +474,15 @@ class RpmPublicationViewSet(PublicationViewSet, RolesMixin): "action": ["retrieve"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.view_rpmpublication", + "condition": "has_model_or_domain_or_obj_perms:rpm.view_rpmpublication", }, { "action": ["create"], "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_perms:rpm.add_rpmpublication", - "has_repo_attr_model_or_obj_perms:rpm.view_rpmrepository", + "has_model_or_domain_perms:rpm.add_rpmpublication", + "has_repo_attr_model_or_domain_or_obj_perms:rpm.view_rpmrepository", ], }, { @@ -490,15 +490,15 @@ class RpmPublicationViewSet(PublicationViewSet, RolesMixin): "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.delete_rpmpublication", - "has_model_or_obj_perms:rpm.view_rpmpublication", + "has_model_or_domain_or_obj_perms:rpm.delete_rpmpublication", + "has_model_or_domain_or_obj_perms:rpm.view_rpmpublication", ], }, { "action": ["list_roles", "add_role", "remove_role"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.manage_roles_rpmpublication", + "condition": "has_model_or_domain_or_obj_perms:rpm.manage_roles_rpmpublication", }, ], "queryset_scoping": {"function": "scope_queryset"}, @@ -600,16 +600,16 @@ class RpmDistributionViewSet(DistributionViewSet, RolesMixin): "action": ["retrieve"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.view_rpmdistribution", + "condition": "has_model_or_domain_or_obj_perms:rpm.view_rpmdistribution", }, { "action": ["create"], "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_perms:rpm.add_rpmdistribution", - "has_publication_param_model_or_obj_perms:rpm.view_rpmpublication", - "has_repo_attr_model_or_obj_perms:rpm.view_rpmrepository", + "has_model_or_domain_perms:rpm.add_rpmdistribution", + "has_publication_param_model_or_domain_or_obj_perms:rpm.view_rpmpublication", + "has_repo_attr_model_or_domain_or_obj_perms:rpm.view_rpmrepository", ], }, { @@ -617,10 +617,10 @@ class RpmDistributionViewSet(DistributionViewSet, RolesMixin): "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.change_rpmdistribution", - "has_model_or_obj_perms:rpm.view_rpmdistribution", - "has_publication_param_model_or_obj_perms:rpm.view_rpmpublication", - "has_repo_attr_model_or_obj_perms:rpm.view_rpmrepository", + "has_model_or_domain_or_obj_perms:rpm.change_rpmdistribution", + "has_model_or_domain_or_obj_perms:rpm.view_rpmdistribution", + "has_publication_param_model_or_domain_or_obj_perms:rpm.view_rpmpublication", + "has_repo_attr_model_or_domain_or_obj_perms:rpm.view_rpmrepository", ], }, { @@ -628,15 +628,15 @@ class RpmDistributionViewSet(DistributionViewSet, RolesMixin): "principal": "authenticated", "effect": "allow", "condition": [ - "has_model_or_obj_perms:rpm.delete_rpmdistribution", - "has_model_or_obj_perms:rpm.view_rpmdistribution", + "has_model_or_domain_or_obj_perms:rpm.delete_rpmdistribution", + "has_model_or_domain_or_obj_perms:rpm.view_rpmdistribution", ], }, { "action": ["list_roles", "add_role", "remove_role"], "principal": "authenticated", "effect": "allow", - "condition": "has_model_or_obj_perms:rpm.manage_roles_rpmdistribution", + "condition": "has_model_or_domain_or_obj_perms:rpm.manage_roles_rpmdistribution", }, ], "queryset_scoping": {"function": "scope_queryset"}, diff --git a/pulp_rpm/tests/conftest.py b/pulp_rpm/tests/conftest.py index d1e9599ed..4450539db 100644 --- a/pulp_rpm/tests/conftest.py +++ b/pulp_rpm/tests/conftest.py @@ -63,10 +63,13 @@ def rpm_repository_api(rpm_client): def rpm_repository_factory(rpm_repository_api, gen_object_with_cleanup): """A factory to generate an RPM Repository with auto-deletion after the test run.""" - def _rpm_repository_factory(**kwargs): + def _rpm_repository_factory(pulp_domain=None, **body): data = {"name": str(uuid.uuid4())} - data.update(kwargs) - return gen_object_with_cleanup(rpm_repository_api, data) + data.update(body) + kwargs = {} + if pulp_domain: + kwargs["pulp_domain"] = pulp_domain + return gen_object_with_cleanup(rpm_repository_api, data, **kwargs) return _rpm_repository_factory @@ -75,10 +78,15 @@ def _rpm_repository_factory(**kwargs): def rpm_rpmremote_factory(rpm_rpmremote_api, gen_object_with_cleanup): """A factory to generate an RPM Remote with auto-deletion after the test run.""" - def _rpm_rpmremote_factory(*, url=RPM_UNSIGNED_FIXTURE_URL, policy="immediate", **kwargs): + def _rpm_rpmremote_factory( + *, url=RPM_UNSIGNED_FIXTURE_URL, policy="immediate", pulp_domain=None, **body + ): data = {"url": url, "policy": policy, "name": str(uuid.uuid4())} - data.update(kwargs) - return gen_object_with_cleanup(rpm_rpmremote_api, data) + data.update(body) + kwargs = {} + if pulp_domain: + kwargs["pulp_domain"] = pulp_domain + return gen_object_with_cleanup(rpm_rpmremote_api, data, **kwargs) return _rpm_rpmremote_factory @@ -87,10 +95,13 @@ def _rpm_rpmremote_factory(*, url=RPM_UNSIGNED_FIXTURE_URL, policy="immediate", def rpm_distribution_factory(rpm_distribution_api, gen_object_with_cleanup): """A factory to generate an RPM Distribution with auto-deletion after the test run.""" - def _rpm_distribution_factory(**kwargs): + def _rpm_distribution_factory(pulp_domain=None, **body): data = {"base_path": str(uuid.uuid4()), "name": str(uuid.uuid4())} - data.update(kwargs) - return gen_object_with_cleanup(rpm_distribution_api, data) + data.update(body) + kwargs = {} + if pulp_domain: + kwargs["pulp_domain"] = pulp_domain + return gen_object_with_cleanup(rpm_distribution_api, data, **kwargs) return _rpm_distribution_factory @@ -99,10 +110,13 @@ def _rpm_distribution_factory(**kwargs): def rpm_publication_factory(rpm_publication_api, gen_object_with_cleanup): """A factory to generate an RPM Publication with auto-deletion after the test run.""" - def _rpm_publication_factory(**kwargs): + def _rpm_publication_factory(pulp_domain=None, **body): # XOR check on repository and repository_version - assert bool("repository" in kwargs) ^ bool("repository_version" in kwargs) - return gen_object_with_cleanup(rpm_publication_api, kwargs) + assert bool("repository" in body) ^ bool("repository_version" in body) + kwargs = {} + if pulp_domain: + kwargs["pulp_domain"] = pulp_domain + return gen_object_with_cleanup(rpm_publication_api, body, **kwargs) return _rpm_publication_factory diff --git a/pulp_rpm/tests/functional/api/test_advisory_upload.py b/pulp_rpm/tests/functional/api/test_advisory_upload.py index 57b881a16..643d81281 100644 --- a/pulp_rpm/tests/functional/api/test_advisory_upload.py +++ b/pulp_rpm/tests/functional/api/test_advisory_upload.py @@ -1,477 +1,24 @@ """Tests that perform actions over advisory content unit upload.""" import pytest import os -import json -from tempfile import NamedTemporaryFile from pulpcore.tests.functional.utils import PulpTaskError from pulp_rpm.tests.functional.constants import ( - RPM_UNSIGNED_FIXTURE_URL, + BEAR_JSON, + CAMEL_BEAR_DOG_JSON, + CAMEL_BIRD_JSON, + CAMEL_JSON, + CESA_2020_5002, + CESA_2020_4910, RPM_PACKAGE_FILENAME, + RPM_UNSIGNED_FIXTURE_URL, ) from pulpcore.client.pulp_rpm.exceptions import ApiException -BASE_TEST_JSON = """{ - "updated": "2014-09-28 00:00:00", - "issued": "2014-09-24 00:00:00", - "id": "RHSA-XXXX:XXXX", - "pkglist": [ - { - "packages": [ - { - "arch": "noarch", - "epoch": "0", - "filename": "bear-4.1-1.noarch.rpm", - "name": "bear", - "reboot_suggested": false, - "relogin_suggested": false, - "restart_suggested": false, - "release": "1", - "src": "http://www.fedoraproject.org", - "sum": "", - "sum_type": "", - "version": "4.1" - } - ] - } - ], - "severity": "", - "description": "Not available", - "reboot_suggested": false, - "solution": "Not available", - "fromstr": "centos-announce@centos.org"}""" - -BEAR_JSON = """{ - "issued": "2020-03-08 20:04:01", - "id": "CEBA-2019--666", - "type": "Bug Fix Advisory", - "release": "1", - "version": "1", - "pkglist": [ - { - "packages": [ - { - "arch": "noarch", - "epoch": "0", - "filename": "bear-4.1-1.noarch.rpm", - "name": "bear", - "reboot_suggested": false, - "relogin_suggested": false, - "restart_suggested": false, - "release": "1", - "src": "http://www.fedoraproject.org", - "sum": "", - "sum_type": "", - "version": "4.1" - } - ] - } - ], - "severity": "", - "description": "Not available", - "reboot_suggested": false, - "updated": "2020-03-08 20:04:01", - "solution": "Not available", - "fromstr": "centos-announce@centos.org" -}""" - -CAMEL_JSON = """{ - "issued": "2020-03-08 20:04:01", - "id": "CEBA-2019--666", - "type": "Bug Fix Advisory", - "release": "1", - "version": "1", - "pkglist": [ - { - "packages": [ - { - "arch": "noarch", - "epoch": "0", - "filename": "camel-0.1-1.noarch.rpm", - "name": "camel", - "reboot_suggested": false, - "relogin_suggested": false, - "restart_suggested": false, - "release": "1", - "src": "http://www.fedoraproject.org", - "sum": "", - "sum_type": "", - "version": "0.1" - } - ] - } - ], - "severity": "", - "description": "Not available", - "reboot_suggested": false, - "updated": "2020-03-08 20:04:01", - "solution": "Not available", - "fromstr": "centos-announce@centos.org" -}""" - -CAMEL_BIRD_JSON = """{ - "issued": "2020-03-08 20:04:01", - "id": "CEBA-2019--666", - "type": "Bug Fix Advisory", - "release": "1", - "version": "1", - "pkglist": [ - { - "packages": [ - { - "arch": "noarch", - "epoch": "0", - "filename": "camel-0.1-1.noarch.rpm", - "name": "camel", - "reboot_suggested": false, - "relogin_suggested": false, - "restart_suggested": false, - "release": "1", - "src": "http://www.fedoraproject.org", - "sum": "", - "sum_type": "", - "version": "0.1" - }, - { - "arch": "noarch", - "epoch": "0", - "filename": "bird-1.2-3.noarch.rpm", - "name": "bird", - "reboot_suggested": false, - "relogin_suggested": false, - "restart_suggested": false, - "release": "3", - "src": "http://www.fedoraproject.org", - "sum": "", - "sum_type": "", - "version": "1.2" - } - ] - } - ], - "severity": "", - "description": "Not available", - "reboot_suggested": false, - "updated": "2020-03-08 20:04:01", - "solution": "Not available", - "fromstr": "centos-announce@centos.org" -}""" - -CAMEL_BEAR_DOG_JSON = """{ - "issued": "2020-03-08 20:04:01", - "id": "CEBA-2019--666", - "type": "Bug Fix Advisory", - "release": "1", - "version": "1", - "pkglist": [ - { - "packages": [ - { - "arch": "noarch", - "epoch": "0", - "filename": "camel-0.1-1.noarch.rpm", - "name": "camel", - "reboot_suggested": false, - "relogin_suggested": false, - "restart_suggested": false, - "release": "1", - "src": "http://www.fedoraproject.org", - "sum": "", - "sum_type": "", - "version": "0.1" - }, - { - "arch": "noarch", - "epoch": "0", - "filename": "bear-4.1-1.noarch.rpm", - "name": "bear", - "reboot_suggested": false, - "relogin_suggested": false, - "restart_suggested": false, - "release": "1", - "src": "http://www.fedoraproject.org", - "sum": "", - "sum_type": "", - "version": "4.1" - }, - { - "arch": "noarch", - "epoch": "0", - "filename": "dog-6.1-6.noarch.rpm", - "name": "dog", - "reboot_suggested": false, - "relogin_suggested": false, - "restart_suggested": false, - "release": "6", - "src": "http://www.fedoraproject.org", - "sum": "", - "sum_type": "", - "version": "6.1" - } - ] - } - ], - "severity": "", - "description": "Not available", - "reboot_suggested": false, - "updated": "2020-03-08 20:04:01", - "solution": "Not available", - "fromstr": "centos-announce@centos.org" -}""" - -CESA_2020_5002 = """{ - "title": "Moderate CentOS curl Security Update", - "type": "security", - "description": "", - "release": "el7", - "version": "1", - "severity": "Moderate", - "status": "final", - "updated": "2020-11-18 17:30:30", - "issued": "2020-11-18 17:30:30", - "pkglist": [ - { - "packages": [ - { - "arch": "x86_64", - "epoch": "0", - "filename": "curl-7.29.0-59.el7_9.1.x86_64.rpm", - "release": "59.el7_9.1", - "name": "curl", - "sum": "dfc95bdd8057839d4b45153318acb4e09f4da257afee1c57c07781870a68ecef", - "sum_type": "sha256" - }, - { - "arch": "i686", - "epoch": "0", - "filename": "libcurl-7.29.0-59.el7_9.1.i686.rpm", - "release": "59.el7_9.1", - "name": "libcurl", - "sum": "3054ca1c0cc8eef5f08ce1d3be56c7a39e97d92361e8bd265bea14d06f590219", - "sum_type": "sha256" - }, - { - "arch": "x86_64", - "epoch": "0", - "filename": "libcurl-7.29.0-59.el7_9.1.x86_64.rpm", - "release": "59.el7_9.1", - "name": "libcurl", - "sum": "4ad0b71e3a6468fba1b43ab82fad024415b5296c7b77d1348fb9afa3f828f98e", - "sum_type": "sha256" - }, - { - "arch": "i686", - "epoch": "0", - "filename": "libcurl-devel-7.29.0-59.el7_9.1.i686.rpm", - "release": "59.el7_9.1", - "name": "libcurl-devel", - "sum": "7ab4f1b0aa285d3773fdbd8bfc529969ca101a627d3ea88bea1f99a42093e132", - "sum_type": "sha256" - }, - { - "arch": "x86_64", - "epoch": "0", - "filename": "libcurl-devel-7.29.0-59.el7_9.1.x86_64.rpm", - "release": "59.el7_9.1", - "name": "libcurl-devel", - "sum": "f92fde3f97c0034135796baa7cd55f87c0550a88ac79adbdcc9c7f64c595614b", - "sum_type": "sha256" - } - ] - } - ], - "id": "TEST-CESA-2020:5002", - "from": "centos-announce@centos.org", - "references": [ - { - "href": "https://access.redhat.com/errata/RHSA-2020:5002", - "ref_id": "CESA-2020:5002", - "title": "Moderate CentOS curl Security Update", - "ref_type": "security" - }, - { - "href": "https://lists.centos.org/pipermail/centos-announce/2020-November/035840.html", - "ref_id": "CESA-2020:5002", - "title": "Moderate CentOS curl Security Update", - "ref_type": "security" - } - ] -}""" # noqa - -CESA_2020_4910 = """{ - "title": "Important CentOS xorg-x11-server Security Update", - "type": "security", - "description": "", - "release": "el7", - "version": "1", - "severity": "Important", - "status": "final", - "updated": "2020-11-06 22:19:48", - "issued": "2020-11-06 22:19:48", - "pkglist": [ - { - "packages": [ - { - "arch": "x86_64", - "epoch": "0", - "filename": "xorg-x11-server-Xdmx-1.20.4-12.el7_9.x86_64.rpm", - "release": "12.el7_9", - "name": "xorg-x11-server-Xdmx", - "sum": "0435f345b2b188c76dbb4a538bf0f878834a41e723491df1926231020fd88efd", - "sum_type": "sha256" - }, - { - "arch": "x86_64", - "epoch": "0", - "filename": "xorg-x11-server-Xephyr-1.20.4-12.el7_9.x86_64.rpm", - "release": "12.el7_9", - "name": "xorg-x11-server-Xephyr", - "sum": "2d21d53b305e30b058ca88d8778bda67000a5d52ab320f04b35e63f6a78f2163", - "sum_type": "sha256" - }, - { - "arch": "x86_64", - "epoch": "0", - "filename": "xorg-x11-server-Xnest-1.20.4-12.el7_9.x86_64.rpm", - "release": "12.el7_9", - "name": "xorg-x11-server-Xnest", - "sum": "51fbacc2e26050a7772549f1fe16c46bd8063ea187825ad89b237c34fa9b4250", - "sum_type": "sha256" - }, - { - "arch": "x86_64", - "epoch": "0", - "filename": "xorg-x11-server-Xorg-1.20.4-12.el7_9.x86_64.rpm", - "release": "12.el7_9", - "name": "xorg-x11-server-Xorg", - "sum": "eb89964d5fd40ec94ee8db97a5a14cc8dd6329b83d82ab29ee1a595653ce5223", - "sum_type": "sha256" - }, - { - "arch": "x86_64", - "epoch": "0", - "filename": "xorg-x11-server-Xvfb-1.20.4-12.el7_9.x86_64.rpm", - "release": "12.el7_9", - "name": "xorg-x11-server-Xvfb", - "sum": "ea32b047fba7fd327bf943da2a18413a1ed3e245cc1b077f34d1c8f6048d9813", - "sum_type": "sha256" - }, - { - "arch": "x86_64", - "epoch": "0", - "filename": "xorg-x11-server-Xwayland-1.20.4-12.el7_9.x86_64.rpm", - "release": "12.el7_9", - "name": "xorg-x11-server-Xwayland", - "sum": "4a6ffb39008edd469d4365bb3bf858f5f5f466129eb9e330d978b28866906891", - "sum_type": "sha256" - }, - { - "arch": "x86_64", - "epoch": "0", - "filename": "xorg-x11-server-common-1.20.4-12.el7_9.x86_64.rpm", - "release": "12.el7_9", - "name": "xorg-x11-server-common", - "sum": "339bcf68cb37a454eddff7218aff4153a36bafc0d36e2b5b6bde8311c6f3eed8", - "sum_type": "sha256" - }, - { - "arch": "i686", - "epoch": "0", - "filename": "xorg-x11-server-devel-1.20.4-12.el7_9.i686.rpm", - "release": "12.el7_9", - "name": "xorg-x11-server-devel", - "sum": "55e13fc8624f8a63b785b5194281c38a4670f03113b0ff2b8fc1df1ca473e1e8", - "sum_type": "sha256" - }, - { - "arch": "x86_64", - "epoch": "0", - "filename": "xorg-x11-server-devel-1.20.4-12.el7_9.x86_64.rpm", - "release": "12.el7_9", - "name": "xorg-x11-server-devel", - "sum": "e2dd0c67f3d88a9506f72fcc21ec0af786a377befabac8e1670d3e012d844b06", - "sum_type": "sha256" - }, - { - "arch": "noarch", - "epoch": "0", - "filename": "xorg-x11-server-source-1.20.4-12.el7_9.noarch.rpm", - "release": "12.el7_9", - "name": "xorg-x11-server-source", - "sum": "1baa9cb2d4f8d4300ac333fbc7bc130dce9145c67aea3bd6efa4a0354fc92b6d", - "sum_type": "sha256" - } - ] - } - ], - "id": "TEST-CESA-2020:4910", - "from": "centos-announce@centos.org", - "references": [ - { - "href": "https://access.redhat.com/errata/RHSA-2020:4910", - "ref_id": "CESA-2020:4910", - "title": "Important CentOS xorg-x11-server Security Update", - "ref_type": "security" - }, - { - "href": "https://lists.centos.org/pipermail/centos-cr-announce/2020-November/012889.html", - "ref_id": "CESA-2020:4910", - "title": "Important CentOS xorg-x11-server Security Update", - "ref_type": "security" - } - ] -}""" # noqa - - -@pytest.fixture -def upload_wrong_file_type(rpm_advisory_api, http_get): - def _upload(remote_path): - with NamedTemporaryFile() as file_to_upload: - file_to_upload.write(http_get(remote_path)) - upload_attrs = { - "file": file_to_upload.name, - } - return rpm_advisory_api.create(**upload_attrs) - - return _upload - - -@pytest.fixture -def upload_advisory_json(rpm_advisory_api): - def _upload(advisory=BASE_TEST_JSON, repository=None): - """Upload advisory from a json file.""" - with NamedTemporaryFile("w+") as file_to_upload: - json.dump(json.loads(advisory), file_to_upload) - upload_attrs = { - "file": file_to_upload.name, - } - if repository: - upload_attrs["repository"] = repository.pulp_href - - file_to_upload.flush() - return rpm_advisory_api.create(**upload_attrs) - - return _upload - - -@pytest.fixture -def assert_uploaded_advisory(rpm_advisory_api): - def _from_results(response, advisory_id): - assert 2 == len(response.created_resources) - vers_href = None - for rsrc in response.created_resources: - if "versions" in rsrc: - vers_href = rsrc - advisories = rpm_advisory_api.list(id=advisory_id, repository_version=vers_href) - assert 1 == len(advisories.results) - return advisories.results[0].pulp_href, vers_href - - return _from_results - - -def test_upload_wrong_type(upload_wrong_file_type, delete_orphans_pre): +@pytest.mark.parallel +def test_upload_wrong_type(upload_wrong_file_type): """Test that a proper error is raised when wrong file content type is uploaded.""" bad_file_to_use = os.path.join(RPM_UNSIGNED_FIXTURE_URL, RPM_PACKAGE_FILENAME) with pytest.raises(ApiException) as e: @@ -479,42 +26,38 @@ def test_upload_wrong_type(upload_wrong_file_type, delete_orphans_pre): assert "JSON" in e.value.body -def test_upload_json(upload_advisory_json, rpm_advisory_api, monitor_task, delete_orphans_pre): +@pytest.mark.parallel +def test_upload_json(upload_advisory_factory): """Test upload advisory from JSON file.""" - upload = upload_advisory_json() - content = monitor_task(upload.task).created_resources[0] - advisory = rpm_advisory_api.read(content) - assert advisory.id == "RHSA-XXXX:XXXX" + advisory1, _, an_id = upload_advisory_factory(set_id=True) + assert advisory1.id == an_id +@pytest.mark.parallel def test_merging( - upload_advisory_json, + upload_advisory_factory, assert_uploaded_advisory, rpm_advisory_api, rpm_repository_factory, - monitor_task, - delete_orphans_pre, ): """Test the 'same' advisory, diff pkglists, into a repo, expecting a merged package-list.""" repo = rpm_repository_factory() - upload = upload_advisory_json(advisory=BEAR_JSON, repository=repo) - task_response = monitor_task(upload.task) - advisory_href, vers_href = assert_uploaded_advisory(task_response, "CEBA-2019--666") + bear, vers_href, an_id = upload_advisory_factory( + advisory=BEAR_JSON, repository=repo, set_id=True + ) + bear = rpm_advisory_api.read(bear.pulp_href) + assert_uploaded_advisory(an_id, vers_href) assert vers_href == f"{repo.pulp_href}versions/1/" - bear = rpm_advisory_api.read(advisory_href) - assert "CEBA-2019--666" == bear.id # Is this suppose to be an equal comparison? assert 1 == len(bear.pkglist) assert 1 == len(bear.pkglist[0].packages) # Second upload, no pkg-intersection - add both collections # NOTE: also check that unnamed-collections are now named "collection_N", so # they can be uniquely identified - upload = upload_advisory_json(advisory=CAMEL_JSON, repository=repo) - task_response = monitor_task(upload.task) - advisory_href, vers_href = assert_uploaded_advisory(task_response, "CEBA-2019--666") + _, vers_href, _ = upload_advisory_factory(advisory=CAMEL_JSON, repository=repo, use_id=an_id) + advisory_href, vers_herf = assert_uploaded_advisory(an_id, vers_href) assert vers_href == f"{repo.pulp_href}versions/2/" cambear = rpm_advisory_api.read(advisory_href) - assert "CEBA-2019--666" == cambear.id assert 2 == len(cambear.pkglist) coll_names = [row.name for row in cambear.pkglist] assert "collection_0" in coll_names @@ -526,19 +69,18 @@ def test_merging( assert "bear" in names # Third upload, two pkgs, intersects with existing, expect AdvisoryConflict failure - upload = upload_advisory_json(advisory=CAMEL_BIRD_JSON, repository=repo) with pytest.raises(PulpTaskError) as ctx: - task_response = monitor_task(upload.task) + _, _, _ = upload_advisory_factory(advisory=CAMEL_BIRD_JSON, repository=repo, use_id=an_id) assert "neither package list is a proper subset of the other" in str(ctx.value) assert "ALLOW_AUTOMATIC_UNSAFE_ADVISORY_CONFLICT_RESOLUTION" in str(ctx.value) # Fourth upload, intersecting pkglists, expecting three pkgs - upload = upload_advisory_json(advisory=CAMEL_BEAR_DOG_JSON, repository=repo) - task_response = monitor_task(upload.task) - advisory_href, vers_href = assert_uploaded_advisory(task_response, "CEBA-2019--666") + cambeardog, vers_href, _ = upload_advisory_factory( + advisory=CAMEL_BEAR_DOG_JSON, repository=repo, use_id=an_id + ) + assert_uploaded_advisory(an_id, vers_href) assert vers_href == f"{repo.pulp_href}versions/3/" - cambeardog = rpm_advisory_api.read(advisory_href) - assert "CEBA-2019--666" == cambeardog.id + assert an_id == cambeardog.id assert 1 == len(cambeardog.pkglist) # Expect one collection, not a merge names = [pkg["name"] for pkg in cambeardog.pkglist[0].packages] @@ -548,13 +90,11 @@ def test_merging( assert "dog" in names +@pytest.mark.parallel def test_8683_error_path( - upload_advisory_json, + upload_advisory_factory, assert_uploaded_advisory, - rpm_advisory_api, rpm_repository_factory, - monitor_task, - delete_orphans_pre, ): """ Test that upload-fail doesn't break all future uploads. @@ -563,19 +103,18 @@ def test_8683_error_path( """ # Upload an advisory repo = rpm_repository_factory() - upload = upload_advisory_json(advisory=CESA_2020_5002, repository=repo) - task_response = monitor_task(upload.task) - assert_uploaded_advisory(task_response, "TEST-CESA-2020:5002") + + _, vers_href, id1 = upload_advisory_factory( + advisory=CESA_2020_5002, repository=repo, set_id=True + ) + assert_uploaded_advisory(id1, vers_href) # Try to upload it 'again' and watch it fail with pytest.raises(PulpTaskError): - upload = upload_advisory_json(advisory=CESA_2020_5002, repository=repo) - monitor_task(upload.task) + _, _, _ = upload_advisory_factory(advisory=CESA_2020_5002, repository=repo, use_id=id1) # Upload a different advisory and Don't Fail - upload = upload_advisory_json(advisory=CESA_2020_4910, repository=repo) - task_response = monitor_task(upload.task) - advisory_href, vers_href = assert_uploaded_advisory(task_response, "TEST-CESA-2020:4910") - advisory = rpm_advisory_api.read(advisory_href) - # Make sure the second advisory was persisted - assert "TEST-CESA-2020:4910" == advisory.id + advisory3, _, id2 = upload_advisory_factory( + advisory=CESA_2020_4910, repository=repo, set_id=True + ) + assert id2 == advisory3.id diff --git a/pulp_rpm/tests/functional/api/test_domains.py b/pulp_rpm/tests/functional/api/test_domains.py new file mode 100644 index 000000000..6eab7a9c1 --- /dev/null +++ b/pulp_rpm/tests/functional/api/test_domains.py @@ -0,0 +1,528 @@ +import json + +import pytest +import uuid + +from django.conf import settings + +from pulpcore.client.pulp_rpm import ApiException, Copy, RpmRepositorySyncURL +from pulpcore.client.pulpcore.exceptions import ApiException as CoreApiException + +from pulp_rpm.tests.functional.utils import ( + gen_rpm_remote, + get_package_repo_path, +) +from pulp_rpm.tests.functional.constants import ( + RPM_SIGNED_FIXTURE_URL, +) + +if not settings.DOMAIN_ENABLED: + pytest.skip("Domains not enabled.", allow_module_level=True) + + +def test_domain_create( + domains_api_client, + gen_object_with_cleanup, + monitor_task, + rpm_package_api, + rpm_repository_api, + rpm_rpmremote_api, +): + """Test repo-creation in a domain.""" + body = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + } + domain = gen_object_with_cleanup(domains_api_client, body) + domain_name = domain.name + + # create and sync in default domain (not specified) + remote = gen_object_with_cleanup(rpm_rpmremote_api, gen_rpm_remote(RPM_SIGNED_FIXTURE_URL)) + repo_body = {"name": str(uuid.uuid4()), "remote": remote.pulp_href} + repo = gen_object_with_cleanup(rpm_repository_api, repo_body) + # Check that we can "find" the new repo in the default-domain + repo = rpm_repository_api.read(repo.pulp_href) + sync_url = RpmRepositorySyncURL(remote=remote.pulp_href) + monitor_task(rpm_repository_api.sync(repo.pulp_href, sync_url).task) + + # check that newly created domain doesn't have a repo or any packages + assert rpm_repository_api.list(pulp_domain=domain_name).count == 0 + assert rpm_package_api.list(pulp_domain=domain_name).count == 0 + + +def test_domain_sync( + cleanup_domains, + domains_api_client, + gen_object_with_cleanup, + monitor_task, + rpm_advisory_api, + rpm_package_api, + rpm_package_category_api, + rpm_package_groups_api, + rpm_package_lang_packs_api, + rpm_repository_api, + rpm_rpmremote_api, +): + """Test repo-sync in a domain.""" + + try: + body = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + } + domain = gen_object_with_cleanup(domains_api_client, body) + domain_name = domain.name + + # create and sync in the newly-created domain + remote = gen_object_with_cleanup( + rpm_rpmremote_api, + {"name": str(uuid.uuid4()), "url": RPM_SIGNED_FIXTURE_URL}, + pulp_domain=domain_name, + ) + repo_body = {"name": str(uuid.uuid4()), "remote": remote.pulp_href} + repo = gen_object_with_cleanup(rpm_repository_api, repo_body, pulp_domain=domain_name) + # Check that we can "find" the new repo in the new domain via filtering + repos = rpm_repository_api.list(name=repo.name, pulp_domain=domain_name).results + assert len(repos) == 1 + assert repos[0].pulp_href == repo.pulp_href + sync = RpmRepositorySyncURL() + monitor_task(rpm_repository_api.sync(repo.pulp_href, sync).task) + repo = rpm_repository_api.read(repo.pulp_href) + + # check that newly created domain has one repo (list works) and the expected contents + assert rpm_repository_api.list(pulp_domain=domain_name).count == 1 + assert ( + rpm_package_api.list( + repository_version=repo.latest_version_href, pulp_domain=domain_name + ).count + == 35 + ) + assert ( + rpm_advisory_api.list( + repository_version=repo.latest_version_href, pulp_domain=domain_name + ).count + == 4 + ) + + assert ( + rpm_package_category_api.list( + repository_version=repo.latest_version_href, pulp_domain=domain_name + ).count + == 1 + ) + assert ( + rpm_package_groups_api.list( + repository_version=repo.latest_version_href, pulp_domain=domain_name + ).count + == 2 + ) + assert ( + rpm_package_lang_packs_api.list( + repository_version=repo.latest_version_href, pulp_domain=domain_name + ).count + == 1 + ) + finally: + cleanup_domains([domain], content_api_client=rpm_package_api, cleanup_repositories=True) + + +@pytest.mark.parallel +def test_object_creation( + domains_api_client, gen_object_with_cleanup, rpm_repository_api, rpm_rpmremote_api +): + """Test basic object creation in a separate domain.""" + body = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + } + domain = gen_object_with_cleanup(domains_api_client, body) + domain_name = domain.name + + repo_body = {"name": str(uuid.uuid4())} + repo = gen_object_with_cleanup(rpm_repository_api, repo_body, pulp_domain=domain_name) + assert f"{domain_name}/api/v3/" in repo.pulp_href + + repos = rpm_repository_api.list(pulp_domain=domain_name) + assert repos.count == 1 + assert repo.pulp_href == repos.results[0].pulp_href + + # list repos on default domain + default_repos = rpm_repository_api.list(name=repo.name) + assert default_repos.count == 0 + + # Try to create an object w/ cross domain relations + default_remote = gen_object_with_cleanup(rpm_rpmremote_api, gen_rpm_remote()) + with pytest.raises(ApiException) as e: + repo_body = {"name": str(uuid.uuid4()), "remote": default_remote.pulp_href} + rpm_repository_api.create(repo_body, pulp_domain=domain.name) + assert e.value.status == 400 + # What key should this error be under? non-field-errors seems wrong + assert json.loads(e.value.body) == { + "non_field_errors": [f"Objects must all be apart of the {domain_name} domain."] + } + + with pytest.raises(ApiException) as e: + sync_body = {"remote": default_remote.pulp_href} + rpm_repository_api.sync(repo.pulp_href, sync_body) + assert e.value.status == 400 + assert json.loads(e.value.body) == { + "non_field_errors": [f"Objects must all be apart of the {domain_name} domain."] + } + + +@pytest.mark.parallel +def test_artifact_from_file( + domains_api_client, + gen_object_with_cleanup, + rpm_artifact_factory, + artifacts_api_client, +): + """Test uploading artifacts in separate domains.""" + body = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + } + domain1 = gen_object_with_cleanup(domains_api_client, body) + + body = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + } + domain2 = gen_object_with_cleanup(domains_api_client, body) + + # Create as-artifact in domain1 + domain1_artifact = rpm_artifact_factory(pulp_domain=domain1.name) + artifacts = artifacts_api_client.list(pulp_domain=domain1.name) + assert artifacts.count == 1 + assert domain1_artifact.pulp_href == artifacts.results[0].pulp_href + + # Create as-artifact in domain2 + domain2_artifact = rpm_artifact_factory(pulp_domain=domain2.name) + artifacts = artifacts_api_client.list(pulp_domain=domain2.name) + assert artifacts.count == 1 + assert domain2_artifact.pulp_href == artifacts.results[0].pulp_href + + # Show same artifact, diff domains + assert domain1_artifact.pulp_href != domain2_artifact.pulp_href + assert domain1_artifact.sha256 == domain2_artifact.sha256 + + # Show that duplicate artifact can not be uploaded in same domain + with pytest.raises(CoreApiException) as e: + rpm_artifact_factory(pulp_domain=domain1.name) + assert e.value.status == 400 + assert json.loads(e.value.body) == { + "non_field_errors": [ + f"Artifact with sha256 checksum of '{domain1_artifact.sha256}' already exists.", + ] + } + + +@pytest.mark.parallel +def test_rpm_from_file( + cleanup_domains, + domains_api_client, + rpm_package_factory, + gen_object_with_cleanup, + rpm_package_api, +): + """Test uploading of RPM content with domains.""" + body = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + } + domain = gen_object_with_cleanup(domains_api_client, body) + + try: + default_content = rpm_package_factory() + domain_content = rpm_package_factory(pulp_domain=domain.name) + assert default_content.pulp_href != domain_content.pulp_href + assert default_content.sha256 == domain_content.sha256 + + domain_contents = rpm_package_api.list(pulp_domain=domain.name) + assert domain_contents.count == 1 + finally: + cleanup_domains([domain], content_api_client=rpm_package_api) + + +@pytest.mark.parallel +def test_content_promotion( + cleanup_domains, + domains_api_client, + download_content_unit, + rpm_repository_api, + rpm_rpmremote_factory, + rpm_publication_api, + rpm_distribution_factory, + gen_object_with_cleanup, + monitor_task, +): + """Tests Content promotion path with domains: Sync->Publish->Distribute""" + body = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + } + domain = gen_object_with_cleanup(domains_api_client, body) + + try: + # Sync task + remote = rpm_rpmremote_factory(pulp_domain=domain.name) + repo_body = {"name": str(uuid.uuid4()), "remote": remote.pulp_href} + repo = rpm_repository_api.create(repo_body, pulp_domain=domain.name) + + task = rpm_repository_api.sync(repo.pulp_href, {}).task + response = monitor_task(task) + assert len(response.created_resources) == 1 + + repo = rpm_repository_api.read(repo.pulp_href) + assert repo.latest_version_href[-2] == "1" + + # Publish task + pub_body = {"repository": repo.pulp_href} + task = rpm_publication_api.create(pub_body, pulp_domain=domain.name).task + response = monitor_task(task) + assert len(response.created_resources) == 1 + pub_href = response.created_resources[0] + pub = rpm_publication_api.read(pub_href) + + assert pub.repository == repo.pulp_href + + # Distribute Task + distro = rpm_distribution_factory(publication=pub.pulp_href, pulp_domain=domain.name) + + assert distro.publication == pub.pulp_href + # Url structure should be host/CONTENT_ORIGIN/DOMAIN_PATH/BASE_PATH + assert domain.name == distro.base_url.rstrip("/").split("/")[-2] + + # Check that content can be downloaded from base_url + for pkg in ("bear-4.1-1.noarch.rpm", "pike-2.2-1.noarch.rpm"): + pkg_path = get_package_repo_path(pkg) + download_content_unit(distro.base_path, pkg_path, domain=domain.name) + + # Cleanup to delete the domain + task = rpm_repository_api.delete(repo.pulp_href).task + monitor_task(task) + finally: + cleanup_domains([domain], cleanup_repositories=True) + + +@pytest.mark.parallel +def test_domain_rbac( + cleanup_domains, domains_api_client, gen_user, gen_object_with_cleanup, rpm_repository_api +): + """Test domain level-roles.""" + body = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + } + domain = gen_object_with_cleanup(domains_api_client, body) + + try: + rpm_viewer = "rpm.rpmrepository_viewer" + rpm_creator = "rpm.rpmrepository_creator" + user_a = gen_user(username="a", domain_roles=[(rpm_viewer, domain.pulp_href)]) + user_b = gen_user(username="b", domain_roles=[(rpm_creator, domain.pulp_href)]) + + # Create two repos in different domains w/ admin user + gen_object_with_cleanup(rpm_repository_api, {"name": str(uuid.uuid4())}) + gen_object_with_cleanup( + rpm_repository_api, {"name": str(uuid.uuid4())}, pulp_domain=domain.name + ) + + with user_b: + repo = gen_object_with_cleanup( + rpm_repository_api, {"name": str(uuid.uuid4())}, pulp_domain=domain.name + ) + repos = rpm_repository_api.list(pulp_domain=domain.name) + assert repos.count == 1 + assert repos.results[0].pulp_href == repo.pulp_href + # Try to create a repository in default domain + with pytest.raises(ApiException) as e: + rpm_repository_api.create({"name": str(uuid.uuid4())}) + assert e.value.status == 403 + + with user_a: + repos = rpm_repository_api.list(pulp_domain=domain.name) + assert repos.count == 2 + # Try to read repos in the default domain + repos = rpm_repository_api.list() + assert repos.count == 0 + # Try to create a repo + with pytest.raises(ApiException) as e: + rpm_repository_api.create({"name": str(uuid.uuid4())}, pulp_domain=domain.name) + assert e.value.status == 403 + + finally: + cleanup_domains([domain], cleanup_repositories=True) + + +@pytest.mark.parallel +def test_advisory_upload_json( + cleanup_domains, + setup_domain, + upload_advisory_factory, +): + """Test upload same advisory from JSON file into different Domains.""" + domain1 = None + domain2 = None + try: + domain1, _, repo1, _ = setup_domain(sync=False) + domain2, _, repo2, _ = setup_domain(sync=False) + + advisory1, _, an_id = upload_advisory_factory( + pulp_domain=domain1.name, repository=repo1, set_id=True + ) + assert advisory1.id == an_id + + advisory2, _, an_id = upload_advisory_factory( + pulp_domain=domain2.name, repository=repo2, set_id=True + ) + assert advisory2.id == an_id + + assert advisory1.pulp_href != advisory2.pulp_href + finally: + cleanup_domains([domain1, domain2], cleanup_repositories=True) + + +@pytest.mark.parallel +def test_cross_domain_copy_all( + monitor_task, + rpm_copy_api, + cleanup_domains, + setup_domain, +): + """Test attempting to copy between different Domains.""" + domain1 = None + domain2 = None + try: + domain1, remote1, src1, dest1 = setup_domain() + domain2, remote2, src2, dest2 = setup_domain() + # Success, everything in domain1 + data = Copy( + config=[ + {"source_repo_version": src1.latest_version_href, "dest_repo": dest1.pulp_href} + ], + dependency_solving=False, + ) + monitor_task(rpm_copy_api.copy_content(data, pulp_domain=domain1.name).task) + + # Failure, call and src domain1, dest domain2 + with pytest.raises(ApiException): + data = Copy( + config=[ + {"source_repo_version": src1.latest_version_href, "dest_repo": dest2.pulp_href} + ], + dependency_solving=False, + ) + monitor_task(rpm_copy_api.copy_content(data, pulp_domain=domain1.name).task) + + # Failure, call domain2, src/dest domain1 + with pytest.raises(ApiException): + data = Copy( + config=[ + {"source_repo_version": src1.latest_version_href, "dest_repo": dest1.pulp_href} + ], + dependency_solving=False, + ) + monitor_task(rpm_copy_api.copy_content(data, pulp_domain=domain2.name).task) + + finally: + cleanup_domains([domain1, domain2], cleanup_repositories=True) + + +@pytest.mark.parallel +def test_cross_domain_content( + cleanup_domains, + monitor_task, + rpm_advisory_api, + rpm_copy_api, + rpm_repository_api, + setup_domain, +): + """Test the content parameter.""" + domain1 = None + domain2 = None + try: + domain1, remote1, src1, dest1 = setup_domain() + domain2, remote2, src2, dest2 = setup_domain() + + # Copy content1 from src1 to dest1, expect 2 copied advisories + advisories1 = rpm_advisory_api.list( + repository_version=src1.latest_version_href, pulp_domain=domain1.name + ).results + advisories_to_copy1 = (advisories1[0].pulp_href, advisories1[1].pulp_href) + + data = Copy( + config=[ + { + "source_repo_version": src1.latest_version_href, + "dest_repo": dest1.pulp_href, + "content": advisories_to_copy1, + } + ], + dependency_solving=False, + ) + monitor_task(rpm_copy_api.copy_content(data, pulp_domain=domain1.name).task) + dest1 = rpm_repository_api.read(dest1.pulp_href) + advisories = rpm_advisory_api.list( + repository_version=dest1.latest_version_href, pulp_domain=domain1.name + ).results + assert 2 == len(advisories) + + # Copy content1 from src1 to dest1, domain2, expect failure + with pytest.raises(ApiException): + data = Copy( + config=[ + { + "source_repo_version": src1.latest_version_href, + "dest_repo": dest2.pulp_href, + "content": advisories_to_copy1, + } + ], + dependency_solving=False, + ) + monitor_task(rpm_copy_api.copy_content(data, pulp_domain=domain1.name).task) + + # Copy content1 from src1 to dest2, domain1, expect failure + with pytest.raises(ApiException): + data = Copy( + config=[ + { + "source_repo_version": src1.latest_version_href, + "dest_repo": dest2.pulp_href, + "content": advisories_to_copy1, + } + ], + dependency_solving=False, + ) + monitor_task(rpm_copy_api.copy_content(data, pulp_domain=domain1.name).task) + + # Copy mixed content from src2 to dest2, domain2, expect failure + advisories2 = rpm_advisory_api.list( + repository_version=src2.latest_version_href, pulp_domain=domain2.name + ).results + advisories_to_copy2 = (advisories2[0].pulp_href, advisories2[1].pulp_href) + + with pytest.raises(ApiException): + data = Copy( + config=[ + { + "source_repo_version": src2.latest_version_href, + "dest_repo": dest2.pulp_href, + "content": advisories_to_copy1 + advisories_to_copy2, + } + ], + dependency_solving=False, + ) + task = rpm_copy_api.copy_content(data, pulp_domain=domain2.name).task + monitor_task(task) + + finally: + cleanup_domains([domain1, domain2], cleanup_repositories=True) diff --git a/pulp_rpm/tests/functional/api/test_pulpimport.py b/pulp_rpm/tests/functional/api/test_pulpimport.py index a5b0bb3a2..d54b72600 100644 --- a/pulp_rpm/tests/functional/api/test_pulpimport.py +++ b/pulp_rpm/tests/functional/api/test_pulpimport.py @@ -11,12 +11,17 @@ from pulp_rpm.tests.functional.constants import RPM_KICKSTART_FIXTURE_URL, RPM_UNSIGNED_FIXTURE_URL +from pulpcore.app import settings + from pulpcore.client.pulp_rpm import RpmRepositorySyncURL NUM_REPOS = 2 ExportFileInfo = namedtuple("ExportFileInfo", "output_file_info") +if settings.DOMAIN_ENABLED: + pytest.skip("Domains do not support import.", allow_module_level=True) + @pytest.fixture def import_export_repositories( diff --git a/pulp_rpm/tests/functional/conftest.py b/pulp_rpm/tests/functional/conftest.py index a4cbff56b..6d8c4d8fb 100644 --- a/pulp_rpm/tests/functional/conftest.py +++ b/pulp_rpm/tests/functional/conftest.py @@ -1,4 +1,6 @@ +import json import uuid +from tempfile import NamedTemporaryFile import pytest @@ -16,12 +18,15 @@ RemotesUlnApi, RpmCopyApi, RpmCompsApi, + RpmRepositorySyncURL, ) from pulp_rpm.tests.functional.constants import ( + BASE_TEST_JSON, RPM_KICKSTART_FIXTURE_URL, RPM_SIGNED_URL, RPM_MODULAR_FIXTURE_URL, + RPM_SIGNED_FIXTURE_URL, ) from pulp_rpm.tests.functional.utils import init_signed_repo_configuration @@ -110,6 +115,51 @@ def signed_artifact(http_get, artifacts_api_client, gen_object_with_cleanup, tmp return gen_object_with_cleanup(artifacts_api_client, temp_file).to_dict() +@pytest.fixture +def rpm_artifact_factory( + http_get, artifacts_api_client, gen_object_with_cleanup, pulp_domain_enabled, tmp_path +): + """Return an artifact created from uploading an RPM file.""" + + def _rpm_artifact_factory(url=RPM_SIGNED_URL, pulp_domain=None): + temp_file = tmp_path / str(uuid.uuid4()) + temp_file.write_bytes(http_get(url)) + kwargs = {} + if pulp_domain: + if not pulp_domain_enabled: + raise RuntimeError("Server does not have domains enabled.") + kwargs["pulp_domain"] = pulp_domain + return gen_object_with_cleanup(artifacts_api_client, temp_file, **kwargs) + + return _rpm_artifact_factory + + +@pytest.fixture +def rpm_package_factory( + gen_object_with_cleanup, + http_get, + pulp_domain_enabled, + rpm_package_api, +): + """Return a Package created from uploading an RPM file.""" + + def _rpm_package_factory(url=RPM_SIGNED_URL, pulp_domain=None): + with NamedTemporaryFile() as file_to_upload: + file_to_upload.write(http_get(url)) + file_to_upload.flush() + upload_attrs = {"file": file_to_upload.name} + + kwargs = {} + if pulp_domain: + if not pulp_domain_enabled: + raise RuntimeError("Server does not have domains enabled.") + kwargs["pulp_domain"] = pulp_domain + + return gen_object_with_cleanup(rpm_package_api, **upload_attrs, **kwargs) + + return _rpm_package_factory + + @pytest.fixture(scope="class") def rpm_unsigned_repo_immediate(init_and_sync): repo, _ = init_and_sync() @@ -146,3 +196,149 @@ def rpm_metadata_signing_service(signing_service_api_client): signing_service = results.results[0] return signing_service + + +@pytest.fixture +def upload_wrong_file_type( + rpm_advisory_api, + http_get, +): + def _upload(remote_path): + with NamedTemporaryFile() as file_to_upload: + file_to_upload.write(http_get(remote_path)) + file_to_upload.flush() + upload_attrs = {"file": file_to_upload.name} + return rpm_advisory_api.create(**upload_attrs) + + return _upload + + +@pytest.fixture +def upload_advisory_factory( + add_to_cleanup, + monitor_task, + pulp_domain_enabled, + rpm_advisory_api, +): + """Upload advisory from a json file, return advisory, vers_href, and id-used.""" + + def _upload_advisory_factory( + advisory=BASE_TEST_JSON, repository=None, pulp_domain=None, set_id=False, use_id=None + ): + kwargs = {} + if pulp_domain: + if not pulp_domain_enabled: + raise RuntimeError("Server does not have domains enabled.") + kwargs["pulp_domain"] = pulp_domain + + with NamedTemporaryFile("w+") as file_to_upload: + json_advisory = json.loads(advisory) + if set_id or use_id: + json_advisory["id"] = use_id if use_id else str(uuid.uuid4()) + used_id = json_advisory["id"] + json.dump(json_advisory, file_to_upload) + file_to_upload.flush() + upload_attrs = {"file": file_to_upload.name} + if repository: + upload_attrs["repository"] = repository.pulp_href + file_to_upload.flush() + response = rpm_advisory_api.create(**upload_attrs, **kwargs) + + task_rslt = monitor_task(response.task) + if repository: + assert 2 == len(task_rslt.created_resources) + else: + assert 1 == len(task_rslt.created_resources) + + vers_href = None + advisory_href = None + for rsrc in task_rslt.created_resources: + if "versions" in rsrc: + vers_href = rsrc + elif "advisories" in rsrc: + advisory_href = rsrc + assert advisory_href + + add_to_cleanup(rpm_advisory_api, advisory_href) + entity = rpm_advisory_api.read(advisory_href) + return entity, vers_href, used_id + + return _upload_advisory_factory + + +@pytest.fixture +def assert_uploaded_advisory(rpm_advisory_api): + """List advisories for a given version-href, and assert that the specified ID is therein.""" + + def _from_results(advisory_id, vers_href): + advisories = rpm_advisory_api.list(id=advisory_id, repository_version=vers_href) + assert 1 == len(advisories.results) + return advisories.results[0].pulp_href, vers_href + + return _from_results + + +@pytest.fixture +def setup_domain( + gen_object_with_cleanup, domains_api_client, rpm_rpmremote_api, rpm_repository_api, monitor_task +): + def _setup_domain(sync=True, url=RPM_SIGNED_FIXTURE_URL, pulp_domain=None): + if not pulp_domain: + body = { + "name": str(uuid.uuid4()), + "storage_class": "pulpcore.app.models.storage.FileSystem", + "storage_settings": {"MEDIA_ROOT": "/var/lib/pulp/media/"}, + } + pulp_domain = gen_object_with_cleanup(domains_api_client, body) + + remote = gen_object_with_cleanup( + rpm_rpmremote_api, {"name": str(uuid.uuid4()), "url": url}, pulp_domain=pulp_domain.name + ) + src = gen_object_with_cleanup( + rpm_repository_api, + {"name": str(uuid.uuid4()), "remote": remote.pulp_href}, + pulp_domain=pulp_domain.name, + ) + + if sync: + sync_url = RpmRepositorySyncURL() + monitor_task(rpm_repository_api.sync(src.pulp_href, sync_url).task) + src = rpm_repository_api.read(src.pulp_href) + + dest = gen_object_with_cleanup( + rpm_repository_api, {"name": str(uuid.uuid4())}, pulp_domain=pulp_domain.name + ) + return pulp_domain, remote, src, dest + + return _setup_domain + + +@pytest.fixture +def cleanup_domains(orphans_cleanup_api_client, monitor_task, rpm_repository_api): + def _cleanup_domains( + domains, + content_api_client=None, + cleanup_repositories=False, + repository_api_client=rpm_repository_api, + ): + for domain in domains: + # clean up each domain specified + if domain: + if cleanup_repositories: + # Delete repos from the domain + for repo in repository_api_client.list(pulp_domain=domain.name).results: + monitor_task(repository_api_client.delete(repo.pulp_href).task) + # Let orphan-cleanup reap the resulting abandoned content + monitor_task( + orphans_cleanup_api_client.cleanup( + {"orphan_protection_time": 0}, pulp_domain=domain.name + ).task + ) + + if content_api_client: + # IF we have a client, check that each domain is empty of that kind-of entity + for domain in domains: + if domain: + assert content_api_client.list(pulp_domain=domain.name).count == 0 + + return _cleanup_domains diff --git a/pulp_rpm/tests/functional/constants.py b/pulp_rpm/tests/functional/constants.py index fce2ebee0..e2878af5f 100644 --- a/pulp_rpm/tests/functional/constants.py +++ b/pulp_rpm/tests/functional/constants.py @@ -1865,3 +1865,414 @@ ], }, ] + +BASE_TEST_JSON = """{ + "updated": "2014-09-28 00:00:00", + "issued": "2014-09-24 00:00:00", + "id": "RHSA-XXXX:XXXX", + "pkglist": [ + { + "packages": [ + { + "arch": "noarch", + "epoch": "0", + "filename": "bear-4.1-1.noarch.rpm", + "name": "bear", + "reboot_suggested": false, + "relogin_suggested": false, + "restart_suggested": false, + "release": "1", + "src": "http://www.fedoraproject.org", + "sum": "", + "sum_type": "", + "version": "4.1" + } + ] + } + ], + "severity": "", + "description": "Not available", + "reboot_suggested": false, + "solution": "Not available", + "fromstr": "centos-announce@centos.org"}""" + +BEAR_JSON = """{ + "issued": "2020-03-08 20:04:01", + "id": "CEBA-2019--666", + "type": "Bug Fix Advisory", + "release": "1", + "version": "1", + "pkglist": [ + { + "packages": [ + { + "arch": "noarch", + "epoch": "0", + "filename": "bear-4.1-1.noarch.rpm", + "name": "bear", + "reboot_suggested": false, + "relogin_suggested": false, + "restart_suggested": false, + "release": "1", + "src": "http://www.fedoraproject.org", + "sum": "", + "sum_type": "", + "version": "4.1" + } + ] + } + ], + "severity": "", + "description": "Not available", + "reboot_suggested": false, + "updated": "2020-03-08 20:04:01", + "solution": "Not available", + "fromstr": "centos-announce@centos.org" +}""" + +CAMEL_JSON = """{ + "issued": "2020-03-08 20:04:01", + "id": "CEBA-2019--666", + "type": "Bug Fix Advisory", + "release": "1", + "version": "1", + "pkglist": [ + { + "packages": [ + { + "arch": "noarch", + "epoch": "0", + "filename": "camel-0.1-1.noarch.rpm", + "name": "camel", + "reboot_suggested": false, + "relogin_suggested": false, + "restart_suggested": false, + "release": "1", + "src": "http://www.fedoraproject.org", + "sum": "", + "sum_type": "", + "version": "0.1" + } + ] + } + ], + "severity": "", + "description": "Not available", + "reboot_suggested": false, + "updated": "2020-03-08 20:04:01", + "solution": "Not available", + "fromstr": "centos-announce@centos.org" +}""" + +CAMEL_BIRD_JSON = """{ + "issued": "2020-03-08 20:04:01", + "id": "CEBA-2019--666", + "type": "Bug Fix Advisory", + "release": "1", + "version": "1", + "pkglist": [ + { + "packages": [ + { + "arch": "noarch", + "epoch": "0", + "filename": "camel-0.1-1.noarch.rpm", + "name": "camel", + "reboot_suggested": false, + "relogin_suggested": false, + "restart_suggested": false, + "release": "1", + "src": "http://www.fedoraproject.org", + "sum": "", + "sum_type": "", + "version": "0.1" + }, + { + "arch": "noarch", + "epoch": "0", + "filename": "bird-1.2-3.noarch.rpm", + "name": "bird", + "reboot_suggested": false, + "relogin_suggested": false, + "restart_suggested": false, + "release": "3", + "src": "http://www.fedoraproject.org", + "sum": "", + "sum_type": "", + "version": "1.2" + } + ] + } + ], + "severity": "", + "description": "Not available", + "reboot_suggested": false, + "updated": "2020-03-08 20:04:01", + "solution": "Not available", + "fromstr": "centos-announce@centos.org" +}""" + +CAMEL_BEAR_DOG_JSON = """{ + "issued": "2020-03-08 20:04:01", + "id": "CEBA-2019--666", + "type": "Bug Fix Advisory", + "release": "1", + "version": "1", + "pkglist": [ + { + "packages": [ + { + "arch": "noarch", + "epoch": "0", + "filename": "camel-0.1-1.noarch.rpm", + "name": "camel", + "reboot_suggested": false, + "relogin_suggested": false, + "restart_suggested": false, + "release": "1", + "src": "http://www.fedoraproject.org", + "sum": "", + "sum_type": "", + "version": "0.1" + }, + { + "arch": "noarch", + "epoch": "0", + "filename": "bear-4.1-1.noarch.rpm", + "name": "bear", + "reboot_suggested": false, + "relogin_suggested": false, + "restart_suggested": false, + "release": "1", + "src": "http://www.fedoraproject.org", + "sum": "", + "sum_type": "", + "version": "4.1" + }, + { + "arch": "noarch", + "epoch": "0", + "filename": "dog-6.1-6.noarch.rpm", + "name": "dog", + "reboot_suggested": false, + "relogin_suggested": false, + "restart_suggested": false, + "release": "6", + "src": "http://www.fedoraproject.org", + "sum": "", + "sum_type": "", + "version": "6.1" + } + ] + } + ], + "severity": "", + "description": "Not available", + "reboot_suggested": false, + "updated": "2020-03-08 20:04:01", + "solution": "Not available", + "fromstr": "centos-announce@centos.org" +}""" + +CESA_2020_5002 = """{ + "title": "Moderate CentOS curl Security Update", + "type": "security", + "description": "", + "release": "el7", + "version": "1", + "severity": "Moderate", + "status": "final", + "updated": "2020-11-18 17:30:30", + "issued": "2020-11-18 17:30:30", + "pkglist": [ + { + "packages": [ + { + "arch": "x86_64", + "epoch": "0", + "filename": "curl-7.29.0-59.el7_9.1.x86_64.rpm", + "release": "59.el7_9.1", + "name": "curl", + "sum": "dfc95bdd8057839d4b45153318acb4e09f4da257afee1c57c07781870a68ecef", + "sum_type": "sha256" + }, + { + "arch": "i686", + "epoch": "0", + "filename": "libcurl-7.29.0-59.el7_9.1.i686.rpm", + "release": "59.el7_9.1", + "name": "libcurl", + "sum": "3054ca1c0cc8eef5f08ce1d3be56c7a39e97d92361e8bd265bea14d06f590219", + "sum_type": "sha256" + }, + { + "arch": "x86_64", + "epoch": "0", + "filename": "libcurl-7.29.0-59.el7_9.1.x86_64.rpm", + "release": "59.el7_9.1", + "name": "libcurl", + "sum": "4ad0b71e3a6468fba1b43ab82fad024415b5296c7b77d1348fb9afa3f828f98e", + "sum_type": "sha256" + }, + { + "arch": "i686", + "epoch": "0", + "filename": "libcurl-devel-7.29.0-59.el7_9.1.i686.rpm", + "release": "59.el7_9.1", + "name": "libcurl-devel", + "sum": "7ab4f1b0aa285d3773fdbd8bfc529969ca101a627d3ea88bea1f99a42093e132", + "sum_type": "sha256" + }, + { + "arch": "x86_64", + "epoch": "0", + "filename": "libcurl-devel-7.29.0-59.el7_9.1.x86_64.rpm", + "release": "59.el7_9.1", + "name": "libcurl-devel", + "sum": "f92fde3f97c0034135796baa7cd55f87c0550a88ac79adbdcc9c7f64c595614b", + "sum_type": "sha256" + } + ] + } + ], + "id": "TEST-CESA-2020:5002", + "from": "centos-announce@centos.org", + "references": [ + { + "href": "https://access.redhat.com/errata/RHSA-2020:5002", + "ref_id": "CESA-2020:5002", + "title": "Moderate CentOS curl Security Update", + "ref_type": "security" + }, + { + "href": "https://lists.centos.org/pipermail/centos-announce/2020-November/035840.html", + "ref_id": "CESA-2020:5002", + "title": "Moderate CentOS curl Security Update", + "ref_type": "security" + } + ] +}""" # noqa + +CESA_2020_4910 = """{ + "title": "Important CentOS xorg-x11-server Security Update", + "type": "security", + "description": "", + "release": "el7", + "version": "1", + "severity": "Important", + "status": "final", + "updated": "2020-11-06 22:19:48", + "issued": "2020-11-06 22:19:48", + "pkglist": [ + { + "packages": [ + { + "arch": "x86_64", + "epoch": "0", + "filename": "xorg-x11-server-Xdmx-1.20.4-12.el7_9.x86_64.rpm", + "release": "12.el7_9", + "name": "xorg-x11-server-Xdmx", + "sum": "0435f345b2b188c76dbb4a538bf0f878834a41e723491df1926231020fd88efd", + "sum_type": "sha256" + }, + { + "arch": "x86_64", + "epoch": "0", + "filename": "xorg-x11-server-Xephyr-1.20.4-12.el7_9.x86_64.rpm", + "release": "12.el7_9", + "name": "xorg-x11-server-Xephyr", + "sum": "2d21d53b305e30b058ca88d8778bda67000a5d52ab320f04b35e63f6a78f2163", + "sum_type": "sha256" + }, + { + "arch": "x86_64", + "epoch": "0", + "filename": "xorg-x11-server-Xnest-1.20.4-12.el7_9.x86_64.rpm", + "release": "12.el7_9", + "name": "xorg-x11-server-Xnest", + "sum": "51fbacc2e26050a7772549f1fe16c46bd8063ea187825ad89b237c34fa9b4250", + "sum_type": "sha256" + }, + { + "arch": "x86_64", + "epoch": "0", + "filename": "xorg-x11-server-Xorg-1.20.4-12.el7_9.x86_64.rpm", + "release": "12.el7_9", + "name": "xorg-x11-server-Xorg", + "sum": "eb89964d5fd40ec94ee8db97a5a14cc8dd6329b83d82ab29ee1a595653ce5223", + "sum_type": "sha256" + }, + { + "arch": "x86_64", + "epoch": "0", + "filename": "xorg-x11-server-Xvfb-1.20.4-12.el7_9.x86_64.rpm", + "release": "12.el7_9", + "name": "xorg-x11-server-Xvfb", + "sum": "ea32b047fba7fd327bf943da2a18413a1ed3e245cc1b077f34d1c8f6048d9813", + "sum_type": "sha256" + }, + { + "arch": "x86_64", + "epoch": "0", + "filename": "xorg-x11-server-Xwayland-1.20.4-12.el7_9.x86_64.rpm", + "release": "12.el7_9", + "name": "xorg-x11-server-Xwayland", + "sum": "4a6ffb39008edd469d4365bb3bf858f5f5f466129eb9e330d978b28866906891", + "sum_type": "sha256" + }, + { + "arch": "x86_64", + "epoch": "0", + "filename": "xorg-x11-server-common-1.20.4-12.el7_9.x86_64.rpm", + "release": "12.el7_9", + "name": "xorg-x11-server-common", + "sum": "339bcf68cb37a454eddff7218aff4153a36bafc0d36e2b5b6bde8311c6f3eed8", + "sum_type": "sha256" + }, + { + "arch": "i686", + "epoch": "0", + "filename": "xorg-x11-server-devel-1.20.4-12.el7_9.i686.rpm", + "release": "12.el7_9", + "name": "xorg-x11-server-devel", + "sum": "55e13fc8624f8a63b785b5194281c38a4670f03113b0ff2b8fc1df1ca473e1e8", + "sum_type": "sha256" + }, + { + "arch": "x86_64", + "epoch": "0", + "filename": "xorg-x11-server-devel-1.20.4-12.el7_9.x86_64.rpm", + "release": "12.el7_9", + "name": "xorg-x11-server-devel", + "sum": "e2dd0c67f3d88a9506f72fcc21ec0af786a377befabac8e1670d3e012d844b06", + "sum_type": "sha256" + }, + { + "arch": "noarch", + "epoch": "0", + "filename": "xorg-x11-server-source-1.20.4-12.el7_9.noarch.rpm", + "release": "12.el7_9", + "name": "xorg-x11-server-source", + "sum": "1baa9cb2d4f8d4300ac333fbc7bc130dce9145c67aea3bd6efa4a0354fc92b6d", + "sum_type": "sha256" + } + ] + } + ], + "id": "TEST-CESA-2020:4910", + "from": "centos-announce@centos.org", + "references": [ + { + "href": "https://access.redhat.com/errata/RHSA-2020:4910", + "ref_id": "CESA-2020:4910", + "title": "Important CentOS xorg-x11-server Security Update", + "ref_type": "security" + }, + { + "href": "https://lists.centos.org/pipermail/centos-cr-announce/2020-November/012889.html", + "ref_id": "CESA-2020:4910", + "title": "Important CentOS xorg-x11-server Security Update", + "ref_type": "security" + } + ] +}""" # noqa diff --git a/template_config.yml b/template_config.yml index 84afc4b25..fb61b5483 100644 --- a/template_config.yml +++ b/template_config.yml @@ -59,7 +59,8 @@ pulp_settings: allowed_import_paths: - /tmp orphan_protection_time: 0 -pulp_settings_azure: null +pulp_settings_azure: + domain_enabled: true pulp_settings_gcp: null pulp_settings_s3: null pulp_settings_stream: null