← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~julian-edwards/launchpad/pcj-requester-bug-810957 into lp:launchpad

 

Julian Edwards has proposed merging lp:~julian-edwards/launchpad/pcj-requester-bug-810957 into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~julian-edwards/launchpad/pcj-requester-bug-810957/+merge/68081

= Summary =
Enable PackageCopyJob to store the Person who requested it.

== Pre-implementation notes ==
Chatted to Gavin who first wrote PCJ.  IJob has a database column for requester
which is not declared in the model code, so we can just use that with some
changes to IJob.

== Implementation details ==
Fix IJob's model implementation, its createMultiple() and PackageCopyJob's
create() and createMutliple().

Note that I've had to add requestor=None to create() even though it's requred,
since I can't be sure of all the places it's called from and they would all need fixing to match the new positional parameters.  The assertion matches the one
done for package_version for the same reason.

== Tests ==
bin/test -cvv test_packagecopyjob

== Demo and Q/A ==
n/a until something uses it.


= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/services/job/interfaces/job.py
  lib/lp/soyuz/model/packagecopyjob.py
  lib/lp/soyuz/interfaces/packagecopyjob.py
  lib/lp/testing/factory.py
  lib/lp/soyuz/tests/test_packagecopyjob.py
  lib/lp/services/job/tests/test_job.py
  lib/lp/services/job/model/job.py
  lib/lp/registry/browser/distroseries.py
-- 
https://code.launchpad.net/~julian-edwards/launchpad/pcj-requester-bug-810957/+merge/68081
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~julian-edwards/launchpad/pcj-requester-bug-810957 into lp:launchpad.
=== modified file 'lib/lp/registry/browser/distroseries.py'
--- lib/lp/registry/browser/distroseries.py	2011-07-08 09:06:24 +0000
+++ lib/lp/registry/browser/distroseries.py	2011-07-15 12:13:27 +0000
@@ -1161,7 +1161,7 @@
             )
             for dsd in self.getUpgrades()]
         getUtility(IPlainPackageCopyJobSource).createMultiple(
-            target_distroseries, copies,
+            target_distroseries, copies, self.user,
             copy_policy=PackageCopyPolicy.MASS_SYNC)
 
         self.request.response.addInfoNotification(

=== modified file 'lib/lp/services/job/interfaces/job.py'
--- lib/lp/services/job/interfaces/job.py	2011-07-06 14:02:54 +0000
+++ lib/lp/services/job/interfaces/job.py	2011-07-15 12:13:27 +0000
@@ -22,6 +22,7 @@
     DBEnumeratedType,
     DBItem,
     )
+from lazr.restful.fields import Reference
 from zope.interface import (
     Attribute,
     Interface,
@@ -35,6 +36,7 @@
     )
 
 from canonical.launchpad import _
+from lp.registry.interfaces.person import IPerson
 
 
 class SuspendJobException(Exception):
@@ -109,6 +111,11 @@
     max_retries = Int(title=_(
         'The number of retries permitted before this job permanently fails.'))
 
+    requester = Reference(
+        IPerson, title=_("The person who requested the job"),
+        required=False, readonly=True
+        )
+
     is_pending = Bool(
         title=_("Whether or not this job's status is such that it "
                 "could eventually complete."))

=== modified file 'lib/lp/services/job/model/job.py'
--- lib/lp/services/job/model/job.py	2011-07-06 14:02:54 +0000
+++ lib/lp/services/job/model/job.py	2011-07-15 12:13:27 +0000
@@ -21,6 +21,10 @@
     Or,
     Select,
     )
+from storm.locals import (
+    Int,
+    Reference,
+    )
 from zope.interface import implements
 
 from canonical.database.constants import UTC_NOW
@@ -73,6 +77,9 @@
 
     max_retries = IntCol(default=0)
 
