← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/remove-change-override-script into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/remove-change-override-script into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/remove-change-override-script/+merge/111116

== Summary ==

Now that *PPH.changeOverride is exported on the webservice, or is about to be as soon as https://code.launchpad.net/~cjwatson/launchpad/export-change-override/+merge/109549 is deployed, we no longer need the change-override.py script.

There are a few tests which are worth converting into model tests, and I've done that, but the rest are just about change-override.py's own publication selection and can therefore be dropped.  A little bit of ftpmasterbase.py (findLatestPublishedBinaries) becomes unused as a result of this change and can also be dropped, along with its tests.

== Tests ==

bin/test -vvct test_publishing.TestChangeOverride -t test_soyuzscript
-- 
https://code.launchpad.net/~cjwatson/launchpad/remove-change-override-script/+merge/111116
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/remove-change-override-script into lp:launchpad.
=== modified file 'lib/lp/soyuz/interfaces/publishing.py'
--- lib/lp/soyuz/interfaces/publishing.py	2012-06-19 10:16:01 +0000
+++ lib/lp/soyuz/interfaces/publishing.py	2012-06-19 22:49:26 +0000
@@ -72,7 +72,6 @@
 from lp.soyuz.interfaces.binarypackagerelease import (
     IBinaryPackageReleaseDownloadCount,
     )
-from lp.soyuz.scripts.ftpmasterbase import SoyuzScriptError
 
 #
 # Exceptions
@@ -105,9 +104,7 @@
 
 
 @error_status(httplib.BAD_REQUEST)
-# XXX cjwatson 2012-06-07: SoyuzScriptError should be changed to Exception
-# once lp.soyuz.scripts.changeoverride is removed.
-class OverrideError(SoyuzScriptError):
+class OverrideError(Exception):
     """Raised when an attempt to change an override fails."""
 
 

=== removed file 'lib/lp/soyuz/scripts/changeoverride.py'
--- lib/lp/soyuz/scripts/changeoverride.py	2012-06-07 17:56:14 +0000
+++ lib/lp/soyuz/scripts/changeoverride.py	1970-01-01 00:00:00 +0000
@@ -1,186 +0,0 @@
-# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Soyuz publication override script."""
-
-__metaclass__ = type
-
-__all__ = [
-    'ChangeOverride',
-    ]
-
-from zope.component import getUtility
-
-from lp.app.errors import NotFoundError
-from lp.soyuz.enums import PackagePublishingPriority
-from lp.soyuz.interfaces.component import IComponentSet
-from lp.soyuz.interfaces.section import ISectionSet
-from lp.soyuz.scripts.ftpmasterbase import (
-    SoyuzScript,
-    SoyuzScriptError,
-    )
-
-
-class ChangeOverride(SoyuzScript):
-
-    usage = '%prog -s <suite> <package name> [-SBt] [-c component]'
-    description = 'OVERRIDE a publication.'
-
-    def add_my_options(self):
-        self.add_transaction_options()
-        self.add_distro_options()
-        self.add_package_location_options()
-
-        self.parser.add_option(
-            "-p", "--priority", dest="priority",
-            help="move package to PRIORITY")
-        self.parser.add_option(
-            "-x", "--section", dest="section",
-            help="move package to SECTION")
-
-        self.parser.add_option(
-            "-S", "--source-and-binary", dest="sourceandchildren",
-            default=False, action="store_true",
-            help="select source and all binaries from this source")
-        self.parser.add_option(
-            "-B", "--binary-and-source", dest="binaryandsource",
-            default=False, action="store_true",
-            help="select source and binary (of the same name)")
-        self.parser.add_option(
-            "-t", "--source-only", dest="sourceonly",
-            default=False, action="store_true",
-            help="select source packages only")
-
-    def setupLocation(self):
-        SoyuzScript.setupLocation(self)
-        self.setupOverrides()
-
-    def setupOverrides(self):
-        """Convert override options into the corresponding DB values.
-
-        The results are stored as attributes of this object:
-
-         * 'component': IComponent or None;
-         * 'section': ISection or None;
-         * 'priority': PackagePublishingPriority or None.
-        """
-        if self.options.component is not None:
-            try:
-                self.component = getUtility(IComponentSet)[
-                    self.options.component]
-            except NotFoundError, err:
-                raise SoyuzScriptError(err)
-            self.logger.info(
-                "Override Component to: '%s'" % self.component.name)
-        else:
-            self.component = None
-
-        if self.options.section is not None:
-            try:
-                self.section = getUtility(ISectionSet)[
-                    self.options.section]
-            except NotFoundError, err:
-                raise SoyuzScriptError(err)
-            self.logger.info("Override Section to: '%s'" % self.section.name)
-        else:
-            self.section = None
-
-        if self.options.priority is not None:
-            try:
-                priority_name = self.options.priority.upper()
-                self.priority = PackagePublishingPriority.items[priority_name]
-            except KeyError, err:
-                raise SoyuzScriptError(err)
-            self.logger.info(
-                "Override Priority to: '%s'" % self.priority.name)
-        else:
-            self.priority = None
-
-    def _validatePublishing(self, currently_published):
-        """Do not validate found publications, because it's not necessary."""
-        pass
-
-    def mainTask(self):
-        """Dispatch override operations according togiven options.
-
-        Iterate over multiple targets given as command-line arguments.
-        """
-        assert self.location, (
-            "Location is not available, call PackageCopier.setupLocation() "
-            "before dealing with mainTask.")
-
-        for package_name in self.args:
-            # Change matching source.
-            if (self.options.sourceonly or self.options.binaryandsource or
-                self.options.sourceandchildren):
-                self.processSourceChange(package_name)
-
-            # Change all binaries for matching source.
-            if self.options.sourceandchildren:
-                self.processChildrenChange(package_name)
-            # Change only binary matching name.
-            elif not self.options.sourceonly:
-                self.processBinaryChange(package_name)
-
-    def processSourceChange(self, package_name):
-        """Perform changes in a given source package name.
-
-        It changes only the current published package release.
-        """
-        publication = self.findLatestPublishedSource(package_name)
-
-        override = publication.changeOverride(
-            new_component=self.component, new_section=self.section)
-
-        if override is None:
-            action_banner = "remained the same"
-        else:
-            action_banner = "source overridden"
-
-        self.logger.info("'%s/%s/%s' %s"
-                      % (publication.sourcepackagerelease.title,
-                         publication.component.name,
-                         publication.section.name, action_banner))
-
-    def processBinaryChange(self, package_name):
-        """Override the published binary version in the given context.
-
-        Receive a binary name and a distroarchseries, warns and return if
-        no published version could be found.
-        """
-        binaries = self.findLatestPublishedBinaries(package_name)
-        for binary in binaries:
-            override = binary.changeOverride(
-                new_component=self.component,
-                new_priority=self.priority,
-                new_section=self.section)
-
-            if override is None:
-                action_banner = "remained the same"
-            else:
-                distroarchseries_title = "%s/%s" % (
-                    override.distroarchseries.distroseries.name,
-                    override.distroarchseries.architecturetag)
-                action_banner = "binary overridden in %s" % (
-                    distroarchseries_title)
-
-            self.logger.info(
-                "'%s/%s/%s/%s' %s"
-                % (binary.binarypackagerelease.title,
-                   binary.component.name, binary.section.name,
-                   binary.priority.name, action_banner))
-
-    def processChildrenChange(self, package_name):
-        """Perform changes on all binary packages generated by this source.
-
-        Affects only the currently published release where the binary is
-        directly related to the source version.
-        """
-        source_pub = self.findLatestPublishedSource(package_name)
-
-        binary_names = set(
-            pub.binarypackagerelease.name
-            for pub in source_pub.getPublishedBinaries())
-
-        for binary_name in sorted(binary_names):
-            self.processBinaryChange(binary_name)

