Merge lp:~stevenk/launchpad/move-ifp-from-idistroseries into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: Steve Kowalik
Approved revision: no longer in the source branch.
Merged at revision: 11336
Proposed branch: lp:~stevenk/launchpad/move-ifp-from-idistroseries
Merge into: lp:launchpad
Diff against target: 853 lines (+303/-351)
9 files modified
lib/lp/archiveuploader/tests/test_uploadprocessor.py (+6/-7)
lib/lp/registry/doc/distroseries.txt (+9/-11)
lib/lp/registry/interfaces/distroseries.py (+0/-29)
lib/lp/registry/model/distroseries.py (+2/-173)
lib/lp/soyuz/doc/initialise-from-parent.txt (+1/-4)
lib/lp/soyuz/doc/soyuz-set-of-uploads.txt (+7/-5)
lib/lp/soyuz/scripts/initialise_distroseries.py (+256/-0)
scripts/ftpmaster-tools/initialise-from-parent.py (+19/-104)
utilities/soyuz-sampledata-setup.py (+3/-18)
To merge this branch: bzr merge lp:~stevenk/launchpad/move-ifp-from-idistroseries
Reviewer Review Type Date Requested Status
Jelmer Vernooij (community) code Approve
Review via email: mp+31520@code.launchpad.net

Commit message

Move IDistroSeries.initialiseFromParent() into lp.soyuz.scripts.initialise_distroseries.InitialiseDistroSeries and clean-up scripts/ftpmaster-tools/initialise-from-parent.py.

Description of the change

This branch shifts IDistroSeries.initialiseFromParent() out into its own seperate class, since it has nothing to do distroseries in general, and as a basis for future work.

I had a few pre-implementation chats with Julian about it, who mentioned it would be nice to drop the cursor() usage from the script, and generally clean up i-f-p, by moving the functions from the script into the new class, and to not behave badly and assert() on error.

It also fixes another bug as a drive-by -- it no longer copies PARTNER archives when initialising.

Running tests:

bin/test -vv -t test_uploadprocessor -t distroseries.txt -t initialise-from-parent.txt -t soyuz-set-of-uploads.txt

Lint: I cleaned up the majority of the lint warnings.

Blood Type: Don't know, and blame jtv for this bit. :-)

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Please add a docstring to InitialiseDistroSeries, perhaps based on the docstring for the original initialiseFromParent().

The module docstring of scripts/ftpmaster-tools/initialise-from-parent.py misses a tail dot.

