Merge lp:~gmb/launchpad/blobjob-cronscript-bug-513190 into lp:launchpad/db-devel
- blobjob-cronscript-bug-513190
- Merge into db-devel
Status: | Merged |
---|---|
Approved by: | Graham Binns |
Approved revision: | not available |
Merged at revision: | not available |
Proposed branch: | lp:~gmb/launchpad/blobjob-cronscript-bug-513190 |
Merge into: | lp:launchpad/db-devel |
Diff against target: |
522 lines (+311/-105) (has conflicts) 8 files modified
configs/development/launchpad-lazr.conf (+3/-0) configs/testrunner/launchpad-lazr.conf (+5/-0) cronscripts/process-apport-blobs.py (+33/-0) database/schema/security.cfg (+8/-0) lib/canonical/config/schema-lazr.conf (+7/-1) lib/canonical/launchpad/browser/temporaryblobstorage.py (+25/-1) lib/lp/bugs/tests/test_apportjob.py (+226/-103) lib/lp/services/mail/sendmail.py (+4/-0) Text conflict in lib/canonical/launchpad/browser/temporaryblobstorage.py Text conflict in lib/lp/bugs/tests/test_apportjob.py Text conflict in lib/lp/services/mail/sendmail.py |
To merge this branch: | bzr merge lp:~gmb/launchpad/blobjob-cronscript-bug-513190 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Abel Deuring (community) | code | Approve | |
Review via email: mp+19184@code.launchpad.net |
Commit message
Add a cronscript for running ProcessApportBl
Description of the change
Graham Binns (gmb) wrote : | # |
Graham Binns (gmb) wrote : | # |
Non-crazy diff:
=== modified file 'configs/
--- configs/
+++ configs/
@@ -221,6 +221,9 @@
error_dir: /var/tmp/poimport
oops_prefix: POI
+[process_
+error_dir: /var/tmp/lperr
+
[supermirror_
error_dir: /var/tmp/
oops_prefix: SMP
=== modified file 'configs/
--- configs/
+++ configs/
@@ -193,6 +193,11 @@
error_dir: /var/tmp/
oops_prefix: TPOI
+[process_
+dbuser: process-
+oops_prefix: TAPPORTBLOB
+error_dir: /var/tmp/lperr.test
+
[rosettabranches]
oops_prefix: TRSBR
error_dir: /var/tmp/
=== added file 'cronscripts/
--- cronscripts/
+++ cronscripts/
@@ -0,0 +1,33 @@
+#!/usr/
+#
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+# pylint: disable-msg=W0403
+
+"""Process uploaded Apport BLOBs."""
+
+__metaclass__ = type
+
+import _pythonpath
+
+from canonical.
+
+from lp.services.
+from lp.bugs.
+
+
+class RunProcessAppor
+ """Run ProcessApportBl
+
+ config_name = 'process_
+ source_interface = IProcessApportB
+
+ def main(self):
+ errorlog.
+ return super(RunProces
+
+
+if __name__ == '__main__':
+ script = RunProcessAppor
+ script.
=== modified file 'database/
--- database/
+++ database/
@@ -1897,3 +1897,11 @@
public.bug = SELECT, UPDATE
public.job = SELECT, UPDATE, DELETE
public.bugjob = SELECT, DELETE
+
+[process-
+type=user
+groups=script,read
+public.job = SELECT, UPDATE, DELETE
+public.apportjob = SELECT, INSERT, UPDATE, DELETE
+public.
+public.
=== modified file 'lib/canonical/
--- lib/canonical/
+++ lib/canonical/
@@ -28,7 +28,6 @@
# datatype: string
base_url: http://
-
[calculate_
# The database user which will be used by this process.
# datatype: string
@@ -37,6 +36,13 @@
error_dir: none
copy_to_zlog: false
+[process_
+# The database user which will be used by this process.
+# datatype...
Abel Deuring (adeuring) wrote : | # |
Hi Graham,
anice branch. But please remove the "import pdb; pdb.set_trace()" from temporaryblobst
Preview Diff
1 | === modified file 'configs/development/launchpad-lazr.conf' | |||
2 | --- configs/development/launchpad-lazr.conf 2010-01-22 04:01:17 +0000 | |||
3 | +++ configs/development/launchpad-lazr.conf 2010-02-12 16:13:42 +0000 | |||
4 | @@ -221,6 +221,9 @@ | |||
5 | 221 | error_dir: /var/tmp/poimport | 221 | error_dir: /var/tmp/poimport |
6 | 222 | oops_prefix: POI | 222 | oops_prefix: POI |
7 | 223 | 223 | ||
8 | 224 | [process_apport_blobs] | ||
9 | 225 | error_dir: /var/tmp/lperr | ||
10 | 226 | |||
11 | 224 | [supermirror_puller] | 227 | [supermirror_puller] |
12 | 225 | error_dir: /var/tmp/codehosting.test | 228 | error_dir: /var/tmp/codehosting.test |
13 | 226 | oops_prefix: SMP | 229 | oops_prefix: SMP |
14 | 227 | 230 | ||
15 | === modified file 'configs/testrunner/launchpad-lazr.conf' | |||
16 | --- configs/testrunner/launchpad-lazr.conf 2010-01-29 17:15:31 +0000 | |||
17 | +++ configs/testrunner/launchpad-lazr.conf 2010-02-12 16:13:42 +0000 | |||
18 | @@ -193,6 +193,11 @@ | |||
19 | 193 | error_dir: /var/tmp/poimport.test | 193 | error_dir: /var/tmp/poimport.test |
20 | 194 | oops_prefix: TPOI | 194 | oops_prefix: TPOI |
21 | 195 | 195 | ||
22 | 196 | [process_apport_blobs] | ||
23 | 197 | dbuser: process-apport-blobs | ||
24 | 198 | oops_prefix: TAPPORTBLOB | ||
25 | 199 | error_dir: /var/tmp/lperr.test | ||
26 | 200 | |||
27 | 196 | [rosettabranches] | 201 | [rosettabranches] |
28 | 197 | oops_prefix: TRSBR | 202 | oops_prefix: TRSBR |
29 | 198 | error_dir: /var/tmp/rosettabranches.test | 203 | error_dir: /var/tmp/rosettabranches.test |
30 | 199 | 204 | ||
31 | === added file 'cronscripts/process-apport-blobs.py' | |||
32 | --- cronscripts/process-apport-blobs.py 1970-01-01 00:00:00 +0000 | |||
33 | +++ cronscripts/process-apport-blobs.py 2010-02-12 16:13:41 +0000 | |||
34 | @@ -0,0 +1,33 @@ | |||
35 | 1 | #!/usr/bin/python2.5 | ||
36 | 2 | # | ||
37 | 3 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
38 | 4 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
39 | 5 | |||
40 | 6 | # pylint: disable-msg=W0403 | ||
41 | 7 | |||
42 | 8 | """Process uploaded Apport BLOBs.""" | ||
43 | 9 | |||
44 | 10 | __metaclass__ = type | ||
45 | 11 | |||
46 | 12 | import _pythonpath | ||
47 | 13 | |||
48 | 14 | from canonical.launchpad.webapp import errorlog | ||
49 | 15 | |||
50 | 16 | from lp.services.job.runner import JobCronScript | ||
51 | 17 | from lp.bugs.interfaces.apportjob import IProcessApportBlobJobSource | ||
52 | 18 | |||
53 | 19 | |||
54 | 20 | class RunProcessApportBlobs(JobCronScript): | ||
55 | 21 | """Run ProcessApportBlobJobs.""" | ||
56 | 22 | |||
57 | 23 | config_name = 'process_apport_blobs' | ||
58 | 24 | source_interface = IProcessApportBlobJobSource | ||
59 | 25 | |||
60 | 26 | def main(self): | ||
61 | 27 | errorlog.globalErrorUtility.configure(self.config_name) | ||
62 | 28 | return super(RunProcessApportBlobs, self).main() | ||
63 | 29 | |||
64 | 30 | |||
65 | 31 | if __name__ == '__main__': | ||
66 | 32 | script = RunProcessApportBlobs() | ||
67 | 33 | script.lock_and_run() | ||
68 | 0 | 34 | ||
69 | === modified file 'database/schema/security.cfg' | |||
70 | --- database/schema/security.cfg 2010-02-02 11:58:46 +0000 | |||
71 | +++ database/schema/security.cfg 2010-02-12 16:13:41 +0000 | |||
72 | @@ -1897,3 +1897,11 @@ | |||
73 | 1897 | public.bug = SELECT, UPDATE | 1897 | public.bug = SELECT, UPDATE |
74 | 1898 | public.job = SELECT, UPDATE, DELETE | 1898 | public.job = SELECT, UPDATE, DELETE |
75 | 1899 | public.bugjob = SELECT, DELETE | 1899 | public.bugjob = SELECT, DELETE |
76 | 1900 | |||
77 | 1901 | [process-apport-blobs] | ||
78 | 1902 | type=user | ||
79 | 1903 | groups=script,read | ||
80 | 1904 | public.job = SELECT, UPDATE, DELETE | ||
81 | 1905 | public.apportjob = SELECT, INSERT, UPDATE, DELETE | ||
82 | 1906 | public.libraryfilealias = SELECT, INSERT, UPDATE | ||
83 | 1907 | public.libraryfilecontent = SELECT, INSERT, UPDATE | ||
84 | 1900 | 1908 | ||
85 | === modified file 'lib/canonical/config/schema-lazr.conf' | |||
86 | --- lib/canonical/config/schema-lazr.conf 2010-01-22 04:01:17 +0000 | |||
87 | +++ lib/canonical/config/schema-lazr.conf 2010-02-12 16:13:41 +0000 | |||
88 | @@ -28,7 +28,6 @@ | |||
89 | 28 | # datatype: string | 28 | # datatype: string |
90 | 29 | base_url: http://ftpmaster.internal/ | 29 | base_url: http://ftpmaster.internal/ |
91 | 30 | 30 | ||
92 | 31 | |||
93 | 32 | [calculate_bug_heat] | 31 | [calculate_bug_heat] |
94 | 33 | # The database user which will be used by this process. | 32 | # The database user which will be used by this process. |
95 | 34 | # datatype: string | 33 | # datatype: string |
96 | @@ -37,6 +36,13 @@ | |||
97 | 37 | error_dir: none | 36 | error_dir: none |
98 | 38 | copy_to_zlog: false | 37 | copy_to_zlog: false |
99 | 39 | 38 | ||
100 | 39 | [process_apport_blobs] | ||
101 | 40 | # The database user which will be used by this process. | ||
102 | 41 | # datatype: string | ||
103 | 42 | dbuser: process-apport-blobs | ||
104 | 43 | oops_prefix: APPORTBLOB | ||
105 | 44 | error_dir: none | ||
106 | 45 | copy_to_zlog: false | ||
107 | 40 | 46 | ||
108 | 41 | [branchscanner] | 47 | [branchscanner] |
109 | 42 | # The database user which will be used by this process. | 48 | # The database user which will be used by this process. |
110 | 43 | 49 | ||
111 | === modified file 'lib/canonical/launchpad/browser/temporaryblobstorage.py' | |||
112 | --- lib/canonical/launchpad/browser/temporaryblobstorage.py 2010-02-10 14:17:10 +0000 | |||
113 | +++ lib/canonical/launchpad/browser/temporaryblobstorage.py 2010-02-12 16:13:41 +0000 | |||
114 | @@ -42,6 +42,7 @@ | |||
115 | 42 | # being named like that. | 42 | # being named like that. |
116 | 43 | @action('Continue', name='FORM_SUBMIT') | 43 | @action('Continue', name='FORM_SUBMIT') |
117 | 44 | def continue_action(self, action, data): | 44 | def continue_action(self, action, data): |
118 | 45 | <<<<<<< TREE | ||
119 | 45 | uuid = self.store_blob(data['blob']) | 46 | uuid = self.store_blob(data['blob']) |
120 | 46 | self.request.response.setHeader('X-Launchpad-Blob-Token', uuid) | 47 | self.request.response.setHeader('X-Launchpad-Blob-Token', uuid) |
121 | 47 | self.request.response.addInfoNotification( | 48 | self.request.response.addInfoNotification( |
122 | @@ -51,12 +52,35 @@ | |||
123 | 51 | """Store a blob and return its UUID.""" | 52 | """Store a blob and return its UUID.""" |
124 | 52 | try: | 53 | try: |
125 | 53 | uuid = getUtility(ITemporaryStorageManager).new(blob) | 54 | uuid = getUtility(ITemporaryStorageManager).new(blob) |
126 | 55 | ======= | ||
127 | 56 | uuid = self.store_blob(data['blob']) | ||
128 | 57 | if uuid is not None: | ||
129 | 58 | self.request.response.setHeader('X-Launchpad-Blob-Token', uuid) | ||
130 | 59 | self.request.response.addInfoNotification( | ||
131 | 60 | 'Your ticket is "%s"' % uuid) | ||
132 | 61 | |||
133 | 62 | def store_blob(self, blob): | ||
134 | 63 | """Store a blob and return its UUID.""" | ||
135 | 64 | try: | ||
136 | 65 | uuid = getUtility(ITemporaryStorageManager).new(blob) | ||
137 | 66 | >>>>>>> MERGE-SOURCE | ||
138 | 54 | except BlobTooLarge: | 67 | except BlobTooLarge: |
139 | 55 | self.addError('Uploaded file was too large.') | 68 | self.addError('Uploaded file was too large.') |
141 | 56 | except UploadFailed: | 69 | return None |
142 | 70 | except UploadFailed, e: | ||
143 | 71 | import pdb; pdb.set_trace() | ||
144 | 57 | self.addError('File storage unavailable - try again later.') | 72 | self.addError('File storage unavailable - try again later.') |
145 | 73 | <<<<<<< TREE | ||
146 | 58 | 74 | ||
147 | 59 | # Create ProcessApportBlobJob for the BLOB. | 75 | # Create ProcessApportBlobJob for the BLOB. |
148 | 60 | blob = getUtility(ITemporaryStorageManager).fetch(uuid) | 76 | blob = getUtility(ITemporaryStorageManager).fetch(uuid) |
149 | 61 | getUtility(IProcessApportBlobJobSource).create(blob) | 77 | getUtility(IProcessApportBlobJobSource).create(blob) |
150 | 62 | return uuid | 78 | return uuid |
151 | 79 | ======= | ||
152 | 80 | return None | ||
153 | 81 | else: | ||
154 | 82 | # Create ProcessApportBlobJob for the BLOB. | ||
155 | 83 | blob = getUtility(ITemporaryStorageManager).fetch(uuid) | ||
156 | 84 | getUtility(IProcessApportBlobJobSource).create(blob) | ||
157 | 85 | return uuid | ||
158 | 86 | >>>>>>> MERGE-SOURCE | ||
159 | 63 | 87 | ||
160 | === modified file 'lib/lp/bugs/tests/test_apportjob.py' | |||
161 | --- lib/lp/bugs/tests/test_apportjob.py 2010-02-11 10:59:08 +0000 | |||
162 | +++ lib/lp/bugs/tests/test_apportjob.py 2010-02-12 16:13:42 +0000 | |||
163 | @@ -13,11 +13,20 @@ | |||
164 | 13 | 13 | ||
165 | 14 | from canonical.config import config | 14 | from canonical.config import config |
166 | 15 | from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet | 15 | from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet |
172 | 16 | from canonical.launchpad.interfaces.temporaryblobstorage import ( | 16 | <<<<<<< TREE |
173 | 17 | ITemporaryStorageManager) | 17 | from canonical.launchpad.interfaces.temporaryblobstorage import ( |
174 | 18 | from canonical.launchpad.webapp.interfaces import ILaunchpadRoot | 18 | ITemporaryStorageManager) |
175 | 19 | from canonical.testing import ( | 19 | from canonical.launchpad.webapp.interfaces import ILaunchpadRoot |
176 | 20 | LaunchpadFunctionalLayer, LaunchpadZopelessLayer) | 20 | from canonical.testing import ( |
177 | 21 | LaunchpadFunctionalLayer, LaunchpadZopelessLayer) | ||
178 | 22 | ======= | ||
179 | 23 | from canonical.launchpad.interfaces.temporaryblobstorage import ( | ||
180 | 24 | ITemporaryStorageManager) | ||
181 | 25 | from canonical.launchpad.webapp.interfaces import ILaunchpadRoot | ||
182 | 26 | from canonical.launchpad.scripts.tests import run_script | ||
183 | 27 | from canonical.testing import ( | ||
184 | 28 | LaunchpadFunctionalLayer, LaunchpadZopelessLayer) | ||
185 | 29 | >>>>>>> MERGE-SOURCE | ||
186 | 21 | 30 | ||
187 | 22 | from lp.bugs.interfaces.apportjob import ApportJobType | 31 | from lp.bugs.interfaces.apportjob import ApportJobType |
188 | 23 | from lp.bugs.model.apportjob import ( | 32 | from lp.bugs.model.apportjob import ( |
189 | @@ -162,104 +171,218 @@ | |||
190 | 162 | "LibrarianFileAlias %s" % ( | 171 | "LibrarianFileAlias %s" % ( |
191 | 163 | attachment['filename'], file_alias.id)) | 172 | attachment['filename'], file_alias.id)) |
192 | 164 | 173 | ||
291 | 165 | def test_getByBlobUUID(self): | 174 | <<<<<<< TREE |
292 | 166 | # ProcessApportBlobJob.getByBlobUUID takes a BLOB UUID as a | 175 | def test_getByBlobUUID(self): |
293 | 167 | # parameter and returns any jobs for that BLOB. | 176 | # ProcessApportBlobJob.getByBlobUUID takes a BLOB UUID as a |
294 | 168 | uuid = self.blob.uuid | 177 | # parameter and returns any jobs for that BLOB. |
295 | 169 | 178 | uuid = self.blob.uuid | |
296 | 170 | job = ProcessApportBlobJob.create(self.blob) | 179 | |
297 | 171 | job_from_uuid = ProcessApportBlobJob.getByBlobUUID(uuid) | 180 | job = ProcessApportBlobJob.create(self.blob) |
298 | 172 | self.assertEqual( | 181 | job_from_uuid = ProcessApportBlobJob.getByBlobUUID(uuid) |
299 | 173 | job, job_from_uuid, | 182 | self.assertEqual( |
300 | 174 | "Job returend by getByBlobUUID() did not match original job.") | 183 | job, job_from_uuid, |
301 | 175 | self.assertEqual( | 184 | "Job returend by getByBlobUUID() did not match original job.") |
302 | 176 | self.blob, job_from_uuid.blob, | 185 | self.assertEqual( |
303 | 177 | "BLOB referenced by Job returned by getByBlobUUID() did not " | 186 | self.blob, job_from_uuid.blob, |
304 | 178 | "match original BLOB.") | 187 | "BLOB referenced by Job returned by getByBlobUUID() did not " |
305 | 179 | 188 | "match original BLOB.") | |
306 | 180 | def test_create_job_creates_only_one(self): | 189 | |
307 | 181 | # ProcessApportBlobJob.create() will create only one | 190 | def test_create_job_creates_only_one(self): |
308 | 182 | # ProcessApportBlobJob for a given BLOB, no matter how many | 191 | # ProcessApportBlobJob.create() will create only one |
309 | 183 | # times it is called. | 192 | # ProcessApportBlobJob for a given BLOB, no matter how many |
310 | 184 | current_jobs = list(ProcessApportBlobJob.iterReady()) | 193 | # times it is called. |
311 | 185 | self.assertEqual( | 194 | current_jobs = list(ProcessApportBlobJob.iterReady()) |
312 | 186 | 0, len(current_jobs), | 195 | self.assertEqual( |
313 | 187 | "There should be no ProcessApportBlobJobs. Found %s" % | 196 | 0, len(current_jobs), |
314 | 188 | len(current_jobs)) | 197 | "There should be no ProcessApportBlobJobs. Found %s" % |
315 | 189 | 198 | len(current_jobs)) | |
316 | 190 | job = ProcessApportBlobJob.create(self.blob) | 199 | |
317 | 191 | current_jobs = list(ProcessApportBlobJob.iterReady()) | 200 | job = ProcessApportBlobJob.create(self.blob) |
318 | 192 | self.assertEqual( | 201 | current_jobs = list(ProcessApportBlobJob.iterReady()) |
319 | 193 | 1, len(current_jobs), | 202 | self.assertEqual( |
320 | 194 | "There should be only one ProcessApportBlobJob. Found %s" % | 203 | 1, len(current_jobs), |
321 | 195 | len(current_jobs)) | 204 | "There should be only one ProcessApportBlobJob. Found %s" % |
322 | 196 | 205 | len(current_jobs)) | |
323 | 197 | another_job = ProcessApportBlobJob.create(self.blob) | 206 | |
324 | 198 | current_jobs = list(ProcessApportBlobJob.iterReady()) | 207 | another_job = ProcessApportBlobJob.create(self.blob) |
325 | 199 | self.assertEqual( | 208 | current_jobs = list(ProcessApportBlobJob.iterReady()) |
326 | 200 | 1, len(current_jobs), | 209 | self.assertEqual( |
327 | 201 | "There should be only one ProcessApportBlobJob. Found %s" % | 210 | 1, len(current_jobs), |
328 | 202 | len(current_jobs)) | 211 | "There should be only one ProcessApportBlobJob. Found %s" % |
329 | 203 | 212 | len(current_jobs)) | |
330 | 204 | # If the job is complete, it will no longer show up in the list | 213 | |
331 | 205 | # of ready jobs. However, it won't be possible to create a new | 214 | # If the job is complete, it will no longer show up in the list |
332 | 206 | # job to process the BLOB because each BLOB can only have one | 215 | # of ready jobs. However, it won't be possible to create a new |
333 | 207 | # ProcessApportBlobJob. | 216 | # job to process the BLOB because each BLOB can only have one |
334 | 208 | job.job.start() | 217 | # ProcessApportBlobJob. |
335 | 209 | job.job.complete() | 218 | job.job.start() |
336 | 210 | current_jobs = list(ProcessApportBlobJob.iterReady()) | 219 | job.job.complete() |
337 | 211 | self.assertEqual( | 220 | current_jobs = list(ProcessApportBlobJob.iterReady()) |
338 | 212 | 0, len(current_jobs), | 221 | self.assertEqual( |
339 | 213 | "There should be no ready ProcessApportBlobJobs. Found %s" % | 222 | 0, len(current_jobs), |
340 | 214 | len(current_jobs)) | 223 | "There should be no ready ProcessApportBlobJobs. Found %s" % |
341 | 215 | 224 | len(current_jobs)) | |
342 | 216 | yet_another_job = ProcessApportBlobJob.create(self.blob) | 225 | |
343 | 217 | current_jobs = list(ProcessApportBlobJob.iterReady()) | 226 | yet_another_job = ProcessApportBlobJob.create(self.blob) |
344 | 218 | self.assertEqual( | 227 | current_jobs = list(ProcessApportBlobJob.iterReady()) |
345 | 219 | 0, len(current_jobs), | 228 | self.assertEqual( |
346 | 220 | "There should be no new ProcessApportBlobJobs. Found %s" % | 229 | 0, len(current_jobs), |
347 | 221 | len(current_jobs)) | 230 | "There should be no new ProcessApportBlobJobs. Found %s" % |
348 | 222 | 231 | len(current_jobs)) | |
349 | 223 | # In fact, yet_another_job will be the same job as before, since | 232 | |
350 | 224 | # it's attached to the same BLOB. | 233 | # In fact, yet_another_job will be the same job as before, since |
351 | 225 | self.assertEqual(job.id, yet_another_job.id, "Jobs do not match.") | 234 | # it's attached to the same BLOB. |
352 | 226 | 235 | self.assertEqual(job.id, yet_another_job.id, "Jobs do not match.") | |
353 | 227 | 236 | ||
354 | 228 | class TestTemporaryBlobStorageAddView(TestCaseWithFactory): | 237 | |
355 | 229 | """Test case for the TemporaryBlobStorageAddView.""" | 238 | class TestTemporaryBlobStorageAddView(TestCaseWithFactory): |
356 | 230 | 239 | """Test case for the TemporaryBlobStorageAddView.""" | |
357 | 231 | layer = LaunchpadFunctionalLayer | 240 | |
358 | 232 | 241 | layer = LaunchpadFunctionalLayer | |
359 | 233 | def setUp(self): | 242 | |
360 | 234 | super(TestTemporaryBlobStorageAddView, self).setUp() | 243 | def setUp(self): |
361 | 235 | 244 | super(TestTemporaryBlobStorageAddView, self).setUp() | |
362 | 236 | # Create a BLOB using existing testing data. | 245 | |
363 | 237 | testfiles = os.path.join(config.root, 'lib/lp/bugs/tests/testfiles') | 246 | # Create a BLOB using existing testing data. |
364 | 238 | blob_file = open( | 247 | testfiles = os.path.join(config.root, 'lib/lp/bugs/tests/testfiles') |
365 | 239 | os.path.join(testfiles, 'extra_filebug_data.msg')) | 248 | blob_file = open( |
366 | 240 | self.blob_data = blob_file.read() | 249 | os.path.join(testfiles, 'extra_filebug_data.msg')) |
367 | 241 | blob_file.close() | 250 | self.blob_data = blob_file.read() |
368 | 242 | 251 | blob_file.close() | |
369 | 243 | def test_adding_blob_adds_job(self): | 252 | |
370 | 244 | # Using the TemporaryBlobStorageAddView to upload a new BLOB | 253 | def test_adding_blob_adds_job(self): |
371 | 245 | # will add a new ProcessApportBlobJob for that BLOB. | 254 | # Using the TemporaryBlobStorageAddView to upload a new BLOB |
372 | 246 | view = create_initialized_view( | 255 | # will add a new ProcessApportBlobJob for that BLOB. |
373 | 247 | getUtility(ILaunchpadRoot), '+storeblob') | 256 | view = create_initialized_view( |
374 | 248 | 257 | getUtility(ILaunchpadRoot), '+storeblob') | |
375 | 249 | # The view's store_blob method stores the blob in the database | 258 | |
376 | 250 | # and returns its UUID. | 259 | # The view's store_blob method stores the blob in the database |
377 | 251 | blob_uuid = view.store_blob(self.blob_data) | 260 | # and returns its UUID. |
378 | 252 | transaction.commit() | 261 | blob_uuid = view.store_blob(self.blob_data) |
379 | 253 | 262 | transaction.commit() | |
380 | 254 | # A new ProcessApportBlobJob will have been created for the | 263 | |
381 | 255 | # BLOB. | 264 | # A new ProcessApportBlobJob will have been created for the |
382 | 256 | blob = getUtility(ITemporaryStorageManager).fetch(blob_uuid) | 265 | # BLOB. |
383 | 257 | job = ProcessApportBlobJob.getByBlobUUID(blob_uuid) | 266 | blob = getUtility(ITemporaryStorageManager).fetch(blob_uuid) |
384 | 258 | 267 | job = ProcessApportBlobJob.getByBlobUUID(blob_uuid) | |
385 | 259 | self.assertEqual( | 268 | |
386 | 260 | blob, job.blob, | 269 | self.assertEqual( |
387 | 261 | "BLOB attached to Job returned by getByBlobUUID() did not match " | 270 | blob, job.blob, |
388 | 262 | "expected BLOB.") | 271 | "BLOB attached to Job returned by getByBlobUUID() did not match " |
389 | 272 | "expected BLOB.") | ||
390 | 273 | ======= | ||
391 | 274 | def test_getByBlobUUID(self): | ||
392 | 275 | # ProcessApportBlobJob.getByBlobUUID takes a BLOB UUID as a | ||
393 | 276 | # parameter and returns any jobs for that BLOB. | ||
394 | 277 | uuid = self.blob.uuid | ||
395 | 278 | |||
396 | 279 | job = ProcessApportBlobJob.create(self.blob) | ||
397 | 280 | job_from_uuid = ProcessApportBlobJob.getByBlobUUID(uuid) | ||
398 | 281 | self.assertEqual( | ||
399 | 282 | job, job_from_uuid, | ||
400 | 283 | "Job returend by getByBlobUUID() did not match original job.") | ||
401 | 284 | self.assertEqual( | ||
402 | 285 | self.blob, job_from_uuid.blob, | ||
403 | 286 | "BLOB referenced by Job returned by getByBlobUUID() did not " | ||
404 | 287 | "match original BLOB.") | ||
405 | 288 | |||
406 | 289 | def test_create_job_creates_only_one(self): | ||
407 | 290 | # ProcessApportBlobJob.create() will create only one | ||
408 | 291 | # ProcessApportBlobJob for a given BLOB, no matter how many | ||
409 | 292 | # times it is called. | ||
410 | 293 | current_jobs = list(ProcessApportBlobJob.iterReady()) | ||
411 | 294 | self.assertEqual( | ||
412 | 295 | 0, len(current_jobs), | ||
413 | 296 | "There should be no ProcessApportBlobJobs. Found %s" % | ||
414 | 297 | len(current_jobs)) | ||
415 | 298 | |||
416 | 299 | job = ProcessApportBlobJob.create(self.blob) | ||
417 | 300 | current_jobs = list(ProcessApportBlobJob.iterReady()) | ||
418 | 301 | self.assertEqual( | ||
419 | 302 | 1, len(current_jobs), | ||
420 | 303 | "There should be only one ProcessApportBlobJob. Found %s" % | ||
421 | 304 | len(current_jobs)) | ||
422 | 305 | |||
423 | 306 | another_job = ProcessApportBlobJob.create(self.blob) | ||
424 | 307 | current_jobs = list(ProcessApportBlobJob.iterReady()) | ||
425 | 308 | self.assertEqual( | ||
426 | 309 | 1, len(current_jobs), | ||
427 | 310 | "There should be only one ProcessApportBlobJob. Found %s" % | ||
428 | 311 | len(current_jobs)) | ||
429 | 312 | |||
430 | 313 | # If the job is complete, it will no longer show up in the list | ||
431 | 314 | # of ready jobs. However, it won't be possible to create a new | ||
432 | 315 | # job to process the BLOB because each BLOB can only have one | ||
433 | 316 | # ProcessApportBlobJob. | ||
434 | 317 | job.job.start() | ||
435 | 318 | job.job.complete() | ||
436 | 319 | current_jobs = list(ProcessApportBlobJob.iterReady()) | ||
437 | 320 | self.assertEqual( | ||
438 | 321 | 0, len(current_jobs), | ||
439 | 322 | "There should be no ready ProcessApportBlobJobs. Found %s" % | ||
440 | 323 | len(current_jobs)) | ||
441 | 324 | |||
442 | 325 | yet_another_job = ProcessApportBlobJob.create(self.blob) | ||
443 | 326 | current_jobs = list(ProcessApportBlobJob.iterReady()) | ||
444 | 327 | self.assertEqual( | ||
445 | 328 | 0, len(current_jobs), | ||
446 | 329 | "There should be no new ProcessApportBlobJobs. Found %s" % | ||
447 | 330 | len(current_jobs)) | ||
448 | 331 | |||
449 | 332 | # In fact, yet_another_job will be the same job as before, since | ||
450 | 333 | # it's attached to the same BLOB. | ||
451 | 334 | self.assertEqual(job.id, yet_another_job.id, "Jobs do not match.") | ||
452 | 335 | |||
453 | 336 | def test_cronscript_succeeds(self): | ||
454 | 337 | # The process-apport-blobs cronscript will run all pending | ||
455 | 338 | # ProcessApportBlobJobs. | ||
456 | 339 | ProcessApportBlobJob.create(self.blob) | ||
457 | 340 | transaction.commit() | ||
458 | 341 | |||
459 | 342 | retcode, stdout, stderr = run_script( | ||
460 | 343 | 'cronscripts/process-apport-blobs.py', [], | ||
461 | 344 | expect_returncode=0) | ||
462 | 345 | self.assertEqual('', stdout) | ||
463 | 346 | self.assertIn( | ||
464 | 347 | 'INFO Ran 1 IProcessApportBlobJobSource jobs.\n', stderr) | ||
465 | 348 | |||
466 | 349 | |||
467 | 350 | class TestTemporaryBlobStorageAddView(TestCaseWithFactory): | ||
468 | 351 | """Test case for the TemporaryBlobStorageAddView.""" | ||
469 | 352 | |||
470 | 353 | layer = LaunchpadFunctionalLayer | ||
471 | 354 | |||
472 | 355 | def setUp(self): | ||
473 | 356 | super(TestTemporaryBlobStorageAddView, self).setUp() | ||
474 | 357 | |||
475 | 358 | # Create a BLOB using existing testing data. | ||
476 | 359 | testfiles = os.path.join(config.root, 'lib/lp/bugs/tests/testfiles') | ||
477 | 360 | blob_file = open( | ||
478 | 361 | os.path.join(testfiles, 'extra_filebug_data.msg')) | ||
479 | 362 | self.blob_data = blob_file.read() | ||
480 | 363 | blob_file.close() | ||
481 | 364 | |||
482 | 365 | def test_adding_blob_adds_job(self): | ||
483 | 366 | # Using the TemporaryBlobStorageAddView to upload a new BLOB | ||
484 | 367 | # will add a new ProcessApportBlobJob for that BLOB. | ||
485 | 368 | view = create_initialized_view( | ||
486 | 369 | getUtility(ILaunchpadRoot), '+storeblob') | ||
487 | 370 | |||
488 | 371 | # The view's store_blob method stores the blob in the database | ||
489 | 372 | # and returns its UUID. | ||
490 | 373 | blob_uuid = view.store_blob(self.blob_data) | ||
491 | 374 | transaction.commit() | ||
492 | 375 | |||
493 | 376 | # A new ProcessApportBlobJob will have been created for the | ||
494 | 377 | # BLOB. | ||
495 | 378 | blob = getUtility(ITemporaryStorageManager).fetch(blob_uuid) | ||
496 | 379 | job = ProcessApportBlobJob.getByBlobUUID(blob_uuid) | ||
497 | 380 | |||
498 | 381 | self.assertEqual( | ||
499 | 382 | blob, job.blob, | ||
500 | 383 | "BLOB attached to Job returned by getByBlobUUID() did not match " | ||
501 | 384 | "expected BLOB.") | ||
502 | 385 | >>>>>>> MERGE-SOURCE | ||
503 | 263 | 386 | ||
504 | 264 | 387 | ||
505 | 265 | def test_suite(): | 388 | def test_suite(): |
506 | 266 | 389 | ||
507 | === modified file 'lib/lp/services/mail/sendmail.py' | |||
508 | --- lib/lp/services/mail/sendmail.py 2010-02-12 09:08:43 +0000 | |||
509 | +++ lib/lp/services/mail/sendmail.py 2010-02-12 16:13:42 +0000 | |||
510 | @@ -30,6 +30,10 @@ | |||
511 | 30 | import sets | 30 | import sets |
512 | 31 | 31 | ||
513 | 32 | from binascii import b2a_qp | 32 | from binascii import b2a_qp |
514 | 33 | <<<<<<< TREE | ||
515 | 34 | ======= | ||
516 | 35 | import sha | ||
517 | 36 | >>>>>>> MERGE-SOURCE | ||
518 | 33 | from email.Encoders import encode_base64 | 37 | from email.Encoders import encode_base64 |
519 | 34 | from email.Utils import getaddresses, make_msgid, formatdate, formataddr | 38 | from email.Utils import getaddresses, make_msgid, formatdate, formataddr |
520 | 35 | from email.Message import Message | 39 | from email.Message import Message |
521 | 36 | 40 | ||
522 | === modified file 'lib/lp/soyuz/model/publishing.py' |
This branch adds a cronscript to run ProcessApportBlob jobs. These jobs are created when a new Apport BLOB is uploaded. The idea is that the processing of large BLOBs should be done outside the request, so that +filebug will no longer time out due to the size of uploaded data.
== configs/ development/ launchpad- lazr.conf ==
- I've added an error_dir directive for the process_ apport_ blobs
script.
== configs/ testrunner/ launchpad- lazr.conf ==
- I've added error_dir and oops_prefix directives for the apport_ blobs script.
process_
== cronscripts/ process- apport- blobs.py ==
- I've created a new cronscript based on JobCronScript to run the tBlob jobs.
ProcessAppor
== database/ schema/ security. cfg ==
- I've added the necessary permissions for the script's db user.
== lib/canonical/ config/ schema- lazr.conf ==
- I've added basic config options for the script.
== lib/canonical/ launchpad/ browser/ temporaryblobst orage.py ==
- I've updated the TemporaryBlobSt orageAddView to ensure that errors
are handled properly if the librarian isn't available for some
reason.
== lib/lp/ bugs/tests/ test_apportjob. py ==
- I've added a test to ensure that the cronscript runs.
No lint.