=== modified file 'lib/lp/soyuz/scripts/ftpmasterbase.py'
--- lib/lp/soyuz/scripts/ftpmasterbase.py	2011-03-03 00:43:44 +0000
+++ lib/lp/soyuz/scripts/ftpmasterbase.py	2012-06-19 22:49:26 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """FTPMaster base classes.
@@ -31,6 +31,7 @@
     The textual content should explain the error.
     """
 
+
 class SoyuzScript(LaunchpadScript):
     """`LaunchpadScript` extended for Soyuz related use.
 
@@ -162,53 +163,6 @@
         self._validatePublishing(latest_source)
         return latest_source
 
-    def findLatestPublishedBinaries(self, name):
-        """Build a list of suitable `BinaryPackagePublishingHistory`.
-
-        Try to find a group of binary package release matching the current
-        context. 'architecture' or 'version', if passed via command-line,
-        will restrict the lookup accordingly.
-        """
-        assert self.location is not None, 'Undefined location.'
-
-        # Avoiding circular imports.
-        from lp.soyuz.interfaces.publishing import active_publishing_status
-
-        target_binaries = []
-
-        if self.options.architecture is None:
-            architectures = self.location.distroseries.architectures
-        else:
-            try:
-                architectures = [
-                    self.location.distroseries[self.options.architecture]]
-            except NotFoundError, err:
-                raise SoyuzScriptError(err)
-
-        for architecture in architectures:
-            binaries = self.location.archive.getAllPublishedBinaries(
-                    name=name, version=self.options.version,
-                    status=active_publishing_status,
-                    distroarchseries=architecture,
-                    pocket=self.location.pocket,
-                    exact_match=True)
-            if not binaries:
-                continue
-            binary = binaries[0]
-            try:
-                self._validatePublishing(binary)
-            except SoyuzScriptError, err:
-                self.logger.warn(err)
-            else:
-                target_binaries.append(binary)
-
-        if not target_binaries:
-            raise SoyuzScriptError(
-                "Could not find binaries for '%s/%s' in %s" % (
-                name, self.options.version, self.location))
-
-        return target_binaries
-
     def _getUserConfirmation(self, full_question=None, valid_answers=None):
         """Use raw_input to collect user feedback.
 
@@ -307,6 +261,3 @@
     def mainTask(self):
         """Main task to be performed by the script"""
         raise NotImplementedError
