← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/copy-custom-uploads into lp:launchpad

 

You have been requested to review the proposed merge of lp:~cjwatson/launchpad/copy-custom-uploads into lp:launchpad.

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/copy-custom-uploads/+merge/111653

== Summary ==

One of the last bugs that cause Ubuntu archive administrators to still require access to lp_publish@cocoplum (a horrendously privileged account) is bug 231371: if we upload installer or upgrader images to -proposed, get them past verification, and copy them to -updates, we have to copy the custom files around by hand.  At the moment we have a pile of manual instructions in https://wiki.ubuntu.com/ArchiveAdministration#Moving_Packages_to_Updates for this, but obviously this should be automated.

== Proposed fix ==

A while back, Jeroen wrote CustomUploadsCopier, which deals with copying custom files across when we open a new distroseries.  I always rather suspected that it wouldn't be too difficult to hook it up to solve this problem too, and it looks as though I was right. :-)

This does make one aspect of direct copies behave a little bit like delayed copies in that they synthesise an upload, and I considered making this case trigger a delayed copy, but consensus seems to be that delayed copies should be removed at the earliest opportunity, and I can't say I disagree.  Besides, CustomUploadsCopier.copyUpload does enough of the work for us that I think it would actually be more code to reuse delayed copies for this.

== LOC Rationale ==

I got it down to +66 with a bit of judicious tidying up of unit tests.  Aside from that, this is part of the general arc of tidying up package copying described in https://code.launchpad.net/~cjwatson/launchpad/pcj-reupload/+merge/111124; as indicated there, even with the addition of this branch that's going to come out substantially LoC-negative.

== Tests ==

bin/test -vvct test_copypackage -t test_custom_uploads_copier -t test_factory -t test_publish_ftpmaster

== Demo and Q/A ==

This will involve two publisher runs on dogfood, so it won't be exactly fast, but it shouldn't be difficult.  Upload debian-installer to precise-proposed on dogfood, let it build, publish it, then use Archive.copyPackage (or 'sru-release -l dogfood' from lp:ubuntu-archive-tools) to copy it to precise-updates.  This should create the appropriate versioned subdirectories and "current" symlinks in dists/precise-updates/main/installer-*/.

== Lint ==

Just one pre-existing and not very interesting entry:

./lib/lp/soyuz/scripts/tests/test_copypackage.py
    1450: E501 line too long (82 characters)

-- 
https://code.launchpad.net/~cjwatson/launchpad/copy-custom-uploads/+merge/111653
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/copy-custom-uploads into lp:launchpad.
=== modified file 'lib/lp/archivepublisher/scripts/publish_ftpmaster.py'
--- lib/lp/archivepublisher/scripts/publish_ftpmaster.py	2012-05-23 00:20:23 +0000
+++ lib/lp/archivepublisher/scripts/publish_ftpmaster.py	2012-06-22 18:04:24 +0000
@@ -18,7 +18,10 @@
 from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
 from lp.archivepublisher.publishing import GLOBAL_PUBLISHER_LOCK
 from lp.registry.interfaces.distribution import IDistributionSet
-from lp.registry.interfaces.pocket import pocketsuffix
+from lp.registry.interfaces.pocket import (
+    PackagePublishingPocket,
+    pocketsuffix,
+    )
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.config import config
 from lp.services.database.bulk import load_related
@@ -588,7 +591,11 @@
                 # This is a fresh series.
                 have_fresh_series = True
                 if series.previous_series is not None:
-                    CustomUploadsCopier(series).copy(series.previous_series)
+                    copier = CustomUploadsCopier(
+                        series, PackagePublishingPocket.RELEASE)
+                    copier.copy(
+                        series.previous_series,
+                        PackagePublishingPocket.RELEASE)
                 self.createIndexes(distribution, suites_needing_indexes)
 
         return have_fresh_series

=== modified file 'lib/lp/soyuz/scripts/custom_uploads_copier.py'
--- lib/lp/soyuz/scripts/custom_uploads_copier.py	2012-05-30 10:25:43 +0000
+++ lib/lp/soyuz/scripts/custom_uploads_copier.py	2012-06-22 18:04:24 +0000
@@ -42,17 +42,20 @@
         PackageUploadCustomFormat.DIST_UPGRADER: DistUpgraderUpload,
         }
 
-    def __init__(self, target_series):
+    def __init__(self, target_series,
+                 target_pocket=PackagePublishingPocket.RELEASE):
         self.target_series = target_series
+        self.target_pocket = target_pocket
 
     def isCopyable(self, upload):
         """Is `upload` the kind of `PackageUploadCustom` that we can copy?"""
         return upload.customformat in self.copyable_types
 
-    def getCandidateUploads(self, source_series):
+    def getCandidateUploads(self, source_series,
+                            source_pocket=PackagePublishingPocket.RELEASE):
         """Find custom uploads that may need copying."""
         uploads = source_series.getPackageUploads(
-            custom_type=self.copyable_types.keys())
+            pocket=source_pocket, custom_type=self.copyable_types.keys())
         load_referencing(PackageUploadCustom, uploads, ['packageuploadID'])
         customs = sum([list(upload.customfiles) for upload in uploads], [])
         customs = filter(self.isCopyable, customs)
@@ -77,15 +80,19 @@
         else:
             return (custom_format, series_key)
 
-    def getLatestUploads(self, source_series):
+    def getLatestUploads(self, source_series,
+                         source_pocket=PackagePublishingPocket.RELEASE):
         """Find the latest uploads.
 
         :param source_series: The `DistroSeries` whose uploads to get.
+        :param source_pocket: The `PackagePublishingPocket` to inspect.
         :return: A dict containing the latest uploads, indexed by keys as
             returned by `getKey`.
         """
