← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rvb/launchpad/dsd-init-bug-808681 into lp:launchpad

 

Raphaël Victor Badin has proposed merging lp:~rvb/launchpad/dsd-init-bug-808681 into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #808681 in Launchpad itself: "Post-first series initializations don't set up the DistroSeriesDifferences rows"
  https://bugs.launchpad.net/launchpad/+bug/808681

For more details, see:
https://code.launchpad.net/~rvb/launchpad/dsd-init-bug-808681/+merge/68375

This branch adds the creation of the DSDs as a part of the initialization process. DSD are created using the methods in lib/lp/registry/scripts/populate_distroseriesdiff.py if the parents have changed (compared to the previous_series' parent) or if only specific packagesets have been selected for the initialization. If not, then a simple copy from previous_series' DSDs is done. (This should speed up the initialization of Ubuntu series.)

= Details =

- New authorizations have been added to database/schema/security.cfg to allow scripts to manipulate the distroseriesdifference table.
- lib/lp/registry/scripts/populate_distroseriesdiff.py has been reworked a bit to be able to call it's methods from outside the script.

= Tests =

./bin/test -vvc test_initialize_distroseries test__has_same_parents_as_previous_series_explicit
./bin/test -vvc test_initialize_distroseries test__has_same_parents_as_previous_series_implicit
./bin/test -vvc test_initialize_distroseries test_not__has_same_parents_as_previous_series
./bin/test -vvc test_initialize_distroseries test_initialization_copy_dsds
./bin/test -vvc test_initialize_distroseries test_initialization_compute_dsds
./bin/test -vvc test_initialize_distroseries test_initialization_compute_dsds_specific_packagesets

= QA =

On DF, make a post-first initialization of a series from multiple parents without changing the (pre-selected on the +initseries page) parents. Make sure that lp.net/distro/series/+localpackagediffs is populated and is the same as lp.net/distro/previous_series/+localpackagediffs.

On DF, make a post-first initialization of a series from multiple parents and add a new parent (compared to previous_series' parents). Make sure that lp.net/distro/series/+localpackagediffs is populated with diffs from the series and all of it's parents.

-- 
https://code.launchpad.net/~rvb/launchpad/dsd-init-bug-808681/+merge/68375
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/launchpad/dsd-init-bug-808681 into lp:launchpad.
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg	2011-07-15 15:35:03 +0000
+++ database/schema/security.cfg	2011-07-19 12:56:28 +0000
@@ -857,6 +857,7 @@
 public.cve                              = SELECT, INSERT
 public.distributionjob                  = SELECT, INSERT, DELETE
 public.distributionsourcepackage        = SELECT, INSERT, UPDATE
+public.distroseriesdifference           = SELECT, INSERT, UPDATE
 public.distroseriesparent               = SELECT, INSERT, UPDATE
 public.flatpackagesetinclusion          = SELECT, INSERT, UPDATE, DELETE
 public.gpgkey                           = SELECT, INSERT, UPDATE
@@ -979,6 +980,7 @@
 public.distributionsourcepackage                = SELECT, INSERT
 public.distroarchseries                         = SELECT, INSERT
 public.distroseries                             = SELECT, UPDATE
+public.distroseriesdifference                   = SELECT, INSERT, UPDATE
 public.distroseriesparent                       = SELECT, INSERT, UPDATE
 public.flatpackagesetinclusion                  = SELECT, INSERT
 public.gpgkey                                   = SELECT

=== modified file 'lib/lp/registry/scripts/populate_distroseriesdiff.py'
--- lib/lp/registry/scripts/populate_distroseriesdiff.py	2011-05-20 14:36:26 +0000
+++ lib/lp/registry/scripts/populate_distroseriesdiff.py	2011-07-19 12:56:28 +0000
@@ -14,6 +14,8 @@
 __metaclass__ = type
 __all__ = [
     'PopulateDistroSeriesDiff',
+    'populate_distroseriesdiff',
+    'update_distroseriesdiff',
     ]
 
 from collections import defaultdict
@@ -21,6 +23,7 @@
     Option,
     OptionValueError,
     )
+
 from storm.locals import ClassAlias
 import transaction
 from zope.component import getUtility
@@ -30,17 +33,18 @@
     quote_identifier,
     )
 from canonical.launchpad.interfaces.lpstorm import IStore
+from canonical.launchpad.utilities.looptuner import TunableLoop
 from lp.registry.enum import (
     DistroSeriesDifferenceStatus,
     DistroSeriesDifferenceType,
     )
-from canonical.launchpad.utilities.looptuner import TunableLoop
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.model.distroseries import DistroSeries
 from lp.registry.model.distroseriesdifference import DistroSeriesDifference
 from lp.registry.model.distroseriesparent import DistroSeriesParent
+from lp.services.log.logger import DevNullLogger
 from lp.services.scripts.base import LaunchpadScript
 from lp.soyuz.interfaces.publishing import active_publishing_status
 
@@ -195,18 +199,29 @@
     store.execute("DROP TABLE IF EXISTS %s" % quote_identifier(table))
 
 
-def populate_distroseriesdiff(logger, derived_series, parent_series):
+def populate_distroseriesdiff(derived_series, parent_series, logger=None):
     """Compare `derived_distroseries` to parent, and register differences.
 
     The differences are registered by creating `DistroSeriesDifference`
     records, insofar as they do not yet exist.
     """
+    if logger is None:
+        logger = DevNullLogger()
     temp_table = "temp_potentialdistroseriesdiff"
 
     store = IStore(derived_series)
     drop_table(store, temp_table)
-    store.execute("CREATE TEMP TABLE %s AS %s" % (
-        quote_identifier(temp_table),
+    quoted_temp_table = quote_identifier(temp_table)
+    store.execute("""
+        CREATE TEMP TABLE %s(
+            sourcepackagename INTEGER,
+            source_version debversion,
+            parent_source_version debversion)
+            ON COMMIT DROP
+        """ % (
+        quoted_temp_table))
+    store.execute("INSERT INTO %s %s" % (
+        quoted_temp_table,
         compose_sql_find_differences(derived_series, parent_series)))
     logger.info(
         "Found %d potential difference(s).",
@@ -214,7 +229,31 @@
     store.execute(
         compose_sql_populate_distroseriesdiff(
             derived_series, parent_series, temp_table))
-    drop_table(store, temp_table)
+
+
+def update_distroseriesdiff(commit, distroseries, logger=None):
+    """Call `DistroSeriesDifference.update()` where appropriate.
+
+    The `DistroSeriesDifference` records we create don't have their
+    details filled out, such as base version or their diffs.
+
+    Only instances where the source package is published in both the
+    parent series and the derived series need to have this done.
+    """
+    if logger is None:
+        logger = DevNullLogger()
+    logger.info(
+        "Updating DSDs for %s.", distroseries.title)
+    store = IStore(distroseries)
+    dsd_ids = store.find(
+        DistroSeriesDifference.id,
+        DistroSeriesDifference.derived_series == distroseries,
+        DistroSeriesDifference.status ==
+            DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
+        DistroSeriesDifference.difference_type ==
+            DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
+        DistroSeriesDifference.base_version == None)
+    DSDUpdater(logger, store, commit, dsd_ids).run()
 
 
 def find_derived_series():
@@ -334,7 +373,7 @@
         self.logger.info(
             "Looking for differences in %s with regards to %s.",
             distroseries, parent)
-        populate_distroseriesdiff(self.logger, distroseries, parent)
+        populate_distroseriesdiff(distroseries, parent, self.logger)
         self.commit()
         self.logger.info("Updating base_versions.")
         self.update(distroseries)
@@ -353,7 +392,7 @@
         relationships = self.getDistroSeries()
         for child in relationships:
             for parent in relationships[child]:
-               self.logger.info(
+                self.logger.info(
                     "%s %s with a parent of %s %s", child.distribution.name,
                     child.name, parent.distribution.name, parent.name)
 
@@ -388,23 +427,5 @@
                 self.processDistroSeries(child, parent)
 
     def update(self, distroseries):
-        """Call `DistroSeriesDifference.update()` where appropriate.
-
-        The `DistroSeriesDifference` records we create don't have their
-        details filled out, such as base version or their diffs.
-
-        Only instances where the source package is published in both the
-        parent series and the derived series need to have this done.
-        """
-        self.logger.info(
-            "Updating DSDs for %s.", distroseries.title)
-        store = IStore(distroseries)
-        dsd_ids = store.find(
-            DistroSeriesDifference.id,
-            DistroSeriesDifference.derived_series == distroseries,
-            DistroSeriesDifference.status ==
-                DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
-            DistroSeriesDifference.difference_type ==
-                DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
-            DistroSeriesDifference.base_version == None)
-        DSDUpdater(self.logger, store, self.commit, dsd_ids).run()
+        """Update `DistroSeriesDifference`s for `distroseries`."""
+        update_distroseriesdiff(self.commit, distroseries, self.logger)

=== modified file 'lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py'
--- lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py	2011-05-23 10:54:01 +0000
+++ lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py	2011-07-19 12:56:28 +0000
@@ -24,11 +24,11 @@
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.model.distroseriesdifference import DistroSeriesDifference
 from lp.registry.scripts.populate_distroseriesdiff import (
-    DSDUpdater,
     compose_sql_difference_type,
+    compose_sql_find_differences,
     compose_sql_find_latest_source_package_releases,
-    compose_sql_find_differences,
     compose_sql_populate_distroseriesdiff,
+    DSDUpdater,
     find_derived_series,
     populate_distroseriesdiff,
     PopulateDistroSeriesDiff,
@@ -37,12 +37,12 @@
     BufferLogger,
     DevNullLogger,
     )
+from lp.soyuz.enums import ArchivePurpose
 from lp.soyuz.interfaces.publishing import (
     active_publishing_status,
     inactive_publishing_status,
     )
 from lp.soyuz.model.archive import Archive
-from lp.soyuz.enums import ArchivePurpose
 from lp.testing import (
     TestCase,
     TestCaseWithFactory,
@@ -444,8 +444,7 @@
     def test_creates_distroseriesdifference(self):
         dsp = self.makeDerivedDistroSeries()
         spph = self.makeSPPH(distroseries=dsp.derived_series)
-        populate_distroseriesdiff(
-            DevNullLogger(), dsp.derived_series, dsp.parent_series)
+        populate_distroseriesdiff(dsp.derived_series, dsp.parent_series)
         dsd = self.getDistroSeriesDiff(dsp.derived_series).one()
         spr = spph.sourcepackagerelease
         self.assertEqual(spr.sourcepackagename, dsd.source_package_name)
@@ -458,7 +457,7 @@
         changelog = self.factory.makeChangelog(versions=['3.1', '3.141'])
         parent_changelog = self.factory.makeChangelog(
             versions=['3.1', '3.14'])
-        transaction.commit() # Yay, librarian.
+        transaction.commit()  # Yay, librarian.
         existing_versions = {
             'base': '3.1',
             'parent': '3.14',

=== modified file 'lib/lp/soyuz/scripts/initialize_distroseries.py'
--- lib/lp/soyuz/scripts/initialize_distroseries.py	2011-07-16 19:39:42 +0000
+++ lib/lp/soyuz/scripts/initialize_distroseries.py	2011-07-19 12:56:28 +0000
@@ -210,6 +210,7 @@
         self._copy_architectures()
         self._copy_packages()
         self._copy_packagesets()
+        self._create_dsds()
         self._set_initialized()
         transaction.commit()
 
@@ -240,6 +241,61 @@
         for distroseriesparent in distroseriesparents:
             distroseriesparent.initialized = True
 
+    def _has_same_parents_as_previous_series(self):
+        # Does this distroseries have the same parents as it's previous
+        # series? (note that the parent's order does not matter here)
+        dsp_set = getUtility(IDistroSeriesParentSet)
+        previous_series_parents = [
+            dsp.parent_series for dsp in dsp_set.getByDerivedSeries(
+                self.distroseries.previous_series)]
+        return set(previous_series_parents) == set(self.parents)
+
+    def _create_dsds(self):
+        if not self.first_derivation:
+            if (self._has_same_parents_as_previous_series() and
+                not self.packagesets):
+                # If the parents are the same as previous_series's
+                # parents and all the packagesets are being copied,
+                # then we simply copy the DSDs from previous_series
+                # for performance reasons.
+                self._copy_dsds_from_previous_series()
+            else:
+                # Either the parents have changed (compared to
+                # previous_series's parent) or a selection only of the
+                # packagesets is being copied so we have to recompute
+                # the DSDs.
+                self._compute_dsds()
+
+    def _copy_dsds_from_previous_series(self):
+        self._store.execute("""
+            INSERT INTO DistroSeriesDifference
+                (derived_series, source_package_name, package_diff,
+                status, difference_type, parent_package_diff,
+                source_version, parent_source_version,
+                base_version, parent_series)
+            SELECT
+                %s AS derived_series, source_package_name,
+                package_diff, status,
+                difference_type, parent_package_diff, source_version,
+                parent_source_version, base_version, parent_series
+            FROM DistroSeriesDifference AS dsd
+                WHERE dsd.derived_series = %s
+            """ % sqlvalues(
+                self.distroseries.id,
+                self.distroseries.previous_series.id))
+
+    def _compute_dsds(self):
+        # Avoid circular imports.
+        from lp.registry.scripts.populate_distroseriesdiff import (
+            populate_distroseriesdiff,
+            update_distroseriesdiff,
+            )
+        for parent in self.parents:
+            populate_distroseriesdiff(self.distroseries, parent)
+        transaction.commit()
+        update_distroseriesdiff(transaction.commit, self.distroseries)
+        transaction.commit()
+
     def _copy_configuration(self):
         self.distroseries.backports_not_automatic = any(
             parent.backports_not_automatic

=== modified file 'lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py'
--- lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py	2011-07-13 11:53:55 +0000
+++ lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py	2011-07-19 12:56:28 +0000
@@ -18,6 +18,9 @@
 from canonical.launchpad.interfaces.lpstorm import IStore
 from canonical.testing.layers import LaunchpadZopelessLayer
 from lp.buildmaster.enums import BuildStatus
+from lp.registry.interfaces.distroseriesdifference import (
+    IDistroSeriesDifferenceSource,
+    )
 from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.soyuz.enums import SourcePackageFormat
@@ -40,6 +43,7 @@
     InitializeDistroSeries,
     )
 from lp.testing import TestCaseWithFactory
+from lp.testing.fakemethod import FakeMethod
 
 
 class InitializationHelperTestCase(TestCaseWithFactory):
@@ -704,7 +708,7 @@
             InitializationError, self._fullInitialize,
             [self.parent1, self.parent2])
 
-    def setUpSeriesWithPreviousSeries(self, parent, previous_parents=(),
+    def setUpSeriesWithPreviousSeries(self, 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
@@ -730,10 +734,9 @@
         # 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])
+        parent, unused = self.setupParent()
         self._fullInitialize([parent], child=child)
 
         # The parent for the derived series is the distroseries given as
@@ -759,9 +762,7 @@
         # 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._fullInitialize([], child=child)
@@ -773,10 +774,8 @@
     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)
 
@@ -852,3 +851,121 @@
         self.assertFalse(
             InitializeDistroSeries._use_cloner(
                 target_archive, target_archive, distroseries))
+
+    def test__has_same_parents_as_previous_series_explicit(self):
+        # IDS._has_same_parents_as_previous_series returns True if the
+        # parents for the series to be initialized are the same as
+        # previous_series' parents.
+        prev_parent1, unused = self.setupParent(packages={u'p1': u'1.2'})
+        prev_parent2, unused = self.setupParent(packages={u'p2': u'1.5'})
+        child = self.setUpSeriesWithPreviousSeries(
+            previous_parents=[prev_parent1, prev_parent2])
+        # The same parents can be explicitely set.
+        ids = InitializeDistroSeries(
+            child, [prev_parent2.id, prev_parent1.id])
+
+        self.assertTrue(ids._has_same_parents_as_previous_series())
+
+    def test__has_same_parents_as_previous_series_implicit(self):
+        # IDS._has_same_parents_as_previous_series returns True if the
+        # parents for the series to be initialized are the same as
+        # previous_series' parents.
+        prev_parent1, unused = self.setupParent(packages={u'p1': u'1.2'})
+        prev_parent2, unused = self.setupParent(packages={u'p2': u'1.5'})
+        child = self.setUpSeriesWithPreviousSeries(
+            previous_parents=[prev_parent1, prev_parent2])
+        # If no parents are provided, the parents from previous_series
+        # will be used.
+        ids = InitializeDistroSeries(child)
+
+        self.assertTrue(ids._has_same_parents_as_previous_series())
+
+    def test_not__has_same_parents_as_previous_series(self):
+        # IDS._has_same_parents_as_previous_series returns False if the
+        # parents for the series to be initialized are *not* the same as
+        # previous_series' parents.
+        prev_parent1, unused = self.setupParent(packages={u'p1': u'1.2'})
+        prev_parent2, unused = self.setupParent(packages={u'p2': u'1.5'})
+        child = self.setUpSeriesWithPreviousSeries(
+            previous_parents=[prev_parent1, prev_parent2])
+        parent3 = self.factory.makeDistroSeries()
+        ids = InitializeDistroSeries(
+            child, [prev_parent2.id, prev_parent1.id, parent3.id])
+
+        self.assertFalse(ids._has_same_parents_as_previous_series())
+
+    def test_initialization_copy_dsds(self):
+        # Post-first initialization of a series with the same parents
+        # than those of the previous_series causes a copy of
+        # previous_series' DSDs.
+        prev_parent1, unused = self.setupParent(packages={u'p1': u'1.2'})
+        prev_parent2, unused = self.setupParent(packages={u'p2': u'1.5'})
+        child = self.setUpSeriesWithPreviousSeries(
+            previous_parents=[prev_parent1, prev_parent2])
+        self.factory.makeDistroSeriesDifference()
+        self.factory.makeDistroSeriesDifference(
+            derived_series=child.previous_series,
+            source_package_name_str=u'p1')
+        self.factory.makeDistroSeriesDifference(
+            derived_series=child.previous_series,
+            source_package_name_str=u'p2')
+        dsd_source = getUtility(IDistroSeriesDifferenceSource)
+        # No DSDs for the child yet.
+        self.assertEquals(0, dsd_source.getForDistroSeries(child).count())
+        self._fullInitialize([], child=child)
+
+        self.assertContentEqual(
+            [u'p1', u'p2'],
+            sorted(
+                [diff.source_package_name.name
+                    for diff in dsd_source.getForDistroSeries(child)]))
+
+    def test_initialization_compute_dsds(self):
+        # Post-first initialization of a series with different parents
+        # than those of the previous_series triggers the computation
+        # of the DSDs (using populate_distroseriesdiff's methods).
+        prev_parent1, unused = self.setupParent(packages={u'p1': u'1.2'})
+        prev_parent2, unused = self.setupParent(packages={u'p2': u'1.5'})
+        child = self.setUpSeriesWithPreviousSeries(
+            previous_parents=[prev_parent1, prev_parent2])
+        parent3, unused = self.setupParent(packages={u'p3': u'2.5'})
+        from lp.registry.scripts import populate_distroseriesdiff as module
+        module.populate_distroseriesdiff = FakeMethod()
+        module.update_distroseriesdiff = FakeMethod()
+        self._fullInitialize(
+            [prev_parent1, prev_parent2, parent3], child=child)
+
+        # populate_distroseriesdiff has been called for each parent.
+        self.assertContentEqual(
+            [((child, prev_parent1), {}),
+             ((child, prev_parent2), {}),
+             ((child, parent3), {})],
+            module.populate_distroseriesdiff.calls)
+
+        # update_distroseriesdiff has been called once for the child series.
+        self.assertContentEqual(
+            [((transaction.commit, child), {})],
+            module.update_distroseriesdiff.calls)
+
+    def test_initialization_compute_dsds_specific_packagesets(self):
+        # Post-first initialization of a series with specific
+        # packagesets triggers the computation (as opposed to the copy)
+        # of the DSDs.
+        prev_parent1, unused = self.setupParent(packages={u'p1': u'1.2'})
+        prev_parent2, unused = self.setupParent(packages={u'p2': u'1.5'})
+        test1 = getUtility(IPackagesetSet).new(
+            u'test1', u'test 1 packageset', prev_parent1.owner,
+            distroseries=prev_parent1)
+        test1.addSources('p1')
+        child = self.setUpSeriesWithPreviousSeries(
+            previous_parents=[prev_parent1, prev_parent2])
+        parent3, unused = self.setupParent(packages={u'p3': u'2.5'})
+        from lp.registry.scripts import populate_distroseriesdiff as module
+        module.update_distroseriesdiff = FakeMethod()
+        ids = InitializeDistroSeries(
+            child, [], packagesets=(str(test1.id),))
+        ids._compute_dsds = FakeMethod()
+        ids.check()
+        ids.initialize()
+
+        self.assertEquals(1, ids._compute_dsds.call_count)


Follow ups