-
-
-

=== removed file 'lib/lp/soyuz/scripts/tests/test_changeoverride.py'
--- lib/lp/soyuz/scripts/tests/test_changeoverride.py	2012-06-19 16:09:38 +0000
+++ lib/lp/soyuz/scripts/tests/test_changeoverride.py	1970-01-01 00:00:00 +0000
@@ -1,507 +0,0 @@
-# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""`ChangeOverride` script class tests."""
-
-__metaclass__ = type
-
-import unittest
-
-from zope.component import getUtility
-
-from lp.registry.interfaces.distribution import IDistributionSet
-from lp.registry.interfaces.person import IPersonSet
-from lp.registry.interfaces.pocket import PackagePublishingPocket
-from lp.registry.interfaces.series import SeriesStatus
-from lp.services.librarian.interfaces import ILibraryFileAliasSet
-from lp.services.log.logger import BufferLogger
-from lp.soyuz.enums import PackagePublishingPriority
-from lp.soyuz.interfaces.component import IComponentSet
-from lp.soyuz.interfaces.publishing import OverrideError
-from lp.soyuz.interfaces.section import ISectionSet
-from lp.soyuz.scripts.changeoverride import ChangeOverride
-from lp.soyuz.scripts.ftpmasterbase import SoyuzScriptError
-from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
-from lp.testing.layers import LaunchpadZopelessLayer
-
-
-class TestChangeOverride(unittest.TestCase):
-    layer = LaunchpadZopelessLayer
-
-    def setUp(self):
-        """`ChangeOverride` test environment setup.
-
-        Setup a `SoyuzTestPublisher` instance and ubuntu/warty/{i386, hppa}.
-        """
-        self.ubuntu = getUtility(IDistributionSet)['ubuntu']
-        self.warty = self.ubuntu.getSeries('warty')
-        self.warty_i386 = self.warty['i386']
-        self.warty_hppa = self.warty['hppa']
-
-        fake_chroot = getUtility(ILibraryFileAliasSet)[1]
-        self.warty_i386.addOrUpdateChroot(fake_chroot)
-        self.warty_hppa.addOrUpdateChroot(fake_chroot)
-
-        self.test_publisher = SoyuzTestPublisher()
-        self.test_publisher.person = getUtility(
-            IPersonSet).getByName("name16")
-
-    def getChanger(self, package_name='mozilla-firefox', package_version=None,
-                   distribution='ubuntu', suite='warty',
-                   arch_tag=None, component=None, section=None, priority=None,
-                   source_and_binary=False, binary_and_source=False,
-                   source_only=False, confirm_all=True):
-        """Return a `ChangeOverride` instance.
-
-        Allow tests to use a set of default options.
-        """
-        test_args = [
-            '-s', suite,
-            '-d', distribution,
-            ]
-
-        if confirm_all:
-            test_args.append('-y')
-
-        if source_and_binary:
-            test_args.append('-S')
-
-        if binary_and_source:
-            test_args.append('-B')
-
-        if source_only:
-            test_args.append('-t')
-
-        if package_version is not None:
-            test_args.extend(['-e', package_version])
-
-        if arch_tag is not None:
-            test_args.extend(['-a', arch_tag])
-
-        if component is not None:
-            test_args.extend(['-c', component])
-
-        if section is not None:
-            test_args.extend(['-x', section])
-
-        if priority is not None:
-            test_args.extend(['-p', priority])
-
-        test_args.extend(package_name.split())
-
-        changer = ChangeOverride(
-            name='change-override', test_args=test_args)
-        changer.logger = BufferLogger()
-        changer.setupLocation()
-        return changer
-
-    def test_changeoverride_initialize(self):
-        """ChangeOverride initialization process.
-
-        Check if the correct attributes are built after initialization.
-        """
-        changer = self.getChanger(
-            component="main", section="base", priority="extra")
-
-        # Processed location inherited from SoyuzScript.
-        self.assertEqual(
-            self.ubuntu, changer.location.distribution)
-        self.assertEqual(
-            self.warty, changer.location.distroseries)
-        self.assertEqual(
-            PackagePublishingPocket.RELEASE, changer.location.pocket)
-
-        # Resolved override values.
-        self.assertEqual(
-            getUtility(IComponentSet)['main'], changer.component)
-        self.assertEqual(
-            getUtility(ISectionSet)['base'], changer.section)
-        self.assertEqual(
-            PackagePublishingPriority.EXTRA, changer.priority)
-
-        # Overrides initialization output.
-        self.assertEqual(
-            changer.logger.getLogBuffer(),
-            "INFO Override Component to: 'main'\n"
-            "INFO Override Section to: 'base'\n"
-            "INFO Override Priority to: 'EXTRA'\n")
-
-    def patchedChanger(self, source_only=False, source_and_binary=False,
-                       binary_and_source=False, package_name='foo'):
-        """Return a patched `ChangeOverride` object.
-
-        All operations are modified to allow test tracing.
-        """
-        changer = self.getChanger(
-            component="main", section="base", priority="extra",
-            source_only=source_only, source_and_binary=source_and_binary,
-            binary_and_source=binary_and_source, package_name=package_name)
-
-        # Patched override operations.
-        def fakeProcessSourceChange(name):
-            changer.logger.info("Source change for '%s'" % name)
-
-        def fakeProcessBinaryChange(name):
-            changer.logger.info("Binary change for '%s'" % name)
-
-        def fakeProcessChildrenChange(name):
-            changer.logger.info("Children change for '%s'" % name)
-
-        # Patch the override operations.
-        changer.processSourceChange = fakeProcessSourceChange
-        changer.processBinaryChange = fakeProcessBinaryChange
-        changer.processChildrenChange = fakeProcessChildrenChange
-
-        # Consume the initialization logging.
-        changer.logger.clearLogBuffer()
-
-        return changer
-
-    def test_changeoverride_modes(self):
-        """Check `ChangeOverride` modes.
-
-        Confirm the expected behaviour of the change-override modes:
-
-         * Binary-only: default mode, only override binaries exactly matching
-              the given name;
-         * Source-only: activated via '-t', override only the matching source;
-         * Binary-and-source: activated via '-B', override source and binaries
-              exactly matching the given name.
-         * Source-and-binaries: activated via '-S', override the source
-              matching the given name and the binaries built from it.
-        """
-        changer = self.patchedChanger()
-        changer.mainTask()
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO Binary change for 'foo'\n")
-
-        changer = self.patchedChanger(source_only=True)
-        changer.mainTask()
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO Source change for 'foo'\n")
-
-        changer = self.patchedChanger(binary_and_source=True)
-        changer.mainTask()
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO Source change for 'foo'\n"
-            "INFO Binary change for 'foo'\n")
-
-        changer = self.patchedChanger(source_and_binary=True)
-        changer.mainTask()
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO Source change for 'foo'\n"
-            "INFO Children change for 'foo'\n")
-
-    def test_changeoverride_multiple_targets(self):
-        """`ChangeOverride` can operate on multiple targets.
-
-        It will perform the defined operation for all given command-line
-        arguments.
-        """
-        changer = self.patchedChanger(package_name='foo bar baz')
-        changer.mainTask()
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO Binary change for 'foo'\n"
-            "INFO Binary change for 'bar'\n"
-            "INFO Binary change for 'baz'\n")
-
-    def assertCurrentBinary(self, distroarchseries, name, version,
-                            component_name, section_name, priority_name):
-        """Assert if the current binary publication matches the given data."""
-        dasbpr = distroarchseries.getBinaryPackage(name)[version]
-        pub = dasbpr.current_publishing_record
-        self.assertTrue(pub.status.name in ['PUBLISHED', 'PENDING'])
-        self.assertEqual(pub.component.name, component_name)
-        self.assertEqual(pub.section.name, section_name)
-        self.assertEqual(pub.priority.name, priority_name)
-
-    def assertCurrentSource(self, distroseries, name, version,
-                            component_name, section_name):
-        """Assert if the current source publication matches the given data."""
-        dsspr = distroseries.getSourcePackage(name)[version]
-        pub = dsspr.current_published
-        self.assertTrue(pub.status.name in ['PUBLISHED', 'PENDING'])
-        self.assertEqual(pub.component.name, component_name)
-        self.assertEqual(pub.section.name, section_name)
-
-    def _setupOverridePublishingContext(self):
-        """Setup publishing context.
-
-         * 'boingo_1.0' source PENDING in ubuntu/warty;
-         * 'boingo-bin_1.0' binaries PENDING in warty i386 & hppa;
-         * 'boingo-data' binaries PENDING in warty i386 & hppa.
-        """
-        source = self.test_publisher.getPubSource(
-            sourcename="boingo", version='1.0', distroseries=self.warty)
-
-        binaries = self.test_publisher.getPubBinaries(
-            'boingo-bin', pub_source=source, distroseries=self.warty)
-
-        build = binaries[0].binarypackagerelease.build
-        other_binary = self.test_publisher.uploadBinaryForBuild(
-            build, 'boingo-data')
-        other_binary.version = '0.9'
-        binaries.extend(
-            self.test_publisher.publishBinaryInArchive(
-                other_binary, source.archive))
-
-    def test_changeoverride_operations(self):
-        """Check if `IArchivePublisher.changeOverride` is wrapped correctly.
-
-        `ChangeOverride` allow three types of override operations:
-
-         * Source-only overrides: `processSourceChange`;
-         * Binary-only overrides: `processBinaryChange`;
-         * Source-children overrides: `processChildrenChange`;
-
-        This test checks the expected behaviour for each of them.
-        """
-        self._setupOverridePublishingContext()
-        self.warty.status = SeriesStatus.DEVELOPMENT
-
-        changer = self.getChanger(
-            component="universe", section="web", priority='extra')
-        changer.logger.clearLogBuffer()
-
-        # Override the source.
-        changer.processSourceChange('boingo')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo - 1.0/main/base' source overridden\n")
-        self.assertCurrentSource(
-            self.warty, 'boingo', '1.0', 'universe', 'web')
-
-        # Override the binaries.
-        changer.processBinaryChange('boingo-bin')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo-bin-1.0/main/base/STANDARD' binary "
-                "overridden in warty/hppa\n"
-            "INFO 'boingo-bin-1.0/main/base/STANDARD' binary "
-                "overridden in warty/i386\n")
-        self.assertCurrentBinary(
-            self.warty_i386, 'boingo-bin', '1.0', 'universe', 'web', 'EXTRA')
-        self.assertCurrentBinary(
-            self.warty_hppa, 'boingo-bin', '1.0', 'universe', 'web', 'EXTRA')
-
-        # Override the source children.
-        changer.processChildrenChange('boingo')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo-bin-1.0/universe/web/EXTRA' remained the same\n"
-            "INFO 'boingo-bin-1.0/universe/web/EXTRA' remained the same\n"
-            "INFO 'boingo-data-0.9/main/base/STANDARD' binary "
-                "overridden in warty/hppa\n"
-            "INFO 'boingo-data-0.9/main/base/STANDARD' binary "
-                "overridden in warty/i386\n")
-        self.assertCurrentBinary(
-            self.warty_i386, 'boingo-data', '0.9', 'universe', 'web', 'EXTRA')
-        self.assertCurrentBinary(
-            self.warty_hppa, 'boingo-data', '0.9', 'universe', 'web', 'EXTRA')
-
-    def test_changeoverride_restricted_by_pocket(self):
-        """`ChangeOverride` operation can be restricted by pocket.
-
-        This behaviour is inherited from `SoyuzScript`.
-        """
-        # Create publications for 'boingo' source and 'boingo-bin' in
-        # warty-security.
-        source = self.test_publisher.getPubSource(
-            sourcename="boingo", version='0.8', distroseries=self.warty,
-            pocket=PackagePublishingPocket.SECURITY)
-        self.test_publisher.getPubBinaries(
-            'boingo-bin', pub_source=source, distroseries=self.warty,
-            pocket=PackagePublishingPocket.SECURITY)
-
-        # Create the default publishing context as 'noise' in order to
-        # test if `ChangeOverride` filters it out properly.
-        self._setupOverridePublishingContext()
-
-        changer = self.getChanger(
-            suite='warty-security', component="universe", section="web",
-            priority='extra')
-        changer.logger.clearLogBuffer()
-
-        # Override the security source and its binaries.
-        changer.processSourceChange('boingo')
-        changer.processChildrenChange('boingo')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo - 0.8/main/base' source overridden\n"
-            "INFO 'boingo-bin-0.8/main/base/STANDARD' binary "
-                "overridden in warty/hppa\n"
-            "INFO 'boingo-bin-0.8/main/base/STANDARD' binary "
-                "overridden in warty/i386\n")
-
-        # Use a more precise lookup approach to reach and verify the
-        # overridden security publications.
-        security_source = changer.findLatestPublishedSource('boingo')
-        self.assertEqual(
-            security_source.sourcepackagerelease.version, '0.8')
-        self.assertEqual(security_source.status.name, 'PENDING')
-        self.assertEqual(security_source.pocket.name, 'SECURITY')
-        self.assertEqual(security_source.component.name, 'universe')
-        self.assertEqual(security_source.section.name, 'web')
-
-        security_binaries = changer.findLatestPublishedBinaries('boingo-bin')
-        for security_binary in security_binaries:
-            self.assertEqual(
-                security_binary.binarypackagerelease.version, '0.8')
-            self.assertEqual(security_binary.status.name, 'PENDING')
-            self.assertEqual(security_binary.pocket.name, 'SECURITY')
-            self.assertEqual(security_binary.component.name, 'universe')
-            self.assertEqual(security_binary.section.name, 'web')
-            self.assertEqual(security_binary.priority.name, 'EXTRA')
-
-    def test_changeoverride_restricted_by_version(self):
-        """`ChangeOverride` operation can be restricted to a version.
-
-        This behaviour is inherited from `SoyuzScript`.
-        """
-        self._setupOverridePublishingContext()
-        self.warty.status = SeriesStatus.DEVELOPMENT
-        changer = self.getChanger(
-            component="universe", section="web", priority='extra',
-            package_version='0.9')
-        changer.logger.clearLogBuffer()
-
-        # No 'boingo_0.9' source available.
-        self.assertRaises(
-            SoyuzScriptError, changer.processSourceChange, 'boingo')
-        self.assertRaises(
-            SoyuzScriptError, changer.processChildrenChange, 'boingo')
-
-        # No 'boingo-bin_0.9' binary available.
-        self.assertRaises(
-            SoyuzScriptError, changer.processBinaryChange, 'boingo-bin')
-
-        # 'boingo-data_0.9' is available and will be overridden.
-        changer.processBinaryChange('boingo-data')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo-data-0.9/main/base/STANDARD' binary "
-                "overridden in warty/hppa\n"
-            "INFO 'boingo-data-0.9/main/base/STANDARD' binary "
-                "overridden in warty/i386\n")
-        self.assertCurrentBinary(
-            self.warty_i386, 'boingo-data', '0.9', 'universe', 'web', 'EXTRA')
-        self.assertCurrentBinary(
-            self.warty_hppa, 'boingo-data', '0.9', 'universe', 'web', 'EXTRA')
-
-    def test_changeoverride_restricted_by_architecture(self):
-        """`ChangeOverride` operation can be restricted to an architecture.
-
-        This behaviour is inherited from `SoyuzScript`.
-        """
-        self._setupOverridePublishingContext()
-        self.warty.status = SeriesStatus.DEVELOPMENT
-        changer = self.getChanger(
-            component="universe", section="web", priority='extra',
-            arch_tag='i386')
-        changer.logger.clearLogBuffer()
-
-        # Source overrides are not affect by architecture restriction.
-        changer.processSourceChange('boingo')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo - 1.0/main/base' source overridden\n")
-        self.assertCurrentSource(
-            self.warty, 'boingo', '1.0', 'universe', 'web')
-
-        # Binary overrides are restricted by architecture.
-        changer.processBinaryChange('boingo-bin')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo-bin-1.0/main/base/STANDARD' binary "
-                "overridden in warty/i386\n")
-        self.assertCurrentBinary(
-            self.warty_i386, 'boingo-bin', '1.0', 'universe', 'web', 'EXTRA')
-        self.assertCurrentBinary(
-            self.warty_hppa, 'boingo-bin', '1.0', 'main', 'base', 'STANDARD')
-
-        # Source-children overrides are also restricted by architecture.
-        changer.processChildrenChange('boingo')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo-bin-1.0/universe/web/EXTRA' remained the same\n"
-            "INFO 'boingo-data-0.9/main/base/STANDARD' binary "
-                "overridden in warty/i386\n")
-        self.assertCurrentBinary(
-            self.warty_i386, 'boingo-data', '0.9', 'universe', 'web', 'EXTRA')
-        self.assertCurrentBinary(
-            self.warty_hppa, 'boingo-data', '0.9', 'main', 'base', 'STANDARD')
-
-    def test_changeoverride_no_change(self):
-        """Override source and/or binary already in the desired state.
-
-        Nothing is done and the event is logged.
-        """
-        self._setupOverridePublishingContext()
-
-        changer = self.getChanger(
-            component="main", section="base", priority='standard')
-        changer.logger.clearLogBuffer()
-
-        changer.processSourceChange('boingo')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo - 1.0/main/base' remained the same\n")
-        self.assertCurrentSource(
-            self.warty, 'boingo', '1.0', 'main', 'base')
-
-        changer.processBinaryChange('boingo-bin')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo-bin-1.0/main/base/STANDARD' remained the same\n"
-            "INFO 'boingo-bin-1.0/main/base/STANDARD' remained the same\n")
-        self.assertCurrentBinary(
-            self.warty_i386, 'boingo-bin', '1.0', 'main', 'base', 'STANDARD')
-        self.assertCurrentBinary(
-            self.warty_hppa, 'boingo-bin', '1.0', 'main', 'base', 'STANDARD')
-
-        changer.processChildrenChange('boingo')
-        self.assertEqual(
-            changer.logger.getLogBufferAndClear(),
-            "INFO 'boingo-bin-1.0/main/base/STANDARD' remained the same\n"
-            "INFO 'boingo-bin-1.0/main/base/STANDARD' remained the same\n"
-            "INFO 'boingo-data-0.9/main/base/STANDARD' remained the same\n"
-            "INFO 'boingo-data-0.9/main/base/STANDARD' remained the same\n")
-        self.assertCurrentBinary(
-            self.warty_i386, 'boingo-data', '0.9', 'main', 'base', 'STANDARD')
-        self.assertCurrentBinary(
-            self.warty_hppa, 'boingo-data', '0.9', 'main', 'base', 'STANDARD')
-
-    def test_overrides_with_changed_archive(self):
-        """Overrides resulting in archive changes are not allowed.
-
-        Changing the component to 'partner' will result in the archive
-        changing on the publishing record.
-        """
-        self._setupOverridePublishingContext()
-
-        changer = self.getChanger(
-            component="partner", section="base", priority="extra")
-
-        self.assertRaises(
-            OverrideError, changer.processSourceChange, 'boingo')
-        self.assertRaises(
-            OverrideError, changer.processBinaryChange, 'boingo-bin')
-        self.assertRaises(
-            OverrideError, changer.processChildrenChange, 'boingo')
-
-    def test_target_publication_not_found(self):
-        """Raises SoyuzScriptError when a source was not found."""
-        changer = self.getChanger(
-            component="main", section="base", priority="extra")
-
-        self.assertRaises(
-            SoyuzScriptError, changer.processSourceChange, 'foobar')
-        self.assertRaises(
-            SoyuzScriptError, changer.processBinaryChange, 'biscuit')
-        self.assertRaises(
-            SoyuzScriptError, changer.processChildrenChange, 'cookie')

