← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rvb/launchpad/init-series-bug-789091-previous-series into lp:launchpad

 

Raphaël Victor Badin has proposed merging lp:~rvb/launchpad/init-series-bug-789091-previous-series into lp:launchpad with lp:~rvb/launchpad/init-series-bug-789091-devel as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #789091 in Launchpad itself: "initialise_distroseries doesn't work with multiple parents"
  https://bugs.launchpad.net/launchpad/+bug/789091

For more details, see:
https://code.launchpad.net/~rvb/launchpad/init-series-bug-789091-previous-series/+merge/64414

This branch is the backend side work to support initializing a series in a distribution with already initialized series. The given parent list will be used to create DSP but the series will be *derived* from the previous_series.

= Tests =

./bin/test -cvv test_initialise_distroseries test_derive_from_previous_parents
./bin/test -cvv test_initialise_distroseries test_derive_from_previous_parents_empty_parents
./bin/test -cvv test_initialise_distroseries test_derive_empty_parents_distribution_not_initialized

= Q/A =

This will be QAed using initialize-from-parent.py and using the UI that will come in a subsequent branch.
-- 
https://code.launchpad.net/~rvb/launchpad/init-series-bug-789091-previous-series/+merge/64414
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/launchpad/init-series-bug-789091-previous-series into lp:launchpad.
=== modified file 'lib/lp/soyuz/scripts/initialise_distroseries.py'
--- lib/lp/soyuz/scripts/initialise_distroseries.py	2011-06-13 15:22:31 +0000
+++ lib/lp/soyuz/scripts/initialise_distroseries.py	2011-06-13 15:22:32 +0000
@@ -47,10 +47,24 @@
     includes all configuration for distroseries as well as distroarchseries,
     publishing and all publishing records for sources and binaries.
 
+    We support 2 use cases here:
+      #1 If the child distribution has zero initialized series:
+        - the parent list can't be empty (otherwise we trigger an error);
+        - the series will be derived from the parents;
+        - the parents will be set to the parents passed as argument;
+        - first_derivation = True.
+      #2 If the child distribution has more than zero initialized series:
+        - the series will be derived from the previous_series'
+          parents;
+        - the parents will be set to the parents passed as argument or
+          the parents of the previous_series if the passed argument is empty;
+        - first_derivation = False.
+
     Preconditions:
       The distroseries must exist, and be completly unused, with no source
       or binary packages existing, as well as no distroarchseries set up.
-      Section and component selections must be empty. It must not have any      parent series.
+      Section and component selections must be empty. It must not have any
+      parent series.
 
     Outcome:
       The distroarchseries set up in the parent series will be copied.
@@ -70,7 +84,7 @@
     """
 
     def __init__(
-        self, distroseries, parents, arches=(), packagesets=(),
+        self, distroseries, parents=(), arches=(), packagesets=(),
         rebuild=False, overlays=(), overlay_pockets=(),
         overlay_components=()):
         # Avoid circular imports
@@ -93,18 +107,48 @@
         self.overlay_components = overlay_components
         self._store = IMasterStore(DistroSeries)
 
+        self.first_derivation = (
+            not self.distroseries.distribution.has_published_sources)
+        if self.first_derivation:
+            # Use-case #1.
+            self.derivation_parents = self.parents
+            self.derivation_parent_ids = self.parent_ids
+        else:
+            # Use-case #2.
+            self.derivation_parents = [self.distroseries.previous_series]
+            self.derivation_parent_ids = [
+                p.id for p in self.derivation_parents]
+            if self.parent_ids == []:
+                self.parents = (
+                    self.distroseries.previous_series.getParentSeries())
+
     def check(self):
         if self.distroseries.is_derived_series:
             raise InitialisationError(
                 ("DistroSeries {child.name} has already been initialized"
                  ".").format(
                     child=self.distroseries))
-        for parent in self.parents:
+        self._checkParents()
+        for parent in self.derivation_parents:
             if self.distroseries.distribution.id == parent.distribution.id:
                 self._checkBuilds(parent)
             self._checkQueue(parent)
         self._checkSeries()
 
+    def _checkParents(self):
+        """
+        If self.first_derivation, the parents list cannot be empty.
+        """
+        if self.first_derivation:
+            # Use-case #1.
+            if len(self.parent_ids) == 0:
+                raise InitialisationError(
+                    ("Distroseries {child.name} cannot be initialized: "
+                     "No other series in the distribution is initialized "
+                     "and no parent was passed to the initilization method"
+                     ".").format(
+                        child=self.distroseries))
+
     def _checkBuilds(self, parent):
         """Assert there are no pending builds for the given parent series.
 