+        candidate_uploads = self.getCandidateUploads(
+            source_series, source_pocket=source_pocket)
         latest_uploads = {}
-        for upload in self.getCandidateUploads(source_series):
+        for upload in candidate_uploads:
             key = self.getKey(upload)
             if key is not None:
                 latest_uploads.setdefault(key, upload)
@@ -120,16 +127,20 @@
         if target_archive is None:
             return None
         package_upload = self.target_series.createQueueEntry(
-            PackagePublishingPocket.RELEASE, target_archive,
+            self.target_pocket, target_archive,
             changes_file_alias=original_upload.packageupload.changesfile)
         custom = package_upload.addCustom(
             original_upload.libraryfilealias, original_upload.customformat)
         package_upload.setAccepted()
         return custom
 
-    def copy(self, source_series):
-        """Copy uploads from `source_series`."""
-        target_uploads = self.getLatestUploads(self.target_series)
-        for upload in self.getLatestUploads(source_series).itervalues():
+    def copy(self, source_series,
+             source_pocket=PackagePublishingPocket.RELEASE):
+        """Copy uploads from `source_series`-`source_pocket`."""
+        target_uploads = self.getLatestUploads(
+            self.target_series, source_pocket=self.target_pocket)
+        source_uploads = self.getLatestUploads(
+            source_series, source_pocket=source_pocket)
+        for upload in source_uploads.itervalues():
             if not self.isObsolete(upload, target_uploads):
                 self.copyUpload(upload)

=== modified file 'lib/lp/soyuz/scripts/packagecopier.py'
--- lib/lp/soyuz/scripts/packagecopier.py	2012-05-21 07:34:15 +0000
+++ lib/lp/soyuz/scripts/packagecopier.py	2012-06-22 18:04:24 +0000
@@ -48,6 +48,7 @@
     IPackageUploadCustom,
     IPackageUploadSet,
     )