=== modified file 'lib/lp/soyuz/scripts/tests/test_soyuzscript.py'
--- lib/lp/soyuz/scripts/tests/test_soyuzscript.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/scripts/tests/test_soyuzscript.py	2012-06-19 22:49:26 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Test the SoyuzScript base class.
@@ -135,91 +135,6 @@
         self.assertRaises(
             SoyuzScriptError, soyuz.findLatestPublishedSource, 'pmount')
 
-    def testFindLatestPublishedBinariesInPRIMARY(self):
-        """Binary lookups in PRIMARY archive."""
-        soyuz = self.getSoyuz()
-        binaries = soyuz.findLatestPublishedBinaries('pmount')
-        self.assertEqual(
-            [b.displayname for b in binaries],
-            ['pmount 2:1.9-1 in hoary hppa', 'pmount 0.1-1 in hoary i386'])
-
-        self.assertRaises(
-            SoyuzScriptError, soyuz.findLatestPublishedBinaries, 'marvin')
-
-        soyuz = self.getSoyuz(suite='warty-security')
-        self.assertRaises(
-            SoyuzScriptError, soyuz.findLatestPublishedBinaries, 'pmount')
-
-    def testFindLatestPublishedBinariesInPARTNER(self):
-        """Binary lookups in PARTNER archive."""
-        soyuz = self.getSoyuz(suite='breezy-autotest', partner=True)
-        binaries = soyuz.findLatestPublishedBinaries('commercialpackage')
-        self.assertEqual(
-            [b.displayname for b in binaries],
-            ['commercialpackage 1.0-1 in breezy-autotest i386'])
-
-        self.assertRaises(
-            SoyuzScriptError, soyuz.findLatestPublishedBinaries, 'marvin')
-
-        soyuz = self.getSoyuz(suite='warty-security')
-        self.assertRaises(
-            SoyuzScriptError, soyuz.findLatestPublishedBinaries,
-            'commercialpackage')
-
-    def testFindLatestPublishedBinariesInPPA(self):
-        """Binary lookups in PPAs."""
-        soyuz = self.getSoyuz(ppa='cprov', suite='warty')
-        binaries = soyuz.findLatestPublishedBinaries('pmount')
-        self.assertEqual(
-            [b.displayname for b in binaries],
-            ['pmount 0.1-1 in warty hppa', 'pmount 0.1-1 in warty i386'])
-
-        self.assertRaises(
-            SoyuzScriptError, soyuz.findLatestPublishedBinaries, 'marvin')
-
-        soyuz = self.getSoyuz(ppa='cprov', suite='warty-security')
-        self.assertRaises(
-            SoyuzScriptError, soyuz.findLatestPublishedBinaries, 'pmount')
-
-    def testFindLatestPublishedBinariesCheckComponent(self):
-        """Each suitable binary publication component is checked.
-
-        For each one of them not matching the given component a warning
-        message is issued. If none of them match the given component (no
-        suitable binary found) an errors is raised.
-        """
-        soyuz = self.getSoyuz(component='main')
-        binaries = soyuz.findLatestPublishedBinaries('pmount')
-        self.assertEqual(
-            [b.displayname for b in binaries],
-            ['pmount 2:1.9-1 in hoary hppa'])
-        self.assertEqual(
-            soyuz.logger.getLogBuffer(),
-            'WARNING pmount 0.1-1 in hoary i386 was skipped '
-            'because it is not in MAIN component\n')
-
-        soyuz = self.getSoyuz(component='multiverse')
-        self.assertRaises(
-            SoyuzScriptError, soyuz.findLatestPublishedBinaries, 'pmount')
-
-    def testFindLatestPublishedBinariesWithSpecificVersion(self):
-        """Binary lookups for specific version."""
-        soyuz = self.getSoyuz(version='0.1-1')
-        binaries = soyuz.findLatestPublishedBinaries('pmount')
-        self.assertEqual(
-            [b.displayname for b in binaries],
-            ['pmount 0.1-1 in hoary i386'])
-
-        soyuz = self.getSoyuz(version='2:1.9-1')
-        binaries = soyuz.findLatestPublishedBinaries('pmount')
-        self.assertEqual(
-            [b.displayname for b in binaries],
-            ['pmount 2:1.9-1 in hoary hppa'])
-
-        soyuz = self.getSoyuz(version='666')
-        self.assertRaises(
-            SoyuzScriptError, soyuz.findLatestPublishedBinaries, 'pmount')
-
     def testFinishProcedure(self):
         """Make sure finishProcedure returns the correct boolean."""
         soyuz = self.getSoyuz()

