Merge lp:~jelmer/launchpad/617393-cleanup-tmp into lp:launchpad
- 617393-cleanup-tmp
- Merge into devel
Proposed by
Jelmer Vernooij
Status: | Merged |
---|---|
Approved by: | Jelmer Vernooij |
Approved revision: | no longer in the source branch. |
Merged at revision: | 11423 |
Proposed branch: | lp:~jelmer/launchpad/617393-cleanup-tmp |
Merge into: | lp:launchpad |
Diff against target: |
575 lines (+190/-156) 4 files modified
lib/lp/archiveuploader/dscfile.py (+131/-106) lib/lp/archiveuploader/nascentupload.py (+0/-7) lib/lp/archiveuploader/tests/test_dscfile.py (+58/-41) lib/lp/archiveuploader/tests/test_nascentuploadfile.py (+1/-2) |
To merge this branch: | bzr merge lp:~jelmer/launchpad/617393-cleanup-tmp |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Abel Deuring (community) | code | Approve | |
Review via email:
|
Commit message
Clean up temporary directory after unpacking source package for changelog/copyright inspection.
Description of the change
This changes the handling of unpacking of source packages to extract copyright and changelog files.
Rather than keeping the unpacked sources around and cleaning them up afterwards, this makes us clean up the unpacked source directory immediately.
This does have the consequence that we keep the changelog file into memory before streaming it to the librarian rather than streaming it from disk. That seems like a reasonable tradeoff though, as there were already checks in place to make sure that the changelog file is never bigger than 10Mb; we also only keep a single changelog in memory.
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jelmer Vernooij (jelmer) wrote : | # |
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Abel Deuring (adeuring) : | # |
review:
Approve
(code)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/archiveuploader/dscfile.py' | |||
2 | --- lib/lp/archiveuploader/dscfile.py 2010-08-20 20:31:18 +0000 | |||
3 | +++ lib/lp/archiveuploader/dscfile.py 2010-08-24 08:53:41 +0000 | |||
4 | @@ -13,10 +13,11 @@ | |||
5 | 13 | 'SignableTagFile', | 13 | 'SignableTagFile', |
6 | 14 | 'DSCFile', | 14 | 'DSCFile', |
7 | 15 | 'DSCUploadedFile', | 15 | 'DSCUploadedFile', |
10 | 16 | 'findChangelog', | 16 | 'find_changelog', |
11 | 17 | 'findCopyright', | 17 | 'find_copyright', |
12 | 18 | ] | 18 | ] |
13 | 19 | 19 | ||
14 | 20 | from cStringIO import StringIO | ||
15 | 20 | import errno | 21 | import errno |
16 | 21 | import glob | 22 | import glob |
17 | 22 | import os | 23 | import os |
18 | @@ -73,6 +74,70 @@ | |||
19 | 73 | from lp.soyuz.interfaces.sourcepackageformat import SourcePackageFormat | 74 | from lp.soyuz.interfaces.sourcepackageformat import SourcePackageFormat |
20 | 74 | 75 | ||
21 | 75 | 76 | ||
22 | 77 | class DpkgSourceError(Exception): | ||
23 | 78 | |||
24 | 79 | _fmt = "Unable to unpack source package (%(result)s): %(output)s" | ||
25 | 80 | |||
26 | 81 | def __init__(self, output, result): | ||
27 | 82 | Exception.__init__( | ||
28 | 83 | self, self._fmt % {"output": output, "result": result}) | ||
29 | 84 | self.output = output | ||
30 | 85 | self.result = result | ||
31 | 86 | |||
32 | 87 | |||
33 | 88 | def unpack_source(dsc_filepath): | ||
34 | 89 | """Unpack a source package into a temporary directory | ||
35 | 90 | |||
36 | 91 | :param dsc_filepath: Path to the dsc file | ||
37 | 92 | :return: Path to the temporary directory with the unpacked sources | ||
38 | 93 | """ | ||
39 | 94 | # Get a temporary dir together. | ||
40 | 95 | unpacked_dir = tempfile.mkdtemp() | ||
41 | 96 | try: | ||
42 | 97 | # chdir into it | ||
43 | 98 | cwd = os.getcwd() | ||
44 | 99 | os.chdir(unpacked_dir) | ||
45 | 100 | try: | ||
46 | 101 | args = ["dpkg-source", "-sn", "-x", dsc_filepath] | ||
47 | 102 | dpkg_source = subprocess.Popen(args, stdout=subprocess.PIPE, | ||
48 | 103 | stderr=subprocess.PIPE) | ||
49 | 104 | output, unused = dpkg_source.communicate() | ||
50 | 105 | result = dpkg_source.wait() | ||
51 | 106 | finally: | ||
52 | 107 | # When all is said and done, chdir out again so that we can | ||
53 | 108 | # clean up the tree with shutil.rmtree without leaving the | ||
54 | 109 | # process in a directory we're trying to remove. | ||
55 | 110 | os.chdir(cwd) | ||
56 | 111 | |||
57 | 112 | if result != 0: | ||
58 | 113 | dpkg_output = prefix_multi_line_string(output, " ") | ||
59 | 114 | raise DpkgSourceError(result=result, output=dpkg_output) | ||
60 | 115 | except: | ||
61 | 116 | shutil.rmtree(unpacked_dir) | ||
62 | 117 | raise | ||
63 | 118 | |||
64 | 119 | return unpacked_dir | ||
65 | 120 | |||
66 | 121 | |||
67 | 122 | def cleanup_unpacked_dir(unpacked_dir): | ||
68 | 123 | """Remove the directory with an unpacked source package. | ||
69 | 124 | |||
70 | 125 | :param unpacked_dir: Path to the directory. | ||
71 | 126 | """ | ||
72 | 127 | try: | ||
73 | 128 | shutil.rmtree(unpacked_dir) | ||
74 | 129 | except OSError, error: | ||
75 | 130 | if errno.errorcode[error.errno] != 'EACCES': | ||
76 | 131 | raise UploadError( | ||
77 | 132 | "couldn't remove tmp dir %s: code %s" % ( | ||
78 | 133 | unpacked_dir, error.errno)) | ||
79 | 134 | else: | ||
80 | 135 | result = os.system("chmod -R u+rwx " + unpacked_dir) | ||
81 | 136 | if result != 0: | ||
82 | 137 | raise UploadError("chmod failed with %s" % result) | ||
83 | 138 | shutil.rmtree(unpacked_dir) | ||
84 | 139 | |||
85 | 140 | |||
86 | 76 | class SignableTagFile: | 141 | class SignableTagFile: |
87 | 77 | """Base class for signed file verification.""" | 142 | """Base class for signed file verification.""" |
88 | 78 | 143 | ||
89 | @@ -160,7 +225,7 @@ | |||
90 | 160 | "rfc2047": rfc2047, | 225 | "rfc2047": rfc2047, |
91 | 161 | "name": name, | 226 | "name": name, |
92 | 162 | "email": email, | 227 | "email": email, |
94 | 163 | "person": person | 228 | "person": person, |
95 | 164 | } | 229 | } |
96 | 165 | 230 | ||
97 | 166 | 231 | ||
98 | @@ -177,9 +242,9 @@ | |||
99 | 177 | 242 | ||
100 | 178 | # Note that files is actually only set inside verify(). | 243 | # Note that files is actually only set inside verify(). |
101 | 179 | files = None | 244 | files = None |
103 | 180 | # Copyright and changelog_path are only set inside unpackAndCheckSource(). | 245 | # Copyright and changelog are only set inside unpackAndCheckSource(). |
104 | 181 | copyright = None | 246 | copyright = None |
106 | 182 | changelog_path = None | 247 | changelog = None |
107 | 183 | 248 | ||
108 | 184 | def __init__(self, filepath, digest, size, component_and_section, | 249 | def __init__(self, filepath, digest, size, component_and_section, |
109 | 185 | priority, package, version, changes, policy, logger): | 250 | priority, package, version, changes, policy, logger): |
110 | @@ -228,12 +293,9 @@ | |||
111 | 228 | else: | 293 | else: |
112 | 229 | self.processSignature() | 294 | self.processSignature() |
113 | 230 | 295 | ||
114 | 231 | self.unpacked_dir = None | ||
115 | 232 | |||
116 | 233 | # | 296 | # |
117 | 234 | # Useful properties. | 297 | # Useful properties. |
118 | 235 | # | 298 | # |
119 | 236 | |||
120 | 237 | @property | 299 | @property |
121 | 238 | def source(self): | 300 | def source(self): |
122 | 239 | """Return the DSC source name.""" | 301 | """Return the DSC source name.""" |
123 | @@ -267,12 +329,11 @@ | |||
124 | 267 | # | 329 | # |
125 | 268 | # DSC file checks. | 330 | # DSC file checks. |
126 | 269 | # | 331 | # |
127 | 270 | |||
128 | 271 | def verify(self): | 332 | def verify(self): |
129 | 272 | """Verify the uploaded .dsc file. | 333 | """Verify the uploaded .dsc file. |
130 | 273 | 334 | ||
133 | 274 | This method is an error generator, i.e, it returns an iterator over all | 335 | This method is an error generator, i.e, it returns an iterator over |
134 | 275 | exceptions that are generated while processing DSC file checks. | 336 | all exceptions that are generated while processing DSC file checks. |
135 | 276 | """ | 337 | """ |
136 | 277 | 338 | ||
137 | 278 | for error in SourceUploadFile.verify(self): | 339 | for error in SourceUploadFile.verify(self): |
138 | @@ -508,82 +569,53 @@ | |||
139 | 508 | self.logger.debug( | 569 | self.logger.debug( |
140 | 509 | "Verifying uploaded source package by unpacking it.") | 570 | "Verifying uploaded source package by unpacking it.") |
141 | 510 | 571 | ||
142 | 511 | # Get a temporary dir together. | ||
143 | 512 | self.unpacked_dir = tempfile.mkdtemp() | ||
144 | 513 | |||
145 | 514 | # chdir into it | ||
146 | 515 | cwd = os.getcwd() | ||
147 | 516 | os.chdir(self.unpacked_dir) | ||
148 | 517 | dsc_in_tmpdir = os.path.join(self.unpacked_dir, self.filename) | ||
149 | 518 | |||
150 | 519 | package_files = self.files + [self] | ||
151 | 520 | try: | 572 | try: |
169 | 521 | for source_file in package_files: | 573 | unpacked_dir = unpack_source(self.filepath) |
170 | 522 | os.symlink( | 574 | except DpkgSourceError, e: |
154 | 523 | source_file.filepath, | ||
155 | 524 | os.path.join(self.unpacked_dir, source_file.filename)) | ||
156 | 525 | args = ["dpkg-source", "-sn", "-x", dsc_in_tmpdir] | ||
157 | 526 | dpkg_source = subprocess.Popen(args, stdout=subprocess.PIPE, | ||
158 | 527 | stderr=subprocess.PIPE) | ||
159 | 528 | output, unused = dpkg_source.communicate() | ||
160 | 529 | result = dpkg_source.wait() | ||
161 | 530 | finally: | ||
162 | 531 | # When all is said and done, chdir out again so that we can | ||
163 | 532 | # clean up the tree with shutil.rmtree without leaving the | ||
164 | 533 | # process in a directory we're trying to remove. | ||
165 | 534 | os.chdir(cwd) | ||
166 | 535 | |||
167 | 536 | if result != 0: | ||
168 | 537 | dpkg_output = prefix_multi_line_string(output, " ") | ||
171 | 538 | yield UploadError( | 575 | yield UploadError( |
172 | 539 | "dpkg-source failed for %s [return: %s]\n" | 576 | "dpkg-source failed for %s [return: %s]\n" |
173 | 540 | "[dpkg-source output: %s]" | 577 | "[dpkg-source output: %s]" |
199 | 541 | % (self.filename, result, dpkg_output)) | 578 | % (self.filename, e.result, e.output)) |
200 | 542 | 579 | return | |
201 | 543 | # Copy debian/copyright file content. It will be stored in the | 580 | |
202 | 544 | # SourcePackageRelease records. | 581 | try: |
203 | 545 | 582 | # Copy debian/copyright file content. It will be stored in the | |
204 | 546 | # Check if 'dpkg-source' created only one directory. | 583 | # SourcePackageRelease records. |
205 | 547 | temp_directories = [ | 584 | |
206 | 548 | dirname for dirname in os.listdir(self.unpacked_dir) | 585 | # Check if 'dpkg-source' created only one directory. |
207 | 549 | if os.path.isdir(dirname)] | 586 | temp_directories = [ |
208 | 550 | if len(temp_directories) > 1: | 587 | dirname for dirname in os.listdir(unpacked_dir) |
209 | 551 | yield UploadError( | 588 | if os.path.isdir(dirname)] |
210 | 552 | 'Unpacked source contains more than one directory: %r' | 589 | if len(temp_directories) > 1: |
211 | 553 | % temp_directories) | 590 | yield UploadError( |
212 | 554 | 591 | 'Unpacked source contains more than one directory: %r' | |
213 | 555 | # XXX cprov 20070713: We should access only the expected directory | 592 | % temp_directories) |
214 | 556 | # name (<sourcename>-<no_epoch(no_revision(version))>). | 593 | |
215 | 557 | 594 | # XXX cprov 20070713: We should access only the expected directory | |
216 | 558 | # Locate both the copyright and changelog files for later processing. | 595 | # name (<sourcename>-<no_epoch(no_revision(version))>). |
217 | 559 | for error in findCopyright(self, self.unpacked_dir, self.logger): | 596 | |
218 | 560 | yield error | 597 | # Locate both the copyright and changelog files for later |
219 | 561 | 598 | # processing. | |
220 | 562 | for error in findChangelog(self, self.unpacked_dir, self.logger): | 599 | try: |
221 | 563 | yield error | 600 | self.copyright = find_copyright(unpacked_dir, self.logger) |
222 | 564 | 601 | except UploadError, error: | |
223 | 565 | self.logger.debug("Cleaning up source tree.") | 602 | yield error |
224 | 603 | return | ||
225 | 604 | except UploadWarning, warning: | ||
226 | 605 | yield warning | ||
227 | 606 | |||
228 | 607 | try: | ||
229 | 608 | self.changelog = find_changelog(unpacked_dir, self.logger) | ||
230 | 609 | except UploadError, error: | ||
231 | 610 | yield error | ||
232 | 611 | return | ||
233 | 612 | except UploadWarning, warning: | ||
234 | 613 | yield warning | ||
235 | 614 | finally: | ||
236 | 615 | self.logger.debug("Cleaning up source tree.") | ||
237 | 616 | cleanup_unpacked_dir(unpacked_dir) | ||
238 | 566 | self.logger.debug("Done") | 617 | self.logger.debug("Done") |
239 | 567 | 618 | ||
240 | 568 | def cleanUp(self): | ||
241 | 569 | if self.unpacked_dir is None: | ||
242 | 570 | return | ||
243 | 571 | try: | ||
244 | 572 | shutil.rmtree(self.unpacked_dir) | ||
245 | 573 | except OSError, error: | ||
246 | 574 | # XXX: dsilvers 2006-03-15: We currently lack a test for this. | ||
247 | 575 | if errno.errorcode[error.errno] != 'EACCES': | ||
248 | 576 | raise UploadError( | ||
249 | 577 | "%s: couldn't remove tmp dir %s: code %s" % ( | ||
250 | 578 | self.filename, self.unpacked_dir, error.errno)) | ||
251 | 579 | else: | ||
252 | 580 | result = os.system("chmod -R u+rwx " + self.unpacked_dir) | ||
253 | 581 | if result != 0: | ||
254 | 582 | raise UploadError("chmod failed with %s" % result) | ||
255 | 583 | shutil.rmtree(self.unpacked_dir) | ||
256 | 584 | self.unpacked_dir = None | ||
257 | 585 | |||
258 | 586 | |||
259 | 587 | def findBuild(self): | 619 | def findBuild(self): |
260 | 588 | """Find and return the SourcePackageRecipeBuild, if one is specified. | 620 | """Find and return the SourcePackageRecipeBuild, if one is specified. |
261 | 589 | 621 | ||
262 | @@ -641,8 +673,8 @@ | |||
263 | 641 | 673 | ||
264 | 642 | changelog_lfa = self.librarian.create( | 674 | changelog_lfa = self.librarian.create( |
265 | 643 | "changelog", | 675 | "changelog", |
268 | 644 | os.stat(self.changelog_path).st_size, | 676 | len(self.changelog), |
269 | 645 | open(self.changelog_path, "r"), | 677 | StringIO(self.changelog), |
270 | 646 | "text/x-debian-source-changelog", | 678 | "text/x-debian-source-changelog", |
271 | 647 | restricted=self.policy.archive.private) | 679 | restricted=self.policy.archive.private) |
272 | 648 | 680 | ||
273 | @@ -702,6 +734,7 @@ | |||
274 | 702 | validation inside DSCFile.verify(); there is no | 734 | validation inside DSCFile.verify(); there is no |
275 | 703 | store_in_database() method. | 735 | store_in_database() method. |
276 | 704 | """ | 736 | """ |
277 | 737 | |||
278 | 705 | def __init__(self, filepath, digest, size, policy, logger): | 738 | def __init__(self, filepath, digest, size, policy, logger): |
279 | 706 | component_and_section = priority = "--no-value--" | 739 | component_and_section = priority = "--no-value--" |
280 | 707 | NascentUploadFile.__init__( | 740 | NascentUploadFile.__init__( |
281 | @@ -721,7 +754,7 @@ | |||
282 | 721 | 754 | ||
283 | 722 | :param source_file: The directory where the source was extracted | 755 | :param source_file: The directory where the source was extracted |
284 | 723 | :param source_dir: The directory where the source was extracted. | 756 | :param source_dir: The directory where the source was extracted. |
286 | 724 | :return fullpath: The full path of the file, else return None if the | 757 | :return fullpath: The full path of the file, else return None if the |
287 | 725 | file is not found. | 758 | file is not found. |
288 | 726 | """ | 759 | """ |
289 | 727 | # Instead of trying to predict the unpacked source directory name, | 760 | # Instead of trying to predict the unpacked source directory name, |
290 | @@ -744,50 +777,42 @@ | |||
291 | 744 | return fullpath | 777 | return fullpath |
292 | 745 | return None | 778 | return None |
293 | 746 | 779 | ||
295 | 747 | def findCopyright(dsc_file, source_dir, logger): | 780 | |
296 | 781 | def find_copyright(source_dir, logger): | ||
297 | 748 | """Find and store any debian/copyright. | 782 | """Find and store any debian/copyright. |
298 | 749 | 783 | ||
299 | 750 | :param dsc_file: A DSCFile object where the copyright will be stored. | ||
300 | 751 | :param source_dir: The directory where the source was extracted. | 784 | :param source_dir: The directory where the source was extracted. |
301 | 752 | :param logger: A logger object for debug output. | 785 | :param logger: A logger object for debug output. |
302 | 786 | :return: Contents of copyright file | ||
303 | 753 | """ | 787 | """ |
309 | 754 | try: | 788 | copyright_file = findFile(source_dir, 'debian/copyright') |
305 | 755 | copyright_file = findFile(source_dir, 'debian/copyright') | ||
306 | 756 | except UploadError, error: | ||
307 | 757 | yield error | ||
308 | 758 | return | ||
310 | 759 | if copyright_file is None: | 789 | if copyright_file is None: |
313 | 760 | yield UploadWarning("No copyright file found.") | 790 | raise UploadWarning("No copyright file found.") |
312 | 761 | return | ||
314 | 762 | 791 | ||
315 | 763 | logger.debug("Copying copyright contents.") | 792 | logger.debug("Copying copyright contents.") |
320 | 764 | dsc_file.copyright = open(copyright_file).read().strip() | 793 | return open(copyright_file).read().strip() |
321 | 765 | 794 | ||
322 | 766 | 795 | ||
323 | 767 | def findChangelog(dsc_file, source_dir, logger): | 796 | def find_changelog(source_dir, logger): |
324 | 768 | """Find and move any debian/changelog. | 797 | """Find and move any debian/changelog. |
325 | 769 | 798 | ||
326 | 770 | This function finds the changelog file within the source package. The | 799 | This function finds the changelog file within the source package. The |
327 | 771 | changelog file is later uploaded to the librarian by | 800 | changelog file is later uploaded to the librarian by |
328 | 772 | DSCFile.storeInDatabase(). | 801 | DSCFile.storeInDatabase(). |
329 | 773 | 802 | ||
330 | 774 | :param dsc_file: A DSCFile object where the copyright will be stored. | ||
331 | 775 | :param source_dir: The directory where the source was extracted. | 803 | :param source_dir: The directory where the source was extracted. |
332 | 776 | :param logger: A logger object for debug output. | 804 | :param logger: A logger object for debug output. |
333 | 805 | :return: Changelog contents | ||
334 | 777 | """ | 806 | """ |
340 | 778 | try: | 807 | changelog_file = findFile(source_dir, 'debian/changelog') |
336 | 779 | changelog_file = findFile(source_dir, 'debian/changelog') | ||
337 | 780 | except UploadError, error: | ||
338 | 781 | yield error | ||
339 | 782 | return | ||
341 | 783 | if changelog_file is None: | 808 | if changelog_file is None: |
342 | 784 | # Policy requires debian/changelog to always exist. | 809 | # Policy requires debian/changelog to always exist. |
345 | 785 | yield UploadError("No changelog file found.") | 810 | raise UploadError("No changelog file found.") |
344 | 786 | return | ||
346 | 787 | 811 | ||
347 | 788 | # Move the changelog file out of the package direcotry | 812 | # Move the changelog file out of the package direcotry |
348 | 789 | logger.debug("Found changelog") | 813 | logger.debug("Found changelog") |
350 | 790 | dsc_file.changelog_path = changelog_file | 814 | return open(changelog_file, 'r').read() |
351 | 815 | |||
352 | 791 | 816 | ||
353 | 792 | def check_format_1_0_files(filename, file_type_counts, component_counts, | 817 | def check_format_1_0_files(filename, file_type_counts, component_counts, |
354 | 793 | bzip2_count): | 818 | bzip2_count): |
355 | 794 | 819 | ||
356 | === modified file 'lib/lp/archiveuploader/nascentupload.py' | |||
357 | --- lib/lp/archiveuploader/nascentupload.py 2010-08-20 20:31:18 +0000 | |||
358 | +++ lib/lp/archiveuploader/nascentupload.py 2010-08-24 08:53:41 +0000 | |||
359 | @@ -893,12 +893,6 @@ | |||
360 | 893 | 'Exception while accepting:\n %s' % e, exc_info=True) | 893 | 'Exception while accepting:\n %s' % e, exc_info=True) |
361 | 894 | self.do_reject(notify) | 894 | self.do_reject(notify) |
362 | 895 | return False | 895 | return False |
363 | 896 | else: | ||
364 | 897 | self.cleanUp() | ||
365 | 898 | |||
366 | 899 | def cleanUp(self): | ||
367 | 900 | if self.changes.dsc is not None: | ||
368 | 901 | self.changes.dsc.cleanUp() | ||
369 | 902 | 896 | ||
370 | 903 | def do_reject(self, notify=True): | 897 | def do_reject(self, notify=True): |
371 | 904 | """Reject the current upload given the reason provided.""" | 898 | """Reject the current upload given the reason provided.""" |
372 | @@ -929,7 +923,6 @@ | |||
373 | 929 | self.queue_root.notify(summary_text=self.rejection_message, | 923 | self.queue_root.notify(summary_text=self.rejection_message, |
374 | 930 | changes_file_object=changes_file_object, logger=self.logger) | 924 | changes_file_object=changes_file_object, logger=self.logger) |
375 | 931 | changes_file_object.close() | 925 | changes_file_object.close() |
376 | 932 | self.cleanUp() | ||
377 | 933 | 926 | ||
378 | 934 | def _createQueueEntry(self): | 927 | def _createQueueEntry(self): |
379 | 935 | """Return a PackageUpload object.""" | 928 | """Return a PackageUpload object.""" |
380 | 936 | 929 | ||
381 | === modified file 'lib/lp/archiveuploader/tests/test_dscfile.py' | |||
382 | --- lib/lp/archiveuploader/tests/test_dscfile.py 2010-08-20 20:31:18 +0000 | |||
383 | +++ lib/lp/archiveuploader/tests/test_dscfile.py 2010-08-24 08:53:41 +0000 | |||
384 | @@ -10,10 +10,12 @@ | |||
385 | 10 | from canonical.launchpad.scripts.logger import QuietFakeLogger | 10 | from canonical.launchpad.scripts.logger import QuietFakeLogger |
386 | 11 | from canonical.testing.layers import LaunchpadZopelessLayer | 11 | from canonical.testing.layers import LaunchpadZopelessLayer |
387 | 12 | from lp.archiveuploader.dscfile import ( | 12 | from lp.archiveuploader.dscfile import ( |
388 | 13 | cleanup_unpacked_dir, | ||
389 | 13 | DSCFile, | 14 | DSCFile, |
392 | 14 | findChangelog, | 15 | find_changelog, |
393 | 15 | findCopyright, | 16 | find_copyright, |
394 | 16 | format_to_file_checker_map, | 17 | format_to_file_checker_map, |
395 | 18 | unpack_source, | ||
396 | 17 | ) | 19 | ) |
397 | 18 | from lp.archiveuploader.nascentuploadfile import UploadError | 20 | from lp.archiveuploader.nascentuploadfile import UploadError |
398 | 19 | from lp.archiveuploader.tests import ( | 21 | from lp.archiveuploader.tests import ( |
399 | @@ -37,9 +39,6 @@ | |||
400 | 37 | 39 | ||
401 | 38 | class TestDscFile(TestCase): | 40 | class TestDscFile(TestCase): |
402 | 39 | 41 | ||
403 | 40 | class MockDSCFile: | ||
404 | 41 | copyright = None | ||
405 | 42 | |||
406 | 43 | def setUp(self): | 42 | def setUp(self): |
407 | 44 | super(TestDscFile, self).setUp() | 43 | super(TestDscFile, self).setUp() |
408 | 45 | self.tmpdir = self.makeTemporaryDirectory() | 44 | self.tmpdir = self.makeTemporaryDirectory() |
409 | @@ -47,7 +46,6 @@ | |||
410 | 47 | os.makedirs(self.dir_path) | 46 | os.makedirs(self.dir_path) |
411 | 48 | self.copyright_path = os.path.join(self.dir_path, "copyright") | 47 | self.copyright_path = os.path.join(self.dir_path, "copyright") |
412 | 49 | self.changelog_path = os.path.join(self.dir_path, "changelog") | 48 | self.changelog_path = os.path.join(self.dir_path, "changelog") |
413 | 50 | self.dsc_file = self.MockDSCFile() | ||
414 | 51 | 49 | ||
415 | 52 | def testBadDebianCopyright(self): | 50 | def testBadDebianCopyright(self): |
416 | 53 | """Test that a symlink as debian/copyright will fail. | 51 | """Test that a symlink as debian/copyright will fail. |
417 | @@ -56,14 +54,10 @@ | |||
418 | 56 | dangling symlink in an attempt to try and access files on the system | 54 | dangling symlink in an attempt to try and access files on the system |
419 | 57 | processing the source packages.""" | 55 | processing the source packages.""" |
420 | 58 | os.symlink("/etc/passwd", self.copyright_path) | 56 | os.symlink("/etc/passwd", self.copyright_path) |
426 | 59 | errors = list(findCopyright( | 57 | error = self.assertRaises( |
427 | 60 | self.dsc_file, self.tmpdir, mock_logger_quiet)) | 58 | UploadError, find_copyright, self.tmpdir, mock_logger_quiet) |
423 | 61 | |||
424 | 62 | self.assertEqual(len(errors), 1) | ||
425 | 63 | self.assertIsInstance(errors[0], UploadError) | ||
428 | 64 | self.assertEqual( | 59 | self.assertEqual( |
431 | 65 | errors[0].args[0], | 60 | error.args[0], "Symbolic link for debian/copyright not allowed") |
430 | 66 | "Symbolic link for debian/copyright not allowed") | ||
432 | 67 | 61 | ||
433 | 68 | def testGoodDebianCopyright(self): | 62 | def testGoodDebianCopyright(self): |
434 | 69 | """Test that a proper copyright file will be accepted""" | 63 | """Test that a proper copyright file will be accepted""" |
435 | @@ -72,11 +66,8 @@ | |||
436 | 72 | file.write(copyright) | 66 | file.write(copyright) |
437 | 73 | file.close() | 67 | file.close() |
438 | 74 | 68 | ||
444 | 75 | errors = list(findCopyright( | 69 | self.assertEquals( |
445 | 76 | self.dsc_file, self.tmpdir, mock_logger_quiet)) | 70 | copyright, find_copyright(self.tmpdir, mock_logger_quiet)) |
441 | 77 | |||
442 | 78 | self.assertEqual(len(errors), 0) | ||
443 | 79 | self.assertEqual(self.dsc_file.copyright, copyright) | ||
446 | 80 | 71 | ||
447 | 81 | def testBadDebianChangelog(self): | 72 | def testBadDebianChangelog(self): |
448 | 82 | """Test that a symlink as debian/changelog will fail. | 73 | """Test that a symlink as debian/changelog will fail. |
449 | @@ -85,14 +76,10 @@ | |||
450 | 85 | dangling symlink in an attempt to try and access files on the system | 76 | dangling symlink in an attempt to try and access files on the system |
451 | 86 | processing the source packages.""" | 77 | processing the source packages.""" |
452 | 87 | os.symlink("/etc/passwd", self.changelog_path) | 78 | os.symlink("/etc/passwd", self.changelog_path) |
458 | 88 | errors = list(findChangelog( | 79 | error = self.assertRaises( |
459 | 89 | self.dsc_file, self.tmpdir, mock_logger_quiet)) | 80 | UploadError, find_changelog, self.tmpdir, mock_logger_quiet) |
455 | 90 | |||
456 | 91 | self.assertEqual(len(errors), 1) | ||
457 | 92 | self.assertIsInstance(errors[0], UploadError) | ||
460 | 93 | self.assertEqual( | 81 | self.assertEqual( |
463 | 94 | errors[0].args[0], | 82 | error.args[0], "Symbolic link for debian/changelog not allowed") |
462 | 95 | "Symbolic link for debian/changelog not allowed") | ||
464 | 96 | 83 | ||
465 | 97 | def testGoodDebianChangelog(self): | 84 | def testGoodDebianChangelog(self): |
466 | 98 | """Test that a proper changelog file will be accepted""" | 85 | """Test that a proper changelog file will be accepted""" |
467 | @@ -101,12 +88,8 @@ | |||
468 | 101 | file.write(changelog) | 88 | file.write(changelog) |
469 | 102 | file.close() | 89 | file.close() |
470 | 103 | 90 | ||
477 | 104 | errors = list(findChangelog( | 91 | self.assertEquals( |
478 | 105 | self.dsc_file, self.tmpdir, mock_logger_quiet)) | 92 | changelog, find_changelog(self.tmpdir, mock_logger_quiet)) |
473 | 106 | |||
474 | 107 | self.assertEqual(len(errors), 0) | ||
475 | 108 | self.assertEqual(self.dsc_file.changelog_path, | ||
476 | 109 | self.changelog_path) | ||
479 | 110 | 93 | ||
480 | 111 | def testOversizedFile(self): | 94 | def testOversizedFile(self): |
481 | 112 | """Test that a file larger than 10MiB will fail. | 95 | """Test that a file larger than 10MiB will fail. |
482 | @@ -125,13 +108,10 @@ | |||
483 | 125 | file.write(empty_file) | 108 | file.write(empty_file) |
484 | 126 | file.close() | 109 | file.close() |
485 | 127 | 110 | ||
490 | 128 | errors = list(findChangelog( | 111 | error = self.assertRaises( |
491 | 129 | self.dsc_file, self.tmpdir, mock_logger_quiet)) | 112 | UploadError, find_changelog, self.tmpdir, mock_logger_quiet) |
488 | 130 | |||
489 | 131 | self.assertIsInstance(errors[0], UploadError) | ||
492 | 132 | self.assertEqual( | 113 | self.assertEqual( |
495 | 133 | errors[0].args[0], | 114 | error.args[0], "debian/changelog file too large, 10MiB max") |
494 | 134 | "debian/changelog file too large, 10MiB max") | ||
496 | 135 | 115 | ||
497 | 136 | 116 | ||
498 | 137 | class TestDscFileLibrarian(TestCaseWithFactory): | 117 | class TestDscFileLibrarian(TestCaseWithFactory): |
499 | @@ -141,6 +121,7 @@ | |||
500 | 141 | 121 | ||
501 | 142 | def getDscFile(self, name): | 122 | def getDscFile(self, name): |
502 | 143 | dsc_path = datadir(os.path.join('suite', name, name + '.dsc')) | 123 | dsc_path = datadir(os.path.join('suite', name, name + '.dsc')) |
503 | 124 | |||
504 | 144 | class Changes: | 125 | class Changes: |
505 | 145 | architectures = ['source'] | 126 | architectures = ['source'] |
506 | 146 | logger = QuietFakeLogger() | 127 | logger = QuietFakeLogger() |
507 | @@ -157,10 +138,7 @@ | |||
508 | 157 | os.chmod(tempdir, 0555) | 138 | os.chmod(tempdir, 0555) |
509 | 158 | try: | 139 | try: |
510 | 159 | dsc_file = self.getDscFile('bar_1.0-1') | 140 | dsc_file = self.getDscFile('bar_1.0-1') |
515 | 160 | try: | 141 | list(dsc_file.verify()) |
512 | 161 | list(dsc_file.verify()) | ||
513 | 162 | finally: | ||
514 | 163 | dsc_file.cleanUp() | ||
516 | 164 | finally: | 142 | finally: |
517 | 165 | os.chmod(tempdir, 0755) | 143 | os.chmod(tempdir, 0755) |
518 | 166 | 144 | ||
519 | @@ -292,3 +270,42 @@ | |||
520 | 292 | # A 3.0 (native) source with component tarballs is invalid. | 270 | # A 3.0 (native) source with component tarballs is invalid. |
521 | 293 | self.assertErrorsForFiles( | 271 | self.assertErrorsForFiles( |
522 | 294 | [self.wrong_files_error], {NATIVE_TARBALL: 1}, {'foo': 1}) | 272 | [self.wrong_files_error], {NATIVE_TARBALL: 1}, {'foo': 1}) |
523 | 273 | |||
524 | 274 | |||
525 | 275 | class UnpackedDirTests(TestCase): | ||
526 | 276 | """Tests for unpack_source and cleanup_unpacked_dir.""" | ||
527 | 277 | |||
528 | 278 | def test_unpack_source(self): | ||
529 | 279 | # unpack_source unpacks in a temporary directory and returns the | ||
530 | 280 | # path. | ||
531 | 281 | unpacked_dir = unpack_source( | ||
532 | 282 | datadir(os.path.join('suite', 'bar_1.0-1', 'bar_1.0-1.dsc'))) | ||
533 | 283 | try: | ||
534 | 284 | self.assertEquals(["bar-1.0"], os.listdir(unpacked_dir)) | ||
535 | 285 | self.assertContentEqual( | ||
536 | 286 | ["THIS_IS_BAR", "debian"], | ||
537 | 287 | os.listdir(os.path.join(unpacked_dir, "bar-1.0"))) | ||
538 | 288 | finally: | ||
539 | 289 | cleanup_unpacked_dir(unpacked_dir) | ||
540 | 290 | |||
541 | 291 | def test_cleanup(self): | ||
542 | 292 | # cleanup_dir removes the temporary directory and all files under it. | ||
543 | 293 | temp_dir = self.makeTemporaryDirectory() | ||
544 | 294 | unpacked_dir = os.path.join(temp_dir, "unpacked") | ||
545 | 295 | os.mkdir(unpacked_dir) | ||
546 | 296 | os.mkdir(os.path.join(unpacked_dir, "bar_1.0")) | ||
547 | 297 | cleanup_unpacked_dir(unpacked_dir) | ||
548 | 298 | self.assertFalse(os.path.exists(unpacked_dir)) | ||
549 | 299 | |||
550 | 300 | def test_cleanup_invalid_mode(self): | ||
551 | 301 | # cleanup_dir can remove a directory even if the mode does | ||
552 | 302 | # not allow it. | ||
553 | 303 | temp_dir = self.makeTemporaryDirectory() | ||
554 | 304 | unpacked_dir = os.path.join(temp_dir, "unpacked") | ||
555 | 305 | os.mkdir(unpacked_dir) | ||
556 | 306 | bar_path = os.path.join(unpacked_dir, "bar_1.0") | ||
557 | 307 | os.mkdir(bar_path) | ||
558 | 308 | os.chmod(bar_path, 0600) | ||
559 | 309 | os.chmod(unpacked_dir, 0600) | ||
560 | 310 | cleanup_unpacked_dir(unpacked_dir) | ||
561 | 311 | self.assertFalse(os.path.exists(unpacked_dir)) | ||
562 | 295 | 312 | ||
563 | === modified file 'lib/lp/archiveuploader/tests/test_nascentuploadfile.py' | |||
564 | --- lib/lp/archiveuploader/tests/test_nascentuploadfile.py 2010-08-20 20:31:18 +0000 | |||
565 | +++ lib/lp/archiveuploader/tests/test_nascentuploadfile.py 2010-08-24 08:53:41 +0000 | |||
566 | @@ -169,8 +169,7 @@ | |||
567 | 169 | uploadfile = self.createDSCFile( | 169 | uploadfile = self.createDSCFile( |
568 | 170 | "foo.dsc", dsc, "main/net", "extra", "dulwich", "0.42", | 170 | "foo.dsc", dsc, "main/net", "extra", "dulwich", "0.42", |
569 | 171 | self.createChangesFile("foo.changes", changes)) | 171 | self.createChangesFile("foo.changes", changes)) |
572 | 172 | (uploadfile.changelog_path, changelog_digest, changelog_size) = ( | 172 | uploadfile.changelog = "DUMMY" |
571 | 173 | self.writeUploadFile("changelog", "DUMMY")) | ||
573 | 174 | uploadfile.files = [] | 173 | uploadfile.files = [] |
574 | 175 | release = uploadfile.storeInDatabase(None) | 174 | release = uploadfile.storeInDatabase(None) |
575 | 176 | self.assertEquals("0.42", release.version) | 175 | self.assertEquals("0.42", release.version) |
Tests: ./bin/test lp.archiveuploader
QA: Run the process uploader and see if any files are left behind in /tmp