← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rvb/launchpad/init-multiple-parents-backend into lp:launchpad/db-devel

 

You have been requested to review the proposed merge of lp:~rvb/launchpad/init-multiple-parents-backend into lp:launchpad/db-devel.

For more details, see:
https://code.launchpad.net/~rvb/launchpad/init-multiple-parents-backend/+merge/62868

This branch:
- fixes initialise_distroseries to make it use DSP and not DS.previous_series
- adds DS.initDerivedDistroSeries to allow initialising a series from multiple parents
- adds overlay support to the derived initialisation job

= Tests =
./bin/test -cvv test_initderiveddistroseries
./bin/test -cvv test_uploadprocessor
./bin/test -cvv test_initialise_distroseries
./bin/test -cvv test_initialisedistroseriesjob
./bin/test -cvv -t distroseries.txt

= QA =
This branch should be QAed using the UI that is in another branch (dependent from this one) lp:~rvb/launchpad/init-multiple-parents.

-- 
https://code.launchpad.net/~rvb/launchpad/init-multiple-parents-backend/+merge/62868
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/launchpad/init-multiple-parents-backend into lp:launchpad/db-devel.
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg	2011-05-30 08:11:33 +0000
+++ database/schema/security.cfg	2011-05-31 15:42:35 +0000
@@ -837,7 +837,7 @@
 public.cve                              = SELECT, INSERT
 public.distributionjob                  = SELECT, INSERT, DELETE
 public.distributionsourcepackage        = SELECT, INSERT, UPDATE
-public.distroseriesparent               = SELECT
+public.distroseriesparent               = SELECT, INSERT, UPDATE
 public.flatpackagesetinclusion          = SELECT, INSERT, UPDATE, DELETE
 public.gpgkey                           = SELECT, INSERT, UPDATE
 public.job                              = SELECT, INSERT, UPDATE
@@ -957,6 +957,7 @@
 public.distributionjob                          = SELECT
 public.distroarchseries                         = SELECT, INSERT
 public.distroseries                             = SELECT, UPDATE
+public.distroseriesparent                       = SELECT, INSERT, UPDATE
 public.flatpackagesetinclusion                  = SELECT, INSERT
 public.gpgkey                                   = SELECT
 public.job                                      = SELECT, INSERT, UPDATE

=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py	2011-05-20 14:36:26 +0000
+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py	2011-05-31 15:42:35 +0000
@@ -486,8 +486,6 @@
 patch_collection_return_type(
     IDistroSeries, 'getPackageUploads', IPackageUpload)
 patch_reference_property(IDistroSeries, 'previous_series', IDistroSeries)
-patch_plain_parameter_type(
-    IDistroSeries, 'deriveDistroSeries', 'distribution', IDistribution)
 patch_collection_return_type(
     IDistroSeries, 'getDerivedSeries', IDistroSeries)
 patch_collection_return_type(
@@ -857,7 +855,7 @@
 # IDistroSeries
 patch_entry_explicit_version(IDistroSeries, 'beta')
 patch_operations_explicit_version(
-    IDistroSeries, 'beta', "deriveDistroSeries", "getDerivedSeries",
+    IDistroSeries, 'beta', "initDerivedDistroSeries", "getDerivedSeries",
     "getParentSeries", "getDistroArchSeries", "getPackageUploads",
     "getSourcePackage", "newMilestone")
 

=== modified file 'lib/lp/archiveuploader/tests/test_uploadprocessor.py'
--- lib/lp/archiveuploader/tests/test_uploadprocessor.py	2011-05-20 03:28:24 +0000
+++ lib/lp/archiveuploader/tests/test_uploadprocessor.py	2011-05-31 15:42:35 +0000
@@ -228,7 +228,7 @@
             '5.10', None, bat.owner)
 
         self.breezy.changeslist = 'breezy-changes@xxxxxxxxxx'
-        ids = InitialiseDistroSeries(bat, self.breezy)
+        ids = InitialiseDistroSeries(self.breezy, [bat.id])
         ids.initialise()
 
         fake_chroot = self.addMockFile('fake_chroot.tar.gz')

=== modified file 'lib/lp/registry/browser/tests/test_distroseries.py'
--- lib/lp/registry/browser/tests/test_distroseries.py	2011-05-29 21:18:09 +0000
+++ lib/lp/registry/browser/tests/test_distroseries.py	2011-05-31 15:42:35 +0000
@@ -300,9 +300,11 @@
         # The difference portlet displays 'The series is initialising.' if
         # there is an initialising job for the series.
         set_derived_series_ui_feature_flag(self)
-        derived_series = self._setupDifferences('deri', 'sid', 0, 0, 0)
+        derived_series = self.factory.makeDistroSeries()
+        parent_series = self.factory.makeDistroSeries()
+        self.simple_user = self.factory.makePerson()
         job_source = getUtility(IInitialiseDistroSeriesJobSource)