=== modified file 'lib/lp/soyuz/tests/test_publishing.py'
--- lib/lp/soyuz/tests/test_publishing.py	2012-06-19 16:09:38 +0000
+++ lib/lp/soyuz/tests/test_publishing.py	2012-06-19 22:49:26 +0000
@@ -1652,40 +1652,78 @@
 class TestChangeOverride(TestNativePublishingBase):
     """Test that changing overrides works."""
 
-    def setUpOverride(self, status, pocket=PackagePublishingPocket.RELEASE,
-                      binary=False):
+    def setUpOverride(self, status=SeriesStatus.DEVELOPMENT,
+                      pocket=PackagePublishingPocket.RELEASE, binary=False,
+                      **kwargs):
         self.distroseries.status = status
         if binary:
             pub = self.getPubBinaries(pocket=pocket)[0]
         else:
             pub = self.getPubSource(pocket=pocket)
-        universe = getUtility(IComponentSet)["universe"]
-        return pub.changeOverride(new_component=universe)
-
-    def assertCanOverride(self, *args, **kwargs):
-        new_pub = self.setUpOverride(*args, **kwargs)
-        self.assertEqual("universe", new_pub.component.name)
-
-    def assertCannotOverride(self, *args, **kwargs):
-        self.assertRaises(OverrideError, self.setUpOverride, *args, **kwargs)
-
-    def test_changeOverride_forbids_stable_RELEASE(self):
+        return pub.changeOverride(**kwargs)
+
+    def assertCanOverride(self, status=SeriesStatus.DEVELOPMENT,
+                          pocket=PackagePublishingPocket.RELEASE, **kwargs):
+        new_pub = self.setUpOverride(status=status, pocket=pocket, **kwargs)
+        self.assertEqual(new_pub.status, PackagePublishingStatus.PENDING)
+        self.assertEqual(new_pub.pocket, pocket)
+        if "new_component" in kwargs:
+            self.assertEqual(kwargs["new_component"], new_pub.component.name)
+        if "new_section" in kwargs:
+            self.assertEqual(kwargs["new_section"], new_pub.section.name)
+        if "new_priority" in kwargs:
+            self.assertEqual(
+                kwargs["new_priority"], new_pub.priority.name.lower())
+
+    def assertCannotOverride(self, **kwargs):
+        self.assertRaises(OverrideError, self.setUpOverride, **kwargs)
+
+    def test_changes_source(self):
+        # SPPH.changeOverride changes the properties of source publications.
+        self.assertCanOverride(new_component="universe", new_section="misc")
+
+    def test_changes_binary(self):
+        # BPPH.changeOverride changes the properties of binary publications.
+        self.assertCanOverride(
+            binary=True,
+            new_component="universe", new_section="misc", new_priority="extra")
+
+    def test_no_change(self):
+        # changeOverride does not create a new publication if the existing
+        # publication is already in the desired state.
+        self.assertIsNone(self.setUpOverride(
+            new_component="main", new_section="base"))
+        self.assertIsNone(self.setUpOverride(
+            binary=True,
+            new_component="main", new_section="base", new_priority="standard"))
+
+    def test_forbids_stable_RELEASE(self):
         # changeOverride is not allowed in the RELEASE pocket of a stable
         # distroseries.
