def test_search_created_regex(): """Can search using regular expressions.""" controller = FakeController() when1 = datetime.datetime(2019, 6, 11, 14, 47, 0, tzinfo=None) when2 = datetime.datetime(2019, 3, 1, 1, 1, 0, tzinfo=None) when3 = datetime.datetime(2019, 6, 1, 1, 1, 0, tzinfo=None) repo1 = Repository(id="repo1", created=when1) repo2 = Repository(id="repo2", created=when2) repo3 = Repository(id="repo3", created=when3) repo4 = Repository(id="repo4") controller.insert_repository(repo1) controller.insert_repository(repo2) controller.insert_repository(repo3) controller.insert_repository(repo4) client = controller.client crit = Criteria.with_field("notes.created", Matcher.regex("19-06")) found = list(client.search_repository(crit).result().as_iter()) assert sorted(found) == [repo1, repo3]
def test_can_upload_units(use_file_object, data_path): """repo.upload_modules() succeeds with fake client and populates units.""" modules_path = os.path.join(data_path, "sample-modules.yaml") controller = FakeController() controller.insert_repository(YumRepository(id="repo1")) client = controller.client repo1 = client.get_repository("repo1").result() to_upload = modules_path if use_file_object: to_upload = open(to_upload, "rb") upload_f = repo1.upload_modules(to_upload) # Upload should complete successfully. tasks = upload_f.result() # At least one task. assert tasks # Every task should have succeeded. for t in tasks: assert t.succeeded # If I now search for content in that repo, or content across all repos... units_in_repo = sorted(repo1.search_content().result(), key=repr) units_all = sorted(client.search_content().result(), key=repr) # They should be equal assert units_all == units_in_repo # And they should be this assert units_in_repo == [ ModulemdDefaultsUnit( unit_id="e3e70682-c209-4cac-629f-6fbed82c07cd", name="ant", repo_id="repo1", # Note, this tests that 1.10 does not get coerced to 1.1, # as happened in some tools previously. stream="1.10", profiles={"1.10": ["default"]}, content_type_id="modulemd_defaults", repository_memberships=["repo1"], ), ModulemdDefaultsUnit( unit_id="d4713d60-c8a7-0639-eb11-67b367a9c378", name="dwm", repo_id="repo1", stream=None, profiles={ "6.0": ["default"], "6.1": ["default"], "6.2": ["default"], "latest": ["default"], }, content_type_id="modulemd_defaults", repository_memberships=["repo1"], ), ModulemdUnit( unit_id="82e2e662-f728-b4fa-4248-5e3a0a5d2f34", name="avocado-vt", stream="82lts", version=3420210902113311, context="035be0ad", arch="x86_64", content_type_id="modulemd", repository_memberships=["repo1"], artifacts=[ "avocado-vt-0:82.0-3.module_f34+12808+b491ffc8.src", "python3-avocado-vt-0:82.0-3.module_f34+12808+b491ffc8.noarch", ], profiles={ "default": { "description": "Common profile installing the avocado-vt plugin.", "rpms": ["python3-avocado-vt"], } }, dependencies=[ModulemdDependency(name="avocado", stream="82lts")], ), ModulemdUnit( unit_id="23a7711a-8133-2876-37eb-dcd9e87a1613", name="dwm", stream="6.0", version=3420210201213909, context="058368ca", arch="x86_64", content_type_id="modulemd", repository_memberships=["repo1"], artifacts=[ "dwm-0:6.0-1.module_f34+11150+aec78cf8.src", "dwm-0:6.0-1.module_f34+11150+aec78cf8.x86_64", "dwm-debuginfo-0:6.0-1.module_f34+11150+aec78cf8.x86_64", "dwm-debugsource-0:6.0-1.module_f34+11150+aec78cf8.x86_64", "dwm-user-0:6.0-1.module_f34+11150+aec78cf8.x86_64", ], profiles={ "default": { "description": "The minimal, distribution-compiled dwm binary.", "rpms": ["dwm"], }, "user": { "description": "Includes distribution-compiled dwm as well as a helper script to apply user patches and configuration, dwm-user.", "rpms": ["dwm", "dwm-user"], }, }, dependencies=[], ), ]
def test_can_construct(): """A fake client can be constructed.""" controller = FakeController() assert controller.client is not None
def test_uploads_shared(data_path): """Upload phase allows for uploads of identical content to be reused.""" pulp_ctrl = FakeController() pulp_ctrl.insert_repository(YumRepository(id="all-rpm-content")) pulp_ctrl.insert_repository(YumRepository(id="repo1")) pulp_ctrl.insert_repository(YumRepository(id="repo2")) pulp_ctrl.insert_repository(YumRepository(id="repo3")) client_wrapper = ClientWrapper(pulp_ctrl.client) ctx = Context() queue = ctx.new_queue() phase = Upload( context=ctx, pulp_client_factory=lambda: client_wrapper, pre_push=None, in_queue=queue, update_push_items=lambda _: None, ) rpm1 = RpmPushItem( name="walrus-5.21-1.noarch.rpm", sha256sum= "e837a635cc99f967a70f34b268baa52e0f412c1502e08e924ff5b09f1f9573f2", src=os.path.join(data_path, "staged-mixed/dest1/RPMS/walrus-5.21-1.noarch.rpm"), ) rpm2 = RpmPushItem( name="test-srpm01-1.0-1.src.rpm", sha256sum= "54cc4713fe704dfc7a4fd5b398f834ceb6a692f53b0c6aefaf89d88417b4c51d", src=os.path.join(data_path, "staged-mixed/dest1/SRPMS/test-srpm01-1.0-1.src.rpm"), ) inputs = [ # Some copies of the same RPM to different repos PulpRpmPushItem(pushsource_item=attr.evolve(rpm1, dest=["repo1"])), PulpRpmPushItem( pushsource_item=attr.evolve(rpm1, dest=["repo2", "repo3"])), # A different RPM PulpRpmPushItem(pushsource_item=attr.evolve(rpm2, dest=["repo1"])), ] # Shove 'em into the queue for item in inputs: queue.put(item) # Put this so that iteration will end queue.put(Phase.FINISHED) # Let the phase run with phase: pass # It should not have failed assert not ctx.has_error # Should have called upload exactly once per file. assert sorted(client_wrapper.uploads) == [ ("rpm", rpm1.src), ("rpm", rpm2.src), ] # Look at the pulp units created. outputs = {} while True: item = phase.out_queue.get() if item is phase.FINISHED: break outputs.setdefault(item.pushsource_item.name, []).append(item.pulp_unit) # Although there were two items dealing with this RPM... assert len(outputs["walrus-5.21-1.noarch.rpm"]) == 2 # If we de-duplicate, we'll find they're actually the same unit since # upload was shared. assert len(set(outputs["walrus-5.21-1.noarch.rpm"])) == 1 # And the non-dupe should just work as normal. assert len(outputs["test-srpm01-1.0-1.src.rpm"]) == 1
def test_can_upload_units(data_path, use_file_object): """repo.upload_rpm() succeeds with fake client and populates units.""" rpm_path = os.path.join(data_path, "rpms/walrus-5.21-1.noarch.rpm") controller = FakeController() controller.insert_repository(YumRepository(id="repo1")) client = controller.client repo1 = client.get_repository("repo1").result() to_upload = rpm_path if use_file_object: to_upload = open(rpm_path, "rb") upload_f = repo1.upload_rpm(to_upload) # Upload should complete successfully. tasks = upload_f.result() # At least one task. assert tasks # Every task should have succeeded. for t in tasks: assert t.succeeded # If I now search for content in that repo, or content across all repos... units_in_repo = sorted(repo1.search_content().result(), key=lambda u: u.sha256sum) units_all = sorted(client.search_content().result(), key=lambda u: u.sha256sum) # They should be equal assert units_all == units_in_repo # And they should be this assert units_in_repo == [ RpmUnit( unit_id="e3e70682-c209-4cac-629f-6fbed82c07cd", name="walrus", version="5.21", release="1", arch="noarch", epoch="0", signing_key="f78fb195", filename="walrus-5.21-1.noarch.rpm", sourcerpm="walrus-5.21-1.src.rpm", md5sum="6a3eec6d45e0ea80eab05870bf7a8d4b", sha1sum="8dea2b64fc52062d79d5f96ba6415bffae4d2153", sha256sum= "e837a635cc99f967a70f34b268baa52e0f412c1502e08e924ff5b09f1f9573f2", content_type_id="rpm", repository_memberships=["repo1"], requires=[ RpmDependency( name="rpmlib(CompressedFileNames)", version="3.0.4", release="1", flags="LE", epoch="0", ), RpmDependency( name="rpmlib(PayloadFilesHavePrefix)", version="4.0", release="1", flags="LE", epoch="0", ), ], provides=[ RpmDependency(name="walrus", version="5.21", release="1", flags="EQ", epoch="0") ], ) ]
class PersistentFake(object): """Wraps pulplib fake client adding persistence of state.""" def __init__(self, state_path): self.ctrl = FakeController() self.state_path = state_path # Register ourselves with pubtools so we can get the task stop hook, # at which point we will save our current state. pm.register(self) def load_initial(self): """Initial load of data into the fake, in the case where no state has previously been persisted. This will populate a hardcoded handful of repos which are expected to always be present in a realistically configured rhsm-pulp server. """ self.ctrl.insert_repository(FileRepository(id="redhat-maintenance")) self.ctrl.insert_repository(FileRepository(id="all-iso-content")) self.ctrl.insert_repository(YumRepository(id="all-rpm-content")) def load(self): """Load data into the fake from previously serialized state (if any). If no state has been previously serialized, load_initial will be used to seed the fake with some hardcoded state. """ if not os.path.exists(self.state_path): return self.load_initial() with open(self.state_path, "rt") as f: # pylint:disable=unspecified-encoding raw = yaml.load(f, Loader=yaml.SafeLoader) repos = raw.get("repos") or [] for repo in deserialize(repos): self.ctrl.insert_repository(repo) units = raw.get("units") or [] for unit in deserialize(units): for repo_id in unit.repository_memberships: repo = self.ctrl.client.get_repository(repo_id).result() self.ctrl.insert_units(repo, [unit]) def save(self): """Serialize the current state of the fake and save it to persistent storage.""" serialized = {} serialized["repos"] = serialize(self.ctrl.repositories) serialized["repos"].sort(key=lambda repo: repo["id"]) all_units = list(self.ctrl.client.search_content()) serialized["units"] = serialize(all_units) # This sort key is a bit expensive since it means we essentially do yaml dump # twice. On the plus side it ensures a stable order across py2 and py3. serialized["units"].sort( key=lambda x: yaml.dump(x, Dumper=yaml.SafeDumper)) path = self.state_path state_dir = os.path.dirname(path) if not os.path.isdir(state_dir): os.makedirs(state_dir) with open(path, "wt") as f: # pylint:disable=unspecified-encoding yaml.dump(serialized, f, Dumper=yaml.SafeDumper) LOG.info("Fake pulp state persisted to %s", path) @hookimpl def task_stop(self, failed): # pylint:disable=unused-argument """Called when a task is ending.""" pm.unregister(self) self.save()
def __init__(self, *args, **kwargs): super(TaskWithPulpClient, self).__init__(*args, **kwargs) self.pulp_ctrl = FakeController()
def __init__(self, *args, **kwargs): super(FakeSetMaintenanceOff, self).__init__(*args, **kwargs) self.pulp_client_controller = FakeController()
def test_remove_filtered_content(): """repo.remove_content() succeeds and removes expected units inserted via controller.""" controller = FakeController() client = controller.client rpm_units = [ RpmUnit(name="bash", version="4.0", release="1", arch="x86_64"), RpmUnit(name="glibc", version="5.0", release="1", arch="x86_64"), ] modulemd_units = [ ModulemdUnit(name="module1", stream="s1", version=1234, context="a1b2", arch="x86_64"), ModulemdUnit(name="module1", stream="s1", version=1235, context="a1b2", arch="x86_64"), ] units = rpm_units + modulemd_units repo = YumRepository(id="repo1") controller.insert_repository(repo) controller.insert_units(repo, units) criteria = Criteria.and_(Criteria.with_unit_type(RpmUnit), Criteria.with_field("name", "bash")) remove_rpms = client.get_repository("repo1").remove_content( criteria=criteria) assert len(remove_rpms) == 1 task = remove_rpms[0] # It should have completed successfully assert task.completed assert task.succeeded # It should have removed the specific rpm assert len(task.units) == 1 assert task.units[0] == sorted(rpm_units)[0] # Try removing a module with incorrect type_ids criteria = Criteria.and_(Criteria.with_unit_type(RpmUnit), Criteria.with_field("name", "module1")) remove_rpms = client.get_repository("repo1").remove_content( criteria=criteria) assert len(remove_rpms) == 1 task = remove_rpms[0] # It should have completed successfully assert task.completed assert task.succeeded # Nothing's removed as criteria isn't fulfilled assert len(task.units) == 0 # Removing module with correct type_ids criteria = Criteria.and_(Criteria.with_unit_type(ModulemdUnit), Criteria.with_field("name", "module1")) remove_rpms = client.get_repository("repo1").remove_content( criteria=criteria) assert len(remove_rpms) == 1 task = remove_rpms[0] # It should have completed successfully assert task.completed assert task.succeeded # It should have removed both the modules as they # match the criteria assert len(task.units) == 2 assert sorted(task.units) == sorted(modulemd_units)
def test_can_upload_with_versionless_deps(data_path): """repo.upload_rpm() succeeds on RPM having versionless deps.""" rpm_path = os.path.join(data_path, "rpms/crash-trace-command-1.0-4.el6.src.rpm") controller = FakeController() controller.insert_repository(YumRepository(id="repo1")) client = controller.client repo1 = client.get_repository("repo1").result() upload_f = repo1.upload_rpm(rpm_path) # Upload should complete successfully. tasks = upload_f.result() # At least one task. assert tasks # Every task should have succeeded. for t in tasks: assert t.succeeded # If I now search for content in that repo, or content across all repos... units_in_repo = sorted(repo1.search_content().result(), key=lambda u: u.sha256sum) units_all = sorted(client.search_content().result(), key=lambda u: u.sha256sum) # They should be equal assert units_all == units_in_repo # And they should be this assert units_in_repo == [ RpmUnit( name="crash-trace-command", version="1.0", release="4.el6", arch="src", signing_key="fd431d51", filename="crash-trace-command-1.0-4.el6.src.rpm", md5sum="e06137db87b9b690fc36e8bac9071771", sha1sum="bacbb2ee0572d39b1ac8543cfe57c3e584ed986a", sha256sum= "633e316eb23603ffc9474f926fcff152b2a61487653be97602fae06fcb46814d", repository_memberships=["repo1"], unit_id="e3e70682-c209-4cac-629f-6fbed82c07cd", requires=[ RpmDependency(name="crash-devel"), RpmDependency(name="zlib-devel"), RpmDependency( name="rpmlib(FileDigests)", version="4.6.0", release="1", epoch="0", flags="LE", ), RpmDependency( name="rpmlib(CompressedFileNames)", version="3.0.4", release="1", epoch="0", flags="LE", ), ], provides=[], ) ]
def _get_fake_controller(*args): controller = FakeController() for repo in args: controller.insert_repository(repo) return controller
def test_updates_product_versions(monkeypatch, tmpdir): """Uploading a productid to a repo will update the product_versions field on that repo and related repos. """ ctrl = FakeController() # Set up a family of repos with various product_versions. repo1 = YumRepository(id="repo1") repo2 = YumRepository( id="repo2", arch="x86_64", eng_product_id=1234, platform_full_version="xyz", product_versions=["a", "b"], ) repo3 = YumRepository( id="repo3", arch="x86_64", eng_product_id=1234, platform_full_version="xyz", product_versions=["b", "c"], ) repo4 = YumRepository( id="repo4", arch="x86_64", eng_product_id=1234, platform_full_version="xyz", product_versions=None, ) repo5 = YumRepository( id="repo5", arch="x86_64", eng_product_id=1234, platform_full_version="xyz", product_versions=["c", "d"], ) repo6 = YumRepository( id="repo6", arch="x86_64", eng_product_id=1234, product_versions=["d", "e"], ) repo7 = YumRepository( id="repo7", arch="s390x", product_versions=["b"], ) repo8 = YumRepository( id="repo8", arch="s390x", product_versions=[], ) ctrl.insert_repository(repo1) ctrl.insert_repository(repo2) ctrl.insert_repository(repo3) ctrl.insert_repository(repo4) ctrl.insert_repository(repo5) ctrl.insert_repository(repo6) ctrl.insert_repository(repo7) ctrl.insert_repository(repo8) # Set up cert parser to use a fake cert object (saves us having to explicitly # generate certs with certain values) monkeypatch.setattr(rhsm.certificate, "create_from_file", lambda _: FakeCert(["a", "d"])) # make a fake productid. # content doesn't matter since we patched the cert parser, it just has # to be an existing file. productid = tmpdir.join("productid") productid.write("") # make an item targeting two of the repos item = PulpProductIdPushItem(pushsource_item=ProductIdPushItem( name="test", src=str(productid), dest=["repo2", "repo7"])) upload_ctx = UploadContext(client=ctrl.client) # Try uploading the item upload_f = item.ensure_uploaded(upload_ctx) # It should succeed uploaded = upload_f.result() # It should be present in the target repos assert uploaded.in_pulp_repos == ["repo2", "repo7"] # Now what we're really interested in: what side effect did that # have on the repos? # Let's use this little helper to find out def get_pv(repo_id): return ctrl.client.get_repository(repo_id).product_versions # nothing changed here as this repo doesn't have any matching notes assert get_pv("repo1") == None # These were all updated to insert ["a", "d"] as expected assert get_pv("repo2") == ["a", "b", "d"] assert get_pv("repo3") == ["a", "b", "c", "d"] assert get_pv("repo4") == ["a", "d"] assert get_pv("repo5") == ["a", "c", "d"] # This was not changed due to platform_full_version mismatch assert get_pv("repo6") == ["d", "e"] # These two were changed. Note that repos 2..5 and repos 7..8 # have different sets of notes, showing that repo 2 found related # repos (3,4,5) and repo 7 found related repo 8. assert get_pv("repo7") == ["a", "b", "d"] assert get_pv("repo8") == ["a", "d"]
def test_upload_overwrite(): """repo.upload_erratum() can overwrite fields of an existing advisory.""" controller = FakeController() controller.insert_repository(YumRepository(id="repo1")) controller.insert_repository(YumRepository(id="repo2")) client = controller.client repo1 = client.get_repository("repo1").result() repo2 = client.get_repository("repo2").result() to_upload1 = ErratumUnit(id="RHBA-1234:56", summary="test advisory", version="1") to_upload2 = ErratumUnit( id="RHBA-1234:56", summary="updated test advisory", description="I've altered the deal", version="2", ) to_upload3 = ErratumUnit(id="RHBA-1234:57", summary="a different advisory") # Upload all three of the above advisories. # Uploads 1 and 2 are for the same advisory (though we use # different repos to see what happens). assert repo1.upload_erratum(to_upload1).result() assert repo2.upload_erratum(to_upload2).result() assert repo2.upload_erratum(to_upload3).result() # Now let's check the outcome in each repo (and entire system). units_repo1 = sorted(repo1.search_content(), key=repr) units_repo2 = sorted(repo2.search_content(), key=repr) units_all = sorted(client.search_content(), key=repr) # What we expect to see is that the three uploads resulted in only # two errata, with the fields on the first advisory set to the values # after the most recent upload of it, and the second advisory equal to # the single upload for that advisory. Also, the first advisory is # now present in two repos. assert units_repo1 == [ ErratumUnit( unit_id="e3e70682-c209-4cac-629f-6fbed82c07cd", id="RHBA-1234:56", summary="updated test advisory", description="I've altered the deal", version="2", repository_memberships=["repo1", "repo2"], ) ] assert units_repo2 == [ ErratumUnit( unit_id="e3e70682-c209-4cac-629f-6fbed82c07cd", id="RHBA-1234:56", summary="updated test advisory", description="I've altered the deal", version="2", repository_memberships=["repo1", "repo2"], ), ErratumUnit( unit_id="e6f4590b-9a16-4106-cf6a-659eb4862b21", id="RHBA-1234:57", summary="a different advisory", repository_memberships=["repo2"], ), ] assert units_repo2 == units_all
def __init__(self, *args, **kwargs): super(FakeClearRepo, self).__init__(*args, **kwargs) self.pulp_client_controller = FakeController() self._udcache_client = FakeUdCache() self._fastpurge_client = FakeFastPurge()
def test_can_remove_content(): """repo.remove() succeeds and removes expected units inserted via controller.""" controller = FakeController() client = controller.client rpm_units = [ RpmUnit(name="bash", version="4.0", release="1", arch="x86_64"), RpmUnit(name="glibc", version="5.0", release="1", arch="x86_64"), ] modulemd_units = [ ModulemdUnit(name="module1", stream="s1", version=1234, context="a1b2", arch="x86_64"), ModulemdUnit(name="module1", stream="s1", version=1235, context="a1b2", arch="x86_64"), ] units = rpm_units + modulemd_units repo = YumRepository(id="repo1") controller.insert_repository(repo) controller.insert_units(repo, units) remove_rpms = client.get_repository("repo1").remove_content( type_ids=["rpm"]) assert len(remove_rpms) == 1 task = remove_rpms[0] # It should have completed successfully assert task.completed assert task.succeeded # It should have removed (only) RPM units assert sorted(task.units) == sorted(rpm_units) # Now if we ask to remove same content again... remove_rpms = client.get_repository("repo1").remove_content( type_ids=["rpm"]) assert len(remove_rpms) == 1 task = remove_rpms[0] # It should have completed successfully, but no RPMs to remove assert task.completed assert task.succeeded assert not task.units # It should still be possible to remove other content remove_all = client.get_repository("repo1").remove_content() assert len(remove_all) == 1 task = remove_all[0] # It should have completed successfully, and removed the modulemds assert task.completed assert task.succeeded assert sorted(task.units) == sorted(modulemd_units)
def fake_pulp(): yield FakeController()
def test_overwrite(tmpdir): """repo.upload_metadata() multiple times with same data type overwrites old data.""" testfile1 = tmpdir.join("testfile1") testfile1.write(b"some bytes") testfile2 = tmpdir.join("testfile2") testfile2.write(b"other bytes") controller = FakeController() controller.insert_repository(YumRepository(id="repo1")) client = controller.client repo1 = client.get_repository("repo1").result() upload_f = repo1.upload_metadata(str(testfile1), "mdtype1") # Upload should complete successfully. upload_f.result() # It should have uploaded the first file assert list(repo1.search_content()) == [ YumRepoMetadataFileUnit( data_type="mdtype1", sha256sum= "0d22cdcc10e6d049dbe1af5123d50873fdfc1a4f58306e58cb6241be9472014d", content_type_id="yum_repo_metadata_file", repository_memberships=["repo1"], unit_id="e3e70682-c209-4cac-629f-6fbed82c07cd", ) ] # Now upload different content with same type upload_f = repo1.upload_metadata(str(testfile2), "mdtype1") # Upload should complete successfully. upload_f.result() # There should still just be one unit, but with the updated content assert list(repo1.search_content()) == [ YumRepoMetadataFileUnit( data_type="mdtype1", sha256sum= "a3ead5eedad5df82318c51685dbc1c147a36d1ff8584fc82de6b08d0bf63a795", content_type_id="yum_repo_metadata_file", repository_memberships=["repo1"], unit_id="e3e70682-c209-4cac-629f-6fbed82c07cd", ) ] # Uploading same content as a *different* type should be fine. upload_f = repo1.upload_metadata(str(testfile2), "mdtype2") # Upload should complete successfully. upload_f.result() # Now the same content is available as two types. assert sorted(repo1.search_content(), key=lambda u: u.data_type) == [ YumRepoMetadataFileUnit( data_type="mdtype1", sha256sum= "a3ead5eedad5df82318c51685dbc1c147a36d1ff8584fc82de6b08d0bf63a795", content_type_id="yum_repo_metadata_file", repository_memberships=["repo1"], unit_id="e3e70682-c209-4cac-629f-6fbed82c07cd", ), YumRepoMetadataFileUnit( data_type="mdtype2", sha256sum= "a3ead5eedad5df82318c51685dbc1c147a36d1ff8584fc82de6b08d0bf63a795", content_type_id="yum_repo_metadata_file", repository_memberships=["repo1"], unit_id="e6f4590b-9a16-4106-cf6a-659eb4862b21", ), ]
def controller(): return FakeController()
def __init__(self, *args, **kwargs): super(FakeUbiPopulate, self).__init__(*args, **kwargs) self.pulp_client_controller = FakeController()