-        job_source.create(derived_series.parent, derived_series)
+        job_source.create(derived_series, [parent_series.id])
         portlet_display = soupmatchers.HTMLContains(
             soupmatchers.Tag(
                 'Derived series', 'h2',

=== modified file 'lib/lp/registry/doc/distroseries.txt'
--- lib/lp/registry/doc/distroseries.txt	2011-05-14 15:03:04 +0000
+++ lib/lp/registry/doc/distroseries.txt	2011-05-31 15:42:35 +0000
@@ -318,7 +318,7 @@
     >>> humpy = ubuntu.newSeries('humpy', 'Humpy Hippo',
     ...                          'The Humpy Hippo', 'Fat', 'Yo Momma',
     ...                          '99.2', None, hoary.owner)
-    >>> ids = InitialiseDistroSeries(hoary, humpy)
+    >>> ids = InitialiseDistroSeries(humpy, [hoary.id])
     >>> ids.initialise()
     >>> hoary.getPublishedSources('pmount').count()
     1
@@ -352,7 +352,7 @@
     >>> bumpy = ubuntu.newSeries('bumpy', 'Bumpy',
     ...                          'The Bumpy', 'Fat', 'Boom',
     ...                          '99.3', None, warty.owner)
-    >>> ids = InitialiseDistroSeries(warty, bumpy)
+    >>> ids = InitialiseDistroSeries(bumpy, [warty.id])
     >>> ids.initialise()
 
 Build a new ISourcePackage based in the new distroseries:

=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py	2011-05-29 21:18:09 +0000
+++ lib/lp/registry/interfaces/distroseries.py	2011-05-31 15:42:35 +0000
@@ -37,7 +37,6 @@
     Reference,
     ReferenceChoice,
     )
-from lazr.restful.interface import copy_field
 from zope.component import getUtility
 from zope.interface import (
     Attribute,
@@ -889,19 +888,10 @@
         """Create a new milestone for this DistroSeries."""
 
     @operation_parameters(
-        name=copy_field(IDistroSeriesPublic['name'], required=True),
-        displayname=copy_field(
-            IDistroSeriesPublic['displayname'], required=False),
-        title=copy_field(IDistroSeriesPublic['title'], required=False),
-        summary=TextLine(
-            title=_("The summary of the distroseries to derive."),
-            required=False),
-        description=copy_field(
-            IDistroSeriesPublic['description'], required=False),
-        version=copy_field(
-            IDistroSeriesPublic['version'], required=False),
-        distribution=copy_field(
-            IDistroSeriesPublic['distribution'], required=False),
+        parents=List(
+            title=_("The list of parents to derive from."),
+            value_type=TextLine(),
+            required=True),
         architectures=List(
             title=_("The list of architectures to copy to the derived "
             "distroseries."), value_type=TextLine(),
@@ -914,34 +904,32 @@
             title=_("If binaries will be copied to the derived "
             "distroseries."),
             required=True),
+        overlays=List(
+            title=_("The list of booleans indicating, for each parent, if "
+            "the parent/child relationship should be an overlay."),
+            value_type=Bool(),
+            required=False),
+        overlay_pockets=List(
+            title=_("The list of overlay pockets."),
+            value_type=TextLine(),
+            required=False),
+        overlay_components=List(
+            title=_("The list of overlay components."),
+            value_type=TextLine(),
+            required=False),
         )
     @call_with(user=REQUEST_USER)
     @export_write_operation()
-    def deriveDistroSeries(user, name, displayname, title, summary,
-                           description, version, distribution,
-                           architectures, packagesets, rebuild):
-        """Derive a distroseries from this one.
-
-        This method performs checks, can create the new distroseries if
-        necessary, and then creates a job to populate the new
-        distroseries.
-
-        :param name: The name of the new distroseries we will create if it
-            doesn't exist, or the name of the distroseries we will look
-            up, and then initialise.
-        :param displayname: The Display Name for the new distroseries.
-            If the distroseries already exists this parameter is ignored.
-        :param title: The Title for the new distroseries. If the
-            distroseries already exists this parameter is ignored.
-        :param summary: The Summary for the new distroseries. If the
-            distroseries already exists this parameter is ignored.
-        :param description: The Description for the new distroseries. If the
-            distroseries already exists this parameter is ignored.
-        :param version: The version for the new distroseries. If the
-            distroseries already exists this parameter is ignored.
-        :param distribution: The distribution the derived series will
-            belong to. If it isn't specified this distroseries'
-            distribution is used.
+    def initDerivedDistroSeries(user, parents, architectures,
+                                packagesets, rebuild, overlays,
+                                overlay_pockets, overlay_components):
+        """Initialize this series from parents.
+
+        This method performs checks and then creates a job to populate
+        the new distroseries.
+
+        :param parents: The list of parent ids this series will derive
+            from.
         :param architectures: The architectures to copy to the derived
             series. If not specified, all of the architectures are copied.
         :param packagesets: The packagesets to copy to the derived series.
@@ -949,6 +937,12 @@
         :param rebuild: Whether binaries will be copied to the derived
             series. If it's true, they will not be, and if it's false, they
             will be.
+        :param overlays: A list of booleans indicating, for each parent, if
+            the parent/child relationship should be an overlay.
+        :param overlay_pockets: The list of pockets names to use for overlay
+            relationships.
+        :param overlay_components: The list of components names to use for
+            overlay relationships.
         """
 
 

=== modified file 'lib/lp/registry/interfaces/distroseriesparent.py'
--- lib/lp/registry/interfaces/distroseriesparent.py	2011-05-19 12:02:35 +0000
+++ lib/lp/registry/interfaces/distroseriesparent.py	2011-05-31 15:42:35 +0000
@@ -76,6 +76,13 @@
         :param parent_series: An `IDistroseries`
         """
 
+    def getByDerivedAndParentSeries(self, derived_series, parent_series):
+        """Get the `DistroSeriesParent` by derived and parent series.
+
+        :param derived_series: The derived `IDistroseries`
+        :param parent_series: The parent `IDistroseries`
+        """
+
     def getFlattenedOverlayTree(derived_series):
         """Get the list of DistroSeriesParents corresponding to the
         flattened overlay tree.

=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py	2011-05-26 15:52:12 +0000
+++ lib/lp/registry/model/distroseries.py	2011-05-31 15:42:35 +0000
@@ -1129,7 +1129,7 @@
         queries.append("archive IN %s" % sqlvalues(archives))
 
         published = SourcePackagePublishingHistory.select(
-            " AND ".join(queries), clauseTables = ['SourcePackageRelease'],
+            " AND ".join(queries), clauseTables=['SourcePackageRelease'],
             orderBy=['-id'])
 
         return published
@@ -1946,53 +1946,22 @@
             ISourcePackageFormatSelectionSet).getBySeriesAndFormat(
                 self, format) is not None
 
-    def deriveDistroSeries(self, user, name, distribution=None,
-                           displayname=None, title=None, summary=None,
-                           description=None, version=None,
-                           architectures=(), packagesets=(), rebuild=False):
+    def initDerivedDistroSeries(self, user, parents, architectures=(),
+                                packagesets=(), rebuild=False, overlays=(),
+                                overlay_pockets=(),
+                                overlay_components=()):
         """See `IDistroSeries`."""
-        if distribution is None:
-            distribution = self.distribution
-        child = IStore(self).find(
-            DistroSeries, name=name, distribution=distribution).one()
-        if child is None:
-            if not displayname:
-                raise DerivationError(
-                    "Display Name needs to be set when creating a "
-                    "distroseries.")
-            if not title:
-                raise DerivationError(
-                    "Title needs to be set when creating a distroseries.")
-            if not summary:
-                raise DerivationError(
-                    "Summary needs to be set when creating a "
-                    "distroseries.")
-            if not description:
-                raise DerivationError(
-                    "Description needs to be set when creating a "
-                    "distroseries.")
-            if not version:
-                raise DerivationError(
-                    "Version needs to be set when creating a "
-                    "distroseries.")
-            child = distribution.newSeries(
-                name=name, displayname=displayname, title=title,
-                summary=summary, description=description,
-                version=version, previous_series=None, registrant=user)
-            IStore(self).add(child)
-        else:
-            if child.previous_series is not None:
-                raise DerivationError(
-                    "DistroSeries %s parent series is %s, "
-                    "but it must not be set" % (
-                        child.name, self.name))
-        initialise_series = InitialiseDistroSeries(self, child)
+        if self.is_derived_series:
+            raise DerivationError(
+                "DistroSeries %s already has parent series." % self.name)
+        initialise_series = InitialiseDistroSeries(self, parents)
         try:
             initialise_series.check()
         except InitialisationError, e:
             raise DerivationError(e)
         getUtility(IInitialiseDistroSeriesJobSource).create(
-            self, child, architectures, packagesets, rebuild)
+            self, parents, architectures, packagesets, rebuild, overlays,
+            overlay_pockets, overlay_components)
 
     def getParentSeries(self):
         """See `IDistroSeriesPublic`."""
@@ -2039,7 +2008,7 @@
         return getUtility(
             IDistroSeriesDifferenceSource).getForDistroSeries(
                 self,
-                difference_type = difference_type,
+                difference_type=difference_type,
                 source_package_name_filter=source_package_name_filter,
                 status=status,
                 child_version_higher=child_version_higher)

=== modified file 'lib/lp/registry/model/distroseriesparent.py'
--- lib/lp/registry/model/distroseriesparent.py	2011-05-20 12:21:39 +0000
+++ lib/lp/registry/model/distroseriesparent.py	2011-05-31 15:42:35 +0000
@@ -89,6 +89,14 @@
             DistroSeriesParent,
             DistroSeriesParent.parent_series_id == parent_series.id)
 
+    def getByDerivedAndParentSeries(self, derived_series, parent_series):
+        """See `IDistroSeriesParentSet`."""
+        store = IStore(DistroSeriesParent)
+        return store.find(
+            DistroSeriesParent,
+            DistroSeriesParent.parent_series_id == parent_series.id,
+            DistroSeriesParent.derived_series_id == derived_series.id).one()
+
     def getFlattenedOverlayTree(self, derived_series):
         """See `IDistroSeriesParentSet`."""
         self.getByDerivedSeries(derived_series)

=== modified file 'lib/lp/registry/stories/webservice/xx-derivedistroseries.txt'
--- lib/lp/registry/stories/webservice/xx-derivedistroseries.txt	2011-05-17 12:49:10 +0000
+++ lib/lp/registry/stories/webservice/xx-derivedistroseries.txt	2011-05-31 15:42:35 +0000
@@ -1,9 +1,8 @@
 Derive Distributions
 --------------------
 
-DistroSeries.deriveDistroSeries() allows us to derive one distroseries
-from another. The to-be-derived distroseries can already exist, or is
-can be created by deriveDistroSeries().
+DistroSeries.initDerivedDistroSeries() allows us to derive one distroseries
+from others.
 
 
 Set Up
@@ -16,11 +15,16 @@
     >>> login(ADMIN_EMAIL)
 
     >>> soyuz_team = factory.makeTeam(name='soyuz-team')
-    >>> previous_series = factory.makeDistroSeries(name="parentseries")
-    >>> previous_series.driver = soyuz_team
+    >>> parent_series = factory.makeDistroSeries(name="parentseries")
     >>> child_series = factory.makeDistroSeries(name='child1')
     >>> child_series_with_parent = factory.makeDistroSeries(
-    ...     name='child-with-parent', previous_series=previous_series)
+    ...     name='child-with-parent')
+    >>> child_series.driver = soyuz_team
+    >>> child_series_with_parent.driver = soyuz_team
+    >>> parent_series.driver = soyuz_team
+    >>> dsp = factory.makeDistroSeriesParent(
+    ...     derived_series=child_series_with_parent,
+    ...     parent_series=parent_series)
     >>> other_series = factory.makeDistroSeries(name="otherseries")
     >>> other_series.driver = soyuz_team
 
@@ -46,50 +50,32 @@
     ...     obj_url = canonical_url(obj, request=api_request)
     ...     return webservice.get(obj_url).jsonBody()
 
-We can't call .deriveDistroSeries() with a distroseries that already
+We can't call .initDerivedDistroSeries() on a distroseries that already
 has a parent series.
 
     >>> ws_child_series_with_parent = ws_object(
     ...     soyuz_team_webservice, child_series_with_parent)
-    >>> ws_other_series = ws_object(soyuz_team_webservice, other_series)
-    >>> ws_distribution = ws_object(soyuz_team_webservice, distribution)
 
     >>> print soyuz_team_webservice.named_post(
-    ...     ws_other_series['self_link'], 'deriveDistroSeries', {},
-    ...     name=ws_child_series_with_parent['name'],
-    ...     distribution=ws_child_series_with_parent['distribution_link'],
+    ...     ws_child_series_with_parent['self_link'],
+    ...     'initDerivedDistroSeries', parents=[str(other_series.id)],
     ...     rebuild=False)
     HTTP/1.1 400 Bad Request
     ...
-    DistroSeries ... parent series is ... but it must not be set
+    DistroSeries ... already has parent series.
 
 If we call it correctly, it works.
 
-    >>> ws_previous_series = ws_object(soyuz_team_webservice, previous_series)
     >>> ws_child_series = ws_object(soyuz_team_webservice, child_series)
 
     >>> print soyuz_team_webservice.named_post(
-    ...     ws_previous_series['self_link'], 'deriveDistroSeries', {},
-    ...     name=ws_child_series['name'],
-    ...     distribution=ws_child_series['distribution_link'],
-    ...     rebuild=False)
-    HTTP/1.1 200 Ok
-    ...
-
-If we call it with all of the arguments, it also works.
-
-    >>> text = 'The Second Child'
-    >>> print soyuz_team_webservice.named_post(
-    ...     ws_previous_series['self_link'], 'deriveDistroSeries', {},
-    ...     name='child2', distribution=ws_distribution['self_link'],
-    ...     displayname=text, title=text, summary=text,
-    ...     description=text, version=version,
-    ...     architectures=('i386',), packagesets=('test1',),
-    ...     rebuild=False)
-    HTTP/1.1 200 Ok
-    ...
-
-And we can verify both jobs exist.
+    ...     ws_child_series['self_link'], 'initDerivedDistroSeries',
+    ...     parents=[str(parent_series.id)],
+    ...     rebuild=False)
+    HTTP/1.1 200 Ok
+    ...
+
+And we can verify that the job has been created.
 
     >>> from zope.component import getUtility
     >>> from lp.soyuz.interfaces.distributionjob import (
@@ -101,4 +87,3 @@
     >>> for job in jobs:
     ...     print job.distroseries.name
     child1
-    child2

=== modified file 'lib/lp/registry/templates/distroseries-portlet-derivation.pt'
--- lib/lp/registry/templates/distroseries-portlet-derivation.pt	2011-05-20 14:36:26 +0000
+++ lib/lp/registry/templates/distroseries-portlet-derivation.pt	2011-05-31 15:42:35 +0000
@@ -60,9 +60,9 @@
         </tal:no_diffs>
       </tal:diffs>
   </tal:is_initialised>
+  </tal:is_derived>
   <tal:is_initialising condition="context/is_initialising">
     <h2>Series initialisation in progress</h2>
       This series is initialising.
   </tal:is_initialising>
-  </tal:is_derived>
 </div>

=== modified file 'lib/lp/registry/tests/test_distroseries.py'
--- lib/lp/registry/tests/test_distroseries.py	2011-05-20 14:36:26 +0000
+++ lib/lp/registry/tests/test_distroseries.py	2011-05-31 15:42:35 +0000
@@ -135,7 +135,7 @@
     def ignore_other_distributions(self):
         # Packages with the same name in other distributions don't
         # affect the returned version.
-        series_in_other_distribution = self.factory.makeDistroRelease()
+        series_in_other_distribution = self.factory.makeDistroSeries()
         self.publisher.getPubSource(version='0.9')
         self.publisher.getPubSource(
             version='1.0', distroseries=series_in_other_distribution)
@@ -174,7 +174,7 @@
     def test_getSuite_release_pocket(self):
         # The suite of a distro series and the release pocket is the name of
         # the distroseries.
-        distroseries = self.factory.makeDistroRelease()
+        distroseries = self.factory.makeDistroSeries()
         self.assertEqual(
             distroseries.name,
             distroseries.getSuite(PackagePublishingPocket.RELEASE))
@@ -183,14 +183,14 @@
         # The suite of a distro series and a non-release pocket is the name of
         # the distroseries followed by a hyphen and the name of the pocket in
         # lower case.
-        distroseries = self.factory.makeDistroRelease()
+        distroseries = self.factory.makeDistroSeries()
         pocket = PackagePublishingPocket.PROPOSED
         suite = '%s-%s' % (distroseries.name, pocket.name.lower())
         self.assertEqual(suite, distroseries.getSuite(pocket))
 
     def test_getDistroArchSeriesByProcessor(self):
         # A IDistroArchSeries can be retrieved by processor
-        distroseries = self.factory.makeDistroRelease()
+        distroseries = self.factory.makeDistroSeries()
         processorfamily = ProcessorFamilySet().getByName('x86')
         distroarchseries = self.factory.makeDistroArchSeries(
             distroseries=distroseries, architecturetag='i386',
@@ -202,7 +202,7 @@
     def test_getDistroArchSeriesByProcessor_none(self):
         # getDistroArchSeriesByProcessor returns None when no distroarchseries
         # is found
-        distroseries = self.factory.makeDistroRelease()
+        distroseries = self.factory.makeDistroSeries()
         processorfamily = ProcessorFamilySet().getByName('x86')
         self.assertIs(None,
             distroseries.getDistroArchSeriesByProcessor(
@@ -217,7 +217,7 @@
         # The registrant is the creator whereas the owner is the
         # distribution's owner.
         registrant = self.factory.makePerson()
-        distroseries = self.factory.makeDistroRelease(registrant=registrant)
+        distroseries = self.factory.makeDistroSeries(registrant=registrant)
         self.assertEquals(distroseries.distribution.owner, distroseries.owner)
         self.assertEquals(registrant, distroseries.registrant)
         self.assertNotEqual(distroseries.registrant, distroseries.owner)
@@ -225,10 +225,11 @@
     def test_is_initialising(self):
         # The series is_initialising only if there is an initialisation
         # job with a pending status attached to this series.
-        distroseries = self.factory.makeDistroRelease()
+        distroseries = self.factory.makeDistroSeries()
+        parent_distroseries = self.factory.makeDistroSeries()
         self.assertEquals(False, distroseries.is_initialising)
         job_source = getUtility(IInitialiseDistroSeriesJobSource)
-        job = job_source.create(distroseries.parent, distroseries)
+        job = job_source.create(distroseries, [parent_distroseries.id])
         self.assertEquals(True, distroseries.is_initialising)
         job.start()
         self.assertEquals(True, distroseries.is_initialising)
@@ -245,7 +246,7 @@
 
     def setUp(self):
         super(TestDistroSeriesPackaging, self).setUp()
-        self.series = self.factory.makeDistroRelease()
+        self.series = self.factory.makeDistroSeries()
         self.user = self.series.distribution.owner
         login('admin@xxxxxxxxxxxxx')
         component_set = getUtility(IComponentSet)
@@ -396,7 +397,7 @@
         self.ref_translatables = self._get_translatables()
 
         new_distroseries = (
-            self.factory.makeDistroRelease(name=u"sampleseries"))
+            self.factory.makeDistroSeries(name=u"sampleseries"))
         with person_logged_in(new_distroseries.distribution.owner):
             new_distroseries.hide_all_translations = False
         transaction.commit()
@@ -431,13 +432,13 @@
                 translatables, self._ref_translatables()))
 
     def test_fromSuite_release_pocket(self):
-        series = self.factory.makeDistroRelease()
+        series = self.factory.makeDistroSeries()
         result = getUtility(IDistroSeriesSet).fromSuite(
             series.distribution, series.name)
         self.assertEqual((series, PackagePublishingPocket.RELEASE), result)
 
     def test_fromSuite_non_release_pocket(self):
-        series = self.factory.makeDistroRelease()
+        series = self.factory.makeDistroSeries()
         suite = '%s-backports' % series.name
         result = getUtility(IDistroSeriesSet).fromSuite(
             series.distribution, suite)

=== renamed file 'lib/lp/registry/tests/test_derivedistroseries.py' => 'lib/lp/registry/tests/test_initderiveddistroseries.py'
--- lib/lp/registry/tests/test_derivedistroseries.py	2011-05-14 15:02:13 +0000
+++ lib/lp/registry/tests/test_initderiveddistroseries.py	2011-05-31 15:42:35 +0000
@@ -2,7 +2,7 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Test initialising a distroseries using
-IDistroSeries.deriveDistroSeries."""
+IDistroSeries.initDerivedDistroSeries."""
 
 __metaclass__ = type
 
@@ -30,62 +30,31 @@
     def setUp(self):
         super(TestDeriveDistroSeries, self).setUp()
         self.parent = self.factory.makeDistroSeries()
-        removeSecurityProxy(self.parent).driver = self.factory.makePerson()
         self.child = self.factory.makeDistroSeries()
-        login_person(self.parent.driver)
+        removeSecurityProxy(self.child).driver = self.factory.makePerson()
+        login_person(self.child.driver)
 
     def test_no_permission_to_call(self):
         login(ANONYMOUS)
         self.assertRaises(
-            Unauthorized, getattr, self.parent, "deriveDistroSeries")
-
-    def test_no_distroseries_and_no_arguments(self):
-        """Test that calling deriveDistroSeries() when the distroseries
-        doesn't exist, and not enough arguments are specified that the
-        function errors."""
-        self.assertRaisesWithContent(
-            DerivationError,
-            'Display Name needs to be set when creating a distroseries.',
-            self.parent.deriveDistroSeries, self.parent.driver,
-            'newdistro')
+            Unauthorized, getattr, self.child, "initDerivedDistroSeries")
 
     def test_parent_is_not_set(self):
-        # When parent_series is set it means that the distroseries has already
-        # been derived, and it is forbidden to derive more than once.
-        removeSecurityProxy(self.child).previous_series = self.parent
+        # When the series already has a parent series, it means that the
+        # distroseries has already been derived, and it is forbidden to
+        # derive more than once.
+        self.factory.makeDistroSeriesParent(
+            derived_series=self.child, parent_series=self.parent)
         self.assertRaisesWithContent(
             DerivationError,
-            ("DistroSeries {self.child.name} parent series is "
-             "{self.parent.name}, but it must not be set").format(self=self),
-            self.parent.deriveDistroSeries, self.parent.driver,
-            self.child.name, self.child.distribution)
+            ("DistroSeries {self.child.name} already has parent "
+             "series.".format(self=self)),
+            self.child.initDerivedDistroSeries, self.child.driver,
+            [self.parent.id])
 
-    def test_create_new_distroseries(self):
-        self.parent.deriveDistroSeries(
-            self.parent.driver, self.child.name, self.child.distribution)
+    def test_init_creates_new_job(self):
+        self.child.initDerivedDistroSeries(
+            self.child.driver, [self.parent.id])
         [job] = list(
             getUtility(IInitialiseDistroSeriesJobSource).iterReady())
         self.assertEqual(job.distroseries, self.child)
-
-    def test_create_fully_new_distroseries(self):
-        self.parent.deriveDistroSeries(
-            self.parent.driver, 'deribuntu', displayname='Deribuntu',
-            title='The Deribuntu', summary='Deribuntu',
-            description='Deribuntu is great', version='11.11')
-        [job] = list(
-            getUtility(IInitialiseDistroSeriesJobSource).iterReady())
-        self.assertEqual(job.distroseries.name, 'deribuntu')
-
-    def test_create_initialises_correct_distribution(self):
-        # Make two distroseries with the same name and different
-        # distributions to check that the right distroseries is initialised.
-        self.factory.makeDistroSeries(name='bar')
-        bar = self.factory.makeDistroSeries(name='bar')
-        self.parent.deriveDistroSeries(
-            self.parent.driver, 'bar', distribution=bar.parent,
-            displayname='Bar', title='The Bar', summary='Bar',
-            description='Bar is good', version='1.0')
-        [job] = list(
-            getUtility(IInitialiseDistroSeriesJobSource).iterReady())
-        self.assertEqual('bar', job.distroseries.name)
-        self.assertEqual(bar.parent.name, job.distribution.name)

=== modified file 'lib/lp/soyuz/doc/soyuz-set-of-uploads.txt'
--- lib/lp/soyuz/doc/soyuz-set-of-uploads.txt	2011-05-27 07:29:08 +0000
+++ lib/lp/soyuz/doc/soyuz-set-of-uploads.txt	2011-05-31 15:42:35 +0000
@@ -83,7 +83,7 @@
   ...     'breezy', 'Breezy Badger', 'The Breezy Badger',
   ...     'Black and White', 'Someone', '5.10', None,
   ...     breezy_autotest.owner)
-  >>> ids = InitialiseDistroSeries(breezy_autotest, breezy)
+  >>> ids = InitialiseDistroSeries(breezy, [breezy_autotest.id])
   >>> ids.initialise()
   >>> breezy.changeslist = 'breezy-changes@xxxxxxxxxx'
   >>> fake_chroot = LibraryFileAlias.get(1)

=== modified file 'lib/lp/soyuz/interfaces/distributionjob.py'
--- lib/lp/soyuz/interfaces/distributionjob.py	2011-05-30 04:06:52 +0000
+++ lib/lp/soyuz/interfaces/distributionjob.py	2011-05-31 15:42:35 +0000
@@ -79,7 +79,8 @@
 class IInitialiseDistroSeriesJobSource(IJobSource):
     """An interface for acquiring IInitialiseDistroSeriesJobs."""
 
-    def create(distroseries, arches, packagesets, rebuild):
+    def create(parents, arches, packagesets, rebuild, overlay,
+               overlay_pockets, overlay_components):
         """Create a new initialisation job for a distroseries."""
 
     def getPendingJobsForDistroseries(distroseries):

=== modified file 'lib/lp/soyuz/model/initialisedistroseriesjob.py'
--- lib/lp/soyuz/model/initialisedistroseriesjob.py	2011-04-13 09:55:48 +0000
+++ lib/lp/soyuz/model/initialisedistroseriesjob.py	2011-05-31 15:42:35 +0000
@@ -27,6 +27,7 @@
     )
 from lp.soyuz.scripts.initialise_distroseries import InitialiseDistroSeries
 from lp.services.job.model.job import Job
+from lp.services.database import bulk
 
 
 class InitialiseDistroSeriesJob(DistributionJobDerived):
@@ -37,13 +38,18 @@
     classProvides(IInitialiseDistroSeriesJobSource)
 
     @classmethod
-    def create(cls, parent, child, arches=(), packagesets=(), rebuild=False):
+    def create(cls, child, parents, arches=(), packagesets=(),
+               rebuild=False, overlays=(), overlay_pockets=(),
+               overlay_components=()):
         """See `IInitialiseDistroSeriesJob`."""
         metadata = {
-            'parent': parent.id,
+            'parents': parents,
             'arches': arches,
             'packagesets': packagesets,
             'rebuild': rebuild,
+            'overlays': overlays,
+            'overlay_pockets': overlay_pockets,
+            'overlay_components': overlay_components,
             }
         job = DistributionJob(
             child.distribution, child, cls.class_job_type,
@@ -63,9 +69,20 @@
             Job._status.is_in(Job.PENDING_STATUSES))
 
     @property
-    def parent(self):
-        return IStore(DistroSeries).get(
-            DistroSeries, self.metadata["parent"])
+    def parents(self):
+        return tuple(self.metadata['parents'])
+
+    @property
+    def overlays(self):
+        return tuple(self.metadata['overlays'])
+
+    @property
+    def overlay_pockets(self):
+        return tuple(self.metadata['overlay_pockets'])
+
+    @property
+    def overlay_components(self):
+        return tuple(self.metadata['overlay_components'])
 
     @property
     def arches(self):
@@ -82,13 +99,14 @@
     def run(self):
         """See `IRunnableJob`."""
         ids = InitialiseDistroSeries(
-            self.parent, self.distroseries, self.arches,
-            self.packagesets, self.rebuild)
+            self.distroseries, self.parents, self.arches,
+            self.packagesets, self.rebuild, self.overlays,
+            self.overlay_pockets, self.overlay_components)
         ids.check()
         ids.initialise()
 
     def getOopsVars(self):
         """See `IRunnableJob`."""
         vars = super(InitialiseDistroSeriesJob, self).getOopsVars()
-        vars.append(('parent_distroseries_id', self.metadata.get("parent")))
+        vars.append(('parent_distroseries_ids', self.metadata.get("parents")))
         return vars

=== modified file 'lib/lp/soyuz/scripts/initialise_distroseries.py'
--- lib/lp/soyuz/scripts/initialise_distroseries.py	2011-05-14 15:03:04 +0000
+++ lib/lp/soyuz/scripts/initialise_distroseries.py	2011-05-31 15:42:35 +0000
@@ -11,6 +11,7 @@
     ]
 
 from operator import methodcaller
+
 import transaction
 from zope.component import getUtility
 
@@ -18,6 +19,7 @@
 from canonical.launchpad.helpers import ensure_unicode
 from canonical.launchpad.interfaces.lpstorm import IMasterStore
 from lp.buildmaster.enums import BuildStatus
+from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.soyuz.adapters.packagelocation import PackageLocation
 from lp.soyuz.enums import (
@@ -25,6 +27,7 @@
     PackageUploadStatus,
     )
 from lp.soyuz.interfaces.archive import IArchiveSet
+from lp.soyuz.interfaces.component import IComponentSet
 from lp.soyuz.interfaces.packagecloner import IPackageCloner
 from lp.soyuz.interfaces.packageset import IPackagesetSet
 from lp.soyuz.model.packageset import Packageset
@@ -63,23 +66,32 @@
     """
 
     def __init__(
-        self, parent, distroseries, arches=(), packagesets=(), rebuild=False):
+        self, distroseries, parents, arches=(), packagesets=(),
+        rebuild=False, overlays=(), overlay_pockets=(),
+        overlay_components=()):
         # Avoid circular imports
         from lp.registry.model.distroseries import DistroSeries
-        self.parent = parent
+
+        # XXX: rvb 2011-05-27 bug=789091: This code should be fixed to support
+        # initialising from multiple parents.
+        self.parent_id = parents[0]
+        self.parent = DistroSeries.selectOneBy(id=int(self.parent_id))
+
         self.distroseries = distroseries
         self.arches = arches
         self.packagesets = [
             ensure_unicode(packageset) for packageset in packagesets]
         self.rebuild = rebuild
+        self.overlays = overlays
+        self.overlay_pockets = overlay_pockets
+        self.overlay_components = overlay_components
         self._store = IMasterStore(DistroSeries)
 
     def check(self):
-        if self.distroseries.previous_series is not None:
+        if self.distroseries.is_derived_series:
             raise InitialisationError(
-                ("DistroSeries {child.name} has been initialized; it already "
-                 "derives from {child.previous_series.distribution.name}/"
-                 "{child.previous_series.name}.").format(
+                ("DistroSeries {child.name} has already been initialized"
+                 ".").format(
                     child=self.distroseries))
         if self.distroseries.distribution.id == self.parent.distribution.id:
             self._checkBuilds()
@@ -137,10 +149,29 @@
         self._copy_architectures()
         self._copy_packages()
         self._copy_packagesets()
+        self._set_initialised()
         transaction.commit()
 
     def _set_parent(self):
-        self.distroseries.previous_series = self.parent
+        # XXX: rvb 2011-05-27 bug=789091: This code should be fixed to support
+        # initialising from multiple parents.
+        if self.overlays and self.overlays[0]:
+            pocket = PackagePublishingPocket.__metaclass__.getTermByToken(
+                PackagePublishingPocket, self.overlay_pockets[0]).value
+            component_set = getUtility(IComponentSet)
+            component = component_set[self.overlay_components[0]]
+            getUtility(IDistroSeriesParentSet).new(self.distroseries,
+                self.parent, initialized=False, is_overlay=True,
+                pocket=pocket, component=component)
+        else:
+            getUtility(IDistroSeriesParentSet).new(self.distroseries,
+                self.parent, initialized=False)
+
+    def _set_initialised(self):
+        dsp_set = getUtility(IDistroSeriesParentSet)
+        distroseriesparent = dsp_set.getByDerivedAndParentSeries(
+            self.distroseries, self.parent)
+        distroseriesparent.initialized = True
 
     def _copy_configuration(self):
         self.distroseries.backports_not_automatic = \
@@ -193,9 +224,8 @@
         # The overhead from looking up each packageset is mitigated by
         # this usually running from a job.
         if self.packagesets:
-            for pkgsetname in self.packagesets:
-                pkgset = getUtility(IPackagesetSet).getByName(
-                    pkgsetname, distroseries=self.parent)
+            for pkgsetid in self.packagesets:
+                pkgset = self._store.find(Packageset, id=int(pkgsetid)).one()
                 spns += list(pkgset.getSourcesIncluded())
 
         for archive in self.parent.distribution.all_distro_archives:
@@ -270,7 +300,7 @@
                 -- the data set for the series being updated, yet results are
                 -- in fact the data from the original series.
                 JOIN Distroseries ChildSeries
-                    ON Packaging.distroseries = ChildSeries.parent_series
+                    ON Packaging.distroseries = %s
             WHERE
                 -- Select only the packaging links that are in the parent
                 -- that are not in the child.
@@ -281,7 +311,7 @@
                     WHERE distroseries in (
                         SELECT id
                         FROM Distroseries
-                        WHERE id = ChildSeries.parent_series
+                        WHERE id = %s
                         )
                     EXCEPT
                     SELECT sourcepackagename
@@ -292,7 +322,7 @@
                         WHERE id = ChildSeries.id
                         )
                     )