+    requester_id = Int(name='requester', allow_none=True)
+    requester = Reference(requester_id, 'Person.id')
+
     # Mapping of valid target states from a given state.
     _valid_transitions = {
         JobStatus.WAITING:
@@ -108,16 +115,19 @@
         return self.status in self.PENDING_STATUSES
 
     @classmethod
-    def createMultiple(self, store, num_jobs):
+    def createMultiple(self, store, num_jobs, requester=None):
         """Create multiple `Job`s at once.
 
         :param store: `Store` to ceate the jobs in.
         :param num_jobs: Number of `Job`s to create.
+        :param request: The `IPerson` requesting the jobs.
         :return: An iterable of `Job.id` values for the new jobs.
         """
-        job_contents = ["(%s)" % quote(JobStatus.WAITING)] * num_jobs
+        job_contents = [
+            "(%s, %s)" % (
+                quote(JobStatus.WAITING), quote(requester))] * num_jobs
         result = store.execute("""
-            INSERT INTO Job (status)
+            INSERT INTO Job (status, requester)
             VALUES %s
             RETURNING id
             """ % ", ".join(job_contents))

=== modified file 'lib/lp/services/job/tests/test_job.py'
--- lib/lp/services/job/tests/test_job.py	2011-07-06 14:02:54 +0000
+++ lib/lp/services/job/tests/test_job.py	2011-07-15 12:13:27 +0000
@@ -22,10 +22,13 @@
     Job,
     LeaseHeld,
     )
-from lp.testing import TestCase
-
-
-class TestJob(TestCase):
+from lp.testing import (
+    TestCase,
+    TestCaseWithFactory,
+    )
+
+
+class TestJob(TestCaseWithFactory):
     """Ensure Job behaves as intended."""
 
     layer = ZopelessDatabaseLayer
@@ -39,6 +42,12 @@
         job = Job()
         self.assertEqual(job.status, JobStatus.WAITING)
 
+    def test_stores_requester(self):
+        job = Job()
+        random_joe = self.factory.makePerson()
+        job.requester = random_joe
+        self.assertEqual(random_joe, job.requester)
+
     def test_createMultiple_creates_requested_number_of_jobs(self):
         job_ids = list(Job.createMultiple(IStore(Job), 3))
         self.assertEqual(3, len(job_ids))
@@ -55,6 +64,17 @@
         job = store.get(Job, Job.createMultiple(store, 1)[0])
         self.assertEqual(JobStatus.WAITING, job.status)
 
+    def test_createMultiple_sets_requester(self):
+        store = IStore(Job)
+        requester = self.factory.makePerson()
+        job = store.get(Job, Job.createMultiple(store, 1, requester)[0])
+        self.assertEqual(requester, job.requester)
+
+    def test_createMultiple_defaults_requester_to_None(self):
+        store = IStore(Job)
+        job = store.get(Job, Job.createMultiple(store, 1)[0])
+        self.assertEqual(None, job.requester)
+
     def test_start(self):
         """Job.start should update the object appropriately.
 

=== modified file 'lib/lp/soyuz/interfaces/packagecopyjob.py'
--- lib/lp/soyuz/interfaces/packagecopyjob.py	2011-07-12 14:23:40 +0000
+++ lib/lp/soyuz/interfaces/packagecopyjob.py	2011-07-15 12:13:27 +0000
@@ -126,7 +126,7 @@
     def create(package_name, source_archive,
                target_archive, target_distroseries, target_pocket,
                include_binaries=False, package_version=None,
-               copy_policy=PackageCopyPolicy.INSECURE):
+               copy_policy=PackageCopyPolicy.INSECURE, requester=None):
         """Create a new `IPlainPackageCopyJob`.
 
         :param package_name: The name of the source package to copy.
@@ -141,9 +141,10 @@
         :param package_version: The version string for the package version
             that is to be copied.
         :param copy_policy: Applicable `PackageCopyPolicy`.
+        :param requester: The user requesting the copy.
         """
 
-    def createMultiple(target_distroseries, copy_tasks,
+    def createMultiple(target_distroseries, copy_tasks, requester,
                        copy_policy=PackageCopyPolicy.INSECURE,
                        include_binaries=False):
         """Create multiple new `IPlainPackageCopyJob`s at once.
@@ -153,6 +154,7 @@
         :param copy_tasks: A list of tuples describing the copies to be
             performed: (package name, package version, source archive,
             target archive, target pocket).
+        :param requester: The user requesting the copy.
         :param copy_policy: Applicable `PackageCopyPolicy`.
         :param include_binaries: As in `do_copy`.
         :return: An iterable of `PackageCopyJob` ids.

=== modified file 'lib/lp/soyuz/model/packagecopyjob.py'
--- lib/lp/soyuz/model/packagecopyjob.py	2011-07-08 09:06:24 +0000
+++ lib/lp/soyuz/model/packagecopyjob.py	2011-07-15 12:13:27 +0000
@@ -131,9 +131,11 @@
         return cls.wrap(IStore(PackageCopyJob).get(PackageCopyJob, pcj_id))
 
     def __init__(self, source_archive, target_archive, target_distroseries,
-                 job_type, metadata, package_name=None, copy_policy=None):
+                 job_type, metadata, requester, package_name=None,
+                 copy_policy=None):
         super(PackageCopyJob, self).__init__()
         self.job = Job()
