Merge ~cjwatson/launchpad:archive-gpg-generate-using-signing-service into launchpad:master
- Git
- lp:~cjwatson/launchpad
- archive-gpg-generate-using-signing-service
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 96b7e00f45b9db262a286028a460a7ff39fcb8b8 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:archive-gpg-generate-using-signing-service |
Merge into: | launchpad:master |
Diff against target: |
530 lines (+196/-53) 11 files modified
lib/lp/archivepublisher/archivegpgsigningkey.py (+55/-23) lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py (+105/-3) lib/lp/services/gpg/handler.py (+3/-7) lib/lp/services/gpg/interfaces.py (+13/-2) lib/lp/services/signing/tests/helpers.py (+2/-2) lib/lp/soyuz/configure.zcml (+0/-1) lib/lp/soyuz/interfaces/archive.py (+2/-0) lib/lp/soyuz/model/archive.py (+7/-6) lib/lp/soyuz/scripts/ppakeygenerator.py (+3/-3) lib/lp/soyuz/scripts/tests/test_ppakeygenerator.py (+5/-5) lib/lp/soyuz/tests/test_archive.py (+1/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ioana Lasc (community) | Approve | ||
Review via email: mp+390668@code.launchpad.net |
Commit message
Generate archive signing keys using the signing service
Description of the change
This is guarded by the feature rule 'archivepublish
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/archivepublisher/archivegpgsigningkey.py b/lib/lp/archivepublisher/archivegpgsigningkey.py |
2 | index a342332..20a4d79 100644 |
3 | --- a/lib/lp/archivepublisher/archivegpgsigningkey.py |
4 | +++ b/lib/lp/archivepublisher/archivegpgsigningkey.py |
5 | @@ -37,17 +37,24 @@ from lp.archivepublisher.run_parts import ( |
6 | from lp.registry.interfaces.gpg import IGPGKeySet |
7 | from lp.services.config import config |
8 | from lp.services.features import getFeatureFlag |
9 | -from lp.services.gpg.interfaces import IGPGHandler |
10 | +from lp.services.gpg.interfaces import ( |
11 | + IGPGHandler, |
12 | + IPymeKey, |
13 | + ) |
14 | from lp.services.osutils import remove_if_exists |
15 | from lp.services.propertycache import ( |
16 | cachedproperty, |
17 | get_property_cache, |
18 | ) |
19 | from lp.services.signing.enums import ( |
20 | + OpenPGPKeyAlgorithm, |
21 | SigningKeyType, |
22 | SigningMode, |
23 | ) |
24 | -from lp.services.signing.interfaces.signingkey import ISigningKeySet |
25 | +from lp.services.signing.interfaces.signingkey import ( |
26 | + ISigningKey, |
27 | + ISigningKeySet, |
28 | + ) |
29 | |
30 | |
31 | @implementer(ISignableArchive) |
32 | @@ -72,7 +79,7 @@ class SignableArchive: |
33 | def can_sign(self): |
34 | """See `ISignableArchive`.""" |
35 | return ( |
36 | - self.archive.signing_key is not None or |
37 | + self.archive.signing_key_fingerprint is not None or |
38 | self._run_parts_dir is not None) |
39 | |
40 | @cachedproperty |
41 | @@ -237,9 +244,9 @@ class ArchiveGPGSigningKey(SignableArchive): |
42 | with open(export_path, 'wb') as export_file: |
43 | export_file.write(key.export()) |
44 | |
45 | - def generateSigningKey(self, log=None): |
46 | + def generateSigningKey(self, log=None, async_keyserver=False): |
47 | """See `IArchiveGPGSigningKey`.""" |
48 | - assert self.archive.signing_key is None, ( |
49 | + assert self.archive.signing_key_fingerprint is None, ( |
50 | "Cannot override signing_keys.") |
51 | |
52 | # Always generate signing keys for the default PPA, even if it |
53 | @@ -257,13 +264,26 @@ class ArchiveGPGSigningKey(SignableArchive): |
54 | |
55 | key_displayname = ( |
56 | "Launchpad PPA for %s" % self.archive.owner.displayname) |
57 | - secret_key = getUtility(IGPGHandler).generateKey( |
58 | - key_displayname, logger=log) |
59 | - self._setupSigningKey(secret_key) |
60 | + if getFeatureFlag(PUBLISHER_GPG_USES_SIGNING_SERVICE): |
61 | + try: |
62 | + signing_key = getUtility(ISigningKeySet).generate( |
63 | + SigningKeyType.OPENPGP, key_displayname, |
64 | + openpgp_key_algorithm=OpenPGPKeyAlgorithm.RSA, length=4096) |
65 | + except Exception as e: |
66 | + if log is not None: |
67 | + log.exception( |
68 | + "Error generating signing key for %s: %s %s" % |
69 | + (self.archive.reference, e.__class__.__name__, e)) |
70 | + raise |
71 | + else: |
72 | + signing_key = getUtility(IGPGHandler).generateKey( |
73 | + key_displayname, logger=log) |
74 | + return self._setupSigningKey( |
75 | + signing_key, async_keyserver=async_keyserver) |
76 | |
77 | def setSigningKey(self, key_path, async_keyserver=False): |
78 | """See `IArchiveGPGSigningKey`.""" |
79 | - assert self.archive.signing_key is None, ( |
80 | + assert self.archive.signing_key_fingerprint is None, ( |
81 | "Cannot override signing_keys.") |
82 | assert os.path.exists(key_path), ( |
83 | "%s does not exist" % key_path) |
84 | @@ -274,34 +294,46 @@ class ArchiveGPGSigningKey(SignableArchive): |
85 | return self._setupSigningKey( |
86 | secret_key, async_keyserver=async_keyserver) |
87 | |
88 | - def _uploadPublicSigningKey(self, secret_key): |
89 | + def _uploadPublicSigningKey(self, signing_key): |
90 | """Upload the public half of a signing key to the keyserver.""" |
91 | # The handler's security proxying doesn't protect anything useful |
92 | # here, and when we're running in a thread we don't have an |
93 | # interaction. |
94 | gpghandler = removeSecurityProxy(getUtility(IGPGHandler)) |
95 | - pub_key = gpghandler.retrieveKey(secret_key.fingerprint) |
96 | - gpghandler.uploadPublicKey(pub_key.fingerprint) |
97 | - return pub_key |
98 | + if IPymeKey.providedBy(signing_key): |
99 | + pub_key = gpghandler.retrieveKey(signing_key.fingerprint) |
100 | + gpghandler.uploadPublicKey(pub_key.fingerprint) |
101 | + return pub_key |
102 | + else: |
103 | + assert ISigningKey.providedBy(signing_key) |
104 | + gpghandler.submitKey(removeSecurityProxy(signing_key).public_key) |
105 | + return signing_key |
106 | |
107 | def _storeSigningKey(self, pub_key): |
108 | """Store signing key reference in the database.""" |
109 | key_owner = getUtility(ILaunchpadCelebrities).ppa_key_guard |
110 | - key, _ = getUtility(IGPGKeySet).activate( |
111 | - key_owner, pub_key, pub_key.can_encrypt) |
112 | - self.archive.signing_key_owner = key.owner |
113 | + if IPymeKey.providedBy(pub_key): |
114 | + key, _ = getUtility(IGPGKeySet).activate( |
115 | + key_owner, pub_key, pub_key.can_encrypt) |
116 | + else: |
117 | + assert ISigningKey.providedBy(pub_key) |
118 | + key = pub_key |
119 | + self.archive.signing_key_owner = key_owner |
120 | self.archive.signing_key_fingerprint = key.fingerprint |
121 | del get_property_cache(self.archive).signing_key |
122 | |
123 | - def _setupSigningKey(self, secret_key, async_keyserver=False): |
124 | + def _setupSigningKey(self, signing_key, async_keyserver=False): |
125 | """Mandatory setup for signing keys. |
126 | |
127 | - * Export the secret key into the protected disk location. |
128 | + * Export the secret key into the protected disk location (for |
129 | + locally-generated keys). |
130 | * Upload public key to the keyserver. |
131 | - * Store the public GPGKey reference in the database and update |
132 | - the context archive.signing_key. |
133 | + * Store the public GPGKey reference in the database (for |
134 | + locally-generated keys) and update the context |
135 | + archive.signing_key. |
136 | """ |
137 | - self.exportSecretKey(secret_key) |
138 | + if IPymeKey.providedBy(signing_key): |
139 | + self.exportSecretKey(signing_key) |
140 | if async_keyserver: |
141 | # If we have an asynchronous keyserver running in the current |
142 | # thread using Twisted, then we need some contortions to ensure |
143 | @@ -310,10 +342,10 @@ class ArchiveGPGSigningKey(SignableArchive): |
144 | # Since that thread won't have a Zope interaction, we need to |
145 | # unwrap the security proxy for it. |
146 | d = deferToThread( |
147 | - self._uploadPublicSigningKey, removeSecurityProxy(secret_key)) |
148 | + self._uploadPublicSigningKey, removeSecurityProxy(signing_key)) |
149 | d.addCallback(ProxyFactory) |
150 | d.addCallback(self._storeSigningKey) |
151 | return d |
152 | else: |
153 | - pub_key = self._uploadPublicSigningKey(secret_key) |
154 | + pub_key = self._uploadPublicSigningKey(signing_key) |
155 | self._storeSigningKey(pub_key) |
156 | diff --git a/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py b/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py |
157 | index 87359e8..67366bd 100644 |
158 | --- a/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py |
159 | +++ b/lib/lp/archivepublisher/tests/test_archivegpgsigningkey.py |
160 | @@ -14,8 +14,15 @@ from testtools.matchers import ( |
161 | FileContains, |
162 | StartsWith, |
163 | ) |
164 | -from testtools.twistedsupport import AsynchronousDeferredRunTest |
165 | -from twisted.internet import defer |
166 | +from testtools.twistedsupport import ( |
167 | + AsynchronousDeferredRunTest, |
168 | + AsynchronousDeferredRunTestForBrokenTwisted, |
169 | + ) |
170 | +import treq |
171 | +from twisted.internet import ( |
172 | + defer, |
173 | + reactor, |
174 | + ) |
175 | from zope.component import getUtility |
176 | |
177 | from lp.archivepublisher.config import getPubConfig |
178 | @@ -26,18 +33,27 @@ from lp.archivepublisher.interfaces.archivegpgsigningkey import ( |
179 | ) |
180 | from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet |
181 | from lp.archivepublisher.tests.test_run_parts import RunPartsMixin |
182 | +from lp.registry.interfaces.gpg import IGPGKeySet |
183 | from lp.services.compat import mock |
184 | from lp.services.features.testing import FeatureFixture |
185 | +from lp.services.gpg.interfaces import IGPGHandler |
186 | +from lp.services.gpg.tests.test_gpghandler import FakeGenerateKey |
187 | from lp.services.log.logger import BufferLogger |
188 | from lp.services.osutils import write_file |
189 | from lp.services.signing.enums import ( |
190 | SigningKeyType, |
191 | SigningMode, |
192 | ) |
193 | +from lp.services.signing.interfaces.signingkey import ISigningKeySet |
194 | from lp.services.signing.tests.helpers import SigningServiceClientFixture |
195 | +from lp.services.twistedsupport.testing import TReqFixture |
196 | +from lp.services.twistedsupport.treq import check_status |
197 | from lp.soyuz.enums import ArchivePurpose |
198 | from lp.testing import TestCaseWithFactory |
199 | -from lp.testing.gpgkeys import gpgkeysdir |
200 | +from lp.testing.gpgkeys import ( |
201 | + gpgkeysdir, |
202 | + test_pubkey_from_email, |
203 | + ) |
204 | from lp.testing.keyserver import InProcessKeyServerFixture |
205 | from lp.testing.layers import ZopelessDatabaseLayer |
206 | |
207 | @@ -271,3 +287,89 @@ class TestSignableArchiveWithRunParts(RunPartsMixin, TestCaseWithFactory): |
208 | FileContains( |
209 | "detached signature of %s (%s, %s/%s)\n" % |
210 | (filename, self.archive_root, self.distro.name, self.suite))) |
211 | + |
212 | + |
213 | +class TestArchiveGPGSigningKey(TestCaseWithFactory): |
214 | + |
215 | + layer = ZopelessDatabaseLayer |
216 | + # treq.content doesn't close the connection before yielding control back |
217 | + # to the test, so we need to spin the reactor at the end to finish |
218 | + # things off. |
219 | + run_tests_with = AsynchronousDeferredRunTestForBrokenTwisted.make_factory( |
220 | + timeout=10000) |
221 | + |
222 | + @defer.inlineCallbacks |
223 | + def setUp(self): |
224 | + super(TestArchiveGPGSigningKey, self).setUp() |
225 | + self.temp_dir = self.makeTemporaryDirectory() |
226 | + self.pushConfig("personalpackagearchive", root=self.temp_dir) |
227 | + self.keyserver = self.useFixture(InProcessKeyServerFixture()) |
228 | + yield self.keyserver.start() |
229 | + |
230 | + @defer.inlineCallbacks |
231 | + def test_generateSigningKey_local(self): |
232 | + # Generating a signing key locally using GPGHandler stores it in the |
233 | + # database and pushes it to the keyserver. |
234 | + self.useFixture(FakeGenerateKey("ppa-sample@canonical.com.sec")) |
235 | + logger = BufferLogger() |
236 | + # Use a display name that matches the pregenerated sample key. |
237 | + owner = self.factory.makePerson( |
238 | + displayname="Celso \xe1\xe9\xed\xf3\xfa Providelo") |
239 | + archive = self.factory.makeArchive(owner=owner) |
240 | + yield IArchiveGPGSigningKey(archive).generateSigningKey( |
241 | + log=logger, async_keyserver=True) |
242 | + # The key is stored in the database. |
243 | + self.assertIsNotNone(archive.signing_key_owner) |
244 | + self.assertIsNotNone(archive.signing_key_fingerprint) |
245 | + # The key is stored as a GPGKey, not a SigningKey. |
246 | + self.assertIsNotNone( |
247 | + getUtility(IGPGKeySet).getByFingerprint( |
248 | + archive.signing_key_fingerprint)) |
249 | + self.assertIsNone( |
250 | + getUtility(ISigningKeySet).get( |
251 | + SigningKeyType.OPENPGP, archive.signing_key_fingerprint)) |
252 | + # The key is uploaded to the keyserver. |
253 | + client = self.useFixture(TReqFixture(reactor)).client |
254 | + response = yield client.get( |
255 | + getUtility(IGPGHandler).getURLForKeyInServer( |
256 | + archive.signing_key_fingerprint, "get")) |
257 | + yield check_status(response) |
258 | + content = yield treq.content(response) |
259 | + self.assertIn(b"-----BEGIN PGP PUBLIC KEY BLOCK-----\n", content) |
260 | + |
261 | + @defer.inlineCallbacks |
262 | + def test_generateSigningKey_signing_service(self): |
263 | + # Generating a signing key on the signing service stores it in the |
264 | + # database and pushes it to the keyserver. |
265 | + self.useFixture( |
266 | + FeatureFixture({PUBLISHER_GPG_USES_SIGNING_SERVICE: "on"})) |
267 | + signing_service_client = self.useFixture( |
268 | + SigningServiceClientFixture(self.factory)) |
269 | + signing_service_client.generate.side_effect = None |
270 | + test_key = test_pubkey_from_email("ftpmaster@canonical.com") |
271 | + signing_service_client.generate.return_value = { |
272 | + "fingerprint": "33C0A61893A5DC5EB325B29E415A12CAC2F30234", |
273 | + "public-key": test_key, |
274 | + } |
275 | + logger = BufferLogger() |
276 | + archive = self.factory.makeArchive() |
277 | + yield IArchiveGPGSigningKey(archive).generateSigningKey( |
278 | + log=logger, async_keyserver=True) |
279 | + # The key is stored in the database. |
280 | + self.assertIsNotNone(archive.signing_key_owner) |
281 | + self.assertIsNotNone(archive.signing_key_fingerprint) |
282 | + # The key is stored as a SigningKey, not a GPGKey. |
283 | + self.assertIsNone( |
284 | + getUtility(IGPGKeySet).getByFingerprint( |
285 | + archive.signing_key_fingerprint)) |
286 | + signing_key = getUtility(ISigningKeySet).get( |
287 | + SigningKeyType.OPENPGP, archive.signing_key_fingerprint) |
288 | + self.assertEqual(test_key, signing_key.public_key) |
289 | + # The key is uploaded to the keyserver. |
290 | + client = self.useFixture(TReqFixture(reactor)).client |
291 | + response = yield client.get( |
292 | + getUtility(IGPGHandler).getURLForKeyInServer( |
293 | + archive.signing_key_fingerprint, "get")) |
294 | + yield check_status(response) |
295 | + content = yield treq.content(response) |
296 | + self.assertIn(test_key, content) |
297 | diff --git a/lib/lp/services/gpg/handler.py b/lib/lp/services/gpg/handler.py |
298 | index b2bcbad..e3eba45 100644 |
299 | --- a/lib/lp/services/gpg/handler.py |
300 | +++ b/lib/lp/services/gpg/handler.py |
301 | @@ -489,12 +489,8 @@ class GPGHandler: |
302 | raise GPGKeyExpired(key) |
303 | return key |
304 | |
305 | - def _submitKey(self, content): |
306 | - """Submit an ASCII-armored public key export to the keyserver. |
307 | - |
308 | - It issues a POST at /pks/add on the keyserver specified in the |
309 | - configuration. |
310 | - """ |
311 | + def submitKey(self, content): |
312 | + """See `IGPGHandler`.""" |
313 | keyserver_http_url = '%s:%s' % ( |
314 | config.gpghandler.host, config.gpghandler.port) |
315 | |
316 | @@ -527,7 +523,7 @@ class GPGHandler: |
317 | return |
318 | |
319 | pub_key = self.retrieveKey(fingerprint) |
320 | - self._submitKey(pub_key.export()) |
321 | + self.submitKey(pub_key.export()) |
322 | |
323 | def getURLForKeyInServer(self, fingerprint, action='index', public=False): |
324 | """See IGPGHandler""" |
325 | diff --git a/lib/lp/services/gpg/interfaces.py b/lib/lp/services/gpg/interfaces.py |
326 | index 78b44c8..d6f0f73 100644 |
327 | --- a/lib/lp/services/gpg/interfaces.py |
328 | +++ b/lib/lp/services/gpg/interfaces.py |
329 | @@ -357,6 +357,17 @@ class IGPGHandler(Interface): |
330 | :return: a `PymeKey`object containing the key information. |
331 | """ |
332 | |
333 | + def submitKey(content): |
334 | + """Submit an ASCII-armored public key export to the keyserver. |
335 | + |
336 | + It issues a POST at /pks/add on the keyserver specified in the |
337 | + configuration. |
338 | + |
339 | + :param content: The exported public key, as a byte string. |
340 | + :raise GPGUploadFailure: if the keyserver could not be reached. |
341 | + :raise AssertionError: if the POST request failed. |
342 | + """ |
343 | + |
344 | def uploadPublicKey(fingerprint): |
345 | """Upload the specified public key to a keyserver. |
346 | |
347 | @@ -365,8 +376,8 @@ class IGPGHandler(Interface): |
348 | |
349 | :param fingerprint: The key fingerprint, which must be an hexadecimal |
350 | string. |
351 | - :raise GPGUploadFailure: if the keyserver could not be reaches. |
352 | - :raise AssertionError: if the POST request doesn't succeed. |
353 | + :raise GPGUploadFailure: if the keyserver could not be reached. |
354 | + :raise AssertionError: if the POST request failed. |
355 | """ |
356 | |
357 | def localKeys(filter=None, secret=False): |
358 | diff --git a/lib/lp/services/signing/tests/helpers.py b/lib/lp/services/signing/tests/helpers.py |
359 | index f819745..6831edb 100644 |
360 | --- a/lib/lp/services/signing/tests/helpers.py |
361 | +++ b/lib/lp/services/signing/tests/helpers.py |
362 | @@ -49,7 +49,7 @@ class SigningServiceClientFixture(fixtures.Fixture): |
363 | openpgp_key_algorithm=None, length=None): |
364 | key = bytes(PrivateKey.generate().public_key) |
365 | data = { |
366 | - "fingerprint": self.factory.getUniqueHexString(40), |
367 | + "fingerprint": self.factory.getUniqueHexString(40).upper(), |
368 | "public-key": key, |
369 | } |
370 | self.generate_returns.append((key_type, data)) |
371 | @@ -69,7 +69,7 @@ class SigningServiceClientFixture(fixtures.Fixture): |
372 | |
373 | def _inject(self, key_type, private_key, public_key, description, |
374 | created_at): |
375 | - data = {'fingerprint': self.factory.getUniqueHexString(40)} |
376 | + data = {'fingerprint': self.factory.getUniqueHexString(40).upper()} |
377 | self.inject_returns.append(data) |
378 | return data |
379 | |
380 | diff --git a/lib/lp/soyuz/configure.zcml b/lib/lp/soyuz/configure.zcml |
381 | index 2e97958..643a85b 100644 |
382 | --- a/lib/lp/soyuz/configure.zcml |
383 | +++ b/lib/lp/soyuz/configure.zcml |
384 | @@ -373,7 +373,6 @@ |
385 | set_schema="lp.soyuz.interfaces.archive.IArchiveRestricted"/> |
386 | <require |
387 | permission="launchpad.InternalScriptsOnly" |
388 | - attributes="signing_key_owner" |
389 | set_attributes="dirty_suites distribution signing_key_owner |
390 | signing_key_fingerprint"/> |
391 | </class> |
392 | diff --git a/lib/lp/soyuz/interfaces/archive.py b/lib/lp/soyuz/interfaces/archive.py |
393 | index 407f953..b6cc663 100644 |
394 | --- a/lib/lp/soyuz/interfaces/archive.py |
395 | +++ b/lib/lp/soyuz/interfaces/archive.py |
396 | @@ -461,6 +461,8 @@ class IArchiveSubscriberView(Interface): |
397 | "explicit publish flag and any other constraints.")) |
398 | series_with_sources = Attribute( |
399 | "DistroSeries to which this archive has published sources") |
400 | + signing_key_owner = Reference( |
401 | + title=_("Archive signing key owner"), required=False, schema=IPerson) |
402 | signing_key_fingerprint = exported( |
403 | Text( |
404 | title=_("Archive signing key fingerprint"), required=False, |
405 | diff --git a/lib/lp/soyuz/model/archive.py b/lib/lp/soyuz/model/archive.py |
406 | index a37b737..0d58e56 100644 |
407 | --- a/lib/lp/soyuz/model/archive.py |
408 | +++ b/lib/lp/soyuz/model/archive.py |
409 | @@ -466,7 +466,7 @@ class Archive(SQLBase): |
410 | return ( |
411 | not config.personalpackagearchive.require_signing_keys or |
412 | not self.is_ppa or |
413 | - self.signing_key is not None) |
414 | + self.signing_key_fingerprint is not None) |
415 | |
416 | @property |
417 | def reference(self): |
418 | @@ -2717,10 +2717,12 @@ class ArchiveSet: |
419 | (owner.name, distribution.name, name)) |
420 | |
421 | # Signing-key for the default PPA is reused when it's already present. |
422 | - signing_key = None |
423 | + signing_key_owner = None |
424 | + signing_key_fingerprint = None |
425 | if purpose == ArchivePurpose.PPA: |
426 | if owner.archive is not None: |
427 | - signing_key = owner.archive.signing_key |
428 | + signing_key_owner = owner.archive.signing_key_owner |
429 | + signing_key_fingerprint = owner.archive.signing_key_fingerprint |
430 | else: |
431 | # owner.archive is a cached property and we've just cached it. |
432 | del get_property_cache(owner).archive |
433 | @@ -2729,9 +2731,8 @@ class ArchiveSet: |
434 | owner=owner, distribution=distribution, name=name, |
435 | displayname=displayname, description=description, |
436 | purpose=purpose, publish=publish, |
437 | - signing_key_owner=signing_key.owner if signing_key else None, |
438 | - signing_key_fingerprint=( |
439 | - signing_key.fingerprint if signing_key else None), |
440 | + signing_key_owner=signing_key_owner, |
441 | + signing_key_fingerprint=signing_key_fingerprint, |
442 | require_virtualized=require_virtualized) |
443 | |
444 | # Upon creation archives are enabled by default. |
445 | diff --git a/lib/lp/soyuz/scripts/ppakeygenerator.py b/lib/lp/soyuz/scripts/ppakeygenerator.py |
446 | index 190b4a0..88e1d84 100644 |
447 | --- a/lib/lp/soyuz/scripts/ppakeygenerator.py |
448 | +++ b/lib/lp/soyuz/scripts/ppakeygenerator.py |
449 | @@ -34,7 +34,7 @@ class PPAKeyGenerator(LaunchpadCronScript): |
450 | (archive.reference, archive.displayname)) |
451 | archive_signing_key = IArchiveGPGSigningKey(archive) |
452 | archive_signing_key.generateSigningKey(log=self.logger) |
453 | - self.logger.info("Key %s" % archive.signing_key.fingerprint) |
454 | + self.logger.info("Key %s" % archive.signing_key_fingerprint) |
455 | |
456 | def main(self): |
457 | """Generate signing keys for the selected PPAs.""" |
458 | @@ -45,11 +45,11 @@ class PPAKeyGenerator(LaunchpadCronScript): |
459 | raise LaunchpadScriptFailure( |
460 | "No archive named '%s' could be found." |
461 | % self.options.archive) |
462 | - if archive.signing_key is not None: |
463 | + if archive.signing_key_fingerprint is not None: |
464 | raise LaunchpadScriptFailure( |
465 | "%s (%s) already has a signing_key (%s)" |
466 | % (archive.reference, archive.displayname, |
467 | - archive.signing_key.fingerprint)) |
468 | + archive.signing_key_fingerprint)) |
469 | archives = [archive] |
470 | else: |
471 | archive_set = getUtility(IArchiveSet) |
472 | diff --git a/lib/lp/soyuz/scripts/tests/test_ppakeygenerator.py b/lib/lp/soyuz/scripts/tests/test_ppakeygenerator.py |
473 | index 56e8710..a5d3caf 100644 |
474 | --- a/lib/lp/soyuz/scripts/tests/test_ppakeygenerator.py |
475 | +++ b/lib/lp/soyuz/scripts/tests/test_ppakeygenerator.py |
476 | @@ -83,7 +83,7 @@ class TestPPAKeyGenerator(TestCase): |
477 | LaunchpadScriptFailure, |
478 | ("~cprov/ubuntu/ppa (PPA for Celso Providelo) already has a " |
479 | "signing_key (%s)" % |
480 | - cprov.archive.signing_key.fingerprint), |
481 | + cprov.archive.signing_key_fingerprint), |
482 | key_generator.main) |
483 | |
484 | def testGenerateKeyForASinglePPA(self): |
485 | @@ -95,14 +95,14 @@ class TestPPAKeyGenerator(TestCase): |
486 | cprov = getUtility(IPersonSet).getByName('cprov') |
487 | self._fixArchiveForKeyGeneration(cprov.archive) |
488 | |
489 | - self.assertTrue(cprov.archive.signing_key is None) |
490 | + self.assertIsNone(cprov.archive.signing_key_fingerprint) |
491 | |
492 | txn = FakeTransaction() |
493 | key_generator = self._getKeyGenerator( |
494 | archive_reference='~cprov/ubuntutest/ppa', txn=txn) |
495 | key_generator.main() |
496 | |
497 | - self.assertTrue(cprov.archive.signing_key is not None) |
498 | + self.assertIsNotNone(cprov.archive.signing_key_fingerprint) |
499 | self.assertEqual(txn.commit_count, 1) |
500 | |
501 | def testGenerateKeyForAllPPA(self): |
502 | @@ -115,13 +115,13 @@ class TestPPAKeyGenerator(TestCase): |
503 | |
504 | for archive in archives: |
505 | self._fixArchiveForKeyGeneration(archive) |
506 | - self.assertTrue(archive.signing_key is None) |
507 | + self.assertIsNone(archive.signing_key_fingerprint) |
508 | |
509 | txn = FakeTransaction() |
510 | key_generator = self._getKeyGenerator(txn=txn) |
511 | key_generator.main() |
512 | |
513 | for archive in archives: |
514 | - self.assertTrue(archive.signing_key is not None) |
515 | + self.assertIsNotNone(archive.signing_key_fingerprint) |
516 | |
517 | self.assertEqual(txn.commit_count, len(archives)) |
518 | diff --git a/lib/lp/soyuz/tests/test_archive.py b/lib/lp/soyuz/tests/test_archive.py |
519 | index 91acf53..5c3f3b6 100644 |
520 | --- a/lib/lp/soyuz/tests/test_archive.py |
521 | +++ b/lib/lp/soyuz/tests/test_archive.py |
522 | @@ -4046,7 +4046,7 @@ class TestSigningKeyPropagation(TestCaseWithFactory): |
523 | |
524 | def test_ppa_created_with_no_signing_key(self): |
525 | ppa = self.factory.makeArchive(purpose=ArchivePurpose.PPA) |
526 | - self.assertIsNone(ppa.signing_key) |
527 | + self.assertIsNone(ppa.signing_key_fingerprint) |
528 | |
529 | def test_default_signing_key_propagated_to_new_ppa(self): |
530 | person = self.factory.makePerson() |
LGTM