-            """ % self.distroseries.id)
+            """ % (self.parent.id, self.distroseries.id, self.parent.id))
 
     def _copy_packagesets(self):
         """Copy packagesets from the parent distroseries."""
@@ -302,7 +332,7 @@
         for parent_ps in packagesets:
             # Cross-distro initialisations get packagesets owned by the
             # distro owner, otherwise the old owner is preserved.
-            if self.packagesets and parent_ps.name not in self.packagesets:
+            if self.packagesets and str(parent_ps.id) not in self.packagesets:
                 continue
             if self.distroseries.distribution == self.parent.distribution:
                 new_owner = parent_ps.owner

=== modified file 'lib/lp/soyuz/scripts/tests/test_initialise_distroseries.py'
--- lib/lp/soyuz/scripts/tests/test_initialise_distroseries.py	2011-05-14 15:03:04 +0000
+++ lib/lp/soyuz/scripts/tests/test_initialise_distroseries.py	2011-05-31 15:42:35 +0000
@@ -18,9 +18,11 @@
 from canonical.launchpad.interfaces.lpstorm import IStore
 from canonical.testing.layers import LaunchpadZopelessLayer
 from lp.buildmaster.enums import BuildStatus
+from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.soyuz.enums import SourcePackageFormat
 from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet
+from lp.soyuz.interfaces.component import IComponentSet
 from lp.soyuz.interfaces.packageset import (
     IPackagesetSet,
     NoSuchPackageSet,
@@ -91,7 +93,7 @@
         # error.
         child = self.factory.makeDistroSeries()
         self.factory.makeDistroArchSeries(distroseries=child)
-        ids = InitialiseDistroSeries(self.parent, child)
+        ids = InitialiseDistroSeries(child, [self.parent.id])
         self.assertRaisesWithContent(
             InitialisationError,
             "Can not copy distroarchseries from parent, there are already "
@@ -107,7 +109,7 @@
         source.createMissingBuilds()
         child = self.factory.makeDistroSeries(
             distribution=self.parent.parent)
-        ids = InitialiseDistroSeries(self.parent, child)
+        ids = InitialiseDistroSeries(child, [self.parent.id])
         self.assertRaisesWithContent(
             InitialisationError, "Parent series has pending builds.",
             ids.check)
@@ -129,7 +131,7 @@
             PackagePublishingPocket.RELEASE,
             'foo.changes', 'bar', self.parent.main_archive)
         child = self.factory.makeDistroSeries()
-        ids = InitialiseDistroSeries(self.parent, child)
+        ids = InitialiseDistroSeries(child, [self.parent.id])
         self.assertRaisesWithContent(
             InitialisationError, "Parent series queues are not empty.",
             ids.check)
@@ -170,11 +172,14 @@
         # Other configuration bits are copied too.
         self.assertTrue(child.backports_not_automatic)
 
-    def _full_initialise(self, arches=(), packagesets=(), rebuild=False,
-                         distribution=None):
-        child = self.factory.makeDistroSeries(distribution=distribution)
+    def _full_initialise(self, child=None, arches=(), packagesets=(),
+                         rebuild=False, distribution=None, overlays=(),
+                         overlay_pockets=(), overlay_components=()):
+        if child is None:
+            child = self.factory.makeDistroSeries(distribution=distribution)
         ids = InitialiseDistroSeries(
-            self.parent, child, arches, packagesets, rebuild)
+            child, [self.parent.id], arches, packagesets, rebuild, overlays,
+            overlay_pockets, overlay_components)
         ids.check()
         ids.initialise()
         return child
