Merge lp:~jtv/launchpad/bug-832661 into lp:launchpad

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: no longer in the source branch.
Merged at revision: 13805
Proposed branch: lp:~jtv/launchpad/bug-832661
Merge into: lp:launchpad
Prerequisite: lp:~jtv/launchpad/pre-832661
Diff against target: 409 lines (+204/-39)
7 files modified
lib/lp/soyuz/interfaces/distributionjob.py (+5/-2)
lib/lp/soyuz/model/distroseriesdifferencejob.py (+17/-2)
lib/lp/soyuz/model/publishing.py (+3/-11)
lib/lp/soyuz/tests/test_distroseriesdifferencejob.py (+119/-1)
lib/lp/soyuz/tests/test_publishing.py (+28/-23)
lib/lp/testing/factory.py (+18/-0)
lib/lp/testing/tests/test_factory.py (+14/-0)
To merge this branch: bzr merge lp:~jtv/launchpad/bug-832661
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+73012@code.launchpad.net

This proposal supersedes a proposal from 2011-08-26.

Commit message

[r=adeuring][bug=832661] Create DSDJs from requestDeletion.

Description of the change

= Summary =

When we mark a SourcePackagePublishingHistory as deleted in the primary archive of a distroseries that is involved in derivation, we also need to generate a DistroSeriesDifferenceJob for it. SPPH.requestDeletion was already doing this but PublishingSet.requestDeletion was not.

== Proposed fix ==

Move some code around and then leave the real problem — making it scale — for someone else.

== Pre-implementation notes ==

Julian pointed out Archive.is_main; it's likely to be cheaper than checking DistroSeries.main_archive, which issues an extra query at least once.

== Implementation details ==

The core of it is IDistroSeriesDifferenceJobSource.createForSPPHs. You'll find a naïve implementation there that was enough to get me through the tests; since we'll be executing this from Gina (which has a huge backlog of "active" publications that need deleting) and when deleting archives, it's probably important to make it scale better. But given time constraints, I made that a separate work item.

== Tests ==

{{{
./bin/test -vvc lp.soyuz.tests.test_publishing -t requestDeletion
./bin/test -vvc lp.soyuz.tests.test_distroseriesdifferencejob -t createForSPPHs
}}}

== Demo and Q/A ==

Deleting an archive should still mark all its source publication records as deleted.

= Launchpad lint =

The lint is actually remaining pre-existing lint from files I only touched in a previous branch (and fixed in yet another branch), both of which I've been unable to land because of bug 833743 and various other buildbot failures. It's been a wild few days. I'll have to re-submit this branch to register the dependencies on those other branches, so the lint reported here will no longer apply.

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/soyuz/model/distroseriesdifferencejob.py
  lib/lp/soyuz/tests/test_publishing.py
  lib/lp/soyuz/model/publishing.py
  lib/lp/soyuz/doc/publishing.txt
  lib/lp/testing/factory.py
  lib/lp/soyuz/interfaces/distributionjob.py
  lib/lp/soyuz/tests/test_distroseriesdifferencejob.py
  lib/lp/soyuz/model/archive.py
  lib/lp/testing/tests/test_factory.py

./lib/lp/soyuz/doc/publishing.txt
     119: want exceeds 78 characters.
     367: want exceeds 78 characters.
     925: want exceeds 78 characters.
     927: want exceeds 78 characters.
     929: want exceeds 78 characters.
     952: want exceeds 78 characters.