@@ -188,11 +232,12 @@
 
     def _copy_configuration(self):
         self.distroseries.backports_not_automatic = any(
-            [parent.backports_not_automatic for parent in self.parents])
+            [parent.backports_not_automatic
+                for parent in self.derivation_parents])
 
     def _copy_architectures(self):
         filtering = ' AND distroseries IN %s ' % (
-                sqlvalues(self.parent_ids))
+                sqlvalues([p.id for p in self.derivation_parents]))
         if self.arches:
             filtering += ' AND architecturetag IN %s ' % (
                 sqlvalues(self.arches))
@@ -208,7 +253,7 @@
         self._store.flush()
         # Take nominatedarchindep from the first parent.
         self.distroseries.nominatedarchindep = self.distroseries[
-            self.parents[0].nominatedarchindep.architecturetag]
+            self.derivation_parents[0].nominatedarchindep.architecturetag]
 
     def _copy_packages(self):
         # Perform the copies
@@ -217,7 +262,7 @@
         # Prepare the lists of distroarchseries for which binary packages
         # shall be copied.
         distroarchseries_lists = {}
-        for parent in self.parents:
+        for parent in self.derivation_parents:
             distroarchseries_lists[parent] = []
             for arch in self.distroseries.architectures:
                 if self.arches and (arch.architecturetag not in self.arches):
@@ -251,7 +296,7 @@
                 pkgset = self._store.get(Packageset, int(pkgsetid))
                 spns += list(pkgset.getSourcesIncluded())
 