-        self.assertCannotOverride(SeriesStatus.CURRENT)
-        self.assertCannotOverride(SeriesStatus.CURRENT, binary=True)
+        self.assertCannotOverride(
+            status=SeriesStatus.CURRENT, new_component="universe")
+        self.assertCannotOverride(
+            status=SeriesStatus.CURRENT, binary=True, new_component="universe")
 
-    def test_changeOverride_allows_development_RELEASE(self):
+    def test_allows_development_RELEASE(self):
         # changeOverride is allowed in the RELEASE pocket of a development
         # distroseries.
-        self.assertCanOverride(SeriesStatus.DEVELOPMENT)
-        self.assertCanOverride(SeriesStatus.DEVELOPMENT, binary=True)
+        self.assertCanOverride(new_component="universe")
+        self.assertCanOverride(binary=True, new_component="universe")
 
-    def test_changeOverride_allows_stable_PROPOSED(self):
+    def test_allows_stable_PROPOSED(self):
         # changeOverride is allowed in the PROPOSED pocket of a stable
         # distroseries.
         self.assertCanOverride(
-            SeriesStatus.CURRENT, pocket=PackagePublishingPocket.PROPOSED)
+            status=SeriesStatus.CURRENT,
+            pocket=PackagePublishingPocket.PROPOSED, new_component="universe")
         self.assertCanOverride(
-            SeriesStatus.CURRENT, pocket=PackagePublishingPocket.PROPOSED,
-            binary=True)
+            status=SeriesStatus.CURRENT,
+            pocket=PackagePublishingPocket.PROPOSED, binary=True,
+            new_component="universe")
+
+    def test_forbids_changing_archive(self):
+        # changeOverride refuses to make changes that would require changing
+        # archive.
+        self.assertCannotOverride(new_component="partner")
+        self.assertCannotOverride(binary=True, new_component="partner")

=== removed file 'scripts/ftpmaster-tools/change-override.py'
--- scripts/ftpmaster-tools/change-override.py	2012-01-01 03:13:08 +0000
+++ scripts/ftpmaster-tools/change-override.py	1970-01-01 00:00:00 +0000
@@ -1,24 +0,0 @@
-#!/usr/bin/python -S
-#
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-# Stop lint warning about relative import:
-# pylint: disable-msg=W0403
-
-"""Change the component of a package.
-
-This tool allows you to change the component of a package.  Changes won't
-take affect till the next publishing run.
-"""
-
-import _pythonpath
-
-from lp.services.config import config
-from lp.soyuz.scripts.changeoverride import ChangeOverride
-
-
-if __name__ == '__main__':
-    script = ChangeOverride(
-        'change-override', dbuser=config.archivepublisher.dbuser)
-    script.lock_and_run()


Follow ups