@@ -192,7 +197,7 @@
             arches=[self.parent_das.architecturetag])
         self.assertDistroSeriesInitialisedCorrectly(child)
         das = list(IStore(DistroArchSeries).find(
-            DistroArchSeries, distroseries = child))
+            DistroArchSeries, distroseries=child))
         self.assertEqual(len(das), 1)
         self.assertEqual(
             das[0].architecturetag, self.parent_das.architecturetag)
@@ -281,7 +286,9 @@
         packages = ('udev', 'chromium', 'libc6')
         for pkg in packages:
             test1.addSources(pkg)
-        child = self._full_initialise(packagesets=('test1',))
+        packageset1 = getUtility(IPackagesetSet).getByName(
+            u'test1', distroseries=self.parent)
+        child = self._full_initialise(packagesets=(str(packageset1.id),))
         child_test1 = getUtility(IPackagesetSet).getByName(
             u'test1', distroseries=child)
         self.assertEqual(test1.description, child_test1.description)
@@ -323,7 +330,7 @@
         self.factory.makeDistroArchSeries(distroseries=self.parent)
         child = self._full_initialise(
             arches=[self.parent_das.architecturetag],
-            packagesets=('test1',), rebuild=True)
+            packagesets=(str(test1.id),), rebuild=True)
         child.updatePackageCount()
         builds = child.getBuildRecords(
             build_state=BuildStatus.NEEDSBUILD,
@@ -332,7 +339,7 @@
         self.assertEqual(child.binarycount, 0)
         self.assertEqual(builds.count(), len(packages))
         das = list(IStore(DistroArchSeries).find(
-            DistroArchSeries, distroseries = child))
+            DistroArchSeries, distroseries=child))
         self.assertEqual(len(das), 1)
         self.assertEqual(
             das[0].architecturetag, self.parent_das.architecturetag)
