Merge ~lgp171188/launchpad:synchronize-oval-data-ppa-directory into launchpad:master

Proposed by Guruprasad
Status: Rejected
Rejected by: Jürgen Gmach
Proposed branch: ~lgp171188/launchpad:synchronize-oval-data-ppa-directory
Merge into: launchpad:master
Diff against target: 409 lines (+344/-0)
2 files modified
lib/lp/archivepublisher/scripts/publishdistro.py (+62/-0)
lib/lp/archivepublisher/tests/test_publishdistro.py (+282/-0)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+441483@code.launchpad.net

Commit message

Synchronize the OVAL data from the staging to the PPA directory

To post a comment you must log in.
Revision history for this message
Guruprasad (lgp171188) :
Revision history for this message
Colin Watson (cjwatson) :
review: Approve
Revision history for this message
Jürgen Gmach (jugmac00) wrote :

Thanks so much, Guruprasad!

I have squashed your changes and added the recommended changes on top at https://code.launchpad.net/~jugmac00/launchpad/+git/launchpad/+merge/441693

Unmerged commits

c5a2f7f... by Guruprasad

Implement more fixes and add more tests

Succeeded
[SUCCEEDED] docs:0 (build)
[SUCCEEDED] lint:0 (build)
[SUCCEEDED] mypy:0 (build)
13 of 3 results
44b3df7... by Guruprasad

Implement fixes and add some unit tests

Succeeded
[SUCCEEDED] docs:0 (build)
[SUCCEEDED] lint:0 (build)
[SUCCEEDED] mypy:0 (build)
13 of 3 results
10e9718... by Guruprasad

