← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~julian-edwards/launchpad/suspend-running-jobs-bug-788612 into lp:launchpad

 

Julian Edwards has proposed merging lp:~julian-edwards/launchpad/suspend-running-jobs-bug-788612 into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~julian-edwards/launchpad/suspend-running-jobs-bug-788612/+merge/62494

= Summary =
Allow running Jobs to suspend themselves.

== Pre-implementation notes ==
Talked to Aaron and we agreed this strategy.

== Implementation details ==
There's a new exception SuspendJobError defined in interfaces/job.py which any job can raise.  It is caught in the main runner code which will subsequently change the job's status to SUSPENDED.

== Tests ==
bin/test -cvv test_runner test_runJob_with_SuspendJobError

== Demo and Q/A ==
n/a

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/services/job/tests/test_runner.py
  lib/lp/services/job/runner.py
  lib/lp/services/job/interfaces/job.py
  lib/lp/services/job/model/job.py
-- 
https://code.launchpad.net/~julian-edwards/launchpad/suspend-running-jobs-bug-788612/+merge/62494
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~julian-edwards/launchpad/suspend-running-jobs-bug-788612 into lp:launchpad.
=== modified file 'lib/lp/services/job/interfaces/job.py'
--- lib/lp/services/job/interfaces/job.py	2011-05-19 15:15:16 +0000
+++ lib/lp/services/job/interfaces/job.py	2011-05-26 14:51:45 +0000
@@ -14,6 +14,7 @@
     'ITwistedJobSource',
     'JobStatus',
     'LeaseHeld',
+    'SuspendJobError',
     ]
 
 
@@ -35,6 +36,11 @@
 from canonical.launchpad import _
 
 
+class SuspendJobError(Exception):
+    """Raised when a running job wants to suspend itself."""
+    pass
+
+
 class LeaseHeld(Exception):
     """Raised when attempting to acquire a list that is already held."""
 

=== modified file 'lib/lp/services/job/model/job.py'
--- lib/lp/services/job/model/job.py	2011-05-19 15:15:16 +0000
+++ lib/lp/services/job/model/job.py	2011-05-26 14:51:45 +0000
@@ -78,6 +78,7 @@
         JobStatus.RUNNING:
             (JobStatus.COMPLETED,
              JobStatus.FAILED,
+             JobStatus.SUSPENDED,
              JobStatus.WAITING),
         JobStatus.FAILED: (),
         JobStatus.COMPLETED: (),

=== modified file 'lib/lp/services/job/runner.py'
--- lib/lp/services/job/runner.py	2011-05-19 15:15:16 +0000
+++ lib/lp/services/job/runner.py	2011-05-26 14:51:45 +0000
@@ -51,6 +51,7 @@
     IJob,
     IRunnableJob,
     LeaseHeld,
+    SuspendJobError,
     )
 from lp.services.mail.sendmail import MailController
 from lp.services.scripts.base import LaunchpadCronScript
@@ -182,6 +183,10 @@
                 self.logger.exception(
                     "Scheduling retry due to %s.", e.__class__.__name__)
                 do_retry = True
+        except SuspendJobError:
+            self.logger.debug("Job suspended itself")
+            job.suspend()
+            self.incomplete_jobs.append(job)
         except Exception:
             self.logger.exception("Job execution raised an exception.")
             transaction.abort()

=== modified file 'lib/lp/services/job/tests/test_runner.py'
--- lib/lp/services/job/tests/test_runner.py	2011-05-25 14:07:19 +0000
+++ lib/lp/services/job/tests/test_runner.py	2011-05-26 14:51:45 +0000
@@ -22,6 +22,7 @@
 from lp.services.job.interfaces.job import (
     IRunnableJob,
     JobStatus,
+    SuspendJobError,
     )
 from lp.services.job.model.job import Job
 from lp.services.job.runner import (
@@ -38,6 +39,7 @@
     TestCaseWithFactory,
     ZopeTestInSubProcess,
     )
+from lp.testing.fakemethod import FakeMethod
 from lp.testing.mail_helpers import pop_notifications
 
 
@@ -363,6 +365,17 @@
         runner.runJobHandleError(job)
         self.assertEqual(1, len(self.oopses))
 
+    def test_runJob_with_SuspendJobError(self):
+        # A job that raises SuspendJobError should end up suspended.
+        job = NullJob('suspended')
+        job.run = FakeMethod(failure=SuspendJobError())
+        runner = JobRunner([job])
+        runner.runJob(job)
+
+        self.assertEqual(JobStatus.SUSPENDED, job.status)
+        self.assertNotIn(job, runner.completed_jobs)
+        self.assertIn(job, runner.incomplete_jobs)
+
 
 class StuckJob(BaseRunnableJob):
     """Simulation of a job that stalls."""