@@ -344,7 +351,7 @@
         ppc_das.enabled = False
         child = self._full_initialise()
         das = list(IStore(DistroArchSeries).find(
-            DistroArchSeries, distroseries = child))
+            DistroArchSeries, distroseries=child))
         self.assertEqual(len(das), 1)
         self.assertEqual(
             das[0].architecturetag, self.parent_das.architecturetag)
@@ -379,3 +386,43 @@
             "DEBUG   Committing transaction." in stderr.split('\n'))
         transaction.commit()
         self.assertDistroSeriesInitialisedCorrectly(child)
+
+    def test_is_initialized(self):
+        # At the end of the initialisation, the distroseriesparent is marked
+        # as 'initialised'.
+        child = self._full_initialise(rebuild=True, overlays={})
+        dsp_set = getUtility(IDistroSeriesParentSet)
+        distroseriesparent = dsp_set.getByDerivedAndParentSeries(
+            child, self.parent)
+
+        self.assertTrue(distroseriesparent.initialized)
+
+    def test_no_overlays(self):
+        # Without the overlay parameter, no overlays are created.
+        child = self._full_initialise(rebuild=True, overlays=[])
+        dsp_set = getUtility(IDistroSeriesParentSet)
+        distroseriesparent = dsp_set.getByDerivedAndParentSeries(
+            child, self.parent)
+
+        self.assertFalse(distroseriesparent.is_overlay)
+
+    def test_setup_overlays(self):
+        # If the overlay parameter is passed, overlays are properly setup.
+        child = self.factory.makeDistroSeries()
+        overlays = [True]
+        overlay_pockets = ['Updates']
+        overlay_components = ['universe']
+        child = self._full_initialise(
+            child=child, rebuild=True, overlays=overlays,
+            overlay_pockets=overlay_pockets,
+            overlay_components=overlay_components)
+        dsp_set = getUtility(IDistroSeriesParentSet)
+        distroseriesparent = dsp_set.getByDerivedAndParentSeries(
+            child, self.parent)
+
+        self.assertTrue(distroseriesparent.is_overlay)
+        self.assertEqual(
+            getUtility(IComponentSet)['universe'],
+            distroseriesparent.component)
+        self.assertEqual(
+            PackagePublishingPocket.UPDATES, distroseriesparent.pocket)