+        self.job.requester = requester
         self.job_type = job_type
         self.source_archive = source_archive
         self.target_archive = target_archive
@@ -250,9 +252,10 @@
     def create(cls, package_name, source_archive,
                target_archive, target_distroseries, target_pocket,
                include_binaries=False, package_version=None,
-               copy_policy=PackageCopyPolicy.INSECURE):
+               copy_policy=PackageCopyPolicy.INSECURE, requester=None):
         """See `IPlainPackageCopyJobSource`."""
         assert package_version is not None, "No package version specified."
+        assert requester is not None, "No requester specified."
         metadata = cls._makeMetadata(
             target_pocket, package_version, include_binaries)
         job = PackageCopyJob(
@@ -262,7 +265,8 @@
             target_distroseries=target_distroseries,
             package_name=package_name,
             copy_policy=copy_policy,
-            metadata=metadata)
+            metadata=metadata,
+            requester=requester)
         IMasterStore(PackageCopyJob).add(job)
         return cls(job)
 
@@ -292,12 +296,12 @@
         return format_string % sqlvalues(*data)
 
     @classmethod
-    def createMultiple(cls, target_distroseries, copy_tasks,
+    def createMultiple(cls, target_distroseries, copy_tasks, requester,
                        copy_policy=PackageCopyPolicy.INSECURE,
                        include_binaries=False):
         """See `IPlainPackageCopyJobSource`."""
         store = IMasterStore(Job)
-        job_ids = Job.createMultiple(store, len(copy_tasks))
+        job_ids = Job.createMultiple(store, len(copy_tasks), requester)
         job_contents = [
             cls._composeJobInsertionTuple(
                 target_distroseries, copy_policy, include_binaries, job_id,

=== modified file 'lib/lp/soyuz/tests/test_packagecopyjob.py'
--- lib/lp/soyuz/tests/test_packagecopyjob.py	2011-07-12 14:52:52 +0000
+++ lib/lp/soyuz/tests/test_packagecopyjob.py	2011-07-15 12:13:27 +0000
@@ -92,9 +92,10 @@
         target_archive = dsd.derived_series.main_archive
         target_distroseries = dsd.derived_series
         target_pocket = self.factory.getAnyPocket()
+        requester = self.factory.makePerson()
         return getUtility(IPlainPackageCopyJobSource).create(
             dsd.source_package_name.name, source_archive, target_archive,
-            target_distroseries, target_pocket,
+            target_distroseries, target_pocket, requester=requester,
             package_version=dsd.parent_source_version, **kwargs)
 
     def runJob(self, job):
@@ -122,13 +123,15 @@
         distroseries = self.factory.makeDistroSeries()
         archive1 = self.factory.makeArchive(distroseries.distribution)
         archive2 = self.factory.makeArchive(distroseries.distribution)
+        requester  = self.factory.makePerson()
         source = getUtility(IPlainPackageCopyJobSource)
         job = source.create(
             package_name="foo", source_archive=archive1,
             target_archive=archive2, target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
             package_version="1.0-1", include_binaries=False,
-            copy_policy=PackageCopyPolicy.MASS_SYNC)
+            copy_policy=PackageCopyPolicy.MASS_SYNC,
+            requester=requester)
         self.assertProvides(job, IPackageCopyJob)
         self.assertEquals(archive1.id, job.source_archive_id)
         self.assertEquals(archive1, job.source_archive)
@@ -140,6 +143,7 @@
         self.assertEqual("1.0-1", job.package_version)
         self.assertEquals(False, job.include_binaries)
         self.assertEquals(PackageCopyPolicy.MASS_SYNC, job.copy_policy)
+        self.assertEqual(requester, job.requester)
 
     def test_createMultiple_creates_one_job_per_copy(self):
         mother = self.factory.makeDistroSeriesParent()
@@ -148,6 +152,7 @@
             derived_series=derived_series)
         mother_package = self.factory.makeSourcePackageName()
         father_package = self.factory.makeSourcePackageName()
+        requester = self.factory.makePerson()
         job_source = getUtility(IPlainPackageCopyJobSource)
         copy_tasks = [
             (
@@ -166,7 +171,8 @@
                 ),
             ]
         job_ids = list(
-            job_source.createMultiple(mother.derived_series, copy_tasks))
+            job_source.createMultiple(mother.derived_series, copy_tasks,
+                                      requester))
         jobs = list(job_source.getActiveJobs(derived_series.main_archive))
         self.assertContentEqual(job_ids, [job.id for job in jobs])
         self.assertEqual(len(copy_tasks), len(set([job.job for job in jobs])))