I think the name InitialiseDistroSeries is a bit confusing (but the same applied to initialiseFromParent) as it suggests to me that it takes care of doing all of the initialization of the DistroSeries, not just the Soyuzy bits. I don't have any good suggestions, perhaps something like SetupDistroSeriesPackaging ?

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/archiveuploader/tests/test_uploadprocessor.py'
2--- lib/lp/archiveuploader/tests/test_uploadprocessor.py 2010-08-06 13:52:46 +0000
3+++ lib/lp/archiveuploader/tests/test_uploadprocessor.py 2010-08-13 01:50:16 +0000
4@@ -39,6 +39,7 @@
5 from lp.registry.model.sourcepackagename import SourcePackageName
6 from lp.soyuz.model.sourcepackagerelease import (
7 SourcePackageRelease)
8+from lp.soyuz.scripts.initialise_distroseries import InitialiseDistroSeries
9 from canonical.launchpad.ftests import import_public_test_keys
10 from lp.registry.interfaces.distribution import IDistributionSet
11 from lp.registry.interfaces.series import SeriesStatus
12@@ -211,15 +212,13 @@
13 name, 'Breezy Badger',
14 'The Breezy Badger', 'Black and White', 'Someone',
15 '5.10', bat, bat.owner)
16- breezy_i386 = self.breezy.newArch(
17- 'i386', bat['i386'].processorfamily, True, self.breezy.owner)
18- self.breezy.nominatedarchindep = breezy_i386
19-
20- fake_chroot = self.addMockFile('fake_chroot.tar.gz')
21- breezy_i386.addOrUpdateChroot(fake_chroot)
22
23 self.breezy.changeslist = 'breezy-changes@ubuntu.com'
24- self.breezy.initialiseFromParent()
25+ ids = InitialiseDistroSeries(self.breezy)
26+ ids.initialise()
27+
28+ fake_chroot = self.addMockFile('fake_chroot.tar.gz')
29+ self.breezy['i386'].addOrUpdateChroot(fake_chroot)
30
31 if permitted_formats is None:
32 permitted_formats = [SourcePackageFormat.FORMAT_1_0]
33
34=== modified file 'lib/lp/registry/doc/distroseries.txt'
35--- lib/lp/registry/doc/distroseries.txt 2010-08-10 02:56:06 +0000
36+++ lib/lp/registry/doc/distroseries.txt 2010-08-13 01:50:16 +0000
37@@ -309,21 +309,21 @@
38 publishing records etc. Essentially this is a "Do not push this button
39 again" type set of assertions.
40
41+ >>> from lp.soyuz.scripts.initialise_distroseries import (
42+ ... InitialiseDistroSeries)
43 >>> login("foo.bar@canonical.com")
44 >>> humpy = ubuntu.newSeries('humpy', 'Humpy Hippo',
45 ... 'The Humpy Hippo', 'Fat', 'Yo Momma',
46 ... '99.2',hoary, hoary.owner)
47- >>> humpy_i386 = humpy.newArch('i386', hoary['i386'].processorfamily,
48- ... True, humpy.owner)
49- >>> humpy.nominatedarchindep = humpy_i386
50- >>> humpy.initialiseFromParent()
51+ >>> ids = InitialiseDistroSeries(humpy)
52+ >>> ids.initialise()
53 >>> len(hoary.getPublishedReleases('pmount'))
54 1
55 >>> len(humpy.getPublishedReleases('pmount'))
56 1
57 >>> len(hoary['i386'].getReleasedPackages('pmount'))
58 1
59- >>> len(humpy_i386.getReleasedPackages('pmount'))
60+ >>> len(humpy['i386'].getReleasedPackages('pmount'))
61 1
62
63 Check if the attributes of an DRSPR instance for the just initialised
64@@ -349,11 +349,8 @@
65 >>> bumpy = ubuntu.newSeries('bumpy', 'Bumpy',
66 ... 'The Bumpy', 'Fat', 'Boom',
67 ... '99.3', warty, warty.owner)
68-
69- >>> bumpy_i386 = bumpy.newArch('i386', warty['i386'].processorfamily,
70- ... True, bumpy.owner)
71- >>> bumpy.nominatedarchindep = bumpy_i386
72- >>> bumpy.initialiseFromParent()
73+ >>> ids = InitialiseDistroSeries(bumpy)
74+ >>> ids.initialise()
75
76 Build a new ISourcePackage based in the new distroseries:
77
78@@ -364,11 +361,12 @@
79 'binaries' should be inherited from parent release.
80
81 >>> bumpy_firefox_sp.currentrelease.binaries.count()
82- 2
83+ 3
84
85 >>> for bin in bumpy_firefox_sp.currentrelease.binaries:
86 ... print bin.id, bin.title, bin.build.distro_arch_series.title
87 27 mozilla-firefox-data-0.9 The Warty Warthog Release for i386 (x86)
88+ 26 mozilla-firefox-0.9 The Warty Warthog Release for hppa (hppa)
89 12 mozilla-firefox-0.9 The Warty Warthog Release for i386 (x86)
90
91
92
93=== modified file 'lib/lp/registry/interfaces/distroseries.py'
94--- lib/lp/registry/interfaces/distroseries.py 2010-08-12 21:02:47 +0000
95+++ lib/lp/registry/interfaces/distroseries.py 2010-08-13 01:50:16 +0000
96@@ -721,35 +721,6 @@
97 supports_virtualized=False):
98 """Create a new port or DistroArchSeries for this DistroSeries."""
99
100- def initialiseFromParent():
101- """Copy in all of the parent distroseries's configuration. This
102- includes all configuration for distroseries and distroarchseries
103- publishing and all publishing records for sources and binaries.
104-
105- Preconditions:
106- The distroseries must have been set up with its distroarchseriess
107- as needed. It should have its nominated arch-indep set up along
108- with all other basic requirements for the structure of the
109- distroseries. This distroseries and all its distroarchseriess
110- must have empty publishing sets. Section and component selections
111- must be empty.
112-
113- Outcome:
114- The publishing structure will be copied from the parent. All
115- PUBLISHED and PENDING packages in the parent will be created in
116- this distroseries and its distroarchseriess. The lucille config
117- will be copied in, all component and section selections will be
118- duplicated as will any permission-related structures.
119-
120- Note:
121- This method will assert all of its preconditions where possible.
122- After this is run, you still need to construct chroots for building,
123- you need to add anything missing wrt. ports etc. This method is
124- only meant to give you a basic copy of a parent series in order
125- to assist you in preparing a new series of a distribution or
126- in the initialisation of a derivative.
127- """
128-
129 def copyTranslationsFromParent(ztm):
130 """Copy any translation done in parent that we lack.
131
132
133=== modified file 'lib/lp/registry/model/distroseries.py'
134--- lib/lp/registry/model/distroseries.py 2010-08-10 21:54:41 +0000
135+++ lib/lp/registry/model/distroseries.py 2010-08-13 01:50:16 +0000
136@@ -31,7 +31,7 @@
137 from canonical.database.datetimecol import UtcDateTimeCol
138 from canonical.database.enumcol import EnumCol
139 from canonical.database.sqlbase import (
140- cursor, flush_database_caches, flush_database_updates, quote_like,
141+ flush_database_caches, flush_database_updates, quote_like,
142 quote, SQLBase, sqlvalues)
143 from canonical.launchpad.components.decoratedresultset import (
144 DecoratedResultSet)
145@@ -40,7 +40,6 @@
146 POFileTranslator)
147 from lp.translations.model.pofile import POFile
148 from canonical.launchpad.interfaces.lpstorm import IStore
149-from lp.soyuz.adapters.packagelocation import PackageLocation
150 from lp.soyuz.model.binarypackagename import BinaryPackageName
151 from lp.soyuz.model.binarypackagerelease import (
152 BinaryPackageRelease)
153@@ -66,7 +65,6 @@
154 from lp.translations.model.languagepack import LanguagePack
155 from lp.registry.model.milestone import (
156 HasMilestonesMixin, Milestone)
157-from lp.soyuz.model.packagecloner import clone_packages
158 from lp.registry.model.packaging import Packaging
159 from lp.registry.model.person import Person
160 from canonical.launchpad.database.librarian import LibraryFileAlias
161@@ -90,7 +88,7 @@
162 HasTranslationImportsMixin)
163 from canonical.launchpad.helpers import shortlist
164 from lp.soyuz.interfaces.archive import (
165- ALLOW_RELEASE_BUILDS, ArchivePurpose, IArchiveSet, MAIN_ARCHIVE_PURPOSES)
166+ ALLOW_RELEASE_BUILDS, ArchivePurpose)
167 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
168 from lp.soyuz.interfaces.buildrecords import IHasBuildRecords
169 from lp.soyuz.interfaces.binarypackagename import (
170@@ -1594,175 +1592,6 @@
171 """See BugTargetBase."""
172 return 'BugTask.distroseries = %s' % sqlvalues(self)
173
174- def initialiseFromParent(self):
175- """See `IDistroSeries`."""
176- archives = self.distribution.all_distro_archive_ids
177- assert self.parent_series is not None, "Parent series must be present"
178- assert SourcePackagePublishingHistory.select("""
179- Distroseries = %s AND
180- Archive IN %s""" % sqlvalues(self.id, archives)).count() == 0, (
181- "Source Publishing must be empty")
182- for arch in self.architectures:
183- assert BinaryPackagePublishingHistory.select("""
184- DistroArchSeries = %s AND
185- Archive IN %s""" % sqlvalues(arch, archives)).count() == 0, (
186- "Binary Publishing must be empty")
187- try:
188- parent_arch = self.parent_series[arch.architecturetag]
189- assert parent_arch.processorfamily == arch.processorfamily, (
190- "The arch tags must match the processor families.")
191- except KeyError:
192- raise AssertionError("Parent series lacks %s" % (
193- arch.architecturetag))
194- assert self.nominatedarchindep is not None, (
195- "Must have a nominated archindep architecture.")
196- assert self.components.count() == 0, (
197- "Component selections must be empty.")
198- assert self.sections.count() == 0, (
199- "Section selections must be empty.")
200-
201- # MAINTAINER: dsilvers: 20051031
202- # Here we go underneath the SQLObject caching layers in order to
203- # generate what will potentially be tens of thousands of rows
204- # in various tables. Thus we flush pending updates from the SQLObject
205- # layer, perform our work directly in the transaction and then throw
206- # the rest of the SQLObject cache away to make sure it hasn't cached
207- # anything that is no longer true.
208-
209- # Prepare for everything by flushing updates to the database.
210- flush_database_updates()
211- cur = cursor()
212-
213- # Perform the copies
214- self._copy_component_section_and_format_selections(cur)
215-
216- # Prepare the list of distroarchseries for which binary packages
217- # shall be copied.
218- distroarchseries_list = []
219- for arch in self.architectures:
220- parent_arch = self.parent_series[arch.architecturetag]
221- distroarchseries_list.append((parent_arch, arch))
222- # Now copy source and binary packages.
223- self._copy_publishing_records(distroarchseries_list)
224- self._copy_lucille_config(cur)
225- self._copy_packaging_links(cur)
226-
227- # Finally, flush the caches because we've altered stuff behind the
228- # back of sqlobject.
229- flush_database_caches()
230-
231- def _copy_lucille_config(self, cur):
232- """Copy all lucille related configuration from our parent series."""
233- cur.execute('''
234- UPDATE DistroSeries SET lucilleconfig=(
235- SELECT pdr.lucilleconfig FROM DistroSeries AS pdr
236- WHERE pdr.id = %s)
237- WHERE id = %s
238- ''' % sqlvalues(self.parent_series.id, self.id))
239-
240- def _copy_publishing_records(self, distroarchseries_list):
241- """Copy the publishing records from the parent arch series
242- to the given arch series in ourselves.
243-
244- We copy all PENDING and PUBLISHED records as PENDING into our own
245- publishing records.
246-
247- We copy only the RELEASE pocket in the PRIMARY and PARTNER
248- archives.
249- """
250- archive_set = getUtility(IArchiveSet)
251-
252- for archive in self.parent_series.distribution.all_distro_archives:
253- # We only want to copy PRIMARY and PARTNER archives.
254- if archive.purpose not in MAIN_ARCHIVE_PURPOSES:
255- continue
256-
257- # XXX cprov 20080612: Implicitly creating a PARTNER archive for
258- # the destination distroseries is bad. Why are we copying
259- # partner to a series in another distribution anyway ?
260- # See bug #239807 for further information.
261- target_archive = archive_set.getByDistroPurpose(
262- self.distribution, archive.purpose)
263- if target_archive is None:
264- target_archive = archive_set.new(
265- distribution=self.distribution, purpose=archive.purpose,
266- owner=self.distribution.owner)
267-
268- origin = PackageLocation(
269- archive, self.parent_series.distribution, self.parent_series,
270- PackagePublishingPocket.RELEASE)
271- destination = PackageLocation(
272- target_archive, self.distribution, self,
273- PackagePublishingPocket.RELEASE)
274- clone_packages(origin, destination, distroarchseries_list)
275-
276- def _copy_component_section_and_format_selections(self, cur):
277- """Copy the section, component and format selections from the parent
278- distro series into this one.
279- """
280- # Copy the component selections
281- cur.execute('''
282- INSERT INTO ComponentSelection (distroseries, component)
283- SELECT %s AS distroseries, cs.component AS component
284- FROM ComponentSelection AS cs WHERE cs.distroseries = %s
285- ''' % sqlvalues(self.id, self.parent_series.id))
286- # Copy the section selections
287- cur.execute('''
288- INSERT INTO SectionSelection (distroseries, section)
289- SELECT %s as distroseries, ss.section AS section
290- FROM SectionSelection AS ss WHERE ss.distroseries = %s
291- ''' % sqlvalues(self.id, self.parent_series.id))
292- # Copy the source format selections
293- cur.execute('''
294- INSERT INTO SourcePackageFormatSelection (distroseries, format)
295- SELECT %s as distroseries, spfs.format AS format
296- FROM SourcePackageFormatSelection AS spfs
297- WHERE spfs.distroseries = %s
298- ''' % sqlvalues(self.id, self.parent_series.id))
299-
300- def _copy_packaging_links(self, cur):
301- """Copy the packaging links from the parent series to this one."""
302- cur.execute("""
303- INSERT INTO
304- Packaging(
305- distroseries, sourcepackagename, productseries,
306- packaging, owner)
307- SELECT
308- ChildSeries.id,
309- Packaging.sourcepackagename,
310- Packaging.productseries,
311- Packaging.packaging,
312- Packaging.owner
313- FROM
314- Packaging
315- -- Joining the parent distroseries permits the query to build
316- -- the data set for the series being updated, yet results are
317- -- in fact the data from the original series.
318- JOIN Distroseries ChildSeries
319- ON Packaging.distroseries = ChildSeries.parent_series
320- WHERE
321- -- Select only the packaging links that are in the parent
322- -- that are not in the child.
323- ChildSeries.id = %s
324- AND Packaging.sourcepackagename in (
325- SELECT sourcepackagename
326- FROM Packaging
327- WHERE distroseries in (
328- SELECT id
329- FROM Distroseries
330- WHERE id = ChildSeries.parent_series
331- )
332- EXCEPT
333- SELECT sourcepackagename
334- FROM Packaging
335- WHERE distroseries in (
336- SELECT id
337- FROM Distroseries
338- WHERE id = ChildSeries.id
339- )
340- )
341- """ % self.id)
342-
343 def copyTranslationsFromParent(self, transaction, logger=None):
344 """See `IDistroSeries`."""
345 if logger is None:
346
347=== modified file 'lib/lp/soyuz/doc/initialise-from-parent.txt'
348--- lib/lp/soyuz/doc/initialise-from-parent.txt 2010-05-14 06:14:12 +0000
349+++ lib/lp/soyuz/doc/initialise-from-parent.txt 2010-08-13 01:50:16 +0000
350@@ -63,12 +63,9 @@
351 DEBUG Check empty mutable queues in parentseries
352 DEBUG Check for no pending builds in parentseries
353 DEBUG Copying distroarchseries from parent and setting nominatedarchindep.
354- Traceback (most recent call last):
355- ...
356- AssertionError: Can not copy distroarchseries from parent, there are already distroarchseries(s) initialised for this series.
357+ ERROR Can not copy distroarchseries from parent, there are already distroarchseries(s) initialised for this series.
358 <BLANKLINE>
359
360-
361 Let's initialise the just created distroseries:
362
363 >>> process = subprocess.Popen([sys.executable, script, "-vv",
364
365=== modified file 'lib/lp/soyuz/doc/soyuz-set-of-uploads.txt'
366--- lib/lp/soyuz/doc/soyuz-set-of-uploads.txt 2010-07-12 13:06:41 +0000
367+++ lib/lp/soyuz/doc/soyuz-set-of-uploads.txt 2010-08-13 01:50:16 +0000
368@@ -70,6 +70,10 @@
369 for the ubuntutest distribution.
370
371 >>> from lp.registry.model.distribution import Distribution
372+ >>> from lp.registry.interfaces.pocket import PackagePublishingPocket
373+ >>> from lp.soyuz.interfaces.queue import PackageUploadStatus
374+ >>> from lp.soyuz.scripts.initialise_distroseries import (
375+ ... InitialiseDistroSeries)
376 >>> from canonical.launchpad.database import LibraryFileAlias
377 >>> ubuntu = Distribution.byName('ubuntu')
378 >>> breezy_autotest = ubuntu['breezy-autotest']
379@@ -78,13 +82,11 @@
380 ... 'breezy', 'Breezy Badger', 'The Breezy Badger',
381 ... 'Black and White', 'Someone', '5.10', breezy_autotest,
382 ... breezy_autotest.owner)
383- >>> breezy_i386 = breezy.newArch(
384- ... 'i386', breezy_autotest['i386'].processorfamily, True, breezy.owner)
385- >>> breezy.nominatedarchindep = breezy_i386
386+ >>> ids = InitialiseDistroSeries(breezy)
387+ >>> ids.initialise()
388 >>> breezy.changeslist = 'breezy-changes@ubuntu.com'
389- >>> breezy.initialiseFromParent()
390 >>> fake_chroot = LibraryFileAlias.get(1)
391- >>> unused = breezy_i386.addOrUpdateChroot(fake_chroot)
392+ >>> unused = breezy['i386'].addOrUpdateChroot(fake_chroot)
393
394 Add disk content for file inherited from ubuntu/breezy-autotest:
395
396
397=== added file 'lib/lp/soyuz/scripts/initialise_distroseries.py'
398--- lib/lp/soyuz/scripts/initialise_distroseries.py 1970-01-01 00:00:00 +0000
399+++ lib/lp/soyuz/scripts/initialise_distroseries.py 2010-08-13 01:50:16 +0000
400@@ -0,0 +1,256 @@
401+# Copyright 2009 Canonical Ltd. This software is licensed under the
402+# GNU Affero General Public License version 3 (see the file LICENSE).
403+
404+"""Initialise a distroseries from its parent distroseries."""
405+
406+
407+__metaclass__ = type
408+__all__ = [
409+ 'InitialisationError',
410+ 'InitialiseDistroSeries',
411+ ]
412+
413+from zope.component import getUtility
414+from canonical.database.sqlbase import sqlvalues
415+from canonical.launchpad.webapp.interfaces import (
416+ IStoreSelector, MAIN_STORE, MASTER_FLAVOR)
417+
418+from lp.buildmaster.interfaces.buildbase import BuildStatus
419+from lp.registry.interfaces.pocket import PackagePublishingPocket
420+from lp.soyuz.adapters.packagelocation import PackageLocation
421+from lp.soyuz.interfaces.archive import ArchivePurpose, IArchiveSet
422+from lp.soyuz.interfaces.queue import PackageUploadStatus
423+from lp.soyuz.model.packagecloner import clone_packages
424+
425+
426+class InitialisationError(Exception):
427+ """Raised when there is an exception during the initialisation process."""
428+
429+
430+class InitialiseDistroSeries:
431+ """Copy in all of the parent distroseries's configuration. This
432+ includes all configuration for distroseries as well as distroarchseries,
433+ publishing and all publishing records for sources and binaries.
434+
435+ Preconditions:
436+ The distroseries must exist, and be completly unused, with no source
437+ or binary packages existing, as well as no distroarchseries set up.
438+ Section and component selections must be empty.
439+
440+ Outcome:
441+ The distroarchseries set up in the parent series will be copied.
442+ The publishing structure will be copied from the parent. All
443+ PUBLISHED and PENDING packages in the parent will be created in
444+ this distroseries and its distroarchseriess. The lucille config
445+ will be copied in, all component and section selections will be
446+ duplicated as will any permission-related structures.
447+
448+ Note:
449+ This method will raise a InitialisationError when the pre-conditions
450+ are not met. After this is run, you still need to construct chroots
451+ for building, you need to add anything missing wrt. ports etc. This
452+ method is only meant to give you a basic copy of a parent series in
453+ order to assist you in preparing a new series of a distribution or
454+ in the initialisation of a derivative.
455+ """
456+
457+ def __init__(self, distroseries):
458+ self.distroseries = distroseries
459+ self.parent = self.distroseries.parent_series
460+ self._store = getUtility(
461+ IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR)
462+
463+ def check(self):
464+ if self.parent is None:
465+ raise InitialisationError("Parent series required.")
466+ self._checkBuilds()
467+ self._checkQueue()
468+ self._checkSeries()
469+
470+ def _checkBuilds(self):
471+ """Assert there are no pending builds for parent series.
472+
473+ Only cares about the RELEASE pocket, which is the only one inherited
474+ via initialiseFromParent method.
475+ """
476+ # only the RELEASE pocket is inherited, so we only check
477+ # pending build records for it.
478+ pending_builds = self.parent.getBuildRecords(
479+ BuildStatus.NEEDSBUILD, pocket=PackagePublishingPocket.RELEASE)
480+
481+ if pending_builds.count():
482+ raise InitialisationError("Parent series has pending builds.")
483+
484+ def _checkQueue(self):
485+ """Assert upload queue is empty on parent series.
486+
487+ Only cares about the RELEASE pocket, which is the only one inherited
488+ via initialiseFromParent method.
489+ """
490+ # only the RELEASE pocket is inherited, so we only check
491+ # queue items for it.
492+ for queue in (
493+ PackageUploadStatus.NEW, PackageUploadStatus.ACCEPTED,
494+ PackageUploadStatus.UNAPPROVED):
495+ items = self.parent.getQueueItems(
496+ queue, pocket=PackagePublishingPocket.RELEASE)
497+ if items:
498+ raise InitialisationError(
499+ "Parent series queues are not empty.")
500+
501+ def _checkSeries(self):
502+ sources = self.distroseries.getAllPublishedSources()
503+ error = (
504+ "Can not copy distroarchseries from parent, there are "
505+ "already distroarchseries(s) initialised for this series.")
506+ if sources.count():
507+ raise InitialisationError(error)
508+ binaries = self.distroseries.getAllPublishedBinaries()
509+ if binaries.count():
510+ raise InitialisationError(error)
511+ if self.distroseries.architectures.count():
512+ raise InitialisationError(error)
513+ if self.distroseries.components.count():
514+ raise InitialisationError(error)
515+ if self.distroseries.sections.count():
516+ raise InitialisationError(error)
517+
518+ def initialise(self):
519+ self._copy_architectures()
520+ self._copy_packages()
521+
522+ def _copy_architectures(self):
523+ self._store.execute("""
524+ INSERT INTO DistroArchSeries
525+ (distroseries, processorfamily, architecturetag, owner, official)
526+ SELECT %s, processorfamily, architecturetag, %s, official
527+ FROM DistroArchSeries WHERE distroseries = %s
528+ """ % sqlvalues(self.distroseries, self.distroseries.owner,
529+ self.parent))
530+
531+ self.distroseries.nominatedarchindep = self.distroseries[
532+ self.parent.nominatedarchindep.architecturetag]
533+
534+ def _copy_packages(self):
535+ # Perform the copies
536+ self._copy_component_section_and_format_selections()
537+
538+ # Prepare the list of distroarchseries for which binary packages
539+ # shall be copied.
540+ distroarchseries_list = []
541+ for arch in self.distroseries.architectures:
542+ parent_arch = self.parent[arch.architecturetag]
543+ distroarchseries_list.append((parent_arch, arch))
544+ # Now copy source and binary packages.
545+ self._copy_publishing_records(distroarchseries_list)
546+ self._copy_lucille_config()
547+ self._copy_packaging_links()
548+
549+ def _copy_lucille_config(self):
550+ """Copy all lucille related configuration from our parent series."""
551+ self._store.execute('''
552+ UPDATE DistroSeries SET lucilleconfig=(
553+ SELECT pdr.lucilleconfig FROM DistroSeries AS pdr
554+ WHERE pdr.id = %s)
555+ WHERE id = %s
556+ ''' % sqlvalues(self.parent.id,
557+ self.distroseries.id))
558+
559+ def _copy_publishing_records(self, distroarchseries_list):
560+ """Copy the publishing records from the parent arch series
561+ to the given arch series in ourselves.
562+
563+ We copy all PENDING and PUBLISHED records as PENDING into our own
564+ publishing records.
565+
566+ We copy only the RELEASE pocket in the PRIMARY and DEBUG archives.
567+ """
568+ archive_set = getUtility(IArchiveSet)
569+
570+ for archive in self.parent.distribution.all_distro_archives:
571+ if archive.purpose not in (
572+ ArchivePurpose.PRIMARY, ArchivePurpose.DEBUG):
573+ continue
574+
575+ target_archive = archive_set.getByDistroPurpose(
576+ self.distroseries.distribution, archive.purpose)
577+ if archive.purpose is ArchivePurpose.PRIMARY:
578+ assert target_archive is not None, (
579+ "Target archive doesn't exist?")
580+ origin = PackageLocation(
581+ archive, self.parent.distribution, self.parent,
582+ PackagePublishingPocket.RELEASE)
583+ destination = PackageLocation(
584+ target_archive, self.distroseries.distribution,
585+ self.distroseries, PackagePublishingPocket.RELEASE)
586+ clone_packages(origin, destination, distroarchseries_list)
587+
588+ def _copy_component_section_and_format_selections(self):
589+ """Copy the section, component and format selections from the parent
590+ distro series into this one.
591+ """
592+ # Copy the component selections
593+ self._store.execute('''
594+ INSERT INTO ComponentSelection (distroseries, component)
595+ SELECT %s AS distroseries, cs.component AS component
596+ FROM ComponentSelection AS cs WHERE cs.distroseries = %s
597+ ''' % sqlvalues(self.distroseries.id,
598+ self.parent.id))
599+ # Copy the section selections
600+ self._store.execute('''
601+ INSERT INTO SectionSelection (distroseries, section)
602+ SELECT %s as distroseries, ss.section AS section
603+ FROM SectionSelection AS ss WHERE ss.distroseries = %s
604+ ''' % sqlvalues(self.distroseries.id,
605+ self.parent.id))
606+ # Copy the source format selections
607+ self._store.execute('''
608+ INSERT INTO SourcePackageFormatSelection (distroseries, format)
609+ SELECT %s as distroseries, spfs.format AS format
610+ FROM SourcePackageFormatSelection AS spfs
611+ WHERE spfs.distroseries = %s
612+ ''' % sqlvalues(self.distroseries.id,
613+ self.parent.id))
614+
615+ def _copy_packaging_links(self):
616+ """Copy the packaging links from the parent series to this one."""
617+ self._store.execute("""
618+ INSERT INTO
619+ Packaging(
620+ distroseries, sourcepackagename, productseries,
621+ packaging, owner)
622+ SELECT
623+ ChildSeries.id,
624+ Packaging.sourcepackagename,
625+ Packaging.productseries,
626+ Packaging.packaging,
627+ Packaging.owner
628+ FROM
629+ Packaging
630+ -- Joining the parent distroseries permits the query to build
631+ -- the data set for the series being updated, yet results are
632+ -- in fact the data from the original series.
633+ JOIN Distroseries ChildSeries
634+ ON Packaging.distroseries = ChildSeries.parent_series
635+ WHERE
636+ -- Select only the packaging links that are in the parent
637+ -- that are not in the child.
638+ ChildSeries.id = %s
639+ AND Packaging.sourcepackagename in (
640+ SELECT sourcepackagename
641+ FROM Packaging
642+ WHERE distroseries in (
643+ SELECT id
644+ FROM Distroseries
645+ WHERE id = ChildSeries.parent_series
646+ )
647+ EXCEPT
648+ SELECT sourcepackagename
649+ FROM Packaging
650+ WHERE distroseries in (
651+ SELECT id
652+ FROM Distroseries
653+ WHERE id = ChildSeries.id
654+ )
655+ )
656+ """ % self.distroseries.id)
657
658=== modified file 'scripts/ftpmaster-tools/initialise-from-parent.py'
659--- scripts/ftpmaster-tools/initialise-from-parent.py 2010-08-02 02:13:52 +0000
660+++ scripts/ftpmaster-tools/initialise-from-parent.py 2010-08-13 01:50:16 +0000
661@@ -3,16 +3,7 @@
662 # Copyright 2009 Canonical Ltd. This software is licensed under the
663 # GNU Affero General Public License version 3 (see the file LICENSE).
664
665-"""Initialise a new distroseries from its parent
666-
667-It performs two additional tasks before call initialiseFromParent:
668-
669-* check_queue (ensure parent's mutable queues are empty)
670-* copy_architectures (copy parent's architectures and set
671- nominatedarchindep properly)
672-
673-which eventually may be integrated in its workflow.
674-"""
675+"""Initialise a new distroseries from its parent series."""
676
677 import _pythonpath
678
679@@ -23,15 +14,14 @@
680 from contrib.glock import GlobalLock
681
682 from canonical.config import config
683-from canonical.database.sqlbase import (
684- sqlvalues, flush_database_updates, cursor, flush_database_caches)
685 from canonical.launchpad.interfaces import IDistributionSet
686 from canonical.launchpad.scripts import (
687 execute_zcml_for_scripts, logger, logger_options)
688 from canonical.lp import initZopeless
689+
690 from lp.app.errors import NotFoundError
691-from lp.soyuz.interfaces.queue import PackageUploadStatus
692-
693+from lp.soyuz.scripts.initialise_distroseries import (
694+ InitialisationError, InitialiseDistroSeries)
695
696 def main():
697 # Parse command-line arguments
698@@ -71,23 +61,21 @@
699 distribution = getUtility(IDistributionSet)[options.distribution]
700 distroseries = distribution[distroseries_name]
701 except NotFoundError, info:
702- log.error(info)
703- return 1
704-
705- # XXX cprov 2006-05-26: these two extra functions must be
706- # integrated in IDistroSeries.initialiseFromParent workflow.
707- log.debug('Check empty mutable queues in parentseries')
708- check_queue(distroseries)
709-
710- log.debug('Check for no pending builds in parentseries')
711- check_builds(distroseries)
712-
713- log.debug('Copying distroarchseries from parent '
714- 'and setting nominatedarchindep.')
715- copy_architectures(distroseries)
716-
717- log.debug('initialising from parent, copying publishing records.')
718- distroseries.initialiseFromParent()
719+ log.error('%s not found' % info)
720+ return 1
721+
722+ try:
723+ log.debug('Check empty mutable queues in parentseries')
724+ log.debug('Check for no pending builds in parentseries')
725+ log.debug('Copying distroarchseries from parent '
726+ 'and setting nominatedarchindep.')
727+ ids = InitialiseDistroSeries(distroseries)
728+ ids.check()
729+ log.debug('initialising from parent, copying publishing records.')
730+ ids.initialise()
731+ except InitialisationError, e:
732+ log.error(e)
733+ return 1
734
735 if options.dryrun:
736 log.debug('Dry-Run mode, transaction aborted.')
737@@ -101,79 +89,6 @@
738 return 0
739
740
741-def check_builds(distroseries):
742- """Assert there are no pending builds for parent series.
743-
744- Only cares about the RELEASE pocket, which is the only one inherited
745- via initialiseFromParent method.
746- """
747- # Avoid circular import.
748- from lp.buildmaster.interfaces.buildbase import BuildStatus
749- from lp.registry.interfaces.pocket import PackagePublishingPocket
750-
751- parentseries = distroseries.parent_series
752-
753- # only the RELEASE pocket is inherited, so we only check
754- # pending build records for it.
755- pending_builds = parentseries.getBuildRecords(
756- BuildStatus.NEEDSBUILD, pocket=PackagePublishingPocket.RELEASE)
757-
758- assert pending_builds.count() == 0, (
759- 'Parent must not have PENDING builds')
760-
761-def check_queue(distroseries):
762- """Assert upload queue is empty on parent series.
763-
764- Only cares about the RELEASE pocket, which is the only one inherited
765- via initialiseFromParent method.
766- """
767- # Avoid circular import.
768- from lp.registry.interfaces.pocket import PackagePublishingPocket
769-
770- parentseries = distroseries.parent_series
771-
772- # only the RELEASE pocket is inherited, so we only check
773- # queue items for it.
774- new_items = parentseries.getQueueItems(
775- PackageUploadStatus.NEW,
776- pocket=PackagePublishingPocket.RELEASE)
777- accepted_items = parentseries.getQueueItems(
778- PackageUploadStatus.ACCEPTED,
779- pocket=PackagePublishingPocket.RELEASE)
780- unapproved_items = parentseries.getQueueItems(
781- PackageUploadStatus.UNAPPROVED,
782- pocket=PackagePublishingPocket.RELEASE)
783-
784- assert new_items.count() == 0, (
785- 'Parent NEW queue must be empty')
786- assert accepted_items.count() == 0, (
787- 'Parent ACCEPTED queue must be empty')
788- assert unapproved_items.count() == 0, (
789- 'Parent UNAPPROVED queue must be empty')
790-
791-def copy_architectures(distroseries):
792- """Overlap SQLObject and copy architecture from the parent.
793-
794- Also set the nominatedarchindep properly in target.
795- """
796- assert distroseries.architectures.count() is 0, (
797- "Can not copy distroarchseries from parent, there are already "
798- "distroarchseries(s) initialised for this series.")
799- flush_database_updates()
800- cur = cursor()
801- cur.execute("""
802- INSERT INTO DistroArchSeries
803- (distroseries, processorfamily, architecturetag, owner, official)
804- SELECT %s, processorfamily, architecturetag, %s, official
805- FROM DistroArchSeries WHERE distroseries = %s
806- """ % sqlvalues(distroseries, distroseries.owner,
807- distroseries.parent_series))
808- flush_database_caches()
809-
810- distroseries.nominatedarchindep = distroseries[
811- distroseries.parent_series.nominatedarchindep.architecturetag]
812-
813-
814 if __name__ == '__main__':
815 sys.exit(main())
816
817
818=== modified file 'utilities/soyuz-sampledata-setup.py'
819--- utilities/soyuz-sampledata-setup.py 2010-07-09 15:19:10 +0000
820+++ utilities/soyuz-sampledata-setup.py 2010-08-13 01:50:16 +0000
821@@ -59,6 +59,7 @@
822 ISourcePackageFormatSelectionSet, SourcePackageFormat)
823 from lp.soyuz.model.section import SectionSelection
824 from lp.soyuz.model.component import ComponentSelection
825+from lp.soyuz.scripts.initialise_distroseries import InitialiseDistroSeries
826 from lp.testing.factory import LaunchpadObjectFactory
827
828
829@@ -235,24 +236,8 @@
830 new_series.status = status
831 notify(ObjectCreatedEvent(new_series))
832
833- # This bit copied from scripts/ftpmaster-tools/initialise-from-parent.py.
834- assert new_series.architectures.count() == 0, (
835- "Cannot copy distroarchseries from parent; this series already has "
836- "distroarchseries.")
837-
838- store = Store.of(parent)
839- store.execute("""
840- INSERT INTO DistroArchSeries
841- (distroseries, processorfamily, architecturetag, owner, official)
842- SELECT %s, processorfamily, architecturetag, %s, official
843- FROM DistroArchSeries WHERE distroseries = %s
844- """ % sqlvalues(new_series, owner, parent))
845-
846- i386 = new_series.getDistroArchSeries('i386')
847- i386.supports_virtualized = True
848- new_series.nominatedarchindep = i386
849-
850- new_series.initialiseFromParent()
851+ ids = InitialiseDistroSeries(new_series)
852+ ids.initialise()
853 return new_series
854
855