=== modified file 'lib/lp/soyuz/tests/test_initialisedistroseriesjob.py'
--- lib/lp/soyuz/tests/test_initialisedistroseriesjob.py	2011-05-14 15:03:04 +0000
+++ lib/lp/soyuz/tests/test_initialisedistroseriesjob.py	2011-05-31 15:42:35 +0000
@@ -15,6 +15,7 @@
     LaunchpadZopelessLayer,
     )
 from lp.buildmaster.enums import BuildStatus
+from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.soyuz.interfaces.distributionjob import (
     IInitialiseDistroSeriesJobSource,
@@ -39,14 +40,14 @@
     def test_getOopsVars(self):
         parent = self.factory.makeDistroSeries()
         distroseries = self.factory.makeDistroSeries()
-        job = self.job_source.create(parent, distroseries)
+        job = self.job_source.create(distroseries, [parent.id])
         vars = job.getOopsVars()
         naked_job = removeSecurityProxy(job)
         self.assertIn(
             ('distribution_id', distroseries.distribution.id), vars)
         self.assertIn(('distroseries_id', distroseries.id), vars)
         self.assertIn(('distribution_job_id', naked_job.context.id), vars)
-        self.assertIn(('parent_distroseries_id', parent.id), vars)
+        self.assertIn(('parent_distroseries_ids', [parent.id]), vars)
 
     def _getJobs(self):
         """Return the pending InitialiseDistroSeriesJobs as a list."""
@@ -63,20 +64,22 @@
         # If there's already a InitialiseDistroSeriesJob for a
         # DistroSeries, InitialiseDistroSeriesJob.create() won't create
         # a new one.
-        self.job_source.create(parent, distroseries)
-        self.job_source.create(parent, distroseries)
+        self.job_source.create(distroseries, [parent.id])
+        self.job_source.create(distroseries, [parent.id])
         self.assertRaises(IntegrityError, flush_database_caches)
 
     def test_run_with_previous_series_already_set(self):
-        # InitialisationError is raised if the parent series is already set on
-        # the child.
+        # InitialisationError is raised if a parent series already exists
+        # for this series.
         parent = self.factory.makeDistroSeries()
-        distroseries = self.factory.makeDistroSeries(previous_series=parent)
-        job = self.job_source.create(parent, distroseries)
+        distroseries = self.factory.makeDistroSeries()
+        getUtility(IDistroSeriesParentSet).new(
+            distroseries, parent, initialized=True)
+
+        job = self.job_source.create(distroseries, [parent.id])
         expected_message = (
-            "DistroSeries {child.name} has been initialized; it already "
-            "derives from {parent.distribution.name}/{parent.name}.").format(
-            parent=parent, child=distroseries)
+            "DistroSeries {child.name} has already been initialized"
+            ".").format(child=distroseries)
         self.assertRaisesWithContent(
             InitialisationError, expected_message, job.run)
 