Synchronize the OVAL data from the staging to the PPA directory

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/archivepublisher/scripts/publishdistro.py b/lib/lp/archivepublisher/scripts/publishdistro.py
2index 87e9a7d..58cfa5d 100644
3--- a/lib/lp/archivepublisher/scripts/publishdistro.py
4+++ b/lib/lp/archivepublisher/scripts/publishdistro.py
5@@ -11,6 +11,7 @@ import os
6 from filecmp import dircmp
7 from optparse import OptionValueError
8 from pathlib import Path
9+from shutil import copy
10 from subprocess import CalledProcessError, check_call
11
12 from storm.store import Store
13@@ -418,6 +419,50 @@ class PublishDistro(PublisherScript):
14 )
15 return False
16
17+ def synchronizeSecondDirectoryWithFirst(self, first_dir, second_dir):
18+ """Synchronize the contents of the second directory with the first."""
19+ comparison = dircmp(str(first_dir), str(second_dir))
20+ files_to_copy = (
21+ comparison.diff_files
22+ + comparison.left_only
23+ + comparison.funny_files
24+ )
25+ files_to_delete = comparison.right_only
26+
27+ for file in files_to_copy:
28+ copy(str(first_dir / file), str(second_dir))
29+
30+ for file in files_to_delete:
31+ os.unlink(str(second_dir / file))
32+
33+ return bool(files_to_copy) or bool(files_to_delete)
34+
35+ def syncOVALDataFilesForSuite(self, archive, suite):
36+ """Synchronize the OVAL data from the staging to the PPA directory."""
37+ updated = False
38+ staged_oval_data_for_suite = (
39+ Path(config.archivepublisher.oval_data_root)
40+ / archive.reference
41+ / suite
42+ )
43+ if staged_oval_data_for_suite.exists():
44+ for item in staged_oval_data_for_suite.iterdir():
45+ if not item.is_dir():
46+ continue
47+ component = item
48+ staged_oval_data_dir = staged_oval_data_for_suite / component
49+ dest_dir = Path(
50+ getPubConfig(archive).distsroot
51+ ) / "{}/{}/oval".format(suite, component.name)
52+ if not dest_dir.exists():
53+ dest_dir.mkdir(parents=True)
54+ files_modified = self.synchronizeSecondDirectoryWithFirst(
55+ staged_oval_data_dir, dest_dir
56+ )
57+ if files_modified:
58+ updated = True
59+ return updated
60+
61 def publishArchive(self, archive, publisher):
62 """Ask `publisher` to publish `archive`.
63
64@@ -428,10 +473,13 @@ class PublishDistro(PublisherScript):
65 for distroseries, pocket in self.findExplicitlyDirtySuites(archive):
66 if not cannot_modify_suite(archive, distroseries, pocket):
67 publisher.markSuiteDirty(distroseries, pocket)
68+
69+ dirty_suites = None
70 if archive.dirty_suites is not None:
71 # Clear the explicit dirt indicator before we start doing
72 # time-consuming publishing, which might race with an
73 # Archive.markSuiteDirty call.
74+ dirty_suites = archive.dirty_suites
75 archive.dirty_suites = None
76 self.txn.commit()
77
78@@ -475,6 +523,20 @@ class PublishDistro(PublisherScript):
79 self.options.enable_release
80 and publishing_method == ArchivePublishingMethod.LOCAL
81 ):
82+ if (
83+ config.archivepublisher.oval_data_rsync_endpoint
84+ and archive.is_ppa
85+ and dirty_suites
86+ ):
87+ for dirty_suite in dirty_suites:
88+ updated = self.syncOVALDataFilesForSuite(
89+ archive, dirty_suite
90+ )
91+ if updated:
92+ self.logger.info(
93+ "Synchronized the OVAL data for %s",
94+ archive.reference,
95+ )
96 publisher.D_writeReleaseFiles(
97 self.isCareful(
98 self.options.careful_apt or self.options.careful_release
99diff --git a/lib/lp/archivepublisher/tests/test_publishdistro.py b/lib/lp/archivepublisher/tests/test_publishdistro.py
100index 049dccb..ef67e81 100644
101--- a/lib/lp/archivepublisher/tests/test_publishdistro.py
102+++ b/lib/lp/archivepublisher/tests/test_publishdistro.py
103@@ -8,6 +8,7 @@ import shutil
104 import subprocess
105 from optparse import OptionValueError
106 from pathlib import Path
107+from unittest.mock import call
108
109 from fixtures import MockPatch
110 from storm.store import Store
111@@ -267,6 +268,7 @@ class TestPublishDistro(TestNativePublishingBase):
112 oval_data_root=self.oval_data_root,
113 oval_data_rsync_timeout=90,
114 )
115+ self.addCleanup(shutil.rmtree, self.oval_data_root)
116
117 def testPublishDistroOVALDataRsyncEndpointNotConfigured(self):
118 """
119@@ -921,6 +923,7 @@ class FakePublisher:
120 self.C_writeIndexes = FakeMethod()
121 self.D_writeReleaseFiles = FakeMethod()
122 self.createSeriesAliases = FakeMethod()
123+ self.markSuiteDirty = FakeMethod()
124
125
126 class TestPublishDistroMethods(TestCaseWithFactory):
127@@ -1479,3 +1482,282 @@ class TestPublishDistroMethods(TestCaseWithFactory):
128 self.assertEqual(
129 [((archive, publisher), {})], script.publishArchive.calls
130 )
131+
132+ def setUpOVALDataRsync(self):
133+ self.oval_data_root = self.makeTemporaryDirectory()
134+ self.pushConfig(
135+ "archivepublisher",
136+ oval_data_rsync_endpoint="oval.internal::oval/",
137+ oval_data_root=self.oval_data_root,
138+ oval_data_rsync_timeout=90,
139+ )
140+ self.ppa_root = self.makeTemporaryDirectory()
141+ self.pushConfig(
142+ "personalpackagearchive",
143+ root=self.ppa_root,
144+ )
145+ self.addCleanup(shutil.rmtree, self.oval_data_root)
146+ self.addCleanup(shutil.rmtree, self.ppa_root)
147+
148+ def test_syncOVALDataFilesForSuite_oval_data_missing_in_destination(self):
149+ self.setUpOVALDataRsync()
150+ self.useFixture(
151+ MockPatch("lp.archivepublisher.scripts.publishdistro.check_call")
152+ )
153+ mock_copy = self.useFixture(
154+ MockPatch("lp.archivepublisher.scripts.publishdistro.copy")
155+ ).mock
156+ mock_unlink = self.useFixture(MockPatch("pathlib.Path.unlink")).mock
157+ archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
158+ incoming_dir = (
159+ Path(self.oval_data_root)
160+ / archive.reference
161+ / "breezy-autotest"
162+ / "main"
163+ )
164+ write_file(str(incoming_dir / "test"), b"test")
165+ script = self.makeScript()
166+ script.txn = FakeTransaction()
167+ script.findDistros = FakeMethod([archive.distribution])
168+ script.getTargetArchives = FakeMethod([archive])
169+ publisher = FakePublisher()
170+ script.getPublisher = FakeMethod(publisher)
171+ script.main()
172+ mock_copy.assert_has_calls(
173+ [
174+ call(
175+ str(incoming_dir / "test"),
176+ "{}/breezy-autotest/main/oval".format(
177+ getPubConfig(archive).distsroot
178+ ),
179+ )
180+ ]
181+ )
182+ mock_unlink.assert_not_called()
183+
184+ def test_syncOVALDataFilesForSuite_oval_data_missing_in_source(self):
185+ self.setUpOVALDataRsync()
186+ self.useFixture(
187+ MockPatch("lp.archivepublisher.scripts.publishdistro.check_call")
188+ )
189+ mock_copy = self.useFixture(
190+ MockPatch("lp.archivepublisher.scripts.publishdistro.copy")
191+ ).mock
192+ mock_unlink = self.useFixture(MockPatch("os.unlink")).mock
193+ archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
194+ incoming_dir = (
195+ Path(self.oval_data_root)
196+ / archive.reference
197+ / "breezy-autotest"
198+ / "main"
199+ )
200+ incoming_dir.mkdir(parents=True, exist_ok=True)
201+ published_dir = (
202+ Path(getPubConfig(archive).distsroot)
203+ / "breezy-autotest"
204+ / "main"
205+ / "oval"
206+ )
207+ write_file(str(published_dir / "foo.oval.xml.bz2"), b"test")
208+ write_file(str(published_dir / "foo2.oval.xml.bz2"), b"test")
209+
210+ script = self.makeScript()
211+ script.txn = FakeTransaction()
212+ script.findDistros = FakeMethod([archive.distribution])
213+ script.getTargetArchives = FakeMethod([archive])
214+ publisher = FakePublisher()
215+ script.getPublisher = FakeMethod(publisher)
216+ script.main()
217+ mock_copy.assert_not_called()
218+ mock_unlink.assert_has_calls(
219+ [
220+ call(str(published_dir / "foo.oval.xml.bz2")),
221+ call(str(published_dir / "foo2.oval.xml.bz2")),
222+ ],
223+ any_order=True,
224+ )
225+
226+ def test_syncOVALDataFilesForSuite_oval_data_unchanged(self):
227+ self.setUpOVALDataRsync()
228+ self.useFixture(
229+ MockPatch("lp.archivepublisher.scripts.publishdistro.check_call")
230+ )
231+ mock_copy = self.useFixture(
232+ MockPatch("lp.archivepublisher.scripts.publishdistro.copy")
233+ ).mock
234+ mock_unlink = self.useFixture(MockPatch("os.unlink")).mock
235+ archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
236+ incoming_dir = (
237+ Path(self.oval_data_root)
238+ / archive.reference
239+ / "breezy-autotest"
240+ / "main"
241+ )
242+ incoming_dir.mkdir(parents=True, exist_ok=True)
243+ write_file(str(incoming_dir / "foo.oval.xml.bz2"), b"test")
244+ write_file(str(incoming_dir / "foo2.oval.xml.bz2"), b"test")
245+ published_dir = (
246+ Path(getPubConfig(archive).distsroot)
247+ / "breezy-autotest"
248+ / "main"
249+ / "oval"
250+ )
251+ write_file(str(published_dir / "foo.oval.xml.bz2"), b"test")
252+ write_file(str(published_dir / "foo2.oval.xml.bz2"), b"test")
253+
254+ script = self.makeScript()
255+ script.txn = FakeTransaction()
256+ script.findDistros = FakeMethod([archive.distribution])
257+ script.getTargetArchives = FakeMethod([archive])
258+ publisher = FakePublisher()
259+ script.getPublisher = FakeMethod(publisher)
260+ script.main()
261+ mock_copy.assert_not_called()
262+ mock_unlink.assert_not_called()
263+
264+ def test_syncOVALDataFilesForSuite_oval_data_updated(self):
265+ self.setUpOVALDataRsync()
266+ self.useFixture(
267+ MockPatch("lp.archivepublisher.scripts.publishdistro.check_call")
268+ )
269+ mock_copy = self.useFixture(
270+ MockPatch("lp.archivepublisher.scripts.publishdistro.copy")
271+ ).mock
272+ mock_unlink = self.useFixture(MockPatch("os.unlink")).mock
273+ archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
274+ incoming_dir = (
275+ Path(self.oval_data_root)
276+ / archive.reference
277+ / "breezy-autotest"
278+ / "main"
279+ )
280+ incoming_dir.mkdir(parents=True, exist_ok=True)
281+ write_file(str(incoming_dir / "foo.oval.xml.bz2"), b"test2")
282+ write_file(str(incoming_dir / "foo2.oval.xml.bz2"), b"test2")
283+ published_dir = (
284+ Path(getPubConfig(archive).distsroot)
285+ / "breezy-autotest"
286+ / "main"
287+ / "oval"
288+ )
289+ write_file(str(published_dir / "foo.oval.xml.bz2"), b"test")
290+ write_file(str(published_dir / "foo2.oval.xml.bz2"), b"test")
291+
292+ script = self.makeScript()
293+ script.txn = FakeTransaction()
294+ script.findDistros = FakeMethod([archive.distribution])
295+ script.getTargetArchives = FakeMethod([archive])
296+ publisher = FakePublisher()
297+ script.getPublisher = FakeMethod(publisher)
298+ script.main()
299+ mock_copy.assert_has_calls(
300+ [
301+ call(
302+ str(incoming_dir / "foo.oval.xml.bz2"),
303+ "{}/breezy-autotest/main/oval".format(
304+ getPubConfig(archive).distsroot
305+ ),
306+ ),
307+ call(
308+ str(incoming_dir / "foo2.oval.xml.bz2"),
309+ "{}/breezy-autotest/main/oval".format(
310+ getPubConfig(archive).distsroot
311+ ),
312+ ),
313+ ],
314+ any_order=True,
315+ )
316+ mock_unlink.assert_not_called()
317+
318+ def test_syncOVALDataFilesForSuite_oval_data_new_files(self):
319+ self.setUpOVALDataRsync()
320+ self.useFixture(
321+ MockPatch("lp.archivepublisher.scripts.publishdistro.check_call")
322+ )
323+ mock_copy = self.useFixture(
324+ MockPatch("lp.archivepublisher.scripts.publishdistro.copy")
325+ ).mock
326+ mock_unlink = self.useFixture(MockPatch("os.unlink")).mock
327+ archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
328+ incoming_dir = (
329+ Path(self.oval_data_root)
330+ / archive.reference
331+ / "breezy-autotest"
332+ / "main"
333+ )
334+ incoming_dir.mkdir(parents=True, exist_ok=True)
335+ write_file(str(incoming_dir / "foo.oval.xml.bz2"), b"test")
336+ write_file(str(incoming_dir / "foo2.oval.xml.bz2"), b"test")
337+ write_file(str(incoming_dir / "foo3.oval.xml.bz2"), b"test")
338+ published_dir = (
339+ Path(getPubConfig(archive).distsroot)
340+ / "breezy-autotest"
341+ / "main"
342+ / "oval"
343+ )
344+ write_file(str(published_dir / "foo.oval.xml.bz2"), b"test")
345+ write_file(str(published_dir / "foo2.oval.xml.bz2"), b"test")
346+
347+ script = self.makeScript()
348+ script.txn = FakeTransaction()
349+ script.findDistros = FakeMethod([archive.distribution])
350+ script.getTargetArchives = FakeMethod([archive])
351+ publisher = FakePublisher()
352+ script.getPublisher = FakeMethod(publisher)
353+ script.main()
354+ mock_copy.assert_has_calls(
355+ [
356+ call(
357+ str(incoming_dir / "foo3.oval.xml.bz2"),
358+ "{}/breezy-autotest/main/oval".format(
359+ getPubConfig(archive).distsroot
360+ ),
361+ ),
362+ ]
363+ )
364+ mock_unlink.assert_not_called()
365+
366+ def test_syncOVALDataFilesForSuite_oval_data_some_files_removed(self):
367+ self.setUpOVALDataRsync()
368+ self.useFixture(
369+ MockPatch("lp.archivepublisher.scripts.publishdistro.check_call")
370+ )
371+ mock_copy = self.useFixture(
372+ MockPatch("lp.archivepublisher.scripts.publishdistro.copy")
373+ ).mock
374+ mock_unlink = self.useFixture(MockPatch("os.unlink")).mock
375+ archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
376+ incoming_dir = (
377+ Path(self.oval_data_root)
378+ / archive.reference
379+ / "breezy-autotest"
380+ / "main"
381+ )
382+ incoming_dir.mkdir(parents=True, exist_ok=True)
383+ write_file(str(incoming_dir / "foo.oval.xml.bz2"), b"test")
384+ published_dir = (
385+ Path(getPubConfig(archive).distsroot)
386+ / "breezy-autotest"
387+ / "main"
388+ / "oval"
389+ )
390+ write_file(str(published_dir / "foo.oval.xml.bz2"), b"test")
391+ write_file(str(published_dir / "foo2.oval.xml.bz2"), b"test")
392+
393+ script = self.makeScript()
394+ script.txn = FakeTransaction()
395+ script.findDistros = FakeMethod([archive.distribution])
396+ script.getTargetArchives = FakeMethod([archive])
397+ publisher = FakePublisher()
398+ script.getPublisher = FakeMethod(publisher)
399+ script.main()
400+ mock_copy.assert_not_called()
401+ mock_unlink.assert_has_calls(
402+ [
403+ call(
404+ "{}/breezy-autotest/main/oval/foo2.oval.xml.bz2".format(
405+ getPubConfig(archive).distsroot
406+ ),
407+ ),
408+ ]
409+ )

Subscribers

People subscribed via source and target branches

to status/vote changes: