Merge lp:~cjwatson/launchpad/custom-uefi into lp:launchpad

Proposed by Colin Watson on 2012-06-22
Status: Merged
Approved by: Brad Crittenden on 2012-06-22
Approved revision: no longer in the source branch.
Merged at revision: 15547
Proposed branch: lp:~cjwatson/launchpad/custom-uefi
Merge into: lp:launchpad
Diff against target: 1106 lines (+473/-95)
25 files modified
lib/lp/archivepublisher/config.py (+6/-1)
lib/lp/archivepublisher/customupload.py (+11/-3)
lib/lp/archivepublisher/ddtp_tarball.py (+16/-10)
lib/lp/archivepublisher/debian_installer.py (+11/-10)
lib/lp/archivepublisher/dist_upgrader.py (+13/-11)
lib/lp/archivepublisher/model/ftparchive.py (+5/-10)
lib/lp/archivepublisher/tests/test_config.py (+6/-0)
lib/lp/archivepublisher/tests/test_ddtp_tarball.py (+8/-1)
lib/lp/archivepublisher/tests/test_debian_installer.py (+8/-1)
lib/lp/archivepublisher/tests/test_dist_upgrader.py (+8/-1)
lib/lp/archivepublisher/tests/test_ftparchive.py (+1/-28)
lib/lp/archivepublisher/tests/test_generate_extra_overrides.py (+2/-7)
lib/lp/archivepublisher/tests/test_uefi.py (+132/-0)
lib/lp/archivepublisher/uefi.py (+141/-0)
lib/lp/archiveuploader/nascentuploadfile.py (+29/-0)
lib/lp/archiveuploader/tests/test_nascentuploadfile.py (+32/-2)
lib/lp/archiveuploader/uploadpolicy.py (+8/-0)
lib/lp/services/osutils.py (+4/-4)
lib/lp/soyuz/browser/queue.py (+2/-1)
lib/lp/soyuz/browser/tests/builder-views.txt (+1/-1)
lib/lp/soyuz/configure.zcml (+1/-0)
lib/lp/soyuz/enums.py (+6/-0)
lib/lp/soyuz/interfaces/queue.py (+4/-2)
lib/lp/soyuz/model/queue.py (+16/-2)
lib/lp/soyuz/scripts/custom_uploads_copier.py (+2/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/custom-uefi
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code 2012-06-22 Approve on 2012-06-22
Review via email: mp+111626@code.launchpad.net

Commit Message

Add UEFI custom upload format.

Description of the Change

== Summary ==

Bug 1016594: Ubuntu Engineering needs Launchpad to support signing UEFI boot loaders and publishing them in the Ubuntu archive.

== Proposed fix ==

Create a new custom upload format for UEFI. This provides a reasonable place for us to sign images on publication.

Make the key/certificate location configurable in launchpad-lazr.conf, for fairly obvious reasons. Filesystem locations may differ, we're only going to want the real key on production, and developer machines don't need a key at all.

Never auto-approve uploads containing UEFI files for signing, since signed images are going to be rather high-value. This means that we don't have to invent some new permissioning system for who's allowed to upload UEFI images or which packages they're allowed to go in; instead, we can just rely on them all being held for manual approval.

== LOC Rationale ==

+311. This feature is critical to UE, and I have 3039 lines of credit right now, so I'd very much like to use some of that. Most of my other work at the moment is coming out LoC-negative.

== Tests ==

bin/test -vvct test_uefi -t test_ftparchive -t test_generate_extra_overrides -t test_nascentuploadfile -t test_custom_uploads_copier

== Demo and Q/A ==

Once sbsigntool has been installed on dogfood along with a test key, we should change efilinux to add a raw-uefi custom tarball and upload that to dogfood. It should be extracted to /dists/quantal/main/uefi/efilinux-<ARCH>/<VERSION>/ and the .efi file signed by the test key, and a current -> <VERSION> symlink should be created.

== Lint ==

Pre-existing lint in *-lazr.conf files, which I don't think I should touch:

./configs/development/launchpad-lazr.conf
      64: Line exceeds 80 characters.
      93: Line exceeds 80 characters.
./lib/lp/services/config/schema-lazr.conf
     457: Line exceeds 80 characters.
    1050: Line exceeds 80 characters.
    1057: Line exceeds 80 characters.
    1595: Line exceeds 80 characters.

To post a comment you must log in.
Brad Crittenden (bac) wrote :

Hi Colin,

What an interesting branch! Well, not the branch per se but the whole endeavor. I knew this work was coming but hadn't paid attention to the details so it was a good read.

<supertrivial>
In lib/lp/archivepublisher/uefi.py the items in __all__ should be alphabetized.
</supertrivial>

The comment in uefi.py:

The filename should be something like:
        <TYPE>_<VERSION>_<ARCH>.tar.gz

seems a bit breezy to me. When I first read it I thought the naming convention was optional. Should the description really read "The filename must be of the form:" ?

In fact, if the filename does not follow that pattern there could be problems in setTargetDirectory. Perhaps there needs to be checks in there and a test to exercise it.

On a related note, it looks like setTargetDirectory and getSeriesKey both embed the same knowledge about the structure of the filename. A common method could be used by both so that the filename parsing (simple as it is) isn't repeated.

I know it was not your doing, but I also know you share an aversion to poor spelling, so could you fix the occurrences of 'wheter' in lib/lp/soyuz/interfaces/queue.py (and perhaps the one other place it appears).

Thanks Colin.

review: Approve (code)
William Grant (wgrant) wrote :

6 [archivepublisher]
7 run_parts_location: none
8 +uefi_key_location: none
9 +uefi_cert_location: none

This is global to the entire config, so it'll affect any other archive that's published under it (eg. partner).

658 + UEFI = DBItem(6, """
659 + uefi
660 +
661 + A signed UEFI boot loader image.
662 + """)

Isn't the custom upload *un*signed?

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/archivepublisher/config.py'
2--- lib/lp/archivepublisher/config.py 2012-01-01 02:58:52 +0000
3+++ lib/lp/archivepublisher/config.py 2012-07-03 12:54:32 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
6+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8 #
9 # This is the python package that defines the
10@@ -81,6 +81,11 @@
11 pubconf.miscroot = None
12 pubconf.germinateroot = None
13
14+ if archive.is_main:
15+ pubconf.uefiroot = pubconf.archiveroot + '-uefi'
16+ else:
17+ pubconf.uefiroot = None
18+
19 pubconf.poolroot = os.path.join(pubconf.archiveroot, 'pool')
20 pubconf.distsroot = os.path.join(pubconf.archiveroot, 'dists')
21
22
23=== modified file 'lib/lp/archivepublisher/customupload.py'
24--- lib/lp/archivepublisher/customupload.py 2012-06-29 08:40:05 +0000
25+++ lib/lp/archivepublisher/customupload.py 2012-07-03 12:54:32 +0000
26@@ -101,11 +101,11 @@
27
28 self.tmpdir = None
29
30- def process(self, archive_root, tarfile_path, distroseries):
31+ def process(self, pubconf, tarfile_path, distroseries):
32 """Process the upload and install it into the archive."""
33 self.tarfile_path = tarfile_path
34 try:
35- self.setTargetDirectory(archive_root, tarfile_path, distroseries)
36+ self.setTargetDirectory(pubconf, tarfile_path, distroseries)
37 self.checkForConflicts()
38 self.extract()
39 self.installFiles()
40@@ -113,7 +113,15 @@
41 finally:
42 self.cleanup()
43
44- def setTargetDirectory(self, archive_root, tarfile_path, distroseries):
45+ @staticmethod
46+ def parsePath(tarfile_path):
47+ """Parse tarfile_path, returning its useful components.
48+
49+ :raises ValueError: If tarfile_path is incorrectly formed.
50+ """
51+ raise NotImplementedError
52+
53+ def setTargetDirectory(self, pubconf, tarfile_path, distroseries):
54 """Set self.targetdir based on parameters.
55
56 This should also set self.version and self.arch (if applicable) as a
57
58=== modified file 'lib/lp/archivepublisher/ddtp_tarball.py'
59--- lib/lp/archivepublisher/ddtp_tarball.py 2012-05-28 13:13:53 +0000
60+++ lib/lp/archivepublisher/ddtp_tarball.py 2012-07-03 12:54:32 +0000
61@@ -12,7 +12,10 @@
62
63 __metaclass__ = type
64
65-__all__ = ['process_ddtp_tarball']
66+__all__ = [
67+ 'DdtpTarballUpload',
68+ 'process_ddtp_tarball',
69+ ]
70
71 import os
72
73@@ -22,7 +25,7 @@
74 class DdtpTarballUpload(CustomUpload):
75 """DDTP (Debian Description Translation Project) tarball upload
76
77- The tarball should be name as:
78+ The tarball filename must be of the form:
79
80 <NAME>_<COMPONENT>_<VERSION>.tar.gz
81
82@@ -44,13 +47,16 @@
83 """
84 custom_type = "ddtp-tarball"
85
86- def setTargetDirectory(self, archive_root, tarfile_path, distroseries):
87- tarfile_base = os.path.basename(tarfile_path)
88- name, component, self.version = tarfile_base.split('_')
89+ @staticmethod
90+ def parsePath(tarfile_path):
91+ name, component, version = os.path.basename(tarfile_path).split("_")
92+ return name, component, version
93+
94+ def setTargetDirectory(self, pubconf, tarfile_path, distroseries):
95+ _, component, self.version = self.parsePath(tarfile_path)
96 self.arch = None
97-
98- self.targetdir = os.path.join(archive_root, 'dists',
99- distroseries, component)
100+ self.targetdir = os.path.join(
101+ pubconf.archiveroot, 'dists', distroseries, component)
102
103 def checkForConflicts(self):
104 # We just overwrite older files, so no conflicts are possible.
105@@ -65,7 +71,7 @@
106 pass
107
108
109-def process_ddtp_tarball(archive_root, tarfile_path, distroseries):
110+def process_ddtp_tarball(pubconf, tarfile_path, distroseries):
111 """Process a raw-ddtp-tarball tarfile.
112
113 Unpacking it into the given archive for the given distroseries.
114@@ -73,4 +79,4 @@
115 anything goes wrong.
116 """
117 upload = DdtpTarballUpload()
118- upload.process(archive_root, tarfile_path, distroseries)
119+ upload.process(pubconf, tarfile_path, distroseries)
120
121=== modified file 'lib/lp/archivepublisher/debian_installer.py'
122--- lib/lp/archivepublisher/debian_installer.py 2012-05-30 10:25:43 +0000
123+++ lib/lp/archivepublisher/debian_installer.py 2012-07-03 12:54:32 +0000
124@@ -22,7 +22,7 @@
125 class DebianInstallerUpload(CustomUpload):
126 """ Debian Installer custom upload.
127
128- The debian-installer filename should be something like:
129+ The debian-installer filename must be of the form:
130
131 <BASE>_<VERSION>_<ARCH>.tar.gz
132
133@@ -40,20 +40,21 @@
134 """
135 custom_type = "installer"
136
137- def setTargetDirectory(self, archive_root, tarfile_path, distroseries):
138- tarfile_base = os.path.basename(tarfile_path)
139- _, self.version, self.arch = tarfile_base.split("_")
140- self.arch = self.arch.split(".")[0]
141+ @staticmethod
142+ def parsePath(tarfile_path):
143+ base, version, arch = os.path.basename(tarfile_path).split("_")
144+ return base, version, arch.split(".")[0]
145
146+ def setTargetDirectory(self, pubconf, tarfile_path, distroseries):
147+ _, self.version, self.arch = self.parsePath(tarfile_path)
148 self.targetdir = os.path.join(
149- archive_root, 'dists', distroseries, 'main',
150+ pubconf.archiveroot, 'dists', distroseries, 'main',
151 'installer-%s' % self.arch)
152
153 @classmethod
154 def getSeriesKey(cls, tarfile_path):
155 try:
156- _, _, arch = os.path.basename(tarfile_path).split("_")
157- return arch.split(".")[0]
158+ return cls.parsePath(tarfile_path)[2]
159 except ValueError:
160 return None
161
162@@ -70,7 +71,7 @@
163 return filename.startswith('%s/' % self.version)
164
165
166-def process_debian_installer(archive_root, tarfile_path, distroseries):
167+def process_debian_installer(pubconf, tarfile_path, distroseries):
168 """Process a raw-installer tarfile.
169
170 Unpacking it into the given archive for the given distroseries.
171@@ -78,4 +79,4 @@
172 wrong.
173 """
174 upload = DebianInstallerUpload()
175- upload.process(archive_root, tarfile_path, distroseries)
176+ upload.process(pubconf, tarfile_path, distroseries)
177
178=== modified file 'lib/lp/archivepublisher/dist_upgrader.py'
179--- lib/lp/archivepublisher/dist_upgrader.py 2012-06-29 08:40:05 +0000
180+++ lib/lp/archivepublisher/dist_upgrader.py 2012-07-03 12:54:32 +0000
181@@ -34,7 +34,7 @@
182 Dist-Upgrader is a tarball containing files for performing automatic
183 distroseries upgrades, driven by architecture.
184
185- The tarball should be name as:
186+ The tarball filename must be of the form:
187
188 <NAME>_<VERSION>_<ARCH>.tar.gz
189
190@@ -57,19 +57,21 @@
191 """
192 custom_type = "dist-upgrader"
193
194- def setTargetDirectory(self, archive_root, tarfile_path, distroseries):
195- tarfile_base = os.path.basename(tarfile_path)
196- name, self.version, self.arch = tarfile_base.split("_")
197- self.arch = self.arch.split(".")[0]
198+ @staticmethod
199+ def parsePath(tarfile_path):
200+ name, version, arch = os.path.basename(tarfile_path).split("_")
201+ return name, version, arch.split(".")[0]
202
203- self.targetdir = os.path.join(archive_root, 'dists', distroseries,
204- 'main', 'dist-upgrader-%s' % self.arch)
205+ def setTargetDirectory(self, pubconf, tarfile_path, distroseries):
206+ _, self.version, self.arch = self.parsePath(tarfile_path)
207+ self.targetdir = os.path.join(
208+ pubconf.archiveroot, 'dists', distroseries, 'main',
209+ 'dist-upgrader-%s' % self.arch)
210
211 @classmethod
212 def getSeriesKey(cls, tarfile_path):
213 try:
214- _, _, arch = os.path.basename(tarfile_path).split("_")
215- return arch.split(".")[0]
216+ return cls.parsePath(tarfile_path)[2]
217 except ValueError:
218 return None
219
220@@ -95,7 +97,7 @@
221 return version and not filename.startswith('current')
222
223
224-def process_dist_upgrader(archive_root, tarfile_path, distroseries):
225+def process_dist_upgrader(pubconf, tarfile_path, distroseries):
226 """Process a raw-dist-upgrader tarfile.
227
228 Unpacking it into the given archive for the given distroseries.
229@@ -103,4 +105,4 @@
230 wrong.
231 """
232 upload = DistUpgraderUpload()
233- upload.process(archive_root, tarfile_path, distroseries)
234+ upload.process(pubconf, tarfile_path, distroseries)
235
236=== modified file 'lib/lp/archivepublisher/model/ftparchive.py'
237--- lib/lp/archivepublisher/model/ftparchive.py 2012-03-27 11:08:12 +0000
238+++ lib/lp/archivepublisher/model/ftparchive.py 2012-07-03 12:54:32 +0000
239@@ -21,6 +21,7 @@
240 from lp.services.database.decoratedresultset import DecoratedResultSet
241 from lp.services.database.stormexpr import Concatenate
242 from lp.services.librarian.model import LibraryFileAlias
243+from lp.services.osutils import write_file
244 from lp.services.webapp.interfaces import (
245 DEFAULT_FLAVOR,
246 IStoreSelector,
247@@ -42,12 +43,6 @@
248 return (os.path.basename(filename).split("_"))[0]
249
250
251-def f_touch(*parts):
252- """Touch the file named by the arguments concatenated as a path."""
253- fname = os.path.join(*parts)
254- open(fname, "w").close()
255-
256-
257 def safe_mkdir(path):
258 """Ensures the path exists, creating it if it doesn't."""
259 if not os.path.exists(path):
260@@ -215,15 +210,15 @@
261 (comp, "debian-installer"),
262 (comp, "src"),
263 ):
264- f_touch(os.path.join(
265+ write_file(os.path.join(
266 self._config.overrideroot,
267- ".".join(("override", suite) + path)))
268+ ".".join(("override", suite) + path)), "")
269
270 # Create empty file lists.
271 def touch_list(*parts):
272- f_touch(os.path.join(
273+ write_file(os.path.join(
274 self._config.overrideroot,
275- "_".join((suite, ) + parts)))
276+ "_".join((suite, ) + parts)), "")
277 touch_list(comp, "source")
278
279 arch_tags = [
280
281=== modified file 'lib/lp/archivepublisher/tests/test_config.py'
282--- lib/lp/archivepublisher/tests/test_config.py 2012-05-21 12:55:59 +0000
283+++ lib/lp/archivepublisher/tests/test_config.py 2012-07-03 12:54:32 +0000
284@@ -48,6 +48,7 @@
285 archiveroot + "-germinate", primary_config.germinateroot)
286 self.assertEqual(
287 self.root + "/ubuntutest-temp", primary_config.temproot)
288+ self.assertEqual(archiveroot + "-uefi", primary_config.uefiroot)
289
290 def test_partner_config(self):
291 # Partner archive configuration is correct.
292@@ -69,6 +70,7 @@
293 self.assertIsNone(partner_config.germinateroot)
294 self.assertEqual(
295 self.root + "/ubuntutest-temp", partner_config.temproot)
296+ self.assertEqual(archiveroot + "-uefi", partner_config.uefiroot)
297
298 def test_debug_config(self):
299 # The publisher configuration for DEBUG archives points to
300@@ -88,6 +90,7 @@
301 self.assertIsNone(debug_config.miscroot)
302 self.assertIsNone(debug_config.germinateroot)
303 self.assertEqual(self.root + "/ubuntutest-temp", debug_config.temproot)
304+ self.assertEqual(archiveroot + "-uefi", debug_config.uefiroot)
305
306 def test_copy_config(self):
307 # In the case of copy archives (used for rebuild testing) the
308@@ -109,6 +112,7 @@
309 self.assertEqual(
310 archiveroot + "-germinate", copy_config.germinateroot)
311 self.assertEqual(archiveroot + "-temp", copy_config.temproot)
312+ self.assertIsNone(copy_config.uefiroot)
313
314
315 class TestGetPubConfigPPA(TestCaseWithFactory):
316@@ -144,6 +148,7 @@
317 self.assertIsNone(self.ppa_config.germinateroot)
318 self.assertEqual(
319 "/var/tmp/archive/ubuntutest-temp", self.ppa_config.temproot)
320+ self.assertIsNone(self.ppa_config.uefiroot)
321
322 def test_private_ppa_separate_root(self):
323 # Private PPAs are published to a different location.
324@@ -172,3 +177,4 @@
325 self.assertIsNone(p3a_config.germinateroot)
326 self.assertEqual(
327 "/var/tmp/archive/ubuntutest-temp", p3a_config.temproot)
328+ self.assertIsNone(p3a_config.uefiroot)
329
330=== modified file 'lib/lp/archivepublisher/tests/test_ddtp_tarball.py'
331--- lib/lp/archivepublisher/tests/test_ddtp_tarball.py 2012-05-25 13:28:31 +0000
332+++ lib/lp/archivepublisher/tests/test_ddtp_tarball.py 2012-07-03 12:54:32 +0000
333@@ -14,11 +14,18 @@
334 from lp.testing import TestCase
335
336
337+class FakeConfig:
338+ """A fake publisher configuration."""
339+ def __init__(self, archiveroot):
340+ self.archiveroot = archiveroot
341+
342+
343 class TestDdtpTarball(TestCase):
344
345 def setUp(self):
346 super(TestDdtpTarball, self).setUp()
347 self.temp_dir = self.makeTemporaryDirectory()
348+ self.pubconf = FakeConfig(self.temp_dir)
349 self.suite = "distroseries"
350 # CustomUpload.installFiles requires a umask of 022.
351 old_umask = os.umask(022)
352@@ -33,7 +40,7 @@
353 def process(self):
354 self.archive.close()
355 self.buffer.close()
356- process_ddtp_tarball(self.temp_dir, self.path, self.suite)
357+ process_ddtp_tarball(self.pubconf, self.path, self.suite)
358
359 def getTranslationsPath(self, filename):
360 return os.path.join(
361
362=== modified file 'lib/lp/archivepublisher/tests/test_debian_installer.py'
363--- lib/lp/archivepublisher/tests/test_debian_installer.py 2012-05-30 10:25:43 +0000
364+++ lib/lp/archivepublisher/tests/test_debian_installer.py 2012-07-03 12:54:32 +0000
365@@ -21,11 +21,18 @@
366 from lp.testing import TestCase
367
368
369+class FakeConfig:
370+ """A fake publisher configuration."""
371+ def __init__(self, archiveroot):
372+ self.archiveroot = archiveroot
373+
374+
375 class TestDebianInstaller(TestCase):
376
377 def setUp(self):
378 super(TestDebianInstaller, self).setUp()
379 self.temp_dir = self.makeTemporaryDirectory()
380+ self.pubconf = FakeConfig(self.temp_dir)
381 self.suite = "distroseries"
382 # CustomUpload.installFiles requires a umask of 022.
383 old_umask = os.umask(022)
384@@ -51,7 +58,7 @@
385 def process(self):
386 self.archive.close()
387 self.buffer.close()
388- process_debian_installer(self.temp_dir, self.path, self.suite)
389+ process_debian_installer(self.pubconf, self.path, self.suite)
390
391 def getInstallerPath(self, versioned_filename=None):
392 installer_path = os.path.join(
393
394=== modified file 'lib/lp/archivepublisher/tests/test_dist_upgrader.py'
395--- lib/lp/archivepublisher/tests/test_dist_upgrader.py 2012-05-30 10:25:43 +0000
396+++ lib/lp/archivepublisher/tests/test_dist_upgrader.py 2012-07-03 12:54:32 +0000
397@@ -22,11 +22,18 @@
398 from lp.testing import TestCase
399
400
401+class FakeConfig:
402+ """A fake publisher configuration."""
403+ def __init__(self, archiveroot):
404+ self.archiveroot = archiveroot
405+
406+
407 class TestDistUpgrader(TestCase):
408
409 def setUp(self):
410 super(TestDistUpgrader, self).setUp()
411 self.temp_dir = self.makeTemporaryDirectory()
412+ self.pubconf = FakeConfig(self.temp_dir)
413 self.suite = "distroseries"
414 # CustomUpload.installFiles requires a umask of 022.
415 old_umask = os.umask(022)
416@@ -41,7 +48,7 @@
417 def process(self):
418 self.archive.close()
419 self.buffer.close()
420- process_dist_upgrader(self.temp_dir, self.path, self.suite)
421+ process_dist_upgrader(self.pubconf, self.path, self.suite)
422
423 def getUpgraderPath(self):
424 return os.path.join(
425
426=== modified file 'lib/lp/archivepublisher/tests/test_ftparchive.py'
427--- lib/lp/archivepublisher/tests/test_ftparchive.py 2012-03-27 12:07:15 +0000
428+++ lib/lp/archivepublisher/tests/test_ftparchive.py 2012-07-03 12:54:32 +0000
429@@ -16,7 +16,6 @@
430 from lp.archivepublisher.diskpool import DiskPool
431 from lp.archivepublisher.model.ftparchive import (
432 AptFTPArchiveFailure,
433- f_touch,
434 FTPArchiveHandler,
435 )
436 from lp.archivepublisher.publishing import Publisher
437@@ -27,10 +26,7 @@
438 BufferLogger,
439 DevNullLogger,
440 )
441-from lp.testing import (
442- TestCase,
443- TestCaseWithFactory,
444- )
445+from lp.testing import TestCaseWithFactory
446 from lp.testing.dbuser import switch_dbuser
447 from lp.testing.layers import (
448 LaunchpadZopelessLayer,
449@@ -493,26 +489,3 @@
450 distro = distroarchseries.distroseries.distribution
451 fa = FTPArchiveHandler(DevNullLogger(), None, None, distro, None)
452 self.assertRaises(AptFTPArchiveFailure, fa.runApt, "bogus-config")
453-
454-
455-class TestFTouch(TestCase):
456- """Tests for f_touch function."""
457-
458- def setUp(self):
459- TestCase.setUp(self)
460- self.test_folder = self.useTempDir()
461-
462- def test_f_touch_new_file(self):
463- # Test f_touch correctly creates a new file.
464- f_touch(self.test_folder, "file_to_touch")
465- self.assertTrue(os.path.exists("%s/file_to_touch" % self.test_folder))
466-
467- def test_f_touch_existing_file(self):
468- # Test f_touch truncates existing files.
469- with open("%s/file_to_truncate" % self.test_folder, "w") as f:
470- f.write("I'm some test contents")
471-
472- f_touch(self.test_folder, "file_to_leave_alone")
473-
474- with open("%s/file_to_leave_alone" % self.test_folder, "r") as f:
475- self.assertEqual("", f.read())
476
477=== modified file 'lib/lp/archivepublisher/tests/test_generate_extra_overrides.py'
478--- lib/lp/archivepublisher/tests/test_generate_extra_overrides.py 2012-05-21 19:03:16 +0000
479+++ lib/lp/archivepublisher/tests/test_generate_extra_overrides.py 2012-07-03 12:54:32 +0000
480@@ -29,6 +29,7 @@
481 from lp.services.osutils import (
482 ensure_directory_exists,
483 open_for_writing,
484+ write_file,
485 )
486 from lp.services.scripts.base import LaunchpadScriptFailure
487 from lp.services.utils import file_exists
488@@ -47,12 +48,6 @@
489 return handle.read()
490
491
492-def touch(path):
493- """Create an empty file at path."""
494- with open_for_writing(path, "a"):
495- pass
496-
497-
498 class TestAtomicFile(TestCaseWithFactory):
499 """Tests for the AtomicFile helper class."""
500
501@@ -562,7 +557,7 @@
502 other_file = "other-file"
503 output = partial(os.path.join, self.script.config.germinateroot)
504 for base in (seed_old_file, seed_new_file, other_file):
505- touch(output(base))
506+ write_file(output(base), "")
507 self.script.removeStaleOutputs(series_name, set([seed_new_file]))
508 self.assertFalse(os.path.exists(output(seed_old_file)))
509 self.assertTrue(os.path.exists(output(seed_new_file)))
510
511=== added file 'lib/lp/archivepublisher/tests/test_uefi.py'
512--- lib/lp/archivepublisher/tests/test_uefi.py 1970-01-01 00:00:00 +0000
513+++ lib/lp/archivepublisher/tests/test_uefi.py 2012-07-03 12:54:32 +0000
514@@ -0,0 +1,132 @@
515+# Copyright 2012 Canonical Ltd. This software is licensed under the
516+# GNU Affero General Public License version 3 (see the file LICENSE).
517+
518+"""Test UEFI custom uploads."""
519+
520+__metaclass__ = type
521+
522+import os
523+
524+from lp.archivepublisher.customupload import (
525+ CustomUploadAlreadyExists,
526+ CustomUploadBadUmask,
527+ )
528+from lp.archivepublisher.uefi import (
529+ UefiConfigurationError,
530+ UefiNothingToSign,
531+ UefiUpload,
532+ )
533+from lp.services.osutils import write_file
534+from lp.services.tarfile_helpers import LaunchpadWriteTarFile
535+from lp.testing import TestCase
536+from lp.testing.fakemethod import FakeMethod
537+
538+
539+class FakeConfig:
540+ """A fake publisher configuration."""
541+ def __init__(self, archiveroot, uefiroot):
542+ self.archiveroot = archiveroot
543+ self.uefiroot = uefiroot
544+
545+
546+class TestUefi(TestCase):
547+
548+ def setUp(self):
549+ super(TestUefi, self).setUp()
550+ self.temp_dir = self.makeTemporaryDirectory()
551+ self.uefi_dir = self.makeTemporaryDirectory()
552+ self.pubconf = FakeConfig(self.temp_dir, self.uefi_dir)
553+ self.suite = "distroseries"
554+ # CustomUpload.installFiles requires a umask of 022.
555+ old_umask = os.umask(022)
556+ self.addCleanup(os.umask, old_umask)
557+
558+ def setUpKeyAndCert(self):
559+ self.key = os.path.join(self.uefi_dir, "uefi.key")
560+ self.cert = os.path.join(self.uefi_dir, "uefi.crt")
561+ write_file(self.key, "")
562+ write_file(self.cert, "")
563+
564+ def openArchive(self, loader_type, version, arch):
565+ self.path = os.path.join(
566+ self.temp_dir, "%s_%s_%s.tar.gz" % (loader_type, version, arch))
567+ self.buffer = open(self.path, "wb")
568+ self.archive = LaunchpadWriteTarFile(self.buffer)
569+
570+ def process(self):
571+ self.archive.close()
572+ self.buffer.close()
573+ upload = UefiUpload()
574+ upload.sign = FakeMethod()
575+ upload.process(self.pubconf, self.path, self.suite)
576+ return upload
577+
578+ def getUefiPath(self, loader_type, arch):
579+ return os.path.join(
580+ self.temp_dir, "dists", self.suite, "main", "uefi",
581+ "%s-%s" % (loader_type, arch))
582+
583+ def test_unconfigured(self):
584+ # If there is no key/cert configuration, processing fails.
585+ self.pubconf = FakeConfig(self.temp_dir, None)
586+ self.openArchive("test", "1.0", "amd64")
587+ self.assertRaises(UefiConfigurationError, self.process)
588+
589+ def test_missing_key_and_cert(self):
590+ # If the configured key/cert are missing, processing fails.
591+ self.openArchive("test", "1.0", "amd64")
592+ self.archive.add_file("1.0/empty.efi", "")
593+ self.assertRaises(UefiConfigurationError, self.process)
594+
595+ def test_no_efi_files(self):
596+ # Tarballs containing no *.efi files are rejected.
597+ self.setUpKeyAndCert()
598+ self.openArchive("empty", "1.0", "amd64")
599+ self.archive.add_file("hello", "world")
600+ self.assertRaises(UefiNothingToSign, self.process)
601+
602+ def test_already_exists(self):
603+ # If the target directory already exists, processing fails.
604+ self.setUpKeyAndCert()
605+ self.openArchive("test", "1.0", "amd64")
606+ self.archive.add_file("1.0/empty.efi", "")
607+ os.makedirs(os.path.join(self.getUefiPath("test", "amd64"), "1.0"))
608+ self.assertRaises(CustomUploadAlreadyExists, self.process)
609+
610+ def test_bad_umask(self):
611+ # The umask must be 022 to avoid incorrect permissions.
612+ self.setUpKeyAndCert()
613+ self.openArchive("test", "1.0", "amd64")
614+ self.archive.add_file("1.0/dir/file.efi", "foo")
615+ os.umask(002) # cleanup already handled by setUp
616+ self.assertRaises(CustomUploadBadUmask, self.process)
617+
618+ def test_correct_signing_command(self):
619+ # getSigningCommand returns the correct command.
620+ self.setUpKeyAndCert()
621+ upload = UefiUpload()
622+ upload.setTargetDirectory(
623+ self.pubconf, "test_1.0_amd64.tar.gz", "distroseries")
624+ expected_command = [
625+ "sbsign", "--key", self.key, "--cert", self.cert, "t.efi"]
626+ self.assertEqual(expected_command, upload.getSigningCommand("t.efi"))
627+
628+ def test_signs_image(self):
629+ # Each image in the tarball is signed.
630+ self.setUpKeyAndCert()
631+ self.openArchive("test", "1.0", "amd64")
632+ self.archive.add_file("1.0/empty.efi", "")
633+ upload = self.process()
634+ self.assertEqual(1, upload.sign.call_count)
635+ self.assertEqual(1, len(upload.sign.calls[0][0]))
636+ self.assertEqual(
637+ "empty.efi", os.path.basename(upload.sign.calls[0][0][0]))
638+
639+ def test_installed(self):
640+ # Files in the tarball are installed correctly.
641+ self.setUpKeyAndCert()
642+ self.openArchive("test", "1.0", "amd64")
643+ self.archive.add_file("1.0/empty.efi", "")
644+ self.process()
645+ self.assertTrue(os.path.exists(os.path.join(
646+ self.getUefiPath("test", "amd64"), "1.0", "empty.efi")))
647
648=== added file 'lib/lp/archivepublisher/uefi.py'
649--- lib/lp/archivepublisher/uefi.py 1970-01-01 00:00:00 +0000
650+++ lib/lp/archivepublisher/uefi.py 2012-07-03 12:54:32 +0000
651@@ -0,0 +1,141 @@
652+# Copyright 2012 Canonical Ltd. This software is licensed under the
653+# GNU Affero General Public License version 3 (see the file LICENSE).
654+
655+"""The processing of UEFI boot loader images.
656+
657+UEFI Secure Boot requires boot loader images to be signed, and we want to
658+have signed images in the archive so that they can be used for upgrades.
659+This cannot be done on the build daemons because they are insufficiently
660+secure to hold signing keys, so we sign them as a custom upload instead.
661+"""
662+
663+__metaclass__ = type
664+
665+__all__ = [
666+ "process_uefi",
667+ "UefiUpload",
668+ ]
669+
670+import os
671+import subprocess
672+
673+from lp.archivepublisher.customupload import (
674+ CustomUpload,
675+ CustomUploadError,
676+ )
677+from lp.services.osutils import remove_if_exists
678+
679+
680+class UefiConfigurationError(CustomUploadError):
681+ """No signing key location is configured."""
682+ def __init__(self, message):
683+ CustomUploadError.__init__(
684+ self, "UEFI signing configuration error: %s" % message)
685+
686+
687+class UefiNothingToSign(CustomUploadError):
688+ """The tarball contained no *.efi files."""
689+ def __init__(self, tarfile_path):
690+ CustomUploadError.__init__(
691+ self, "UEFI upload '%s' contained no *.efi files" % tarfile_path)
692+
693+
694+class UefiUpload(CustomUpload):
695+ """UEFI boot loader custom upload.
696+
697+ The filename must be of the form:
698+
699+ <TYPE>_<VERSION>_<ARCH>.tar.gz
700+
701+ where:
702+
703+ * TYPE: loader type (e.g. 'efilinux');
704+ * VERSION: encoded version;
705+ * ARCH: targeted architecture tag (e.g. 'amd64').
706+
707+ The contents are extracted in the archive in the following path:
708+
709+ <ARCHIVE>/dists/<SUITE>/main/uefi/<TYPE>-<ARCH>/<VERSION>
710+
711+ A 'current' symbolic link points to the most recent version. The
712+ tarfile must contain at least one file matching the wildcard *.efi, and
713+ any such files are signed using the archive's UEFI signing key.
714+
715+ Signing keys may be installed in the "uefiroot" directory specified in
716+ publisher configuration. In this directory, the private key is
717+ "uefi.key" and the certificate is "uefi.crt".
718+ """
719+ custom_type = "UEFI"
720+
721+ @staticmethod
722+ def parsePath(tarfile_path):
723+ loader_type, version, arch = os.path.basename(tarfile_path).split("_")
724+ return loader_type, version, arch.split(".")[0]
725+
726+ def setTargetDirectory(self, pubconf, tarfile_path, distroseries):
727+ if pubconf.uefiroot is None:
728+ raise UefiConfigurationError(
729+ "no UEFI root configured for this archive")
730+ self.key = os.path.join(pubconf.uefiroot, "uefi.key")
731+ self.cert = os.path.join(pubconf.uefiroot, "uefi.crt")
732+ if not os.access(self.key, os.R_OK):
733+ raise UefiConfigurationError(
734+ "UEFI private key %s not readable" % self.key)
735+ if not os.access(self.cert, os.R_OK):
736+ raise UefiConfigurationError(
737+ "UEFI certificate %s not readable" % self.cert)
738+
739+ loader_type, self.version, self.arch = self.parsePath(tarfile_path)
740+ self.targetdir = os.path.join(
741+ pubconf.archiveroot, "dists", distroseries, "main", "uefi",
742+ "%s-%s" % (loader_type, self.arch))
743+
744+ @classmethod
745+ def getSeriesKey(cls, tarfile_path):
746+ try:
747+ loader_type, _, arch = cls.parsePath(tarfile_path)
748+ return loader_type, arch
749+ except ValueError:
750+ return None
751+
752+ def findEfiFilenames(self):
753+ """Find all the *.efi files in an extracted tarball."""
754+ for dirpath, dirnames, filenames in os.walk(self.tmpdir):
755+ for filename in filenames:
756+ if filename.endswith(".efi"):
757+ yield os.path.join(dirpath, filename)
758+
759+ def getSigningCommand(self, image):
760+ """Return the command used to sign an image."""
761+ return ["sbsign", "--key", self.key, "--cert", self.cert, image]
762+
763+ def sign(self, image):
764+ """Sign an image."""
765+ subprocess.check_call(self.getSigningCommand(image))
766+
767+ def extract(self):
768+ """Copy the custom upload to a temporary directory, and sign it.
769+
770+ No actual extraction is required.
771+ """
772+ super(UefiUpload, self).extract()
773+ efi_filenames = list(self.findEfiFilenames())
774+ if not efi_filenames:
775+ raise UefiNothingToSign(self.tarfile_path)
776+ for efi_filename in efi_filenames:
777+ remove_if_exists("%s.signed" % efi_filename)
778+ self.sign(efi_filename)
779+
780+ def shouldInstall(self, filename):
781+ return filename.startswith("%s/" % self.version)
782+
783+
784+def process_uefi(pubconf, tarfile_path, distroseries):
785+ """Process a raw-uefi tarfile.
786+
787+ Unpacking it into the given archive for the given distroseries.
788+ Raises CustomUploadError (or some subclass thereof) if anything goes
789+ wrong.
790+ """
791+ upload = UefiUpload()
792+ upload.process(pubconf, tarfile_path, distroseries)
793
794=== modified file 'lib/lp/archiveuploader/nascentuploadfile.py'
795--- lib/lp/archiveuploader/nascentuploadfile.py 2012-06-29 08:40:05 +0000
796+++ lib/lp/archiveuploader/nascentuploadfile.py 2012-07-03 12:54:32 +0000
797@@ -31,6 +31,10 @@
798 from zope.component import getUtility
799
800 from lp.app.errors import NotFoundError
801+from lp.archivepublisher.debian_installer import DebianInstallerUpload
802+from lp.archivepublisher.dist_upgrader import DistUpgraderUpload
803+from lp.archivepublisher.ddtp_tarball import DdtpTarballUpload
804+from lp.archivepublisher.uefi import UefiUpload
805 from lp.archiveuploader.utils import (
806 determine_source_file_type,
807 prefix_multi_line_string,
808@@ -268,6 +272,14 @@
809 PackageUploadCustomFormat.STATIC_TRANSLATIONS,
810 'raw-meta-data':
811 PackageUploadCustomFormat.META_DATA,
812+ 'raw-uefi': PackageUploadCustomFormat.UEFI,
813+ }
814+
815+ custom_handlers = {
816+ PackageUploadCustomFormat.DEBIAN_INSTALLER: DebianInstallerUpload,
817+ PackageUploadCustomFormat.DIST_UPGRADER: DistUpgraderUpload,
818+ PackageUploadCustomFormat.DDTP_TARBALL: DdtpTarballUpload,
819+ PackageUploadCustomFormat.UEFI: UefiUpload,
820 }
821
822 @property
823@@ -284,6 +296,16 @@
824 if self.section_name not in self.custom_sections:
825 yield UploadError(
826 "Unsupported custom section name %r" % self.section_name)
827+ else:
828+ handler = self.custom_handlers.get(
829+ self.custom_sections[self.section_name])
830+ if handler is not None:
831+ try:
832+ handler.parsePath(self.filename)
833+ except ValueError:
834+ yield UploadError(
835+ "Invalid filename %r for section name %r" % (
836+ self.filename, self.section_name))
837
838 def storeInDatabase(self):
839 """Create and return the corresponding LibraryFileAlias reference."""
840@@ -294,6 +316,13 @@
841 restricted=self.policy.archive.private)
842 return libraryfile
843
844+ def autoApprove(self):
845+ """Return whether this custom upload can be automatically approved."""
846+ # UEFI uploads are signed, and must therefore be approved by a human.
847+ if self.custom_type == PackageUploadCustomFormat.UEFI:
848+ return False
849+ return True
850+
851
852 class PackageUploadFile(NascentUploadFile):
853 """Base class to model sources and binary files contained in a upload. """
854
855=== modified file 'lib/lp/archiveuploader/tests/test_nascentuploadfile.py'
856--- lib/lp/archiveuploader/tests/test_nascentuploadfile.py 2012-04-27 14:20:20 +0000
857+++ lib/lp/archiveuploader/tests/test_nascentuploadfile.py 2012-07-03 12:54:32 +0000
858@@ -25,6 +25,7 @@
859 from lp.buildmaster.enums import BuildStatus
860 from lp.registry.interfaces.pocket import PackagePublishingPocket
861 from lp.services.log.logger import BufferLogger
862+from lp.services.osutils import write_file
863 from lp.soyuz.enums import (
864 PackagePublishingStatus,
865 PackageUploadCustomFormat,
866@@ -92,6 +93,36 @@
867 self.assertEquals("bla.txt", libraryfile.filename)
868 self.assertEquals("application/octet-stream", libraryfile.mimetype)
869
870+ def test_debian_installer_verify(self):
871+ # debian-installer uploads are required to have sensible filenames.
872+ uploadfile = self.createCustomUploadFile(
873+ "debian-installer-images_20120627_i386.tar.gz", "data",
874+ "main/raw-installer", "extra")
875+ self.assertEqual([], list(uploadfile.verify()))
876+ uploadfile = self.createCustomUploadFile(
877+ "bla.txt", "data", "main/raw-installer", "extra")
878+ errors = list(uploadfile.verify())
879+ self.assertEqual(1, len(errors))
880+ self.assertIsInstance(errors[0], UploadError)
881+
882+ def test_no_handler_no_verify(self):
883+ # Uploads without special handlers have no filename checks.
884+ uploadfile = self.createCustomUploadFile(
885+ "bla.txt", "data", "main/raw-meta-data", "extra")
886+ self.assertEqual([], list(uploadfile.verify()))
887+
888+ def test_debian_installer_auto_approved(self):
889+ # debian-installer uploads are auto-approved.
890+ uploadfile = self.createCustomUploadFile(
891+ "bla.txt", "data", "main/raw-installer", "extra")
892+ self.assertTrue(uploadfile.autoApprove())
893+
894+ def test_uefi_not_auto_approved(self):
895+ # UEFI uploads are auto-approved.
896+ uploadfile = self.createCustomUploadFile(
897+ "bla.txt", "data", "main/raw-uefi", "extra")
898+ self.assertFalse(uploadfile.autoApprove())
899+
900
901 class PackageUploadFileTestCase(NascentUploadFileTestCase):
902 """Base class for all tests of classes deriving from PackageUploadFile."""
903@@ -286,8 +317,7 @@
904 "data.tar.%s" % data_format,
905 ]
906 for member in members:
907- with open(os.path.join(tempdir, member), "w") as f:
908- pass
909+ write_file(os.path.join(tempdir, member), "")
910 retcode = subprocess.call(
911 ["ar", "rc", filename] + members, cwd=tempdir)
912 self.assertEqual(0, retcode)
913
914=== modified file 'lib/lp/archiveuploader/uploadpolicy.py'
915--- lib/lp/archiveuploader/uploadpolicy.py 2012-06-19 03:26:57 +0000
916+++ lib/lp/archiveuploader/uploadpolicy.py 2012-07-03 12:54:32 +0000
917@@ -317,6 +317,14 @@
918 raise AssertionError(
919 "Upload is not sourceful, binaryful or mixed.")
920
921+ def autoApprove(self, upload):
922+ """Check that all custom files in this upload can be auto-approved."""
923+ if upload.binaryful:
924+ for custom_file in upload.changes.custom_files:
925+ if not custom_file.autoApprove():
926+ return False
927+ return True
928+
929
930 class SyncUploadPolicy(AbstractUploadPolicy):
931 """This policy is invoked when processing sync uploads."""
932
933=== modified file 'lib/lp/services/osutils.py'
934--- lib/lp/services/osutils.py 2012-06-29 08:40:05 +0000
935+++ lib/lp/services/osutils.py 2012-07-03 12:54:32 +0000
936@@ -1,4 +1,4 @@
937-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
938+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
939 # GNU Affero General Public License version 3 (see the file LICENSE).
940
941 """Utilities for doing the sort of thing the os module does."""
942@@ -15,6 +15,7 @@
943 'remove_tree',
944 'two_stage_kill',
945 'until_no_eintr',
946+ 'write_file',
947 ]
948
949 from contextlib import contextmanager
950@@ -207,6 +208,5 @@
951
952
953 def write_file(path, content):
954- f = open(path, 'w')
955- f.write(content)
956- f.close()
957+ with open_for_writing(path, 'w') as f:
958+ f.write(content)
959
960=== modified file 'lib/lp/soyuz/browser/queue.py'
961--- lib/lp/soyuz/browser/queue.py 2012-06-29 08:40:05 +0000
962+++ lib/lp/soyuz/browser/queue.py 2012-07-03 12:54:32 +0000
963@@ -1,4 +1,4 @@
964-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
965+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
966 # GNU Affero General Public License version 3 (see the file LICENSE).
967
968 """Browser views for package queue."""
969@@ -578,6 +578,7 @@
970 (self.contains_installer, ("Installer", 'ubuntu-icon')),
971 (self.contains_upgrader, ("Upgrader", 'ubuntu-icon')),
972 (self.contains_ddtp, (ddtp, 'ubuntu-icon')),
973+ (self.contains_uefi, ("Signed UEFI boot loader", 'ubuntu-icon')),
974 ]
975 return [
976 self.composeIcon(*details)
977
978=== modified file 'lib/lp/soyuz/browser/tests/builder-views.txt'
979--- lib/lp/soyuz/browser/tests/builder-views.txt 2012-05-29 01:45:39 +0000
980+++ lib/lp/soyuz/browser/tests/builder-views.txt 2012-07-03 12:54:32 +0000
981@@ -283,7 +283,7 @@
982
983 * title: the title that will be presented for this category in the UI;
984
985- * virtualized: wheter the category represents the virtualized or
986+ * virtualized: whether the category represents the virtualized or
987 non-virtualized build-farm;
988
989 * groups: a property that return all `BuilderGroup` instanced
990
991=== modified file 'lib/lp/soyuz/configure.zcml'
992--- lib/lp/soyuz/configure.zcml 2012-06-22 05:25:35 +0000
993+++ lib/lp/soyuz/configure.zcml 2012-07-03 12:54:32 +0000
994@@ -173,6 +173,7 @@
995 contains_installer
996 contains_upgrader
997 contains_ddtp
998+ contains_uefi
999 displayname
1000 displayarchs
1001 displayversion
1002
1003=== modified file 'lib/lp/soyuz/enums.py'
1004--- lib/lp/soyuz/enums.py 2012-05-28 15:12:00 +0000
1005+++ lib/lp/soyuz/enums.py 2012-07-03 12:54:32 +0000
1006@@ -474,6 +474,12 @@
1007 the Software Center.
1008 """)
1009
1010+ UEFI = DBItem(6, """
1011+ uefi
1012+
1013+ A UEFI boot loader image to be signed.
1014+ """)
1015+
1016
1017 class PackageUploadStatus(DBEnumeratedType):
1018 """Distro Release Queue Status
1019
1020=== modified file 'lib/lp/soyuz/interfaces/queue.py'
1021--- lib/lp/soyuz/interfaces/queue.py 2012-06-30 17:41:37 +0000
1022+++ lib/lp/soyuz/interfaces/queue.py 2012-07-03 12:54:32 +0000
1023@@ -207,9 +207,11 @@
1024 contains_translation = Attribute(
1025 "whether or not this upload contains translations")
1026 contains_upgrader = Attribute(
1027- "wheter or not this upload contains upgrader images")
1028+ "whether or not this upload contains upgrader images")
1029 contains_ddtp = Attribute(
1030- "wheter or not this upload contains DDTP images")
1031+ "whether or not this upload contains DDTP images")
1032+ contains_uefi = Attribute(
1033+ "whether or not this upload contains a signed UEFI boot loader image")
1034 isPPA = Attribute(
1035 "Return True if this PackageUpload is a PPA upload.")
1036 is_delayed_copy = Attribute(
1037
1038=== modified file 'lib/lp/soyuz/model/queue.py'
1039--- lib/lp/soyuz/model/queue.py 2012-07-03 08:04:35 +0000
1040+++ lib/lp/soyuz/model/queue.py 2012-07-03 12:54:32 +0000
1041@@ -629,6 +629,12 @@
1042 return (PackageUploadCustomFormat.DDTP_TARBALL
1043 in self._customFormats)
1044
1045+ @cachedproperty
1046+ def contains_uefi(self):
1047+ """See `IPackageUpload`."""
1048+ return (PackageUploadCustomFormat.UEFI
1049+ in self._customFormats)
1050+
1051 @property
1052 def package_name(self):
1053 """See `IPackageUpload`."""
1054@@ -1270,8 +1276,7 @@
1055 try:
1056 # See the XXX near the import for getPubConfig.
1057 archive_config = getPubConfig(self.packageupload.archive)
1058- action_method(
1059- archive_config.archiveroot, temp_filename, suite)
1060+ action_method(archive_config, temp_filename, suite)
1061 finally:
1062 shutil.rmtree(os.path.dirname(temp_filename))
1063
1064@@ -1376,6 +1381,14 @@
1065 self.libraryfilealias.open()
1066 copy_and_close(self.libraryfilealias, file_obj)
1067
1068+ def publishUefi(self, logger=None):
1069+ """See `IPackageUploadCustom`."""
1070+ # XXX cprov 2005-03-03: We need to use the Zope Component Lookup
1071+ # to instantiate the object in question and avoid circular imports
1072+ from lp.archivepublisher.uefi import process_uefi
1073+
1074+ self._publishCustom(process_uefi)
1075+
1076 publisher_dispatch = {
1077 PackageUploadCustomFormat.DEBIAN_INSTALLER: publishDebianInstaller,
1078 PackageUploadCustomFormat.ROSETTA_TRANSLATIONS:
1079@@ -1385,6 +1398,7 @@
1080 PackageUploadCustomFormat.STATIC_TRANSLATIONS:
1081 publishStaticTranslations,
1082 PackageUploadCustomFormat.META_DATA: publishMetaData,
1083+ PackageUploadCustomFormat.UEFI: publishUefi,
1084 }
1085
1086 # publisher_dispatch must have an entry for each value of
1087
1088=== modified file 'lib/lp/soyuz/scripts/custom_uploads_copier.py'
1089--- lib/lp/soyuz/scripts/custom_uploads_copier.py 2012-06-22 17:26:53 +0000
1090+++ lib/lp/soyuz/scripts/custom_uploads_copier.py 2012-07-03 12:54:32 +0000
1091@@ -18,6 +18,7 @@
1092
1093 from lp.archivepublisher.debian_installer import DebianInstallerUpload
1094 from lp.archivepublisher.dist_upgrader import DistUpgraderUpload
1095+from lp.archivepublisher.uefi import UefiUpload
1096 from lp.registry.interfaces.pocket import PackagePublishingPocket
1097 from lp.services.database.bulk import load_referencing
1098 from lp.soyuz.enums import PackageUploadCustomFormat
1099@@ -40,6 +41,7 @@
1100 copyable_types = {
1101 PackageUploadCustomFormat.DEBIAN_INSTALLER: DebianInstallerUpload,
1102 PackageUploadCustomFormat.DIST_UPGRADER: DistUpgraderUpload,
1103+ PackageUploadCustomFormat.UEFI: UefiUpload,
1104 }
1105
1106 def __init__(self, target_series,