@@ -86,32 +89,39 @@
         parent = self.factory.makeDistroSeries()
         distroseries = self.factory.makeDistroSeries()
         arches = (u'i386', u'amd64')
-        packagesets = (u'foo', u'bar', u'baz')
+        packagesets = (u'1', u'2', u'3')
+        overlays = (True, )
+        overlay_pockets = ('Updates', )
+        overlay_components = ('restricted', )
 
         job = self.job_source.create(
-            parent, distroseries, arches, packagesets)
+            distroseries, [parent.id], arches, packagesets, False, overlays,
+            overlay_pockets, overlay_components)
 
         naked_job = removeSecurityProxy(job)
         self.assertEqual(naked_job.distroseries, distroseries)
         self.assertEqual(naked_job.arches, arches)
         self.assertEqual(naked_job.packagesets, packagesets)
         self.assertEqual(naked_job.rebuild, False)
-        self.assertEqual(naked_job.metadata["parent"], parent.id)
+        self.assertEqual(naked_job.parents, (parent.id, ))
+        self.assertEqual(naked_job.overlays, overlays)
+        self.assertEqual(naked_job.overlay_pockets, overlay_pockets)
+        self.assertEqual(naked_job.overlay_components, overlay_components)
 
     def test_parent(self):
         parent = self.factory.makeDistroSeries()
         distroseries = self.factory.makeDistroSeries()
