Merge lp:~mwhudson/launchpad/reduce-concurrent-job-count into lp:launchpad
- reduce-concurrent-job-count
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Michael Hudson-Doyle | ||||
Approved revision: | not available | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~mwhudson/launchpad/reduce-concurrent-job-count | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
492 lines (+93/-79) 15 files modified
cronscripts/code-import-dispatcher.py (+8/-1) lib/canonical/config/schema-lazr.conf (+1/-1) lib/lp/code/doc/xmlrpc-codeimport-scheduler.txt (+3/-6) lib/lp/code/interfaces/codeimportjob.py (+1/-1) lib/lp/code/interfaces/codeimportmachine.py (+1/-1) lib/lp/code/interfaces/codeimportscheduler.py (+1/-1) lib/lp/code/model/codeimportjob.py (+2/-2) lib/lp/code/model/codeimportmachine.py (+2/-3) lib/lp/code/model/tests/test_codeimport.py (+1/-1) lib/lp/code/model/tests/test_codeimportjob.py (+3/-3) lib/lp/code/model/tests/test_codeimportmachine.py (+9/-11) lib/lp/code/xmlrpc/codeimportscheduler.py (+3/-2) lib/lp/codehosting/codeimport/dispatcher.py (+4/-2) lib/lp/codehosting/codeimport/tests/test_dispatcher.py (+53/-43) lib/lp/codehosting/codeimport/tests/test_workermonitor.py (+1/-1) |
||||
To merge this branch: | bzr merge lp:~mwhudson/launchpad/reduce-concurrent-job-count | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Penhey (community) | Approve | ||
Michael Nelson (community) | code | Needs Information | |
Review via email: mp+19574@code.launchpad.net |
Commit message
Allow a code import machine to set its own limit for how many jobs to run, and lower the default to 3.
Description of the change
Michael Hudson-Doyle (mwhudson) wrote : | # |
Michael Nelson (michael.nelson) wrote : | # |
Hi Michael,
doesn't this need to be a change on the lp-production-
production/
55:max_
Michael Hudson-Doyle (mwhudson) wrote : | # |
Hm, I don't think that config setting takes effect at the moment. I'll investigate further!
Michael Hudson-Doyle (mwhudson) wrote : | # |
Yeah, it doesn't apply, so I'll remove it: https:/
Tim Penhey (thumper) : | # |
Michael Hudson-Doyle (mwhudson) wrote : | # |
OK, so as discussed in IRC, I've made the worker limit settable on the code-import-
Tim Penhey (thumper) wrote : | # |
lib/lp/
You have:
if worker_limit is None:
Which doesn't set self.worker_limit if worker_limit is passed in.
if worker_limit is None:
self.
perhaps.
How hard would it be to add an int Option? That way the OptionParser object does the integer validation and conversion for you.
--max-jobs
Michael Hudson-Doyle (mwhudson) wrote : | # |
Tim,
can you rereview now please?
Cheers,
mwh
Preview Diff
1 | === modified file 'cronscripts/code-import-dispatcher.py' |
2 | --- cronscripts/code-import-dispatcher.py 2009-10-13 14:38:07 +0000 |
3 | +++ cronscripts/code-import-dispatcher.py 2010-02-22 19:42:20 +0000 |
4 | @@ -18,6 +18,12 @@ |
5 | |
6 | class CodeImportDispatcherScript(LaunchpadScript): |
7 | |
8 | + def add_my_options(self): |
9 | + self.parser.add_option( |
10 | + "--max-jobs", dest="max_jobs", type=int, |
11 | + default=config.codeimportdispatcher.max_jobs_per_machine, |
12 | + help="The maximum number of jobs to run on this machine.") |
13 | + |
14 | def run(self, use_web_security=False, implicit_begin=True, |
15 | isolation=None): |
16 | """See `LaunchpadScript.run`. |
17 | @@ -30,7 +36,8 @@ |
18 | def main(self): |
19 | globalErrorUtility.configure('codeimportdispatcher') |
20 | |
21 | - CodeImportDispatcher(self.logger).findAndDispatchJob( |
22 | + dispatcher = CodeImportDispatcher(self.logger, self.options.max_jobs) |
23 | + dispatcher.findAndDispatchJob( |
24 | ServerProxy(config.codeimportdispatcher.codeimportscheduler_url)) |
25 | |
26 | |
27 | |
28 | === modified file 'lib/canonical/config/schema-lazr.conf' |
29 | --- lib/canonical/config/schema-lazr.conf 2010-02-20 04:13:47 +0000 |
30 | +++ lib/canonical/config/schema-lazr.conf 2010-02-22 19:42:20 +0000 |
31 | @@ -449,7 +449,7 @@ |
32 | codeimportscheduler_url: http://xmlrpc-private.launchpad.dev:8087/codeimportscheduler |
33 | |
34 | # The maximum number of jobs to run on a machine at one time. |
35 | -max_jobs_per_machine: 10 |
36 | +max_jobs_per_machine: 3 |
37 | |
38 | # See [error_reports]. |
39 | copy_to_zlog: False |
40 | |
41 | === modified file 'lib/lp/code/doc/xmlrpc-codeimport-scheduler.txt' |
42 | --- lib/lp/code/doc/xmlrpc-codeimport-scheduler.txt 2009-10-22 11:55:51 +0000 |
43 | +++ lib/lp/code/doc/xmlrpc-codeimport-scheduler.txt 2010-02-22 19:42:20 +0000 |
44 | @@ -32,12 +32,9 @@ |
45 | getJobForMachine(), that returns the id of the job that the code |
46 | import slave should next run. |
47 | |
48 | - >>> codeimportscheduler_api.getJobForMachine('bazaar-importer') |
49 | + >>> codeimportscheduler_api.getJobForMachine('bazaar-importer', 2) |
50 | 1 |
51 | |
52 | - >>> from canonical.database.sqlbase import flush_database_updates |
53 | - >>> flush_database_updates() |
54 | - |
55 | The method just calls the 'getJobForMachine' method from the |
56 | ICodeImportJobSet interface, and tests all the details of what it does |
57 | can be found in the tests for ICodeImportJobSet. |
58 | @@ -49,7 +46,7 @@ |
59 | >>> codeimportscheduler = xmlrpclib.ServerProxy( |
60 | ... 'http://xmlrpc-private.launchpad.dev:8087/codeimportscheduler', |
61 | ... transport=XMLRPCTestTransport()) |
62 | - >>> codeimportscheduler.getJobForMachine('bazaar-importer') |
63 | + >>> codeimportscheduler.getJobForMachine('bazaar-importer', 2) |
64 | 0 |
65 | |
66 | This includes the behaviour of auto-creating machine rows for |
67 | @@ -60,7 +57,7 @@ |
68 | >>> print getUtility(ICodeImportMachineSet).getByHostname( |
69 | ... 'doesnt-exist-yet') |
70 | None |
71 | - >>> codeimportscheduler.getJobForMachine('doesnt-exist-yet') |
72 | + >>> codeimportscheduler.getJobForMachine('doesnt-exist-yet', 1) |
73 | 0 |
74 | >>> new_machine = getUtility(ICodeImportMachineSet).getByHostname( |
75 | ... 'doesnt-exist-yet') |
76 | |
77 | === modified file 'lib/lp/code/interfaces/codeimportjob.py' |
78 | --- lib/lp/code/interfaces/codeimportjob.py 2009-06-25 04:06:00 +0000 |
79 | +++ lib/lp/code/interfaces/codeimportjob.py 2010-02-22 19:42:20 +0000 |
80 | @@ -122,7 +122,7 @@ |
81 | # we implement endpoint specific authentication for the private xml-rpc |
82 | # server. |
83 | |
84 | - def getJobForMachine(hostname): |
85 | + def getJobForMachine(hostname, worker_limit): |
86 | """Select a job for the given machine to run and mark it as started. |
87 | |
88 | If there is not already a CodeImportMachine with the given hostname, |
89 | |
90 | === modified file 'lib/lp/code/interfaces/codeimportmachine.py' |
91 | --- lib/lp/code/interfaces/codeimportmachine.py 2009-06-25 04:06:00 +0000 |
92 | +++ lib/lp/code/interfaces/codeimportmachine.py 2010-02-22 19:42:20 +0000 |
93 | @@ -47,7 +47,7 @@ |
94 | description=_("When the controller deamon last recorded it was" |
95 | " running.")) |
96 | |
97 | - def shouldLookForJob(): |
98 | + def shouldLookForJob(worker_limit): |
99 | """Should we look for a job to run on this machine? |
100 | |
101 | There are three reasons we might not look for a job: |
102 | |
103 | === modified file 'lib/lp/code/interfaces/codeimportscheduler.py' |
104 | --- lib/lp/code/interfaces/codeimportscheduler.py 2009-06-25 04:06:00 +0000 |
105 | +++ lib/lp/code/interfaces/codeimportscheduler.py 2010-02-22 19:42:20 +0000 |
106 | @@ -28,7 +28,7 @@ |
107 | when they need more work to do. |
108 | """ |
109 | |
110 | - def getJobForMachine(hostname): |
111 | + def getJobForMachine(hostname, worker_limit): |
112 | """Get a job to run on the slave 'hostname'. |
113 | |
114 | This method selects the most appropriate job for the machine, |
115 | |
116 | === modified file 'lib/lp/code/model/codeimportjob.py' |
117 | --- lib/lp/code/model/codeimportjob.py 2010-01-27 02:48:13 +0000 |
118 | +++ lib/lp/code/model/codeimportjob.py 2010-02-22 19:42:20 +0000 |
119 | @@ -102,7 +102,7 @@ |
120 | except SQLObjectNotFound: |
121 | return None |
122 | |
123 | - def getJobForMachine(self, hostname): |
124 | + def getJobForMachine(self, hostname, worker_limit): |
125 | """See `ICodeImportJobSet`.""" |
126 | job_workflow = getUtility(ICodeImportJobWorkflow) |
127 | for job in self.getReclaimableJobs(): |
128 | @@ -111,7 +111,7 @@ |
129 | if machine is None: |
130 | machine = getUtility(ICodeImportMachineSet).new( |
131 | hostname, CodeImportMachineState.ONLINE) |
132 | - elif not machine.shouldLookForJob(): |
133 | + elif not machine.shouldLookForJob(worker_limit): |
134 | return None |
135 | job = CodeImportJob.selectOne( |
136 | """id IN (SELECT id FROM CodeImportJob |
137 | |
138 | === modified file 'lib/lp/code/model/codeimportmachine.py' |
139 | --- lib/lp/code/model/codeimportmachine.py 2009-06-25 04:06:00 +0000 |
140 | +++ lib/lp/code/model/codeimportmachine.py 2010-02-22 19:42:20 +0000 |
141 | @@ -51,7 +51,7 @@ |
142 | 'CodeImportEvent', joinColumn='machine', |
143 | orderBy=['-date_created', '-id']) |
144 | |
145 | - def shouldLookForJob(self): |
146 | + def shouldLookForJob(self, worker_limit): |
147 | """See `ICodeImportMachine`.""" |
148 | job_count = self.current_jobs.count() |
149 | |
150 | @@ -64,8 +64,7 @@ |
151 | CodeImportMachineOfflineReason.QUIESCED) |
152 | return False |
153 | elif self.state == CodeImportMachineState.ONLINE: |
154 | - max_jobs = config.codeimportdispatcher.max_jobs_per_machine |
155 | - return job_count < max_jobs |
156 | + return job_count < worker_limit |
157 | else: |
158 | raise AssertionError( |
159 | "Unknown machine state %r??" % self.state) |
160 | |
161 | === modified file 'lib/lp/code/model/tests/test_codeimport.py' |
162 | --- lib/lp/code/model/tests/test_codeimport.py 2010-02-01 03:55:59 +0000 |
163 | +++ lib/lp/code/model/tests/test_codeimport.py 2010-02-22 19:42:20 +0000 |
164 | @@ -195,7 +195,7 @@ |
165 | |
166 | def makeApprovedImportWithRunningJob(self): |
167 | code_import = self.makeApprovedImportWithPendingJob() |
168 | - job = CodeImportJobSet().getJobForMachine('machine') |
169 | + job = CodeImportJobSet().getJobForMachine('machine', 10) |
170 | self.assertEqual(code_import.import_job, job) |
171 | return code_import |
172 | |
173 | |
174 | === modified file 'lib/lp/code/model/tests/test_codeimportjob.py' |
175 | --- lib/lp/code/model/tests/test_codeimportjob.py 2010-02-01 03:55:59 +0000 |
176 | +++ lib/lp/code/model/tests/test_codeimportjob.py 2010-02-22 19:42:20 +0000 |
177 | @@ -114,7 +114,7 @@ |
178 | def assertJobIsSelected(self, desired_job): |
179 | """Assert that the expected job is chosen by getJobForMachine.""" |
180 | observed_job = getUtility(ICodeImportJobSet).getJobForMachine( |
181 | - self.machine.hostname) |
182 | + self.machine.hostname, 10) |
183 | self.assert_(observed_job is not None, "No job was selected.") |
184 | self.assertEqual(desired_job, observed_job, |
185 | "Expected job not selected.") |
186 | @@ -122,7 +122,7 @@ |
187 | def assertNoJobSelected(self): |
188 | """Assert that no job is selected.""" |
189 | observed_job = getUtility(ICodeImportJobSet).getJobForMachine( |
190 | - 'machine') |
191 | + 'machine', 10) |
192 | self.assert_(observed_job is None, "Job unexpectedly selected.") |
193 | |
194 | def test_nothingSelectedIfNothingCreated(self): |
195 | @@ -274,7 +274,7 @@ |
196 | machine = self.factory.makeCodeImportMachine(set_online=True) |
197 | login(ANONYMOUS) |
198 | getUtility(ICodeImportJobSet).getJobForMachine( |
199 | - machine.hostname) |
200 | + machine.hostname, 10) |
201 | login_for_code_imports() |
202 | # Now there are no reclaimable jobs. |
203 | self.assertReclaimableJobs([]) |
204 | |
205 | === modified file 'lib/lp/code/model/tests/test_codeimportmachine.py' |
206 | --- lib/lp/code/model/tests/test_codeimportmachine.py 2009-06-25 04:06:00 +0000 |
207 | +++ lib/lp/code/model/tests/test_codeimportmachine.py 2010-02-22 19:42:20 +0000 |
208 | @@ -9,7 +9,6 @@ |
209 | |
210 | from zope.component import getUtility |
211 | |
212 | -from canonical.config import config |
213 | from canonical.database.constants import UTC_NOW |
214 | from lp.code.enums import ( |
215 | CodeImportMachineOfflineReason, CodeImportMachineState) |
216 | @@ -39,13 +38,13 @@ |
217 | def test_machineIsOffline(self): |
218 | # When the machine is offline, we shouldn't look for any jobs. |
219 | self.machine.setOffline(CodeImportMachineOfflineReason.STOPPED) |
220 | - self.assertFalse(self.machine.shouldLookForJob()) |
221 | + self.assertFalse(self.machine.shouldLookForJob(10)) |
222 | |
223 | def test_machineIsQuiescingNoJobsRunning(self): |
224 | # When the machine is quiescing and no jobs are running on this |
225 | # machine, we should set the machine to OFFLINE and not look for jobs. |
226 | self.machine.setQuiescing(self.factory.makePerson()) |
227 | - self.assertFalse(self.machine.shouldLookForJob()) |
228 | + self.assertFalse(self.machine.shouldLookForJob(10)) |
229 | self.assertEqual(self.machine.state, CodeImportMachineState.OFFLINE) |
230 | |
231 | def test_machineIsQuiescingWithJobsRunning(self): |
232 | @@ -53,20 +52,19 @@ |
233 | # machine, we shouldn't look for any more jobs. |
234 | self.createJobRunningOnMachine(self.machine) |
235 | self.machine.setQuiescing(self.factory.makePerson()) |
236 | - self.assertFalse(self.machine.shouldLookForJob()) |
237 | + self.assertFalse(self.machine.shouldLookForJob(10)) |
238 | self.assertEqual(self.machine.state, CodeImportMachineState.QUIESCING) |
239 | |
240 | def test_enoughJobsRunningOnMachine(self): |
241 | # When there are already enough jobs running on this machine, we |
242 | # shouldn't look for any more jobs. |
243 | - for i in range(config.codeimportdispatcher.max_jobs_per_machine): |
244 | - self.createJobRunningOnMachine(self.machine) |
245 | - self.assertFalse(self.machine.shouldLookForJob()) |
246 | + self.createJobRunningOnMachine(self.machine) |
247 | + self.assertFalse(self.machine.shouldLookForJob(worker_limit=1)) |
248 | |
249 | def test_shouldLook(self): |
250 | # If the machine is online and there are not already |
251 | # max_jobs_per_machine jobs running, then we should look for a job. |
252 | - self.assertTrue(self.machine.shouldLookForJob()) |
253 | + self.assertTrue(self.machine.shouldLookForJob(worker_limit=1)) |
254 | |
255 | def test_noHeartbeatWhenCreated(self): |
256 | # Machines are created with a NULL heartbeat. |
257 | @@ -75,18 +73,18 @@ |
258 | def test_noHeartbeatUpdateWhenOffline(self): |
259 | # When the machine is offline, the heartbeat is not updated. |
260 | self.machine.setOffline(CodeImportMachineOfflineReason.STOPPED) |
261 | - self.machine.shouldLookForJob() |
262 | + self.machine.shouldLookForJob(10) |
263 | self.assertTrue(self.machine.heartbeat is None) |
264 | |
265 | def test_heartbeatUpdateWhenQuiescing(self): |
266 | # When the machine is quiescing, the heartbeat is updated. |
267 | self.machine.setQuiescing(self.factory.makePerson()) |
268 | - self.machine.shouldLookForJob() |
269 | + self.machine.shouldLookForJob(10) |
270 | self.assertSqlAttributeEqualsDate(self.machine, 'heartbeat', UTC_NOW) |
271 | |
272 | def test_heartbeatUpdateWhenOnline(self): |
273 | # When the machine is online, the heartbeat is updated. |
274 | - self.machine.shouldLookForJob() |
275 | + self.machine.shouldLookForJob(10) |
276 | self.assertSqlAttributeEqualsDate(self.machine, 'heartbeat', UTC_NOW) |
277 | |
278 | |
279 | |
280 | === modified file 'lib/lp/code/xmlrpc/codeimportscheduler.py' |
281 | --- lib/lp/code/xmlrpc/codeimportscheduler.py 2009-06-25 04:06:00 +0000 |
282 | +++ lib/lp/code/xmlrpc/codeimportscheduler.py 2010-02-22 19:42:20 +0000 |
283 | @@ -22,9 +22,10 @@ |
284 | |
285 | implements(ICodeImportScheduler) |
286 | |
287 | - def getJobForMachine(self, hostname): |
288 | + def getJobForMachine(self, hostname, worker_limit): |
289 | """See `ICodeImportScheduler`.""" |
290 | - job = getUtility(ICodeImportJobSet).getJobForMachine(hostname) |
291 | + job = getUtility(ICodeImportJobSet).getJobForMachine( |
292 | + hostname, worker_limit) |
293 | if job is not None: |
294 | return job.id |
295 | else: |
296 | |
297 | === modified file 'lib/lp/codehosting/codeimport/dispatcher.py' |
298 | --- lib/lp/codehosting/codeimport/dispatcher.py 2009-06-25 04:06:00 +0000 |
299 | +++ lib/lp/codehosting/codeimport/dispatcher.py 2010-02-22 19:42:20 +0000 |
300 | @@ -32,12 +32,13 @@ |
301 | worker_script = os.path.join( |
302 | config.root, 'scripts', 'code-import-worker-db.py') |
303 | |
304 | - def __init__(self, logger): |
305 | + def __init__(self, logger, worker_limit): |
306 | """Initialize an instance. |
307 | |
308 | :param logger: A `Logger` object. |
309 | """ |
310 | self.logger = logger |
311 | + self.worker_limit = worker_limit |
312 | |
313 | def getHostname(self): |
314 | """Return the hostname of this machine. |
315 | @@ -66,7 +67,8 @@ |
316 | def findAndDispatchJob(self, scheduler_client): |
317 | """Check for and dispatch a job if necessary.""" |
318 | |
319 | - job_id = scheduler_client.getJobForMachine(self.getHostname()) |
320 | + job_id = scheduler_client.getJobForMachine( |
321 | + self.getHostname(), self.worker_limit) |
322 | |
323 | if job_id == 0: |
324 | self.logger.info("No jobs pending.") |
325 | |
326 | === modified file 'lib/lp/codehosting/codeimport/tests/test_dispatcher.py' |
327 | --- lib/lp/codehosting/codeimport/tests/test_dispatcher.py 2009-12-14 18:11:07 +0000 |
328 | +++ lib/lp/codehosting/codeimport/tests/test_dispatcher.py 2010-02-22 19:42:20 +0000 |
329 | @@ -11,64 +11,60 @@ |
330 | import shutil |
331 | import socket |
332 | import tempfile |
333 | -from textwrap import dedent |
334 | from unittest import TestLoader |
335 | |
336 | -from twisted.trial.unittest import TestCase |
337 | - |
338 | -from canonical.config import config |
339 | -from lp.codehosting.codeimport.dispatcher import CodeImportDispatcher |
340 | from canonical.launchpad import scripts |
341 | from canonical.launchpad.scripts.logger import QuietFakeLogger |
342 | -from canonical.testing.layers import TwistedLaunchpadZopelessLayer |
343 | +from canonical.testing.layers import BaseLayer |
344 | + |
345 | +from lp.codehosting.codeimport.dispatcher import CodeImportDispatcher |
346 | +from lp.testing import TestCase |
347 | |
348 | |
349 | class StubSchedulerClient: |
350 | - """A stub scheduler client that returns a pre-arranged answer.""" |
351 | + """A scheduler client that returns a pre-arranged answer.""" |
352 | |
353 | def __init__(self, id_to_return): |
354 | self.id_to_return = id_to_return |
355 | |
356 | - def getJobForMachine(self, machine): |
357 | + def getJobForMachine(self, machine, limit): |
358 | return self.id_to_return |
359 | |
360 | |
361 | +class MockSchedulerClient: |
362 | + """A scheduler client that records calls to `getJobForMachine`.""" |
363 | + |
364 | + def __init__(self): |
365 | + self.calls = [] |
366 | + |
367 | + def getJobForMachine(self, machine, limit): |
368 | + self.calls.append((machine, limit)) |
369 | + return 0 |
370 | + |
371 | + |
372 | class TestCodeImportDispatcherUnit(TestCase): |
373 | """Unit tests for `CodeImportDispatcher`.""" |
374 | |
375 | - layer = TwistedLaunchpadZopelessLayer |
376 | + layer = BaseLayer |
377 | |
378 | def setUp(self): |
379 | - self.config_count = 0 |
380 | - self.pushConfig(forced_hostname='none') |
381 | - self.dispatcher = CodeImportDispatcher(QuietFakeLogger()) |
382 | - |
383 | - def pushConfig(self, **args): |
384 | - """Push some key-value pairs into the codeimportdispatcher config. |
385 | - |
386 | - The config values will be restored during test tearDown. |
387 | - """ |
388 | - self.config_count += 1 |
389 | - name = 'test%d' % self.config_count |
390 | - body = '\n'.join(["%s: %s"%(k, v) for k, v in args.iteritems()]) |
391 | - config.push(name, dedent(""" |
392 | - [codeimportdispatcher] |
393 | - %s |
394 | - """ % body)) |
395 | - self.addCleanup(config.pop, name) |
396 | + TestCase.setUp(self) |
397 | + self.pushConfig('codeimportdispatcher', forced_hostname='none') |
398 | + |
399 | + def makeDispatcher(self, worker_limit=10): |
400 | + """Make a `CodeImportDispatcher`.""" |
401 | + return CodeImportDispatcher(QuietFakeLogger(), worker_limit) |
402 | |
403 | def test_getHostname(self): |
404 | # By default, getHostname return the same as socket.gethostname() |
405 | - self.assertEqual( |
406 | - self.dispatcher.getHostname(), |
407 | - socket.gethostname()) |
408 | + dispatcher = self.makeDispatcher() |
409 | + self.assertEqual(socket.gethostname(), dispatcher.getHostname()) |
410 | |
411 | def test_getHostnameOverride(self): |
412 | # getHostname can be overriden by the config for testing, however. |
413 | - self.pushConfig(forced_hostname='test-value') |
414 | - self.assertEqual( |
415 | - self.dispatcher.getHostname(), |
416 | - 'test-value') |
417 | + dispatcher = self.makeDispatcher() |
418 | + self.pushConfig('codeimportdispatcher', forced_hostname='test-value') |
419 | + self.assertEqual('test-value', dispatcher.getHostname()) |
420 | |
421 | def writePythonScript(self, script_path, script_body): |
422 | """Write out an executable Python script. |
423 | @@ -94,6 +90,7 @@ |
424 | |
425 | # We create a script that writes its command line arguments to |
426 | # some a temporary file and examine that. |
427 | + dispatcher = self.makeDispatcher() |
428 | tmpdir = tempfile.mkdtemp() |
429 | self.addCleanup(shutil.rmtree, tmpdir) |
430 | script_path = os.path.join(tmpdir, 'script.py') |
431 | @@ -102,26 +99,39 @@ |
432 | script_path, |
433 | ['import sys', |
434 | 'open(%r, "w").write(str(sys.argv[1:]))' % output_path]) |
435 | - self.dispatcher.worker_script = script_path |
436 | - proc = self.dispatcher.dispatchJob(10) |
437 | + dispatcher.worker_script = script_path |
438 | + proc = dispatcher.dispatchJob(10) |
439 | proc.wait() |
440 | arglist = self.filterOutLoggingOptions(eval(open(output_path).read())) |
441 | - self.assertEqual(arglist, ['10']) |
442 | + self.assertEqual(['10'], arglist) |
443 | |
444 | def test_findAndDispatchJob_jobWaiting(self): |
445 | - # If there is a job to dispatch, then we call dispatchJob with its id. |
446 | + # If there is a job to dispatch, then we call dispatchJob with its id |
447 | + # and the worker_limit supplied to the dispatcher. |
448 | calls = [] |
449 | - self.dispatcher.dispatchJob = lambda job_id: calls.append(job_id) |
450 | - self.dispatcher.findAndDispatchJob(StubSchedulerClient(10)) |
451 | - self.assertEqual(calls, [10]) |
452 | + dispatcher = self.makeDispatcher() |
453 | + dispatcher.dispatchJob = lambda job_id: calls.append(job_id) |
454 | + dispatcher.findAndDispatchJob(StubSchedulerClient(10)) |
455 | + self.assertEqual([10], calls) |
456 | |
457 | def test_findAndDispatchJob_noJobWaiting(self): |
458 | # If there is no job to dispatch, then we just exit quietly. |
459 | calls = [] |
460 | - self.dispatcher.dispatchJob = lambda job_id: calls.append(job_id) |
461 | - self.dispatcher.findAndDispatchJob(StubSchedulerClient(0)) |
462 | - self.assertEqual(calls, []) |
463 | + dispatcher = self.makeDispatcher() |
464 | + dispatcher.dispatchJob = lambda job_id: calls.append(job_id) |
465 | + dispatcher.findAndDispatchJob(StubSchedulerClient(0)) |
466 | + self.assertEqual([], calls) |
467 | |
468 | + def test_findAndDispatchJob_calls_getJobForMachine_with_limit(self): |
469 | + # findAndDispatchJob calls getJobForMachine on the scheduler client |
470 | + # with the hostname and supplied worker limit. |
471 | + worker_limit = self.factory.getUniqueInteger() |
472 | + dispatcher = self.makeDispatcher(worker_limit) |
473 | + scheduler_client = MockSchedulerClient() |
474 | + dispatcher.findAndDispatchJob(scheduler_client) |
475 | + self.assertEqual( |
476 | + [(dispatcher.getHostname(), worker_limit)], |
477 | + scheduler_client.calls) |
478 | |
479 | def test_suite(): |
480 | return TestLoader().loadTestsFromName(__name__) |
481 | |
482 | === modified file 'lib/lp/codehosting/codeimport/tests/test_workermonitor.py' |
483 | --- lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2010-02-03 19:29:27 +0000 |
484 | +++ lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2010-02-22 19:42:20 +0000 |
485 | @@ -566,7 +566,7 @@ |
486 | code_import.updateFromData( |
487 | {'review_status': CodeImportReviewStatus.REVIEWED}, |
488 | self.factory.makePerson()) |
489 | - job = getUtility(ICodeImportJobSet).getJobForMachine('machine') |
490 | + job = getUtility(ICodeImportJobSet).getJobForMachine('machine', 10) |
491 | self.assertEqual(code_import, job.code_import) |
492 | return job |
493 |
As discussed in the linked bug, we should probably do something more sophisticated here to give a machine a reasonable number of jobs to run, but this is much easier for sure.