@@ -185,17 +191,24 @@
             for job in jobs]
         self.assertEqual(copy_tasks, requested_copies)
 
+        # The passed requester should be the same on all jobs.
+        actual_requester = set(job.requester for job in jobs)
+        self.assertEqual(1, len(actual_requester))
+        self.assertEqual(requester, jobs[0].requester)
+
     def test_getActiveJobs(self):
         # getActiveJobs() can retrieve all active jobs for an archive.
         distroseries = self.factory.makeDistroSeries()
         archive1 = self.factory.makeArchive(distroseries.distribution)
         archive2 = self.factory.makeArchive(distroseries.distribution)
         source = getUtility(IPlainPackageCopyJobSource)
+        requester = self.factory.makePerson()
         job = source.create(
             package_name="foo", source_archive=archive1,
             target_archive=archive2, target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
-            package_version="1.0-1", include_binaries=False)
+            package_version="1.0-1", include_binaries=False,
+            requester=requester)
         self.assertContentEqual([job], source.getActiveJobs(archive2))
 
     def test_getActiveJobs_gets_oldest_first(self):
@@ -249,12 +262,14 @@
         distroseries = self.factory.makeDistroSeries()
         archive1 = self.factory.makeArchive(distroseries.distribution)
         archive2 = self.factory.makeArchive(distroseries.distribution)
+        requester = self.factory.makePerson()
         job_source = getUtility(IPlainPackageCopyJobSource)
         job = job_source.create(
             package_name="foo", source_archive=archive1,
             target_archive=archive2, target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
-            package_version="1.0-1", include_binaries=False)
+            package_version="1.0-1", include_binaries=False,
+            requester=requester)
         naked_job = removeSecurityProxy(job)
         naked_job.reportFailure = FakeMethod()
 
@@ -268,12 +283,14 @@
         package = self.factory.makeSourcePackageName()
         archive1 = self.factory.makeArchive(distroseries.distribution)
         archive2 = self.factory.makeArchive(distroseries.distribution)
+        requester = self.factory.makePerson()
         source = getUtility(IPlainPackageCopyJobSource)
         job = source.create(
             package_name=package.name, source_archive=archive1,
             target_archive=archive2, target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.UPDATES,
-            include_binaries=False, package_version='1.0')
+            include_binaries=False, package_version='1.0',
+            requester=requester)
 
         naked_job = removeSecurityProxy(job)
         naked_job.reportFailure = FakeMethod()
@@ -315,12 +332,14 @@
             archive=target_archive)
 
         source = getUtility(IPlainPackageCopyJobSource)
+        requester = self.factory.makePerson()
         job = source.create(
             package_name="libc",
             source_archive=breezy_archive, target_archive=target_archive,
             target_distroseries=target_series,
             target_pocket=PackagePublishingPocket.RELEASE,
-            package_version="2.8-1", include_binaries=False)
+            package_version="2.8-1", include_binaries=False,
+            requester=requester)
         self.assertEqual("libc", job.package_name)
         self.assertEqual("2.8-1", job.package_version)
 
@@ -348,12 +367,14 @@
         distroseries = self.factory.makeDistroSeries()
         archive1 = self.factory.makeArchive(distroseries.distribution)
         archive2 = self.factory.makeArchive(distroseries.distribution)