-        for parent in self.parents:
+        for parent in self.derivation_parents:
             distroarchseries_list = distroarchseries_lists[parent]
             for archive in parent.distribution.all_distro_archives:
                 if archive.purpose not in (
@@ -289,14 +334,14 @@
             SELECT DISTINCT %s AS distroseries, cs.component AS component
             FROM ComponentSelection AS cs WHERE cs.distroseries IN %s
             ''' % sqlvalues(self.distroseries.id,
-            self.parent_ids))
+            self.derivation_parent_ids))
         # Copy the section selections
         self._store.execute('''
             INSERT INTO SectionSelection (distroseries, section)
             SELECT DISTINCT %s as distroseries, ss.section AS section
             FROM SectionSelection AS ss WHERE ss.distroseries IN %s
             ''' % sqlvalues(self.distroseries.id,
-            self.parent_ids))
+            self.derivation_parent_ids))
         # Copy the source format selections
         self._store.execute('''
             INSERT INTO SourcePackageFormatSelection (distroseries, format)
@@ -304,7 +349,7 @@
             FROM SourcePackageFormatSelection AS spfs
             WHERE spfs.distroseries IN %s
             ''' % sqlvalues(self.distroseries.id,
-            self.parent_ids))
+            self.derivation_parent_ids))
 
     def _copy_packaging_links(self):
         """Copy the packaging links from the parent series to this one."""
@@ -348,7 +393,8 @@
                         )
                     )
             """ % sqlvalues(
-                self.parent_ids, self.distroseries.id, self.parent_ids))
+                self.derivation_parent_ids, self.distroseries.id,
+                self.derivation_parent_ids))
 
     def _copy_packagesets(self):
         """Copy packagesets from the parent distroseries."""
@@ -356,11 +402,11 @@
         from lp.registry.model.distroseries import DistroSeries
 
         packagesets = self._store.find(
-            Packageset, DistroSeries.id.is_in(self.parent_ids))
+            Packageset, DistroSeries.id.is_in(self.derivation_parent_ids))
         parent_to_child = {}
         # Create the packagesets, and any archivepermissions
         parent_distro_ids = [
-            parent.distribution.id for parent in self.parents]
+            parent.distribution.id for parent in self.derivation_parents]
         for parent_ps in packagesets:
             # Cross-distro initialisations get packagesets owned by the
             # distro owner, otherwise the old owner is preserved.

=== modified file 'lib/lp/soyuz/scripts/tests/test_initialise_distroseries.py'
--- lib/lp/soyuz/scripts/tests/test_initialise_distroseries.py	2011-06-13 15:22:31 +0000
+++ lib/lp/soyuz/scripts/tests/test_initialise_distroseries.py	2011-06-13 15:22:32 +0000
@@ -112,7 +112,7 @@
             pocket=PackagePublishingPocket.RELEASE)
         source.createMissingBuilds()
         child = self.factory.makeDistroSeries(
-            distribution=self.parent.parent)
+            distribution=self.parent.parent, previous_series=self.parent)
         ids = InitialiseDistroSeries(child, [self.parent.id])
         self.assertRaisesWithContent(
             InitialisationError, "Parent series has pending builds.",
@@ -180,11 +180,13 @@
         # Other configuration bits are copied too.
         self.assertTrue(child.backports_not_automatic)
 
-    def _fullInitialise(self, parents, child=None, arches=(), packagesets=(),
-                        rebuild=False, distribution=None, overlays=(),
+    def _fullInitialise(self, parents, child=None, previous_series=None,
+                        arches=(), packagesets=(), rebuild=False,
+                        distribution=None, overlays=(),
                         overlay_pockets=(), overlay_components=()):
         if child is None:
-            child = self.factory.makeDistroSeries(distribution=distribution)
+            child = self.factory.makeDistroSeries(
+                distribution=distribution, previous_series=previous_series)
         ids = InitialiseDistroSeries(
             child, [parent.id for parent in parents], arches, packagesets,
             rebuild, overlays, overlay_pockets, overlay_components)
@@ -269,7 +271,7 @@
     def test_packageset_owner_preserved_within_distro(self):
         # When initialising a new series within a distro, the copied
         # packagesets have ownership preserved.
-        self.parent, self.parent_das = self.setupParent()
+        self.parent, self.parent_das = self.setupParent(packages={})
         ps_owner = self.factory.makePerson()
         getUtility(IPackagesetSet).new(
             u'ps', u'packageset', ps_owner, distroseries=self.parent)
@@ -389,12 +391,12 @@
         test1.addSources('udev')
         getUtility(IArchivePermissionSet).newPackagesetUploader(
             self.parent.main_archive, uploader, test1)
-        # The child must have a parent series because initialise-from-parent
-        # expects it; this script supports the old-style derivation of
-        # distribution series where the parent series is specified at the time
-        # of adding the series. New-style derivation leaves the specification
-        # of the parent series until later.
         child = self.factory.makeDistroSeries(previous_series=self.parent)
+        # Create an initialized series in the distribution.
+        other_series = self.factory.makeDistroSeries(
+            distribution=child.parent)
+        self.factory.makeSourcePackagePublishingHistory(
+            distroseries=other_series)
         transaction.commit()
         ifp = os.path.join(
             config.root, 'scripts', 'ftpmaster-tools',
@@ -599,3 +601,89 @@
         self.assertEquals(
             u'0.3-1',
             published_sources[0].sourcepackagerelease.version)
+
+    def setUpSeriesWithPreviousSeries(self, parent, previous_parents=(),
+                                      publish_in_distribution=True):
+        # Helper method to create a series within an initialized
+        # distribution (i.e. that has an initialized series) with a
+        # 'previous_series' with parents.
+
+        # Create a previous_series derived from 2 parents.
+        previous_series = self._fullInitialise(previous_parents)
+
+        child = self.factory.makeDistroSeries(previous_series=previous_series)
+
+        # Add a publishing in another series from this distro.
+        other_series = self.factory.makeDistroSeries(
+            distribution=child.distribution)
+        if publish_in_distribution:
+            self.factory.makeSourcePackagePublishingHistory(
+                distroseries=other_series)
+
+        return child
+
+    def test_derive_from_previous_parents(self):
+        # If the series to be initialized is in a distribution with
+        # initialized series, the series is *derived* from
+        # the previous_series' parents.
+        previous_parent1, unused = self.setupParent(packages={u'p1': u'1.2'})
+        previous_parent2, unused = self.setupParent(packages={u'p2': u'1.5'})
+        parent, unused = self.setupParent()
+        child = self.setUpSeriesWithPreviousSeries(
+            parent=parent,
+            previous_parents=[previous_parent1, previous_parent2])
+        self._fullInitialise([parent], child=child)
+
+        # The parent for the derived series is the distroseries given as
+        # argument to InitializeSeries.
+        self.assertContentEqual(
+            [parent],
+            child.getParentSeries())
+
+        # The new series has been derived from previous_series.
+        published_sources = child.main_archive.getPublishedSources(
+            distroseries=child)
+        self.assertEquals(2, published_sources.count())
+        pub_sources = sorted(
+            [(s.sourcepackagerelease.sourcepackagename.name,
+              s.sourcepackagerelease.version)
+                for s in published_sources])
+        self.assertEquals(
+            [(u'p1', u'1.2'), (u'p2', u'1.5')],
+            pub_sources)
+
+    def test_derive_from_previous_parents_empty_parents(self):
+        # If an empty list is passed to InitialiseDistroSeries, the
+        # parents of the previous series are used as parents.
+        previous_parent1, unused = self.setupParent(packages={u'p1': u'1.2'})
+        previous_parent2, unused = self.setupParent(packages={u'p2': u'1.5'})
+        parent, unused = self.setupParent()
+        child = self.setUpSeriesWithPreviousSeries(
+            parent=parent,
+            previous_parents=[previous_parent1, previous_parent2])
+        # Initialize from an empty list of parents.
+        self._fullInitialise([], child=child)
+
+        self.assertContentEqual(
+            [previous_parent1, previous_parent2],
+            child.getParentSeries())
+
+    def test_derive_empty_parents_distribution_not_initialized(self):
+        # Initializing a series with an empty parent list if the series'
+        # distribution has no initialized series triggers an error.
+        parent, unused = self.setupParent()
+        previous_parent1, unused = self.setupParent(packages={u'p1': u'1.2'})
+        child = self.setUpSeriesWithPreviousSeries(
+            parent=parent,
+            previous_parents=[previous_parent1],
+            publish_in_distribution=False)
+
+        # Initialize from an empty list of parents.
+        ids = InitialiseDistroSeries(child, [])
+        self.assertRaisesWithContent(
+            InitialisationError,
+            ("Distroseries {child.name} cannot be initialized: "
+             "No other series in the distribution is initialized "
+             "and no parent was passed to the initilization method"
+             ".").format(child=child),
+             ids.check)

=== modified file 'scripts/ftpmaster-tools/initialise-from-parent.py'
--- scripts/ftpmaster-tools/initialise-from-parent.py	2011-05-30 17:56:56 +0000
+++ scripts/ftpmaster-tools/initialise-from-parent.py	2011-06-13 15:22:32 +0000
@@ -81,13 +81,7 @@
         arches = ()
         if options.arches is not None:
             arches = tuple(options.arches.split(','))
-        # InitialiseDistroSeries does not like it if the parent series is
-        # specified on the child, so we must unset it and pass it in. This is
-        # a temporary hack until confidence in InitialiseDistroSeriesJob is
-        # good, at which point this script will be obsolete.
-        parent, distroseries.previous_series = (
-            distroseries.previous_series, None)
-        ids = InitialiseDistroSeries(distroseries, [parent.id], arches)
+        ids = InitialiseDistroSeries(distroseries, arches=arches)
         ids.check()
         log.debug('initialising from parent(s), copying publishing records.')
         ids.initialise()