+from lp.soyuz.scripts.custom_uploads_copier import CustomUploadsCopier
 from lp.soyuz.scripts.ftpmasterbase import (
     SoyuzScript,
     SoyuzScriptError,
@@ -715,6 +716,7 @@
         publications.
     """
     copies = []
+    custom_files = []
 
     # Copy source if it's not yet copied.
     source_in_destination = archive.getPublishedSources(
@@ -746,21 +748,33 @@
         copies.append(source_copy)
     else:
         source_copy = source_in_destination.first()
-
-    if not include_binaries:
-        source_copy.createMissingBuilds()
-        return copies
-
-    # Copy missing binaries for the matching architectures in the
-    # destination series. ISPPH.getBuiltBinaries() return only
-    # unique publication per binary package releases (i.e. excludes
-    # irrelevant arch-indep publications) and IBPPH.copy is prepared
-    # to expand arch-indep publications.
-    binary_copies = getUtility(IPublishingSet).copyBinariesTo(
-        source.getBuiltBinaries(), series, pocket, archive, policy=policy)
-
-    if binary_copies is not None:
-        copies.extend(binary_copies)
+    if source_copy.packageupload is not None:
+        custom_files.extend(source_copy.packageupload.customfiles)
+
+    if include_binaries:
+        # Copy missing binaries for the matching architectures in the
+        # destination series. ISPPH.getBuiltBinaries() return only unique
+        # publication per binary package releases (i.e. excludes irrelevant
+        # arch-indep publications) and IBPPH.copy is prepared to expand
+        # arch-indep publications.
+        binary_copies = getUtility(IPublishingSet).copyBinariesTo(
+            source.getBuiltBinaries(), series, pocket, archive, policy=policy)
+
+        if binary_copies is not None:
+            copies.extend(binary_copies)
+            binary_uploads = set(
+                bpph.binarypackagerelease.build.package_upload
+                for bpph in binary_copies)
+            for binary_upload in binary_uploads:
+                if binary_upload is not None:
+                    custom_files.extend(binary_upload.customfiles)
+
+    if custom_files:
+        # Custom uploads aren't modelled as publication history records, so
+        # we have to send these through the upload queue.
+        custom_copier = CustomUploadsCopier(series, target_pocket=pocket)
+        for custom in custom_files:
+            custom_copier.copyUpload(custom)
 
     # Always ensure the needed builds exist in the copy destination
     # after copying the binaries.

=== modified file 'lib/lp/soyuz/scripts/tests/test_copypackage.py'
--- lib/lp/soyuz/scripts/tests/test_copypackage.py	2012-02-10 09:31:39 +0000
+++ lib/lp/soyuz/scripts/tests/test_copypackage.py	2012-06-22 18:04:24 +0000
@@ -111,11 +111,9 @@
 
         Their filename, mimetype and file contents should be the same.
         """
-        self.assertEquals(
-            old.filename, new.filename, 'Filename mismatch.')
-        self.assertEquals(
-            old.mimetype, new.mimetype, 'MIME type mismatch.')
-        self.assertEquals(old.read(), new.read(), 'Content mismatch.')
+        self.assertEqual(old.filename, new.filename, 'Filename mismatch.')
+        self.assertEqual(old.mimetype, new.mimetype, 'MIME type mismatch.')
+        self.assertEqual(old.read(), new.read(), 'Content mismatch.')
 
     def assertFileIsReset(self, reuploaded_file):
         """Assert the given `ILibraryFileAlias` attributes were reset.
@@ -123,10 +121,10 @@
         The expiration date and the hits counter are reset and the
         last access records was on file creation.
         """
-        self.assertIs(reuploaded_file.expires, None)
-        self.assertEquals(
+        self.assertIsNone(reuploaded_file.expires)
+        self.assertEqual(
             reuploaded_file.last_accessed, reuploaded_file.date_created)
-        self.assertEquals(reuploaded_file.hits, 0)
+        self.assertEqual(reuploaded_file.hits, 0)
 
     def testReUploadFileToTheSameContext(self):
         # Re-uploading a librarian file to the same privacy/server
@@ -139,7 +137,7 @@
         transaction.commit()
 
         self.assertIsNot(old, new)
-        self.assertEquals(
+        self.assertEqual(
             old.restricted, new.restricted, 'New file still private.')
         self.assertSameContent(old, new)
         self.assertFileIsReset(new)
@@ -223,9 +221,8 @@
 
     def assertNewFiles(self, new_files, result):
         """Check new files created during update_files_privacy."""
-        self.assertEquals(
-            sorted([new_file.filename for new_file in new_files]),
-            result)
+        self.assertEqual(
+            sorted([new_file.filename for new_file in new_files]), result)
 
     def _checkSourceFilesPrivacy(self, pub_record, restricted,
                                  expected_n_files):
@@ -233,20 +230,20 @@
         n_files = 0
         source = pub_record.sourcepackagerelease
         for source_file in source.files:
-            self.assertEquals(
+            self.assertEqual(
                 source_file.libraryfile.restricted, restricted,
                 'Privacy mismatch on %s' % source_file.libraryfile.filename)
             n_files += 1
-        self.assertEquals(
+        self.assertEqual(
             source.upload_changesfile.restricted, restricted,
             'Privacy mismatch on %s' % source.upload_changesfile.filename)
         n_files += 1
         for diff in source.package_diffs:
-            self.assertEquals(
+            self.assertEqual(
                 diff.diff_content.restricted, restricted,
                 'Privacy mismatch on %s' % diff.diff_content.filename)
             n_files += 1
-        self.assertEquals(
+        self.assertEqual(
             n_files, expected_n_files,
             'Expected %d and got %d files' % (expected_n_files, n_files))
 
@@ -335,20 +332,20 @@
         n_files = 0
         binary = pub_record.binarypackagerelease
         for binary_file in binary.files:
-            self.assertEquals(
+            self.assertEqual(
                 binary_file.libraryfile.restricted, restricted,
                 'Privacy mismatch on %s' % binary_file.libraryfile.filename)
             n_files += 1
         build = binary.build
-        self.assertEquals(
+        self.assertEqual(
             build.upload_changesfile.restricted, restricted,
             'Privacy mismatch on %s' % build.upload_changesfile.filename)
         n_files += 1
-        self.assertEquals(
+        self.assertEqual(
             build.log.restricted, restricted,
             'Privacy mismatch on %s' % build.log.filename)
         n_files += 1
-        self.assertEquals(
+        self.assertEqual(
             n_files, expected_n_files,
             'Expected %d and got %d files' % (expected_n_files, n_files))
 
@@ -463,18 +460,17 @@
            given state.
         """
         copy_checker = CopyChecker(self.archive, include_binaries=False)
-        self.assertIs(
-            None,
+        self.assertIsNone(
             copy_checker.checkCopy(
                 self.source, self.series, self.pocket,
                 check_permissions=False))
         checked_copies = list(copy_checker.getCheckedCopies())
-        self.assertEquals(1, len(checked_copies))
+        self.assertEqual(1, len(checked_copies))
         [checked_copy] = checked_copies
-        self.assertEquals(
+        self.assertEqual(
             BuildSetStatus.NEEDSBUILD,
             checked_copy.getStatusSummaryForBuilds()['status'])
-        self.assertEquals(delayed, checked_copy.delayed)
+        self.assertEqual(delayed, checked_copy.delayed)
 
     def assertCanCopyBinaries(self, delayed=False):
         """Source and binary copy is allowed.
@@ -491,18 +487,17 @@
            given state.
         """
         copy_checker = CopyChecker(self.archive, include_binaries=True)
-        self.assertIs(
-            None,
+        self.assertIsNone(
             copy_checker.checkCopy(
                 self.source, self.series, self.pocket,
                 check_permissions=False))
         checked_copies = list(copy_checker.getCheckedCopies())
-        self.assertEquals(1, len(checked_copies))
+        self.assertEqual(1, len(checked_copies))
         [checked_copy] = checked_copies
         self.assertTrue(
             checked_copy.getStatusSummaryForBuilds()['status'] >=
             BuildSetStatus.FULLYBUILT_PENDING)
-        self.assertEquals(delayed, checked_copy.delayed)
+        self.assertEqual(delayed, checked_copy.delayed)
 
     def assertCannotCopySourceOnly(self, msg, person=None,
                                    check_permissions=False):
@@ -516,7 +511,7 @@
             copy_checker.checkCopy, self.source, self.series, self.pocket,
             person, check_permissions)
         checked_copies = list(copy_checker.getCheckedCopies())
-        self.assertEquals(0, len(checked_copies))
+        self.assertEqual(0, len(checked_copies))
 
     def assertCannotCopyBinaries(self, msg):
         """`CopyChecker.checkCopy()` including binaries raises CannotCopy.
@@ -529,7 +524,7 @@
             copy_checker.checkCopy, self.source, self.series, self.pocket,
             None, False)
         checked_copies = list(copy_checker.getCheckedCopies())
-        self.assertEquals(0, len(checked_copies))
+        self.assertEqual(0, len(checked_copies))
 
     def test_cannot_copy_binaries_from_building(self):
         [build] = self.source.createMissingBuilds()
@@ -607,13 +602,12 @@
         with StormStatementRecorder() as recorder:
             copy_checker = CopyChecker(self.archive, include_binaries=False)
             for source in sources:
-                self.assertIs(
-                    None,
+                self.assertIsNone(
                     copy_checker.checkCopy(
                         source, self.series, self.pocket, person=person,
                         check_permissions=check_permissions))
             checked_copies = list(copy_checker.getCheckedCopies())
-            self.assertEquals(nb_of_sources, len(checked_copies))
+            self.assertEqual(nb_of_sources, len(checked_copies))
         return recorder
 
     def test_queries_copy_check(self):
@@ -832,13 +826,11 @@
 
         # At this point copy is allowed with or without binaries.
         copy_checker = CopyChecker(archive, include_binaries=False)
-        self.assertIs(
-            None,
+        self.assertIsNone(
             copy_checker.checkCopy(
                 source, series, pocket, check_permissions=False))
         copy_checker = CopyChecker(archive, include_binaries=True)
-        self.assertIs(
-            None,
+        self.assertIsNone(
             copy_checker.checkCopy(
                 source, series, pocket, check_permissions=False))
 
@@ -850,8 +842,8 @@
 
         # Now source-only copies are allowed.
         copy_checker = CopyChecker(archive, include_binaries=False)
-        self.assertIs(
-            None, copy_checker.checkCopy(
+        self.assertIsNone(
+            copy_checker.checkCopy(
                 source, series, pocket, check_permissions=False))
 
         # Copies with binaries are denied.
@@ -988,8 +980,7 @@
 
         # The first source-only copy is allowed, thus stored in the
         # copy checker inventory.
-        self.assertIs(
-            None,
+        self.assertIsNone(
             copy_checker.checkCopy(
                 source, source.distroseries, source.pocket,
                 check_permissions=False))
@@ -1162,7 +1153,7 @@
         self.test_publisher.prepareBreezyAutotest()
 
     def assertCopied(self, copies, series, arch_tags):
-        self.assertEquals(
+        self.assertEqual(
             [u'foo 666 in %s' % series.name] +
             [u'foo-bin 666 in %s %s' % (series.name, arch_tag)
              for arch_tag in arch_tags],
@@ -1266,9 +1257,9 @@
 
     def assertComponentSectionAndPriority(self, component, source,
                                           destination):
-        self.assertEquals(component, destination.component)
-        self.assertEquals(source.section, destination.section)
-        self.assertEquals(source.priority, destination.priority)
+        self.assertEqual(component, destination.component)
+        self.assertEqual(source.section, destination.section)
+        self.assertEqual(source.priority, destination.priority)
 
     def test_new_publication_overrides(self):
         # When we copy publications, if the destination primary archive has
@@ -1295,7 +1286,7 @@
         [copied_source, copied_bin_i386, copied_bin_hppa] = self.doCopy(
             source, target_archive, nobby, source.pocket, True)
         universe = getUtility(IComponentSet)['universe']
-        self.assertEquals(universe, copied_source.component)
+        self.assertEqual(universe, copied_source.component)
         self.assertComponentSectionAndPriority(
             universe, bin_i386, copied_bin_i386)
         self.assertComponentSectionAndPriority(
@@ -1331,7 +1322,7 @@
 
         [copied_source, copied_bin_i386, copied_bin_hppa] = self.doCopy(
             source, target_archive, nobby, source.pocket, True)
-        self.assertEquals(copied_source.component, existing_source.component)
+        self.assertEqual(copied_source.component, existing_source.component)
         self.assertComponentSectionAndPriority(
             ebin_i386.component, ebin_i386, copied_bin_i386)
         self.assertComponentSectionAndPriority(
@@ -1363,7 +1354,7 @@
 
         [copied_source, copied_bin_i386, copied_bin_hppa] = self.doCopy(
             source, archive, nobby, PackagePublishingPocket.UPDATES, True)
-        self.assertEquals(copied_source.component, source.component)
+        self.assertEqual(copied_source.component, source.component)
         self.assertComponentSectionAndPriority(
             bin_i386.component, bin_i386, copied_bin_i386)
         self.assertComponentSectionAndPriority(
@@ -1385,7 +1376,7 @@
         [copied_source, copied_bin_i386, copied_bin_hppa] = self.doCopy(
             source, target_archive, nobby, source.pocket, True)
         main = getUtility(IComponentSet)['main']
-        self.assertEquals(main, copied_source.component)
+        self.assertEqual(main, copied_source.component)
         self.assertComponentSectionAndPriority(
             main, bin_i386, copied_bin_i386)
         self.assertComponentSectionAndPriority(
@@ -1442,9 +1433,8 @@
             person=target_archive.owner, check_permissions=False,
             send_email=True)
         [notification] = pop_notifications()
-        self.assertEquals(
-            get_ppa_reference(target_archive),
-            notification['X-Launchpad-PPA'])
+        self.assertEqual(
+            get_ppa_reference(target_archive), notification['X-Launchpad-PPA'])
         body = notification.get_payload()[0].get_payload()
         expected = dedent("""\
             Accepted:
@@ -1481,11 +1471,10 @@
             person=source.sourcepackagerelease.creator,
             check_permissions=False, send_email=True)
         [notification, announcement] = pop_notifications()
-        self.assertEquals(
-            'Foo Bar <foo.bar@xxxxxxxxxxxxx>', notification['To'])
-        self.assertEquals('nobby-changes@xxxxxxxxxxx', announcement['To'])
+        self.assertEqual('Foo Bar <foo.bar@xxxxxxxxxxxxx>', notification['To'])
+        self.assertEqual('nobby-changes@xxxxxxxxxxx', announcement['To'])
         for mail in (notification, announcement):
-            self.assertEquals(
+            self.assertEqual(
                 '[ubuntutest/nobby] foo 1.0-2 (Accepted)', mail['Subject'])
         expected_text = dedent("""\
             foo (1.0-2) unstable; urgency=3Dlow
@@ -1521,7 +1510,7 @@
                     check_permissions=False, send_email=True,
                     sponsored=sponsored_person)
         [notification, announcement] = pop_notifications()
-        self.assertEquals(
+        self.assertEqual(
             'Sponsored <sponsored@xxxxxxxxxxx>', announcement['From'])
         self.assertEqual(sponsored_person, copied_source.creator)
 
@@ -1600,11 +1589,9 @@
         notifications = pop_notifications()
         self.assertEqual(1, len(notifications))
         [notification] = notifications
-        self.assertEquals(
-            'Foo Bar <foo.bar@xxxxxxxxxxxxx>', notification['To'])
-        self.assertEquals(
-            '[ubuntutest/nobby] foo 1.0-2 (Rejected)',
-            notification['Subject'])
+        self.assertEqual('Foo Bar <foo.bar@xxxxxxxxxxxxx>', notification['To'])
+        self.assertEqual(
+            '[ubuntutest/nobby] foo 1.0-2 (Rejected)', notification['Subject'])
         expected_text = (
             "Rejected:\n"
             "foo 1.0-2 in breezy-autotest (a different source with the same "
@@ -1621,7 +1608,7 @@
             [source], target_archive, nobby, source.pocket, False,
             person=target_archive.owner, check_permissions=False,
             send_email=False)
-        self.assertEquals([], pop_notifications())
+        self.assertEqual([], pop_notifications())
 
     def test_copying_unsupported_arch_with_override(self):
         # When the copier is passed an unsupported arch with an override
@@ -1657,9 +1644,7 @@
             person=target_archive.owner, check_permissions=False,
             send_email=False)
 
-        self.assertEqual(
-            target_archive.owner,
-            copied_source.creator)
+        self.assertEqual(target_archive.owner, copied_source.creator)
 
     def test_unsponsored_copy_does_not_set_sponsor(self):
         # If the copy is not sponsored, SPPH.sponsor is none
@@ -1671,9 +1656,42 @@
             person=target_archive.owner, check_permissions=False,
             send_email=False)
 
+        self.assertIsNone(copied_source.sponsor)
+
+    def test_copy_custom_upload_files(self):
+        # Custom upload files are queued for republication when they are
+        # copied.
+        self.test_publisher.breezy_autotest.status = SeriesStatus.CURRENT
+        source = self.test_publisher.getPubSource(
+            pocket=PackagePublishingPocket.PROPOSED)
+        self.test_publisher.getPubBinaries(
+            pocket=PackagePublishingPocket.PROPOSED, pub_source=source)
+        [build] = source.getBuilds()
+        custom_file = self.factory.makeLibraryFileAlias()
+        build.package_upload.addCustom(
+            custom_file, PackageUploadCustomFormat.DIST_UPGRADER)
+        # Make the new librarian file available.
+        self.layer.txn.commit()
+
+        self.doCopy(
+            source, source.archive, source.distroseries,
+            PackagePublishingPocket.UPDATES, include_binaries=True)
+        [upload] = source.distroseries.getPackageUploads(
+            status=PackageUploadStatus.ACCEPTED, archive=source.archive,
+            pocket=PackagePublishingPocket.UPDATES,
+            custom_type=PackageUploadCustomFormat.DIST_UPGRADER)
+
+        # The upload is targeted to the right publishing context.
+        self.assertEqual(source.archive, upload.archive)
+        self.assertEqual(source.distroseries, upload.distroseries)
+        self.assertEqual(PackagePublishingPocket.UPDATES, upload.pocket)
+
+        # It contains only the custom files.
+        self.assertEqual([], list(upload.sources))
+        self.assertEqual([], list(upload.builds))
         self.assertEqual(
-            copied_source.sponsor,
-            None)
+            [custom_file],
+            [custom.libraryfilealias for custom in upload.customfiles])
 
 
 class TestDoDelayedCopy(TestCaseWithFactory, BaseDoCopyTests):
@@ -1694,16 +1712,13 @@
 
         # Make ubuntutest/breezy-autotest CURRENT so uploads to SECURITY
         # pocket can be accepted.
-        self.test_publisher.breezy_autotest.status = (
-            SeriesStatus.CURRENT)
+        self.test_publisher.breezy_autotest.status = SeriesStatus.CURRENT
 
     def assertCopied(self, copy, series, arch_tags):
-        self.assertEquals(
-            copy.sources[0].sourcepackagerelease.title,
-            'foo - 666')
-        self.assertEquals(
-            sorted(arch_tags),
-            sorted([pub.build.arch_tag for pub in copy.builds]))
+        self.assertEqual(
+            copy.sources[0].sourcepackagerelease.title, 'foo - 666')
+        self.assertContentEqual(
+            arch_tags, [pub.build.arch_tag for pub in copy.builds])
 
     def doCopy(self, source, archive, series, pocket, include_binaries):
         return _do_delayed_copy(source, archive, series, pocket, True)
@@ -1756,35 +1771,32 @@
 
         # A delayed-copy `IPackageUpload` record is returned.
         self.assertTrue(delayed_copy.is_delayed_copy)
-        self.assertEquals(
-            PackageUploadStatus.ACCEPTED, delayed_copy.status)
+        self.assertEqual(PackageUploadStatus.ACCEPTED, delayed_copy.status)
 
         # The returned object has a more descriptive 'displayname'
         # attribute than plain `IPackageUpload` instances.
-        self.assertEquals(
+        self.assertEqual(
             'Delayed copy of foocomm - '
             '1.0-2 (source, i386, raw-dist-upgrader)',
             delayed_copy.displayname)
 
         # It is targeted to the right publishing context.
-        self.assertEquals(self.copy_archive, delayed_copy.archive)
-        self.assertEquals(self.copy_series, delayed_copy.distroseries)
-        self.assertEquals(self.copy_pocket, delayed_copy.pocket)
+        self.assertEqual(self.copy_archive, delayed_copy.archive)
+        self.assertEqual(self.copy_series, delayed_copy.distroseries)
+        self.assertEqual(self.copy_pocket, delayed_copy.pocket)
 
         # And it contains the source, build and custom files.
-        self.assertEquals(
+        self.assertEqual(
             [source.sourcepackagerelease],
             [pus.sourcepackagerelease for pus in delayed_copy.sources])
 
         [build] = source.getBuilds()
-        self.assertEquals(
-            [build],
-            [pub.build for pub in delayed_copy.builds])
+        self.assertEqual([build], [pub.build for pub in delayed_copy.builds])
 
         [custom_file] = [
             custom.libraryfilealias
             for custom in build.package_upload.customfiles]
-        self.assertEquals(
+        self.assertEqual(
             [custom_file],
             [custom.libraryfilealias for custom in delayed_copy.customfiles])
 
@@ -1841,23 +1853,20 @@
         # Setup and execute the delayed copy procedure. This should
         # now result in an accepted delayed upload.
         delayed_copy = self.do_delayed_copy(source)
-        self.assertEquals(
-            PackageUploadStatus.ACCEPTED, delayed_copy.status)
+        self.assertEqual(PackageUploadStatus.ACCEPTED, delayed_copy.status)
 
         # And it contains the source, build and custom files.
-        self.assertEquals(
+        self.assertEqual(
             [source.sourcepackagerelease],
             [pus.sourcepackagerelease for pus in delayed_copy.sources])
 
         [build] = source.getBuilds()
-        self.assertEquals(
-            [build],
-            [pub.build for pub in delayed_copy.builds])
+        self.assertEqual([build], [pub.build for pub in delayed_copy.builds])
 
         [custom_file] = [
             custom.libraryfilealias
             for custom in build.package_upload.customfiles]
-        self.assertEquals(
+        self.assertEqual(
             [custom_file],
             [custom.libraryfilealias for custom in delayed_copy.customfiles])
 
@@ -1913,9 +1922,8 @@
         # archive context. Also after this point, the same delayed-copy
         # request will be denied by `CopyChecker`.
         [build_hppa, build_i386] = source.getBuilds()
-        self.assertEquals(
-            [build_i386],
-            [pub.build for pub in delayed_copy.builds])
+        self.assertEqual(
+            [build_i386], [pub.build for pub in delayed_copy.builds])
 
 
 class CopyPackageScriptTestCase(unittest.TestCase):
@@ -2063,8 +2071,7 @@
         self.assertEqual(len(copied), size)
 
         for candidate in copied:
-            self.assertEqual(
-                candidate.status, PackagePublishingStatus.PENDING)
+            self.assertEqual(PackagePublishingStatus.PENDING, candidate.status)
 
         def excludeOlds(found, old_pending_ids):
             return [pub.id for pub in found if pub.id not in old_pending_ids]
@@ -2394,16 +2401,14 @@
         active_warty_architectures = [
             arch.architecturetag for arch in warty.architectures
             if arch.getChroot()]
-        self.assertEqual(
-            active_warty_architectures, ['i386'])
+        self.assertEqual(active_warty_architectures, ['i386'])
 
         # Setup ubuntu/hoary supporting i386 and hppa architetures.
         hoary = ubuntu.getSeries('hoary')
         test_publisher.addFakeChroots(hoary)
         active_hoary_architectures = [
             arch.architecturetag for arch in hoary.architectures]
-        self.assertEqual(
-            sorted(active_hoary_architectures), ['hppa', 'i386'])
+        self.assertEqual(sorted(active_hoary_architectures), ['hppa', 'i386'])
 
         # We will create an architecture-specific source and its binaries
         # for i386 in ubuntu/warty. They will be copied over.
@@ -2495,8 +2500,7 @@
         # architecture (hppa).
         [new_build] = copied_source.getBuilds()
         self.assertEqual(
-            new_build.title,
-            'hppa build of boing 1.0 in ubuntu hoary RELEASE')
+            new_build.title, 'hppa build of boing 1.0 in ubuntu hoary RELEASE')
 
     def testVersionConflictInDifferentPockets(self):
         """Copy-package stops copies conflicting in different pocket.
@@ -2621,8 +2625,7 @@
 
         [copied_source, original_source] = sources
 
-        self.assertEqual(
-            copied_source.pocket, PackagePublishingPocket.UPDATES)
+        self.assertEqual(copied_source.pocket, PackagePublishingPocket.UPDATES)
         self.assertEqual(
             original_source.pocket, PackagePublishingPocket.SECURITY)
 
@@ -2668,8 +2671,7 @@
         copied = copy_helper.mainTask()
 
         [source_copy, i386_copy] = copied
-        self.assertEqual(
-            source_copy.displayname, 'lazy-building 1.0 in hoary')
+        self.assertEqual(source_copy.displayname, 'lazy-building 1.0 in hoary')
         self.assertEqual(i386_copy.displayname, 'lazy-bin 1.0 in hoary i386')
 
         target_archive = copy_helper.destination.archive
@@ -2730,12 +2732,8 @@
             from_suite='warty', to_suite='hoary', to_ppa='mark')
         copied = copy_helper.mainTask()
 
-        self.assertEqual(
-            str(copy_helper.location),
-            'cprov: warty-RELEASE')
-        self.assertEqual(
-            str(copy_helper.destination),
-            'mark: hoary-RELEASE')
+        self.assertEqual('cprov: warty-RELEASE', str(copy_helper.location))
+        self.assertEqual('mark: hoary-RELEASE', str(copy_helper.destination))
 
         target_archive = copy_helper.destination.archive
         self.checkCopies(copied, target_archive, 2)
@@ -3237,10 +3235,10 @@
             section='misc')
 
         checker = CopyChecker(warty.main_archive, include_binaries=False)
-        self.assertIs(
-            None,
-            checker.checkCopy(proposed_source, warty,
-            PackagePublishingPocket.UPDATES, check_permissions=False))
+        self.assertIsNone(
+            checker.checkCopy(
+                proposed_source, warty, PackagePublishingPocket.UPDATES,
+                check_permissions=False))
 
     def testCopySourceWithConflictingFilesInPPAs(self):
         """We can copy source if the source files match, both in name and
@@ -3325,10 +3323,10 @@
         self.layer.txn.commit()
 
         checker = CopyChecker(dest_ppa, include_binaries=False)
-        self.assertIs(
-            None,
-            checker.checkCopy(test2_source, warty,
-            PackagePublishingPocket.RELEASE, check_permissions=False))
+        self.assertIsNone(
+            checker.checkCopy(
+                test2_source, warty, PackagePublishingPocket.RELEASE,
+                check_permissions=False))
 
     def testCopySourceWithExpiredSourcesInDestination(self):
         """We can also copy sources if the destination archive has expired
@@ -3371,7 +3369,7 @@
         switch_dbuser(self.dbuser)
 
         checker = CopyChecker(dest_ppa, include_binaries=False)
-        self.assertIs(
-            None,
-            checker.checkCopy(test2_source, warty,
-            PackagePublishingPocket.RELEASE, check_permissions=False))
+        self.assertIsNone(
+            checker.checkCopy(
+                test2_source, warty, PackagePublishingPocket.RELEASE,
+                check_permissions=False))

=== modified file 'lib/lp/soyuz/scripts/tests/test_custom_uploads_copier.py'
--- lib/lp/soyuz/scripts/tests/test_custom_uploads_copier.py	2012-05-30 10:25:43 +0000
+++ lib/lp/soyuz/scripts/tests/test_custom_uploads_copier.py	2012-06-22 18:04:24 +0000
@@ -6,6 +6,7 @@
 __metaclass__ = type
 
 from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.registry.interfaces.series import SeriesStatus
 from lp.soyuz.enums import (
     ArchivePurpose,
     PackageUploadCustomFormat,
@@ -108,7 +109,7 @@
     # Alas, PackageUploadCustom relies on the Librarian.
     layer = LaunchpadZopelessLayer
 
-    def makeUpload(self, distroseries=None,
+    def makeUpload(self, distroseries=None, pocket=None,
                    custom_type=PackageUploadCustomFormat.DEBIAN_INSTALLER,
                    version=None, arch=None):
         """Create a `PackageUploadCustom`."""
@@ -121,7 +122,7 @@
             arch = self.factory.getUniqueString()
         filename = "%s.tar.gz" % '_'.join([package_name, version, arch])
         package_upload = self.factory.makeCustomPackageUpload(
-            distroseries=distroseries, custom_type=custom_type,
+            distroseries=distroseries, pocket=pocket, custom_type=custom_type,
             filename=filename)
         return package_upload.customfiles[0]
 
@@ -213,6 +214,19 @@
             upload.id for upload in copier.getCandidateUploads(source_series)]
         self.assertEqual(sorted(candidate_ids, reverse=True), candidate_ids)
 
+    def test_getCandidateUploads_filters_by_pocket(self):
+        # getCandidateUploads ignores uploads for other pockets.
+        source_series = self.factory.makeDistroSeries()
+        matching_upload = self.makeUpload(
+            source_series, pocket=PackagePublishingPocket.PROPOSED)
+        nonmatching_upload = self.makeUpload(
+            source_series, pocket=PackagePublishingPocket.BACKPORTS)
+        copier = CustomUploadsCopier(FakeDistroSeries())
+        candidate_uploads = copier.getCandidateUploads(
+            source_series, PackagePublishingPocket.PROPOSED)
+        self.assertContentEqual([matching_upload], candidate_uploads)
+        self.assertNotIn(nonmatching_upload, candidate_uploads)
+
     def test_getKey_includes_format_and_architecture(self):
         # The key returned by getKey consists of custom upload type,
         # and architecture.
@@ -221,7 +235,7 @@
         # component name as well.
         source_series = self.factory.makeDistroSeries()
         upload = self.makeUpload(
-            source_series, PackageUploadCustomFormat.DIST_UPGRADER,
+            source_series, custom_type=PackageUploadCustomFormat.DIST_UPGRADER,
             arch='mips')
         copier = CustomUploadsCopier(FakeDistroSeries())
         expected_key = (
@@ -379,8 +393,8 @@
         # copyUpload copies the original upload into the release pocket,
         # even though the original is more likely to be in another
         # pocket.
-        original_upload = self.makeUpload()
-        original_upload.packageupload.pocket = PackagePublishingPocket.UPDATES
+        original_upload = self.makeUpload(
+            pocket=PackagePublishingPocket.UPDATES)
         target_series = self.factory.makeDistroSeries()
         copier = CustomUploadsCopier(target_series)
         copied_upload = copier.copyUpload(original_upload)
@@ -388,6 +402,21 @@
             PackagePublishingPocket.RELEASE,
             copied_upload.packageupload.pocket)
 
+    def test_copyUpload_to_updates_pocket(self):
+        # copyUpload copies an upload between pockets in the same series if
+        # requested.
+        source_series = self.factory.makeDistroSeries(
+            status=SeriesStatus.CURRENT)
+        original_upload = self.makeUpload(
+            distroseries=source_series,
+            pocket=PackagePublishingPocket.PROPOSED)
+        copier = CustomUploadsCopier(
+            source_series, target_pocket=PackagePublishingPocket.UPDATES)
+        copied_upload = copier.copyUpload(original_upload)
+        self.assertEqual(
+            PackagePublishingPocket.UPDATES,
+            copied_upload.packageupload.pocket)
+
     def test_copyUpload_accepts_upload(self):
         # Uploads created by copyUpload are automatically accepted.
         original_upload = self.makeUpload()

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2012-06-11 09:55:13 +0000
+++ lib/lp/testing/factory.py	2012-06-22 18:04:24 +0000
@@ -3493,28 +3493,32 @@
             sourcepackagename=sourcepackagename, component=component))
         return upload
 
-    def makeBuildPackageUpload(self, distroseries=None,
-                               binarypackagename=None):
+    def makeBuildPackageUpload(self, distroseries=None, pocket=None,
+                               binarypackagename=None,
+                               source_package_release=None):
         """Make a `PackageUpload` with a `PackageUploadBuild` attached."""
         if distroseries is None:
             distroseries = self.makeDistroSeries()
         upload = self.makePackageUpload(
-            distroseries=distroseries, archive=distroseries.main_archive)
-        build = self.makeBinaryPackageBuild()
+            distroseries=distroseries, archive=distroseries.main_archive,
+            pocket=pocket)
+        build = self.makeBinaryPackageBuild(
+            source_package_release=source_package_release, pocket=pocket)
         upload.addBuild(build)
         self.makeBinaryPackageRelease(
             binarypackagename=binarypackagename, build=build)
         return upload
 
-    def makeCustomPackageUpload(self, distroseries=None, custom_type=None,
-                                filename=None):
+    def makeCustomPackageUpload(self, distroseries=None, pocket=None,
+                                custom_type=None, filename=None):
         """Make a `PackageUpload` with a `PackageUploadCustom` attached."""
         if distroseries is None:
             distroseries = self.makeDistroSeries()
         if custom_type is None:
             custom_type = PackageUploadCustomFormat.DEBIAN_INSTALLER
         upload = self.makePackageUpload(
-            distroseries=distroseries, archive=distroseries.main_archive)
+            distroseries=distroseries, archive=distroseries.main_archive,
+            pocket=pocket)
         file_alias = self.makeLibraryFileAlias(filename=filename)
         upload.addCustom(file_alias, custom_type)
         return upload

=== modified file 'lib/lp/testing/tests/test_factory.py'
--- lib/lp/testing/tests/test_factory.py	2012-01-01 02:58:52 +0000
+++ lib/lp/testing/tests/test_factory.py	2012-06-22 18:04:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for the Launchpad object factory."""
@@ -781,10 +781,12 @@
         distroseries = self.factory.makeDistroSeries()
         bpn = self.factory.makeBinaryPackageName()
         pu = self.factory.makeBuildPackageUpload(
-            distroseries=distroseries, binarypackagename=bpn)
+            distroseries=distroseries, pocket=PackagePublishingPocket.PROPOSED,
+            binarypackagename=bpn)
         build = list(pu.builds)[0].build
         self.assertEqual(distroseries, pu.distroseries)
         self.assertEqual(distroseries.distribution, pu.archive.distribution)
+        self.assertEqual(PackagePublishingPocket.PROPOSED, pu.pocket)
         release = IStore(distroseries).find(
             BinaryPackageRelease, BinaryPackageRelease.build == build).one()
         self.assertEqual(bpn, release.binarypackagename)
@@ -803,11 +805,12 @@
         custom_type = PackageUploadCustomFormat.ROSETTA_TRANSLATIONS
         filename = self.factory.getUniqueString()
         pu = self.factory.makeCustomPackageUpload(
-            distroseries=distroseries, custom_type=custom_type,
-            filename=filename)
+            distroseries=distroseries, pocket=PackagePublishingPocket.PROPOSED,
+            custom_type=custom_type, filename=filename)
         custom = list(pu.customfiles)[0]
         self.assertEqual(distroseries, pu.distroseries)
         self.assertEqual(distroseries.distribution, pu.archive.distribution)
+        self.assertEqual(PackagePublishingPocket.PROPOSED, pu.pocket)
         self.assertEqual(custom_type, custom.customformat)
         self.assertEqual(filename, custom.libraryfilealias.filename)
 


References