+        requester = self.factory.makePerson()
         source = getUtility(IPlainPackageCopyJobSource)
         job = source.create(
             package_name="foo", source_archive=archive1,
             target_archive=archive2, target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
-            package_version="1.0-1", include_binaries=False)
+            package_version="1.0-1", include_binaries=False,
+            requester=requester)
         oops_vars = job.getOopsVars()
         naked_job = removeSecurityProxy(job)
         self.assertIn(
@@ -374,6 +395,7 @@
         distroseries = publisher.breezy_autotest
         archive1 = self.factory.makeArchive(distroseries.distribution)
         archive2 = self.factory.makeArchive(distroseries.distribution)
+        requester = self.factory.makePerson()
         publisher.getPubSource(
             distroseries=distroseries, sourcename="libc",
             version="2.8-1", status=PackagePublishingStatus.PUBLISHED,
@@ -382,7 +404,8 @@
             package_name="libc", source_archive=archive1,
             target_archive=archive2, target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
-            package_version="2.8-1", include_binaries=False)
+            package_version="2.8-1", include_binaries=False,
+            requester=requester)
         transaction.commit()
 
         out, err, exit_code = run_script(
@@ -401,12 +424,14 @@
         distroseries = self.factory.makeDistroSeries()
         archive1 = self.factory.makeArchive(distroseries.distribution)
         archive2 = self.factory.makeArchive(distroseries.distribution)
+        requester = self.factory.makePerson()
         source = getUtility(IPlainPackageCopyJobSource)
         job = source.create(
             package_name="foo", source_archive=archive1,
             target_archive=archive2, target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
-            package_version="1.0-1", include_binaries=True)
+            package_version="1.0-1", include_binaries=True,
+            requester=requester)
         self.assertEqual(
             ("<PlainPackageCopyJob to copy package foo from "
              "{distroseries.distribution.name}/{archive1.name} to "
@@ -519,6 +544,7 @@
         # target_archive.
 
         source = getUtility(IPlainPackageCopyJobSource)
+        requester = self.factory.makePerson()
         job = source.create(
             package_name="libc",
             package_version="2.8-1",
@@ -526,7 +552,8 @@
             target_archive=target_archive,
             target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
-            include_binaries=False)
+            include_binaries=False,
+            requester=requester)
 
         self.runJob(job)
 
@@ -556,6 +583,7 @@
         # Now, run the copy job, which should raise an error because
         # there's no ancestry.
         source = getUtility(IPlainPackageCopyJobSource)
+        requester = self.factory.makePerson()
         job = source.create(
             package_name="copyme",
             package_version="2.8-1",
@@ -563,7 +591,8 @@
             target_archive=target_archive,
             target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
-            include_binaries=False)
+            include_binaries=False,
+            requester=requester)
 
         self.assertRaises(SuspendJobException, self.runJob, job)
         # Simulate the job runner suspending after getting a
@@ -619,6 +648,7 @@
         # Now, run the copy job.
 
         source = getUtility(IPlainPackageCopyJobSource)
+        requester = self.factory.makePerson()
         job = source.create(
             package_name="copyme",
             package_version="2.8-1",
@@ -626,7 +656,8 @@
             target_archive=target_archive,
             target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
-            include_binaries=False)
+            include_binaries=False,
+            requester=requester)
 
         # The job should be suspended and there's a PackageUpload with
         # its package_copy_job set.
@@ -672,6 +703,7 @@
 
         # Now, run the copy job.
         source = getUtility(IPlainPackageCopyJobSource)
+        requester = self.factory.makePerson()
         job = source.create(
             package_name="copyme",
             package_version="2.8-1",
@@ -679,7 +711,8 @@
             target_archive=target_archive,
             target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
-            include_binaries=False)
+            include_binaries=False,
+            requester=requester)
 
         # The job should be suspended and there's a PackageUpload with
         # its package_copy_job set in the UNAPPROVED queue.
@@ -715,6 +748,7 @@
             archive=source_archive)
 
         source = getUtility(IPlainPackageCopyJobSource)
+        requester = self.factory.makePerson()
         job = source.create(
             package_name="copyme",
             package_version="2.8-1",
@@ -722,7 +756,8 @@
             target_archive=target_archive,
             target_distroseries=distroseries,
             target_pocket=PackagePublishingPocket.RELEASE,
-            include_binaries=False)
+            include_binaries=False,
+            requester=requester)
 
         # Run the job so it gains a PackageUpload.
         self.assertRaises(SuspendJobException, self.runJob, job)

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2011-07-13 20:55:34 +0000
+++ lib/lp/testing/factory.py	2011-07-15 12:13:27 +0000
@@ -4165,7 +4165,8 @@
 
     def makePlainPackageCopyJob(
         self, package_name=None, package_version=None, source_archive=None,
-        target_archive=None, target_distroseries=None, target_pocket=None):
+        target_archive=None, target_distroseries=None, target_pocket=None,
+        requester=None):
         """Create a new `PlainPackageCopyJob`."""
         if package_name is None and package_version is None:
             package_name = self.makeSourcePackageName().name
@@ -4178,10 +4179,12 @@
             target_distroseries = self.makeDistroSeries()
         if target_pocket is None:
             target_pocket = self.getAnyPocket()
+        if requester is None:
+            requester = self.makePerson()
         return getUtility(IPlainPackageCopyJobSource).create(
             package_name, source_archive, target_archive,
             target_distroseries, target_pocket,
-            package_version=package_version)
+            package_version=package_version, requester=requester)
 
 
 # Some factory methods return simple Python types. We don't add