./lib/lp/soyuz/model/archive.py
     350: redefinition of function 'private' from line 346

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/interfaces/distributionjob.py'
2--- lib/lp/soyuz/interfaces/distributionjob.py 2011-07-27 10:34:53 +0000
3+++ lib/lp/soyuz/interfaces/distributionjob.py 2011-08-26 10:07:22 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2010 Canonical Ltd. This software is licensed under the
6+# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 __metaclass__ = type
10@@ -122,7 +122,7 @@
11 """An `IJob` for creating `DistroSeriesDifference`s."""
12
13 def createForPackagePublication(derivedseries, sourcepackagename, pocket):
14- """Create jobs as appropriate for a given status publication.
15+ """Create jobs as appropriate for a given package publication.
16
17 :param derived_series: A `DistroSeries` that is assumed to be
18 derived from `parent_series`.
19@@ -132,6 +132,9 @@
20 :return: An iterable of `DistroSeriesDifferenceJob`.
21 """
22
23+ def createForSPPHs(spphs):
24+ """Create jobs for given `SourcePackagePublishingHistory`s."""
25+
26 def massCreateForSeries(derived_series):
27 """Create jobs for all the publications inside the given distroseries
28 with reference to the given parent series.
29
30=== modified file 'lib/lp/soyuz/model/distroseriesdifferencejob.py'
31--- lib/lp/soyuz/model/distroseriesdifferencejob.py 2011-08-09 10:01:02 +0000
32+++ lib/lp/soyuz/model/distroseriesdifferencejob.py 2011-08-26 10:07:22 +0000
33@@ -195,13 +195,16 @@
34 """See `IDistroSeriesDifferenceJobSource`."""
35 if not getFeatureFlag(FEATURE_FLAG_ENABLE_MODULE):
36 return
37+
38 # -backports and -proposed are not really part of a standard
39 # distribution's packages so we're ignoring them here. They can
40 # always be manually synced by the users if necessary, in the
41 # rare occasions that they require them.
42- if pocket in (
43+ ignored_pockets = [
44 PackagePublishingPocket.BACKPORTS,
45- PackagePublishingPocket.PROPOSED):
46+ PackagePublishingPocket.PROPOSED,
47+ ]
48+ if pocket in ignored_pockets:
49 return
50
51 # Create jobs for DSDs between the derived_series' parents and
52@@ -221,6 +224,18 @@
53 return parent_series_jobs + derived_series_jobs
54
55 @classmethod
56+ def createForSPPHs(cls, spphs):
57+ """See `IDistroSeriesDifferenceJobSource`."""
58+ # XXX JeroenVermeulen 2011-08-25, bug=834499: This won't do for
59+ # some of the mass deletions we're planning to support.
60+ # Optimize.
61+ for spph in spphs:
62+ if spph.archive.is_main:
63+ cls.createForPackagePublication(
64+ spph.distroseries,
65+ spph.sourcepackagerelease.sourcepackagename, spph.pocket)
66+
67+ @classmethod
68 def massCreateForSeries(cls, derived_series):
69 """See `IDistroSeriesDifferenceJobSource`."""
70 if not getFeatureFlag(FEATURE_FLAG_ENABLE_MODULE):
71
72=== modified file 'lib/lp/soyuz/model/publishing.py'
73--- lib/lp/soyuz/model/publishing.py 2011-08-26 10:07:15 +0000
74+++ lib/lp/soyuz/model/publishing.py 2011-08-26 10:07:22 +0000
75@@ -339,16 +339,6 @@
76 self.removed_by = removed_by
77 self.removal_comment = removal_comment
78
79- def requestDeletion(self, removed_by, removal_comment=None):
80- """See `IPublishing`."""
81- self.setDeleted(removed_by, removal_comment)
82- if ISourcePackagePublishingHistory.providedBy(self):
83- if self.archive == self.distroseries.main_archive:
84- dsd_job_source = getUtility(IDistroSeriesDifferenceJobSource)
85- dsd_job_source.createForPackagePublication(
86- self.distroseries,
87- self.sourcepackagerelease.sourcepackagename, self.pocket)
88-
89 def requestObsolescence(self):
90 """See `IArchivePublisher`."""
91 # The tactic here is to bypass the domination step when publishing,
92@@ -901,7 +891,7 @@
93 def requestDeletion(self, removed_by, removal_comment=None):
94 """See `IPublishing`."""
95 self.setDeleted(removed_by, removal_comment)
96- if self.archive == self.distroseries.main_archive:
97+ if self.archive.is_main:
98 dsd_job_source = getUtility(IDistroSeriesDifferenceJobSource)
99 dsd_job_source.createForPackagePublication(
100 self.distroseries,
101@@ -2010,6 +2000,8 @@
102 SourcePackagePublishingHistory, spph_ids, removed_by,
103 removal_comment=removal_comment)
104
105+ getUtility(IDistroSeriesDifferenceJobSource).createForSPPHs(sources)
106+
107 # Mark binary publications deleted.
108 bpph_ids = [
109 bpph.id
110
111=== modified file 'lib/lp/soyuz/tests/test_distroseriesdifferencejob.py'
112--- lib/lp/soyuz/tests/test_distroseriesdifferencejob.py 2011-08-16 10:11:32 +0000
113+++ lib/lp/soyuz/tests/test_distroseriesdifferencejob.py 2011-08-26 10:07:22 +0000
114@@ -27,7 +27,10 @@
115 from lp.services.database import bulk
116 from lp.services.features.testing import FeatureFixture
117 from lp.services.job.interfaces.job import JobStatus
118-from lp.soyuz.enums import PackagePublishingStatus
119+from lp.soyuz.enums import (
120+ ArchivePurpose,
121+ PackagePublishingStatus,
122+ )
123 from lp.soyuz.interfaces.distributionjob import (
124 DistributionJobType,
125 IDistroSeriesDifferenceJobSource,
126@@ -322,6 +325,121 @@
127 [],
128 find_waiting_jobs(dsp.derived_series, package, dsp.parent_series))
129
130+ def test_createForSPPHs_creates_job_for_derived_series(self):
131+ dsp = self.factory.makeDistroSeriesParent()
132+ spph = self.factory.makeSourcePackagePublishingHistory(
133+ dsp.parent_series, pocket=PackagePublishingPocket.RELEASE)
134+ spn = spph.sourcepackagerelease.sourcepackagename
135+
136+ self.getJobSource().createForSPPHs([spph])
137+
138+ self.assertEqual(
139+ 1, len(find_waiting_jobs(
140+ dsp.derived_series, spn, dsp.parent_series)))
141+
142+ def test_createForSPPHs_creates_job_for_parent_series(self):
143+ dsp = self.factory.makeDistroSeriesParent()
144+ spph = self.factory.makeSourcePackagePublishingHistory(
145+ dsp.derived_series, pocket=PackagePublishingPocket.RELEASE)
146+ spn = spph.sourcepackagerelease.sourcepackagename
147+
148+ self.getJobSource().createForSPPHs([spph])
149+
150+ self.assertEqual(
151+ 1, len(find_waiting_jobs(
152+ dsp.derived_series, spn, dsp.parent_series)))
153+
154+ def test_createForSPPHs_obeys_feature_flag(self):
155+ self.useFixture(FeatureFixture({FEATURE_FLAG_ENABLE_MODULE: ''}))
156+ dsp = self.factory.makeDistroSeriesParent()
157+ spph = self.factory.makeSourcePackagePublishingHistory(
158+ dsp.parent_series, pocket=PackagePublishingPocket.RELEASE)
159+ spn = spph.sourcepackagerelease.sourcepackagename
160+ self.getJobSource().createForSPPHs([spph])
161+ self.assertContentEqual(
162+ [], find_waiting_jobs(dsp.derived_series, spn, dsp.parent_series))
163+
164+ def test_createForSPPHs_ignores_backports_and_proposed(self):
165+ dsp = self.factory.makeDistroSeriesParent()
166+ spr = self.factory.makeSourcePackageRelease()
167+ spn = spr.sourcepackagename
168+ ignored_pockets = [
169+ PackagePublishingPocket.BACKPORTS,
170+ PackagePublishingPocket.PROPOSED,
171+ ]
172+ spphs = [
173+ self.factory.makeSourcePackagePublishingHistory(
174+ distroseries=dsp.parent_series, sourcepackagerelease=spr,
175+ pocket=pocket)
176+ for pocket in ignored_pockets]
177+ self.getJobSource().createForSPPHs(spphs)
178+ self.assertContentEqual(
179+ [], find_waiting_jobs(dsp.derived_series, spn, dsp.parent_series))
180+
181+ def test_createForSPPHs_creates_no_jobs_for_unrelated_series(self):
182+ dsp = self.factory.makeDistroSeriesParent()
183+ other_series = self.factory.makeDistroSeries(
184+ distribution=dsp.derived_series.distribution)
185+ spph = self.factory.makeSourcePackagePublishingHistory(
186+ dsp.parent_series, pocket=PackagePublishingPocket.RELEASE)
187+ spn = spph.sourcepackagerelease.sourcepackagename
188+ self.getJobSource().createForSPPHs([spph])
189+ self.assertContentEqual(
190+ [], find_waiting_jobs(dsp.derived_series, spn, other_series))
191+
192+ def test_createForSPPHs_accepts_SPPHs_for_multiple_distroseries(self):
193+ derived_distro = self.factory.makeDistribution()
194+ spn = self.factory.makeSourcePackageName()
195+ series = [
196+ self.factory.makeDistroSeries(derived_distro)
197+ for counter in xrange(2)]
198+ dsps = [
199+ self.factory.makeDistroSeriesParent(derived_series=distroseries)
200+ for distroseries in series]
201+
202+ for distroseries in series:
203+ self.factory.makeSourcePackagePublishingHistory(
204+ distroseries, pocket=PackagePublishingPocket.RELEASE,
205+ sourcepackagerelease=self.factory.makeSourcePackageRelease(
206+ sourcepackagename=spn))
207+
208+ job_counts = dict(
209+ (dsp.derived_series, len(find_waiting_jobs(
210+ dsp.derived_series, spn, dsp.parent_series)))
211+ for dsp in dsps)
212+ self.assertEqual(
213+ dict((distroseries, 1) for distroseries in series),
214+ job_counts)
215+
216+ def test_createForSPPHs_behaves_sensibly_if_job_already_exists(self):
217+ # If a job already existed, createForSPPHs may create a
218+ # redundant one but it certainly won't do anything weird like
219+ # delete what was there or create too many.
220+ dsp = self.factory.makeDistroSeriesParent()
221+ spph = self.factory.makeSourcePackagePublishingHistory(
222+ dsp.parent_series, pocket=PackagePublishingPocket.RELEASE)
223+ spn = spph.sourcepackagerelease.sourcepackagename
224+
225+ create_jobs = range(1, 3)
226+ for counter in create_jobs:
227+ self.getJobSource().createForSPPHs([spph])
228+
229+ job_count = len(find_waiting_jobs(
230+ dsp.derived_series, spn, dsp.parent_series))
231+ self.assertIn(job_count, create_jobs)
232+
233+ def test_createForSPPHs_creates_no_jobs_for_ppas(self):
234+ dsp = self.factory.makeDistroSeriesParent()
235+ series = dsp.parent_series
236+ spph = self.factory.makeSourcePackagePublishingHistory(
237+ series, pocket=PackagePublishingPocket.RELEASE,
238+ archive=self.factory.makeArchive(
239+ distribution=series.distribution, purpose=ArchivePurpose.PPA))
240+ spn = spph.sourcepackagerelease.sourcepackagename
241+ self.getJobSource().createForSPPHs([spph])
242+ self.assertContentEqual(
243+ [], find_waiting_jobs(dsp.derived_series, spn, dsp.parent_series))
244+
245 def test_massCreateForSeries_obeys_feature_flag(self):
246 self.useFixture(FeatureFixture({FEATURE_FLAG_ENABLE_MODULE: ''}))
247 dsp = self.factory.makeDistroSeriesParent()
248
249=== modified file 'lib/lp/soyuz/tests/test_publishing.py'
250--- lib/lp/soyuz/tests/test_publishing.py 2011-08-26 10:07:15 +0000
251+++ lib/lp/soyuz/tests/test_publishing.py 2011-08-26 10:07:22 +0000
252@@ -37,6 +37,7 @@
253 from lp.registry.interfaces.pocket import PackagePublishingPocket
254 from lp.registry.interfaces.sourcepackage import SourcePackageUrgency
255 from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
256+from lp.services.features.testing import FeatureFixture
257 from lp.services.log.logger import DevNullLogger
258 from lp.soyuz.adapters.overrides import UnknownOverridePolicy
259 from lp.soyuz.enums import (
260@@ -55,6 +56,10 @@
261 )
262 from lp.soyuz.interfaces.queue import QueueInconsistentStateError
263 from lp.soyuz.interfaces.section import ISectionSet
264+from lp.soyuz.model.distroseriesdifferencejob import (
265+ FEATURE_FLAG_ENABLE_MODULE,
266+ find_waiting_jobs,
267+ )
268 from lp.soyuz.model.distroseriespackagecache import DistroSeriesPackageCache
269 from lp.soyuz.model.processor import ProcessorFamily
270 from lp.soyuz.model.publishing import (
271@@ -1167,33 +1172,14 @@
272
273 layer = ZopelessDatabaseLayer
274
275- def makeMatchingSourceAndBinaryPPH(self):
276- """Produce a matching pair of SPPH and BPPH.
277-
278- Returns a tuple of one SourcePackagePublishingHistory "spph" and
279- one BinaryPackagePublishingHistory "bpph" stemming from the same
280- source package, published into the same distroseries, pocket, and
281- archive.
282- """
283- bpph = self.factory.makeBinaryPackagePublishingHistory()
284- bpr = bpph.binarypackagerelease
285- self.assertNotEqual(None, bpr)
286- spph = self.factory.makeSourcePackagePublishingHistory(
287- distroseries=bpph.distroarchseries.distroseries,
288- sourcepackagerelease=bpr.build.source_package_release,
289- pocket=bpph.pocket, archive=bpph.archive)
290- return spph, bpph
291-
292- def test_makeMatchingSourceAndBinaryPPH(self):
293- # makeMatchingSourceAndBinaryPPH really produces a matching pair
294- # of spph and bpph.
295- spph, bpph = self.makeMatchingSourceAndBinaryPPH()
296- self.assertContentEqual([bpph], spph.getPublishedBinaries())
297+ def enableDistroDerivation(self):
298+ self.useFixture(FeatureFixture({FEATURE_FLAG_ENABLE_MODULE: u'on'}))
299
300 def test_requestDeletion_marks_SPPHs_deleted(self):
301 spph = self.factory.makeSourcePackagePublishingHistory()
302 getUtility(IPublishingSet).requestDeletion(
303 [spph], self.factory.makePerson())
304+ # XXX JeroenVermeulen 2011-08-25, bug=834388: obviate commit.
305 transaction.commit()
306 self.assertEqual(PackagePublishingStatus.DELETED, spph.status)
307
308@@ -1202,13 +1188,16 @@
309 other_spph = self.factory.makeSourcePackagePublishingHistory()
310 getUtility(IPublishingSet).requestDeletion(
311 [other_spph], self.factory.makePerson())
312+ # XXX JeroenVermeulen 2011-08-25, bug=834388: obviate commit.
313 transaction.commit()
314 self.assertEqual(PackagePublishingStatus.PENDING, spph.status)
315
316 def test_requestDeletion_marks_attached_BPPHs_deleted(self):
317- spph, bpph = self.makeMatchingSourceAndBinaryPPH()
318+ bpph = self.factory.makeBinaryPackagePublishingHistory()
319+ spph = self.factory.makeSPPHForBPPH(bpph)
320 getUtility(IPublishingSet).requestDeletion(
321 [spph], self.factory.makePerson())
322+ # XXX JeroenVermeulen 2011-08-25, bug=834388: obviate commit.
323 transaction.commit()
324 self.assertEqual(PackagePublishingStatus.DELETED, spph.status)
325
326@@ -1217,6 +1206,7 @@
327 unrelated_spph = self.factory.makeSourcePackagePublishingHistory()
328 getUtility(IPublishingSet).requestDeletion(
329 [unrelated_spph], self.factory.makePerson())
330+ # XXX JeroenVermeulen 2011-08-25, bug=834388: obviate commit.
331 transaction.commit()
332 self.assertEqual(PackagePublishingStatus.PENDING, bpph.status)
333
334@@ -1226,6 +1216,21 @@
335 # The test is that this does not fail.
336 Store.of(person).flush()
337
338+ def test_requestDeletion_creates_DistroSeriesDifferenceJobs(self):
339+ dsp = self.factory.makeDistroSeriesParent()
340+ series = dsp.derived_series
341+ spph = self.factory.makeSourcePackagePublishingHistory(
342+ series, pocket=PackagePublishingPocket.RELEASE)
343+ spn = spph.sourcepackagerelease.sourcepackagename
344+
345+ self.enableDistroDerivation()
346+ getUtility(IPublishingSet).requestDeletion(
347+ [spph], self.factory.makePerson())
348+
349+ self.assertEqual(
350+ 1, len(find_waiting_jobs(
351+ dsp.derived_series, spn, dsp.parent_series)))
352+
353
354 class TestSourceDomination(TestNativePublishingBase):
355 """Test SourcePackagePublishingHistory.supersede() operates correctly."""
356
357=== modified file 'lib/lp/testing/factory.py'
358--- lib/lp/testing/factory.py 2011-08-16 09:14:07 +0000
359+++ lib/lp/testing/factory.py 2011-08-26 10:07:22 +0000
360@@ -3799,6 +3799,24 @@
361 naked_bpph.datepublished = UTC_NOW
362 return bpph
363
364+ def makeSPPHForBPPH(self, bpph):
365+ """Produce a `SourcePackagePublishingHistory` to match `bpph`.
366+
367+ :param bpph: A `BinaryPackagePublishingHistory`.
368+ :return: A `SourcePackagePublishingHistory` stemming from the same
369+ source package as `bpph`, published into the same distroseries,
370+ pocket, and archive.
371+ """
372+ # JeroenVermeulen 2011-08-25, bug=834370: Julian says this isn't
373+ # very complete, and ignores architectures. Improve so we can
374+ # remove more of our reliance on the SoyuzTestPublisher.
375+ bpr = bpph.binarypackagerelease
376+ spph = self.makeSourcePackagePublishingHistory(
377+ distroseries=bpph.distroarchseries.distroseries,
378+ sourcepackagerelease=bpr.build.source_package_release,
379+ pocket=bpph.pocket, archive=bpph.archive)
380+ return spph
381+
382 def makeBinaryPackageName(self, name=None):
383 """Make an `IBinaryPackageName`."""
384 if name is None:
385
386=== modified file 'lib/lp/testing/tests/test_factory.py'
387--- lib/lp/testing/tests/test_factory.py 2011-08-03 11:00:11 +0000
388+++ lib/lp/testing/tests/test_factory.py 2011-08-26 10:07:22 +0000
389@@ -519,6 +519,20 @@
390 dsc_maintainer_rfc822=maintainer)
391 self.assertEqual(maintainer, spr.dsc_maintainer_rfc822)
392
393+ # makeSPPHForBPPH
394+ def test_makeSPPHForBPPH_returns_ISPPH(self):
395+ bpph = self.factory.makeBinaryPackagePublishingHistory()
396+ spph = self.factory.makeSPPHForBPPH(bpph)
397+ self.assertThat(spph, IsProxied())
398+ self.assertThat(
399+ removeSecurityProxy(spph),
400+ Provides(ISourcePackagePublishingHistory))
401+
402+ def test_makeSPPHForBPPH_returns_SPPH_for_BPPH(self):
403+ bpph = self.factory.makeBinaryPackagePublishingHistory()
404+ spph = self.factory.makeSPPHForBPPH(bpph)
405+ self.assertContentEqual([bpph], spph.getPublishedBinaries())
406+
407 # makeSuiteSourcePackage
408 def test_makeSuiteSourcePackage_returns_ISuiteSourcePackage(self):
409 ssp = self.factory.makeSuiteSourcePackage()