-        job = self.job_source.create(parent, distroseries)
+        job = self.job_source.create(distroseries, [parent.id])
         naked_job = removeSecurityProxy(job)
-        self.assertEqual(parent, naked_job.parent)
+        self.assertEqual(parent.id, naked_job.parents[0])
 
     def test_getPendingJobsForDistroseries(self):
         # Pending initialisation jobs can be retrieved per distroseries.
         parent = self.factory.makeDistroSeries()
         distroseries = self.factory.makeDistroSeries()
         another_distroseries = self.factory.makeDistroSeries()
-        self.job_source.create(parent, distroseries)
-        self.job_source.create(parent, another_distroseries)
+        self.job_source.create(distroseries, [parent.id])
+        self.job_source.create(another_distroseries, [parent.id])
         initialise_utility = getUtility(IInitialiseDistroSeriesJobSource)
         [job] = list(initialise_utility.getPendingJobsForDistroseries(
             distroseries))
@@ -150,6 +160,7 @@
         test1 = getUtility(IPackagesetSet).new(
             u'test1', u'test 1 packageset', parent.owner,
             distroseries=parent)
+        self.test1_packageset_id = str(test1.id)
         test1.addSources('udev')
         parent.updatePackageCount()
         child = self.factory.makeDistroSeries()
@@ -159,7 +170,7 @@
 
     def test_job(self):
         parent, child = self._create_child()
-        job = self.job_source.create(parent, child)
+        job = self.job_source.create(child, [parent.id])
         self.layer.switchDbUser('initialisedistroseries')
 
         job.run()
@@ -171,8 +182,8 @@
         parent, child = self._create_child()
         arch = parent.nominatedarchindep.architecturetag
         job = self.job_source.create(
-            parent, child, packagesets=('test1',), arches=(arch,),
-            rebuild=True)
+            child, [parent.id], packagesets=(self.test1_packageset_id,),
+            arches=(arch,), rebuild=True)
         self.layer.switchDbUser('initialisedistroseries')
 
         job.run()

=== modified file 'scripts/ftpmaster-tools/initialise-from-parent.py'
--- scripts/ftpmaster-tools/initialise-from-parent.py	2011-05-14 15:04:20 +0000
+++ scripts/ftpmaster-tools/initialise-from-parent.py	2011-05-31 15:42:35 +0000
@@ -76,7 +76,7 @@
     try:
         log.debug('Check empty mutable queues in parentseries')
         log.debug('Check for no pending builds in parentseries')
-        log.debug('Copying distroarchseries from parent '
+        log.debug('Copying distroarchseries from parent(s) '
                       'and setting nominatedarchindep.')
         arches = ()
         if options.arches is not None:
@@ -87,9 +87,9 @@
         # good, at which point this script will be obsolete.
         parent, distroseries.previous_series = (
             distroseries.previous_series, None)
-        ids = InitialiseDistroSeries(parent, distroseries, arches)
+        ids = InitialiseDistroSeries(distroseries, [parent.id], arches)
         ids.check()
-        log.debug('initialising from parent, copying publishing records.')
+        log.debug('initialising from parent(s), copying publishing records.')
         ids.initialise()
     except InitialisationError, e:
         log.error(e)