Guruprasad has proposed merging ~lgp171188/launchpad:synchronize-oval-data-ppa-directory into launchpad:master.
Commit message: Synchronize the OVAL data from the staging to the PPA directory Requested reviews: Launchpad code reviewers (launchpad-reviewers) For more details, see: https://code.launchpad.net/~lgp171188/launchpad/+git/launchpad/+merge/441483 -- Your team Launchpad code reviewers is requested to review the proposed merge of ~lgp171188/launchpad:synchronize-oval-data-ppa-directory into launchpad:master.
diff --git a/lib/lp/archivepublisher/scripts/publishdistro.py b/lib/lp/archivepublisher/scripts/publishdistro.py index 87e9a7d..58cfa5d 100644 --- a/lib/lp/archivepublisher/scripts/publishdistro.py +++ b/lib/lp/archivepublisher/scripts/publishdistro.py @@ -11,6 +11,7 @@ import os from filecmp import dircmp from optparse import OptionValueError from pathlib import Path +from shutil import copy from subprocess import CalledProcessError, check_call from storm.store import Store @@ -418,6 +419,50 @@ class PublishDistro(PublisherScript): ) return False + def synchronizeSecondDirectoryWithFirst(self, first_dir, second_dir): + """Synchronize the contents of the second directory with the first.""" + comparison = dircmp(str(first_dir), str(second_dir)) + files_to_copy = ( + comparison.diff_files + + comparison.left_only + + comparison.funny_files + ) + files_to_delete = comparison.right_only + + for file in files_to_copy: + copy(str(first_dir / file), str(second_dir)) + + for file in files_to_delete: + os.unlink(str(second_dir / file)) + + return bool(files_to_copy) or bool(files_to_delete) + + def syncOVALDataFilesForSuite(self, archive, suite): + """Synchronize the OVAL data from the staging to the PPA directory.""" + updated = False + staged_oval_data_for_suite = ( + Path(config.archivepublisher.oval_data_root) + / archive.reference + / suite + ) + if staged_oval_data_for_suite.exists(): + for item in staged_oval_data_for_suite.iterdir(): + if not item.is_dir(): + continue + component = item + staged_oval_data_dir = staged_oval_data_for_suite / component + dest_dir = Path( + getPubConfig(archive).distsroot + ) / "{}/{}/oval".format(suite, component.name) + if not dest_dir.exists(): + dest_dir.mkdir(parents=True) + files_modified = self.synchronizeSecondDirectoryWithFirst( + staged_oval_data_dir, dest_dir + ) + if files_modified: + updated = True + return updated + def publishArchive(self, archive, publisher): """Ask `publisher` to publish `archive`. @@ -428,10 +473,13 @@ class PublishDistro(PublisherScript): for distroseries, pocket in self.findExplicitlyDirtySuites(archive): if not cannot_modify_suite(archive, distroseries, pocket): publisher.markSuiteDirty(distroseries, pocket) + + dirty_suites = None if archive.dirty_suites is not None: # Clear the explicit dirt indicator before we start doing # time-consuming publishing, which might race with an # Archive.markSuiteDirty call. + dirty_suites = archive.dirty_suites archive.dirty_suites = None self.txn.commit() @@ -475,6 +523,20 @@ class PublishDistro(PublisherScript): self.options.enable_release and publishing_method == ArchivePublishingMethod.LOCAL ): + if ( + config.archivepublisher.oval_data_rsync_endpoint + and archive.is_ppa + and dirty_suites + ): + for dirty_suite in dirty_suites: + updated = self.syncOVALDataFilesForSuite( + archive, dirty_suite + ) + if updated: + self.logger.info( + "Synchronized the OVAL data for %s", + archive.reference, + ) publisher.D_writeReleaseFiles( self.isCareful( self.options.careful_apt or self.options.careful_release diff --git a/lib/lp/archivepublisher/tests/test_publishdistro.py b/lib/lp/archivepublisher/tests/test_publishdistro.py index 049dccb..ef67e81 100644 --- a/lib/lp/archivepublisher/tests/test_publishdistro.py +++ b/lib/lp/archivepublisher/tests/test_publishdistro.py @@ -8,6 +8,7 @@ import shutil import subprocess from optparse import OptionValueError from pathlib import Path +from unittest.mock import call from fixtures import MockPatch from storm.store import Store @@ -267,6 +268,7 @@ class TestPublishDistro(TestNativePublishingBase): oval_data_root=self.oval_data_root, oval_data_rsync_timeout=90, ) + self.addCleanup(shutil.rmtree, self.oval_data_root) def testPublishDistroOVALDataRsyncEndpointNotConfigured(self): """ @@ -921,6 +923,7 @@ class FakePublisher: self.C_writeIndexes = FakeMethod() self.D_writeReleaseFiles = FakeMethod() self.createSeriesAliases = FakeMethod() + self.markSuiteDirty = FakeMethod() class TestPublishDistroMethods(TestCaseWithFactory): @@ -1479,3 +1482,282 @@ class TestPublishDistroMethods(TestCaseWithFactory): self.assertEqual( [((archive, publisher), {})], script.publishArchive.calls ) + + def setUpOVALDataRsync(self): + self.oval_data_root = self.makeTemporaryDirectory() + self.pushConfig( + "archivepublisher", + oval_data_rsync_endpoint="oval.internal::oval/", + oval_data_root=self.oval_data_root, + oval_data_rsync_timeout=90, + ) + self.ppa_root = self.makeTemporaryDirectory() + self.pushConfig( + "personalpackagearchive", + root=self.ppa_root, + ) + self.addCleanup(shutil.rmtree, self.oval_data_root) + self.addCleanup(shutil.rmtree, self.ppa_root) + + def test_syncOVALDataFilesForSuite_oval_data_missing_in_destination(self): + self.setUpOVALDataRsync() + self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.check_call") + ) + mock_copy = self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.copy") + ).mock + mock_unlink = self.useFixture(MockPatch("pathlib.Path.unlink")).mock + archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA) + incoming_dir = ( + Path(self.oval_data_root) + / archive.reference + / "breezy-autotest" + / "main" + ) + write_file(str(incoming_dir / "test"), b"test") + script = self.makeScript() + script.txn = FakeTransaction() + script.findDistros = FakeMethod([archive.distribution]) + script.getTargetArchives = FakeMethod([archive]) + publisher = FakePublisher() + script.getPublisher = FakeMethod(publisher) + script.main() + mock_copy.assert_has_calls( + [ + call( + str(incoming_dir / "test"), + "{}/breezy-autotest/main/oval".format( + getPubConfig(archive).distsroot + ), + ) + ] + ) + mock_unlink.assert_not_called() + + def test_syncOVALDataFilesForSuite_oval_data_missing_in_source(self): + self.setUpOVALDataRsync() + self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.check_call") + ) + mock_copy = self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.copy") + ).mock + mock_unlink = self.useFixture(MockPatch("os.unlink")).mock + archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA) + incoming_dir = ( + Path(self.oval_data_root) + / archive.reference + / "breezy-autotest" + / "main" + ) + incoming_dir.mkdir(parents=True, exist_ok=True) + published_dir = ( + Path(getPubConfig(archive).distsroot) + / "breezy-autotest" + / "main" + / "oval" + ) + write_file(str(published_dir / "foo.oval.xml.bz2"), b"test") + write_file(str(published_dir / "foo2.oval.xml.bz2"), b"test") + + script = self.makeScript() + script.txn = FakeTransaction() + script.findDistros = FakeMethod([archive.distribution]) + script.getTargetArchives = FakeMethod([archive]) + publisher = FakePublisher() + script.getPublisher = FakeMethod(publisher) + script.main() + mock_copy.assert_not_called() + mock_unlink.assert_has_calls( + [ + call(str(published_dir / "foo.oval.xml.bz2")), + call(str(published_dir / "foo2.oval.xml.bz2")), + ], + any_order=True, + ) + + def test_syncOVALDataFilesForSuite_oval_data_unchanged(self): + self.setUpOVALDataRsync() + self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.check_call") + ) + mock_copy = self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.copy") + ).mock + mock_unlink = self.useFixture(MockPatch("os.unlink")).mock + archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA) + incoming_dir = ( + Path(self.oval_data_root) + / archive.reference + / "breezy-autotest" + / "main" + ) + incoming_dir.mkdir(parents=True, exist_ok=True) + write_file(str(incoming_dir / "foo.oval.xml.bz2"), b"test") + write_file(str(incoming_dir / "foo2.oval.xml.bz2"), b"test") + published_dir = ( + Path(getPubConfig(archive).distsroot) + / "breezy-autotest" + / "main" + / "oval" + ) + write_file(str(published_dir / "foo.oval.xml.bz2"), b"test") + write_file(str(published_dir / "foo2.oval.xml.bz2"), b"test") + + script = self.makeScript() + script.txn = FakeTransaction() + script.findDistros = FakeMethod([archive.distribution]) + script.getTargetArchives = FakeMethod([archive]) + publisher = FakePublisher() + script.getPublisher = FakeMethod(publisher) + script.main() + mock_copy.assert_not_called() + mock_unlink.assert_not_called() + + def test_syncOVALDataFilesForSuite_oval_data_updated(self): + self.setUpOVALDataRsync() + self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.check_call") + ) + mock_copy = self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.copy") + ).mock + mock_unlink = self.useFixture(MockPatch("os.unlink")).mock + archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA) + incoming_dir = ( + Path(self.oval_data_root) + / archive.reference + / "breezy-autotest" + / "main" + ) + incoming_dir.mkdir(parents=True, exist_ok=True) + write_file(str(incoming_dir / "foo.oval.xml.bz2"), b"test2") + write_file(str(incoming_dir / "foo2.oval.xml.bz2"), b"test2") + published_dir = ( + Path(getPubConfig(archive).distsroot) + / "breezy-autotest" + / "main" + / "oval" + ) + write_file(str(published_dir / "foo.oval.xml.bz2"), b"test") + write_file(str(published_dir / "foo2.oval.xml.bz2"), b"test") + + script = self.makeScript() + script.txn = FakeTransaction() + script.findDistros = FakeMethod([archive.distribution]) + script.getTargetArchives = FakeMethod([archive]) + publisher = FakePublisher() + script.getPublisher = FakeMethod(publisher) + script.main() + mock_copy.assert_has_calls( + [ + call( + str(incoming_dir / "foo.oval.xml.bz2"), + "{}/breezy-autotest/main/oval".format( + getPubConfig(archive).distsroot + ), + ), + call( + str(incoming_dir / "foo2.oval.xml.bz2"), + "{}/breezy-autotest/main/oval".format( + getPubConfig(archive).distsroot + ), + ), + ], + any_order=True, + ) + mock_unlink.assert_not_called() + + def test_syncOVALDataFilesForSuite_oval_data_new_files(self): + self.setUpOVALDataRsync() + self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.check_call") + ) + mock_copy = self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.copy") + ).mock + mock_unlink = self.useFixture(MockPatch("os.unlink")).mock + archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA) + incoming_dir = ( + Path(self.oval_data_root) + / archive.reference + / "breezy-autotest" + / "main" + ) + incoming_dir.mkdir(parents=True, exist_ok=True) + write_file(str(incoming_dir / "foo.oval.xml.bz2"), b"test") + write_file(str(incoming_dir / "foo2.oval.xml.bz2"), b"test") + write_file(str(incoming_dir / "foo3.oval.xml.bz2"), b"test") + published_dir = ( + Path(getPubConfig(archive).distsroot) + / "breezy-autotest" + / "main" + / "oval" + ) + write_file(str(published_dir / "foo.oval.xml.bz2"), b"test") + write_file(str(published_dir / "foo2.oval.xml.bz2"), b"test") + + script = self.makeScript() + script.txn = FakeTransaction() + script.findDistros = FakeMethod([archive.distribution]) + script.getTargetArchives = FakeMethod([archive]) + publisher = FakePublisher() + script.getPublisher = FakeMethod(publisher) + script.main() + mock_copy.assert_has_calls( + [ + call( + str(incoming_dir / "foo3.oval.xml.bz2"), + "{}/breezy-autotest/main/oval".format( + getPubConfig(archive).distsroot + ), + ), + ] + ) + mock_unlink.assert_not_called() + + def test_syncOVALDataFilesForSuite_oval_data_some_files_removed(self): + self.setUpOVALDataRsync() + self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.check_call") + ) + mock_copy = self.useFixture( + MockPatch("lp.archivepublisher.scripts.publishdistro.copy") + ).mock + mock_unlink = self.useFixture(MockPatch("os.unlink")).mock + archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA) + incoming_dir = ( + Path(self.oval_data_root) + / archive.reference + / "breezy-autotest" + / "main" + ) + incoming_dir.mkdir(parents=True, exist_ok=True) + write_file(str(incoming_dir / "foo.oval.xml.bz2"), b"test") + published_dir = ( + Path(getPubConfig(archive).distsroot) + / "breezy-autotest" + / "main" + / "oval" + ) + write_file(str(published_dir / "foo.oval.xml.bz2"), b"test") + write_file(str(published_dir / "foo2.oval.xml.bz2"), b"test") + + script = self.makeScript() + script.txn = FakeTransaction() + script.findDistros = FakeMethod([archive.distribution]) + script.getTargetArchives = FakeMethod([archive]) + publisher = FakePublisher() + script.getPublisher = FakeMethod(publisher) + script.main() + mock_copy.assert_not_called() + mock_unlink.assert_has_calls( + [ + call( + "{}/breezy-autotest/main/oval/foo2.oval.xml.bz2".format( + getPubConfig(archive).distsroot + ), + ), + ] + )
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : launchpad-reviewers@lists.launchpad.net Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp