← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~jtv/launchpad/bug-832647 into lp:launchpad

 

Jeroen T. Vermeulen has proposed merging lp:~jtv/launchpad/bug-832647 into lp:launchpad with lp:~jtv/launchpad/pre-832647 as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #832647 in Launchpad itself: "Dominate Debian"
  https://bugs.launchpad.net/launchpad/+bug/832647

For more details, see:
https://code.launchpad.net/~jtv/launchpad/bug-832647/+merge/74380

= Summary =

We can't start delete Debian packages that have been removed from Debian's Sources list until Gina, the script we use to import Debian's source packages, learns to perform domination.

Domination is the process of figuring out which versions of a package have been superseded, and by which other version.  The superseding version is called the dominant.


== Proposed fix ==

This involves a generalization of the domination process that people have been wanting for a long time.  Let me explain.

Given a series of BinaryPackagePublicationHistory or SourcePackagePublicationHistory objects for a single package in a single distroseries, on the same archive and in the same pocket.  We'll call them “pubs” for short, partly because it saves so much work and partly because there's something comfortable about working with pubs.

Traditional domination: find the pub for the latest version, using the pubs' creation dates as the tie-breaker.  Mark all the rest as superseded by this latest version.

New domination: multiple versions can stay live.  Mark the rest superseded by a newer version that stays live, or where no such version exists, mark as deleted instead.

Traditional domination re-implemented: find the pub for the latest version, using creation dates as the tie-breaker.  This is the one version that stays live.  Pass the pubs and this one version to the new domination code.

Domination in gina: Get a list of versions for the package, as found in the Sources list for the distroseries / archive / pocket.  All these stay live.  Pass the pubs and this list of current versions to the new domination code.


== Pre-implementation notes ==

Extensive — trust me on this, I still have the dents in my desk — discussions with both Julian and William.

Julian tells me there are no plans for the foreseeable future to use gina for importing any archives that may have binaries, but William says we may need to dust off that part of gina over the next year or so.  I've made it as easy as possible to implement binary domination, but didn't do it myself (yet) as it's not essential to a vastly over-extended feature rotation.  I left an XXX.


== Implementation details ==

In test_gina you may notice the test_suite boilerplate that's not usually needed any more.  In this case it doesn't seem to run any doctests, but it does trigger a test for one of the handlers.  I left it intact.


== Tests ==

{{{
./bin/test -vvc lp.soyuz -t gina
./bin/test -vvc archivepublisher -t dominat
}}}


== Demo and Q/A ==

We'll have to make sure that both gina and publish-ftpmaster still work, and that both dominate properly.  The new feature is gina doing domination.


= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/archivepublisher/domination.py
  lib/lp/soyuz/scripts/gina/retire.py
  lib/lp/soyuz/model/publishing.py
  lib/lp/soyuz/interfaces/publishing.py
  scripts/gina.py
  lib/lp/archivepublisher/tests/test_dominator.py
  lib/lp/soyuz/scripts/tests/test_gina.py

./lib/lp/soyuz/interfaces/publishing.py
     381: E261 at least two spaces before inline comment
     478: E261 at least two spaces before inline comment
     511: E261 at least two spaces before inline comment
     681: E261 at least two spaces before inline comment
     767: E261 at least two spaces before inline comment
./scripts/gina.py
      26: '_pythonpath' imported but unused
-- 
https://code.launchpad.net/~jtv/launchpad/bug-832647/+merge/74380
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jtv/launchpad/bug-832647 into lp:launchpad.
=== modified file 'lib/lp/archivepublisher/domination.py'
--- lib/lp/archivepublisher/domination.py	2011-09-07 10:14:25 +0000
+++ lib/lp/archivepublisher/domination.py	2011-09-07 10:14:26 +0000
@@ -190,35 +190,72 @@
         self.logger = logger
         self.archive = archive
 
-    def _dominatePublications(self, pubs):
+    def dominatePackage(self, publications, live_versions, generalization):
+        """Dominate publications for a single package.
+
+        :param publications: Iterable of publications for the same package,
+            in the same archive, series, and pocket, all with status
+            `PackagePublishingStatus.PUBLISHED`.
+        :param live_versions: Iterable of version strings that are still
+            considered live for this package.  The given publications will
+            remain active insofar as they represent any of these versions;
+            the other publications will be marked superseded.
+        :param generalization: A `GeneralizedPublication` helper representing
+            the kind of publications these are--source or binary.
+        """
+        # Go through publications from latest version to oldest.  This
+        # makes it easy to figure out which release superseded which:
+        # the dominant is always the oldest live release that is newer
+        # than the one being superseded.
+        publications = sorted(
+            publications, cmp=generalization.compare, reverse=True)
+
+        current_dominant = None
+        for pub in publications:
+            if generalization.getPackageVersion(pub) in live_versions:
+                # This publication stays active; if any publications
+                # that follow right after this are to be superseded,
+                # this is the release that they are superseded by.
+                current_dominant = pub
+            elif current_dominant is None:
+                # This publication is no longer live, but there is no
+                # newer version to supersede it either.  Therefore it
+                # must be deleted.
+                pub.requestDeletion(None)
+            else:
+                # This publication is superseded.  This is what we're
+                # here to do.
+                pub.supersede(current_dominant, logger=self.logger)
+
+    def _dominatePublications(self, pubs, generalization):
         """Perform dominations for the given publications.
 
         :param pubs: A dict mapping names to a list of publications. Every
             publication must be PUBLISHED or PENDING, and the first in each
             list will be treated as dominant (so should be the latest).
+        :param generalization: A `GeneralizedPublication` helper representing
+            the kind of publications these are--source or binary.
         """
         self.logger.debug("Dominating packages...")
 
         for name, publications in pubs.iteritems():
             assert publications, "Empty list of publications for %s." % name
-            for pubrec in publications[1:]:
-                pubrec.supersede(publications[0], logger=self.logger)
+            latest_version = generalization.getPackageVersion(publications[0])
+            self.dominatePackage(
+                publications, [latest_version], generalization)
 
-    def _sortPackages(self, pkglist, is_source=True):
+    def _sortPackages(self, pkglist, generalization):
         """Map out packages by name, and sort by descending version.
 
         :param pkglist: An iterable of `SourcePackagePublishingHistory` or
             `BinaryPackagePublishingHistory`.
-        :param is_source: Whether this call involves source package
-            publications.  If so, work with `SourcePackagePublishingHistory`.
-            If not, work with `BinaryPackagepublishingHistory`.
+        :param generalization: A `GeneralizedPublication` helper representing
+            the kind of publications these are--source or binary.
         :return: A dict mapping each package name to a list of publications
             from `pkglist`, newest first.
         """
         self.logger.debug("Sorting packages...")
 
-        generalization = GeneralizedPublication(is_source)
-
         outpkgs = {}
         for inpkg in pkglist:
             key = generalization.getPackageName(inpkg)
@@ -353,6 +390,8 @@
         # Avoid circular imports.
         from lp.soyuz.model.publishing import BinaryPackagePublishingHistory
 
+        generalization = GeneralizedPublication(is_source=False)
+
         for distroarchseries in distroseries.architectures:
             self.logger.debug(
                 "Performing domination across %s/%s (%s)",
@@ -388,7 +427,8 @@
                     BinaryPackageFormat.DDEB,
                 bpph_location_clauses)
             self.logger.debug("Dominating binaries...")
-            self._dominatePublications(self._sortPackages(binaries, False))
+            self._dominatePublications(
+                self._sortPackages(binaries, generalization), generalization)
 
     def _composeActiveSourcePubsCondition(self, distroseries, pocket):
         """Compose ORM condition for restricting relevant source pubs."""
@@ -412,6 +452,8 @@
         # Avoid circular imports.
         from lp.soyuz.model.publishing import SourcePackagePublishingHistory
 
+        generalization = GeneralizedPublication(is_source=True)
+
         self.logger.debug(
             "Performing domination across %s/%s (Source)",
             distroseries.name, pocket.title)
@@ -422,7 +464,7 @@
             Count(SourcePackagePublishingHistory.id) > 1)
         candidate_source_names = Select(
             SourcePackageName.id,
-            And(join_spr_spn(), join_spph_spr(), spph_location_clauses),
+            And(join_spph_spr(), join_spr_spn(), spph_location_clauses),
             group_by=SourcePackageName.id,
             having=having_multiple_active_publications)
         sources = IStore(SourcePackagePublishingHistory).find(
@@ -433,9 +475,41 @@
             spph_location_clauses)
 
         self.logger.debug("Dominating sources...")
-        self._dominatePublications(self._sortPackages(sources))
+        self._dominatePublications(
+            self._sortPackages(sources, generalization), generalization)
         flush_database_updates()
 
+    def dominateRemovedSourceVersions(self, distroseries, pocket,
+                                      package_name, live_versions):
+        """Dominate source publications based on a set of "live" versions.
+
+        Active publications for the "live" versions will remain active.  All
+        other active publications for the same package (and the same archive,
+        distroseries, and pocket) are marked superseded.
+
+        Unlike traditional domination, this allows multiple versions of a
+        package to stay active in the same distroseries, archive, and pocket.
+
+        :param distroseries: `DistroSeries` to dominate.
+        :param pocket: `PackagePublishingPocket` to dominate.
+        :param package_name: Source package name, as text.
+        :param live_versions: Iterable of all version strings that are to
+            remain active.
+        """
+        # Avoid circular imports.
+        from lp.soyuz.model.publishing import SourcePackagePublishingHistory
+
+        generalization = GeneralizedPublication(is_source=True)
+
+        package_pubs = IStore(SourcePackagePublishingHistory).find(
+            SourcePackagePublishingHistory,
+            join_spph_spr(),
+            join_spr_spn(),
+            SourcePackageName.name == package_name,
+            self._composeActiveSourcePubsCondition(distroseries, pocket))
+
+        self.dominatePackage(package_pubs, live_versions, generalization)
+
     def judge(self, distroseries, pocket):
         """Judge superseded sources and binaries."""
         # Avoid circular imports.

=== modified file 'lib/lp/archivepublisher/tests/test_dominator.py'
--- lib/lp/archivepublisher/tests/test_dominator.py	2011-09-07 10:14:25 +0000
+++ lib/lp/archivepublisher/tests/test_dominator.py	2011-09-07 10:14:26 +0000
@@ -8,6 +8,7 @@
 import datetime
 
 import apt_pkg
+from zope.security.proxy import removeSecurityProxy
 
 from canonical.database.sqlbase import flush_database_updates
 from canonical.testing.layers import ZopelessDatabaseLayer
@@ -17,8 +18,11 @@
     STAY_OF_EXECUTION,
     )
 from lp.archivepublisher.publishing import Publisher
+from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
+from lp.services.log.logger import DevNullLogger
 from lp.soyuz.enums import PackagePublishingStatus
+from lp.soyuz.interfaces.publishing import ISourcePackagePublishingHistory
 from lp.soyuz.tests.test_publishing import TestNativePublishingBase
 from lp.testing import TestCaseWithFactory
 
@@ -58,6 +62,8 @@
                 foo_10_source, foo_10_binaries[0])
 
     def dominateAndCheck(self, dominant, dominated, supersededby):
+        generalization = GeneralizedPublication(
+            is_source=ISourcePackagePublishingHistory.providedBy(dominant))
         dominator = Dominator(self.logger, self.ubuntutest.main_archive)
 
         # The _dominate* test methods require a dictionary where the
@@ -66,7 +72,7 @@
         # and dominated, the subsequents.
         pubs = {'foo': [dominant, dominated]}
 
-        dominator._dominatePublications(pubs)
+        dominator._dominatePublications(pubs, generalization)
         flush_database_updates()
 
         # The dominant version remains correctly published.
@@ -153,7 +159,9 @@
         # This isn't a really good exception. It should probably be
         # something more indicative of bad input.
         self.assertRaises(
-            AssertionError, dominator._dominatePublications, pubs)
+            AssertionError,
+            dominator._dominatePublications,
+            pubs, GeneralizedPublication(True))
 
 
 class TestDomination(TestNativePublishingBase):
@@ -210,40 +218,41 @@
             SeriesStatus.OBSOLETE)
 
 
+def make_spphs_for_versions(factory, versions):
+    """Create publication records for each of `versions`.
+
+    They records are created in the same order in which they are specified.
+    Make the order irregular to prove that version ordering is not a
+    coincidence of object creation order etc.
+
+    Versions may also be identical; each publication record will still have
+    its own package release.
+    """
+    spn = factory.makeSourcePackageName()
+    distroseries = factory.makeDistroSeries()
+    pocket = factory.getAnyPocket()
+    sprs = [
+        factory.makeSourcePackageRelease(
+            sourcepackagename=spn, version=version)
+        for version in versions]
+    return [
+        factory.makeSourcePackagePublishingHistory(
+            distroseries=distroseries, pocket=pocket,
+            sourcepackagerelease=spr,
+            status=PackagePublishingStatus.PUBLISHED)
+        for spr in sprs]
+
+
+def list_source_versions(spphs):
+    """Extract the versions from `spphs` as a list, in the same order."""
+    return [spph.sourcepackagerelease.version for spph in spphs]
+
+
 class TestGeneralizedPublication(TestCaseWithFactory):
     """Test publication generalization helpers."""
 
     layer = ZopelessDatabaseLayer
 
-    def makeSPPHsForVersions(self, versions):
-        """Create publication records for each of `versions`.
-
-        They records are created in the same order in which they are
-        specified.  Make the order irregular to prove that version ordering
-        is not a coincidence of object creation order etc.
-
-        Versions may also be identical; each publication record will still
-        have its own package release.
-        """
-        distroseries = self.factory.makeDistroSeries()
-        pocket = self.factory.getAnyPocket()
-        sprs = [
-            self.factory.makeSourcePackageRelease(version=version)
-            for version in versions]
-        return [
-            self.factory.makeSourcePackagePublishingHistory(
-                distroseries=distroseries, pocket=pocket,
-                sourcepackagerelease=spr)
-            for spr in sprs]
-
-    def listSourceVersions(self, spphs):
-        """Extract the versions from `spphs` as a list, in the same order."""
-        return [spph.sourcepackagerelease.version for spph in spphs]
-
-    def listCreaitonDates(self, spphs):
-        """Extract creation dates from `spphs` into a list."""
-        return [spph.datecreated for spph in spphs]
-
     def alterCreationDates(self, spphs, ages):
         """Set `datecreated` on each of `spphs` according to `ages`.
 
@@ -275,11 +284,10 @@
             '1.1v1',
             '1.1v3',
             ]
-        spphs = self.makeSPPHsForVersions(versions)
+        spphs = make_spphs_for_versions(self.factory, versions)
         sorted_spphs = sorted(spphs, cmp=GeneralizedPublication().compare)
         self.assertEqual(
-            sorted(versions),
-            self.listSourceVersions(sorted_spphs))
+            sorted(versions), list_source_versions(sorted_spphs))
 
     def test_compare_orders_versions_by_debian_rules(self):
         versions = [
@@ -288,7 +296,7 @@
             '1.1',
             '1.1ubuntu0',
             ]
-        spphs = self.makeSPPHsForVersions(versions)
+        spphs = make_spphs_for_versions(self.factory, versions)
 
         debian_sorted_versions = sorted(versions, cmp=apt_pkg.VersionCompare)
 
@@ -300,7 +308,7 @@
         sorted_spphs = sorted(spphs, cmp=GeneralizedPublication().compare)
         self.assertEqual(
             sorted(versions, cmp=apt_pkg.VersionCompare),
-            self.listSourceVersions(sorted_spphs))
+            list_source_versions(sorted_spphs))
 
     def test_compare_breaks_tie_with_creation_date(self):
         # When two publications are tied for comparison because they are
@@ -348,3 +356,116 @@
         self.assertEqual(
             [spphs[2], spphs[0], spphs[1]],
             sorted(spphs, cmp=GeneralizedPublication().compare))
+
+
+class TestDominatorMethods(TestCaseWithFactory):
+
+    layer = ZopelessDatabaseLayer
+
+    def makeDominator(self, publications):
+        if len(publications) == 0:
+            archive = self.factory.makeArchive()
+        else:
+            archive = publications[0].archive
+        return Dominator(DevNullLogger(), archive)
+
+    def test_dominatePackage_survives_empty_publications_list(self):
+        # Nothing explodes when dominatePackage is called with an empty
+        # packages list.
+        self.makeDominator([]).dominatePackage(
+            [], [], GeneralizedPublication(True))
+        # The test is that we get here without error.
+        pass
+
+    def test_dominatePackage_leaves_live_version_untouched(self):
+        # dominatePackage does not supersede live versions.
+        [pub] = make_spphs_for_versions(self.factory, ['3.1'])
+        self.makeDominator([pub]).dominatePackage(
+            [pub], ['3.1'], GeneralizedPublication(True))
+        self.assertEqual(PackagePublishingStatus.PUBLISHED, pub.status)
+
+    def test_dominatePackage_deletes_dead_version_without_successor(self):
+        # dominatePackage marks non-live package versions without
+        # superseding versions as deleted.
+        [pub] = make_spphs_for_versions(self.factory, ['1.1'])
+        self.makeDominator([pub]).dominatePackage(
+            [pub], [], GeneralizedPublication(True))
+        self.assertEqual(PackagePublishingStatus.DELETED, pub.status)
+
+    def test_dominatePackage_supersedes_older_pub_with_newer_live_pub(self):
+        # When marking a package as superseded, dominatePackage
+        # designates a newer live version as the superseding version.
+        pubs = make_spphs_for_versions(self.factory, ['1.0', '1.1'])
+        self.makeDominator(pubs).dominatePackage(
+            pubs, ['1.1'], GeneralizedPublication(True))
+        self.assertEqual(PackagePublishingStatus.SUPERSEDED, pubs[0].status)
+        self.assertEqual(pubs[1].sourcepackagerelease, pubs[0].supersededby)
+        self.assertEqual(PackagePublishingStatus.PUBLISHED, pubs[1].status)
+
+    def test_dominatePackage_only_supersedes_with_live_pub(self):
+        # When marking a package as superseded, dominatePackage will
+        # only pick a live version as the superseding one.
+        pubs = make_spphs_for_versions(
+            self.factory, ['1.0', '2.0', '3.0', '4.0'])
+        self.makeDominator(pubs).dominatePackage(
+            pubs, ['3.0'], GeneralizedPublication(True))
+        self.assertEqual([
+                pubs[2].sourcepackagerelease,
+                pubs[2].sourcepackagerelease,
+                None,
+                None,
+                ],
+            [pub.supersededby for pub in pubs])
+
+    def test_dominatePackage_supersedes_with_oldest_newer_live_pub(self):
+        # When marking a package as superseded, dominatePackage picks
+        # the oldest of the newer, live versions as the superseding one.
+        pubs = make_spphs_for_versions(self.factory, ['2.7', '2.8', '2.9'])
+        self.makeDominator(pubs).dominatePackage(
+            pubs, ['2.8', '2.9'], GeneralizedPublication(True))
+        self.assertEqual(pubs[1].sourcepackagerelease, pubs[0].supersededby)
+
+    def test_dominatePackage_only_supersedes_with_newer_live_pub(self):
+        # When marking a package as superseded, dominatePackage only
+        # considers a newer version as the superseding one.
+        pubs = make_spphs_for_versions(self.factory, ['0.1', '0.2'])
+        self.makeDominator(pubs).dominatePackage(
+            pubs, ['0.1'], GeneralizedPublication(True))
+        self.assertEqual(None, pubs[1].supersededby)
+        self.assertEqual(PackagePublishingStatus.DELETED, pubs[1].status)
+
+    def test_dominateRemovedSourceVersions_dominates_publications(self):
+        # dominateRemovedSourceVersions finds the publications for a
+        # package and calls dominatePackage on them.
+        pubs = make_spphs_for_versions(self.factory, ['0.1', '0.2', '0.3'])
+        package_name = pubs[0].sourcepackagerelease.sourcepackagename.name
+
+        self.makeDominator(pubs).dominateRemovedSourceVersions(
+            pubs[0].distroseries, pubs[0].pocket, package_name, ['0.2'])
+        self.assertEqual([
+                PackagePublishingStatus.SUPERSEDED,
+                PackagePublishingStatus.PUBLISHED,
+                PackagePublishingStatus.DELETED,
+                ],
+            [pub.status for pub in pubs])
+        self.assertEqual(
+            [pubs[1].sourcepackagerelease, None, None],
+            [pub.supersededby for pub in pubs])
+
+    def test_dominateRemovedSourceVersions_ignores_other_pockets(self):
+        # dominateRemovedSourceVersions ignores publications in other
+        # pockets than the one specified.
+        pubs = make_spphs_for_versions(self.factory, ['2.3', '2.4'])
+        package_name = pubs[0].sourcepackagerelease.sourcepackagename.name
+        removeSecurityProxy(pubs[0]).pocket = PackagePublishingPocket.UPDATES
+        removeSecurityProxy(pubs[1]).pocket = PackagePublishingPocket.PROPOSED
+        self.makeDominator(pubs).dominateRemovedSourceVersions(
+            pubs[0].distroseries, pubs[0].pocket, package_name, ['2.3'])
+        self.assertEqual(PackagePublishingStatus.PUBLISHED, pubs[1].status)
+
+    def test_dominateRemovedSourceVersions_ignores_other_packages(self):
+        pubs = make_spphs_for_versions(self.factory, ['1.0', '1.1'])
+        other_package_name = self.factory.makeSourcePackageName().name
+        self.makeDominator(pubs).dominateRemovedSourceVersions(
+            pubs[0].distroseries, pubs[0].pocket, other_package_name, ['1.1'])
+        self.assertEqual(PackagePublishingStatus.PUBLISHED, pubs[0].status)

=== added file 'lib/lp/soyuz/scripts/gina/retire.py'
--- lib/lp/soyuz/scripts/gina/retire.py	1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/scripts/gina/retire.py	2011-09-07 10:14:26 +0000
@@ -0,0 +1,27 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Retirement of packages that are removed upstream."""
+
+__metaclass__ = type
+__all__ = [
+    'dominate_imported_source_packages',
+    ]
+
+from zope.component import getUtility
+
+from lp.archivepublisher.domination import Dominator
+from lp.registry.interfaces.distribution import IDistributionSet
+
+
+def dominate_imported_source_packages(logger, distro_name, series_name,
+                                      pocket, packages_map):
+    """Perform domination."""
+    series = getUtility(IDistributionSet)[distro_name].getSeries(series_name)
+    dominator = Dominator(logger, series.main_archive)
+    for package_name, entries in packages_map.src_map.iteritems():
+        live_versions = [
+            entry['Version']
+            for entry in entries if 'Version' in entry]
+        dominator.dominateRemovedSourceVersions(
+            series, pocket, package_name, live_versions)

=== modified file 'lib/lp/soyuz/scripts/tests/test_gina.py'
--- lib/lp/soyuz/scripts/tests/test_gina.py	2010-08-20 20:31:18 +0000
+++ lib/lp/soyuz/scripts/tests/test_gina.py	2011-09-07 10:14:26 +0000
@@ -1,13 +1,41 @@
-# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 from doctest import DocTestSuite
-import unittest
+from unittest import TestLoader
 
+from canonical.testing.layers import ZopelessDatabaseLayer
+from lp.services.log.logger import DevNullLogger
+from lp.soyuz.enums import PackagePublishingStatus
 import lp.soyuz.scripts.gina.handlers
+from lp.soyuz.scripts.gina.retire import dominate_imported_source_packages
+from lp.testing import TestCaseWithFactory
+
+
+class TestGina(TestCaseWithFactory):
+
+    layer = ZopelessDatabaseLayer
+
+    def test_dominate_imported_source_packages(self):
+
+        class SimpleFakePackagesMap:
+            def __init__(self, src_map):
+                self.src_map = src_map
+
+        logger = DevNullLogger()
+        pub = self.factory.makeSourcePackagePublishingHistory(
+            status=PackagePublishingStatus.PUBLISHED)
+        series = pub.distroseries
+        spr = pub.sourcepackagerelease
+        package = spr.sourcepackagename
+        packages_map = SimpleFakePackagesMap({package.name: []})
+        dominate_imported_source_packages(
+            logger, series.distribution.name, series.name, pub.pocket,
+            packages_map)
+        self.assertEqual(PackagePublishingStatus.DELETED, pub.status)
 
 
 def test_suite():
-    suite = unittest.TestSuite()
+    suite = TestLoader().loadTestsFromName(__name__)
     suite.addTest(DocTestSuite(lp.soyuz.scripts.gina.handlers))
     return suite

=== modified file 'scripts/gina.py'
--- scripts/gina.py	2011-09-07 10:14:25 +0000
+++ scripts/gina.py	2011-09-07 10:14:26 +0000
@@ -53,6 +53,7 @@
     PoolFileNotFound,
     SourcePackageData,
     )
+from lp.soyuz.scripts.gina.retire import dominate_imported_source_packages
 
 # Set to non-zero if you'd like to be warned every so often
 COUNTDOWN = 0
@@ -152,6 +153,10 @@
         packages_map, kdb, package_root, keyrings, importer_handler)
     importer_handler.commit()
 
+    # XXX JeroenVermeulen 2011-09-07 bug=843728: Dominate binaries as well.
+    dominate_imported_source_packages(
+        log, distro, distroseries, pocket, packages_map)
+
     if source_only:
         log.info('Source only mode... done')
         return


Follow ups