Merge ~cjwatson/launchpad:sync-signingkeys-options into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: f5757ce5e641a25d8a0c5e4e53e8ce532010a303
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:sync-signingkeys-options
Merge into: launchpad:master
Diff against target: 773 lines (+357/-134)
8 files modified
database/schema/security.cfg (+1/-1)
lib/lp/archivepublisher/scripts/sync_signingkeys.py (+87/-26)
lib/lp/archivepublisher/signing.py (+1/-1)
lib/lp/archivepublisher/tests/test_sync_signingkeys.py (+181/-10)
lib/lp/services/signing/interfaces/signingkey.py (+14/-6)
lib/lp/services/signing/model/signingkey.py (+24/-24)
lib/lp/services/signing/tests/test_signingkey.py (+43/-65)
scripts/sync-signingkeys.py (+6/-1)
Reviewer Review Type Date Requested Status
Thiago F. Pappacena (community) Approve
Review via email: mp+387203@code.launchpad.net

Commit message

Add several new options to sync-signingkeys

Description of the change

--archive and --type allow limiting processing to a single archive and/or signing key type.

--overwrite allows overwriting keys that already exist on the signing service. Use with care, and probably only in conjunction with --archive and --type.

--dry-run just reports what would be done. This may be useful if preparing for an overwriting run.

I had to do some rearrangement of ArchiveSigningKeySet in preparation for this.

To post a comment you must log in.
Revision history for this message
Thiago F. Pappacena (pappacena) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/database/schema/security.cfg b/database/schema/security.cfg
2index 93dc9f8..0b3996c 100644
3--- a/database/schema/security.cfg
4+++ b/database/schema/security.cfg
5@@ -1207,7 +1207,7 @@ public.account = SELECT, INSERT, UPDATE
6 public.archive = SELECT, INSERT, UPDATE
7 public.archivearch = SELECT, INSERT, UPDATE, DELETE
8 public.archivejob = SELECT, INSERT
9-public.archivesigningkey = SELECT, INSERT, UPDATE
10+public.archivesigningkey = SELECT, INSERT, UPDATE, DELETE
11 public.binarypackagebuild = SELECT, INSERT, UPDATE
12 public.binarypackagefile = SELECT, INSERT, UPDATE
13 public.binarypackagename = SELECT, INSERT, UPDATE
14diff --git a/lib/lp/archivepublisher/scripts/sync_signingkeys.py b/lib/lp/archivepublisher/scripts/sync_signingkeys.py
15index e0b89d2..aa7fb53 100644
16--- a/lib/lp/archivepublisher/scripts/sync_signingkeys.py
17+++ b/lib/lp/archivepublisher/scripts/sync_signingkeys.py
18@@ -16,15 +16,20 @@ from datetime import datetime
19 import os
20
21 from pytz import utc
22+from storm.locals import Store
23 import transaction
24 from zope.component import getUtility
25
26 from lp.archivepublisher.config import getPubConfig
27 from lp.archivepublisher.model.publisherconfig import PublisherConfig
28 from lp.services.database.interfaces import IStore
29-from lp.services.scripts.base import LaunchpadScript
30+from lp.services.scripts.base import (
31+ LaunchpadScript,
32+ LaunchpadScriptFailure,
33+ )
34 from lp.services.signing.enums import SigningKeyType
35 from lp.services.signing.interfaces.signingkey import IArchiveSigningKeySet
36+from lp.soyuz.interfaces.archive import IArchiveSet
37 from lp.soyuz.model.archive import Archive
38
39
40@@ -35,23 +40,62 @@ class SyncSigningKeysScript(LaunchpadScript):
41
42 def add_my_options(self):
43 self.parser.add_option(
44+ "-A", "--archive",
45+ help=(
46+ "The reference of the archive to process "
47+ "(default: all archives)."))
48+ self.parser.add_option(
49+ "-t", "--type",
50+ help="The type of keys to process (default: all types).")
51+
52+ self.parser.add_option(
53 "-l", "--limit", dest="limit", type=int,
54 help="How many archives to fetch.")
55-
56 self.parser.add_option(
57 "-o", "--offset", dest="offset", type=int,
58 help="Offset on archives list.")
59
60+ self.parser.add_option(
61+ "--overwrite", action="store_true", default=False,
62+ help="Overwrite keys that already exist on the signing service.")
63+ self.parser.add_option(
64+ "-n", "--dry-run", action="store_true", default=False,
65+ help="Report what would be done, but don't actually inject keys.")
66+
67 def getArchives(self):
68 """Gets the list of archives that should be processed."""
69- archives = IStore(Archive).find(
70- Archive,
71- PublisherConfig.distribution_id == Archive.distributionID)
72- archives = archives.order_by(Archive.id)
73+ if self.options.archive is not None:
74+ archive = getUtility(IArchiveSet).getByReference(
75+ self.options.archive)
76+ if archive is None:
77+ raise LaunchpadScriptFailure(
78+ "No archive named '%s' could be found." %
79+ self.options.archive)
80+ archives = [archive]
81+ else:
82+ archives = IStore(Archive).find(
83+ Archive,
84+ PublisherConfig.distribution_id == Archive.distributionID)
85+ archives = archives.order_by(Archive.id)
86 start = self.options.offset if self.options.offset else 0
87 end = start + self.options.limit if self.options.limit else None
88 return archives[start:end]
89
90+ def getKeyTypes(self):
91+ """Gets the list of key types that should be processed."""
92+ if self.options.type is not None:
93+ try:
94+ key_type = SigningKeyType.getTermByToken(
95+ self.options.type).value
96+ except LookupError:
97+ raise LaunchpadScriptFailure(
98+ "There is no signing key type named '%s'." %
99+ self.options.type)
100+ key_types = [key_type]
101+ else:
102+ key_types = SigningKeyType.items
103+ return key_types
104+
105 def getKeysPerType(self, dir):
106 """Returns the existing key files per type in the given directory.
107
108@@ -68,7 +112,7 @@ class SyncSigningKeysScript(LaunchpadScript):
109 os.path.join("fit", "fit.crt")),
110 }
111 found_keys_per_type = {}
112- for key_type in SigningKeyType.items:
113+ for key_type in self.getKeyTypes():
114 files = [os.path.join(dir, f) for f in keys_per_type[key_type]]
115 self.logger.debug("Checking files %s...", ', '.join(files))
116 if all(os.path.exists(f) for f in files):
117@@ -102,25 +146,39 @@ class SyncSigningKeysScript(LaunchpadScript):
118
119 def inject(self, archive, key_type, series, priv_key_path, pub_key_path):
120 arch_signing_key_set = getUtility(IArchiveSigningKeySet)
121- existing_signing_key = arch_signing_key_set.getSigningKey(
122+ existing_archive_signing_key = arch_signing_key_set.get(
123 key_type, archive, series, exact_match=True)
124- if existing_signing_key is not None:
125- self.logger.info("Signing key for %s / %s / %s already exists",
126- key_type, archive.reference,
127- series.name if series else None)
128- return existing_signing_key
129-
130- with open(priv_key_path, 'rb') as fd:
131- private_key = fd.read()
132- with open(pub_key_path, 'rb') as fd:
133- public_key = fd.read()
134-
135- now = datetime.now().replace(tzinfo=utc)
136- description = u"%s key for %s" % (key_type.name, archive.reference)
137- return arch_signing_key_set.inject(
138- key_type, private_key, public_key,
139- description, now, archive,
140- earliest_distro_series=series)
141+ if existing_archive_signing_key is not None:
142+ if self.options.overwrite:
143+ self.logger.info(
144+ "Overwriting existing signing key for %s / %s / %s",
145+ key_type, archive.reference,
146+ series.name if series else None)
147+ Store.of(existing_archive_signing_key).remove(
148+ existing_archive_signing_key)
149+ else:
150+ self.logger.info(
151+ "Signing key for %s / %s / %s already exists",
152+ key_type, archive.reference,
153+ series.name if series else None)
154+ return existing_archive_signing_key
155+
156+ if self.options.dry_run:
157+ self.logger.info(
158+ "Would inject signing key for %s / %s / %s",
159+ key_type, archive.reference, series.name if series else None)
160+ else:
161+ with open(priv_key_path, 'rb') as fd:
162+ private_key = fd.read()
163+ with open(pub_key_path, 'rb') as fd:
164+ public_key = fd.read()
165+
166+ now = datetime.now().replace(tzinfo=utc)
167+ description = u"%s key for %s" % (key_type.name, archive.reference)
168+ return arch_signing_key_set.inject(
169+ key_type, private_key, public_key,
170+ description, now, archive,
171+ earliest_distro_series=series)
172
173 def processArchive(self, archive):
174 for series, path in self.getSeriesPaths(archive).items():
175@@ -137,5 +195,8 @@ class SyncSigningKeysScript(LaunchpadScript):
176 self.logger.info(
177 "#%s - Processing keys for archive %s.", i, archive.reference)
178 self.processArchive(archive)
179- transaction.commit()
180+ if self.options.dry_run:
181+ transaction.abort()
182+ else:
183+ transaction.commit()
184 self.logger.info("Finished processing archives injections.")
185diff --git a/lib/lp/archivepublisher/signing.py b/lib/lp/archivepublisher/signing.py
186index 1eca0a4..0425242 100644
187--- a/lib/lp/archivepublisher/signing.py
188+++ b/lib/lp/archivepublisher/signing.py
189@@ -453,7 +453,7 @@ class SigningUpload(CustomUpload):
190 return
191
192 key_set = getUtility(IArchiveSigningKeySet)
193- current_key = key_set.getSigningKey(
194+ current_key = key_set.get(
195 key_type, self.archive, None, exact_match=True)
196 if current_key is not None:
197 self.logger.info("Skipping injection for key type %s: archive "
198diff --git a/lib/lp/archivepublisher/tests/test_sync_signingkeys.py b/lib/lp/archivepublisher/tests/test_sync_signingkeys.py
199index eb185de..d20e676 100644
200--- a/lib/lp/archivepublisher/tests/test_sync_signingkeys.py
201+++ b/lib/lp/archivepublisher/tests/test_sync_signingkeys.py
202@@ -21,6 +21,7 @@ from fixtures import (
203 from pytz import utc
204 from testtools.content import text_content
205 from testtools.matchers import (
206+ ContainsAll,
207 Equals,
208 MatchesDict,
209 MatchesStructure,
210@@ -31,6 +32,7 @@ from zope.component import getUtility
211 from lp.archivepublisher.model.publisherconfig import PublisherConfig
212 from lp.archivepublisher.scripts.sync_signingkeys import SyncSigningKeysScript
213 from lp.services.compat import mock
214+from lp.services.config import config
215 from lp.services.config.fixture import (
216 ConfigFixture,
217 ConfigUseFixture,
218@@ -43,6 +45,7 @@ from lp.services.signing.testing.fixture import SigningServiceFixture
219 from lp.services.signing.tests.helpers import SigningServiceClientFixture
220 from lp.soyuz.model.archive import Archive
221 from lp.testing import TestCaseWithFactory
222+from lp.testing.dbuser import dbuser
223 from lp.testing.layers import ZopelessDatabaseLayer
224 from lp.testing.script import run_script
225
226@@ -65,7 +68,9 @@ class TestSyncSigningKeysScript(TestCaseWithFactory):
227 self.useFixture(ConfigUseFixture(config_name))
228
229 def makeScript(self, test_args):
230- script = SyncSigningKeysScript("test-sync", test_args=test_args)
231+ script = SyncSigningKeysScript(
232+ "test-sync", dbuser=config.archivepublisher.dbuser,
233+ test_args=test_args)
234 script.logger = BufferLogger()
235 return script
236
237@@ -110,6 +115,22 @@ class TestSyncSigningKeysScript(TestCaseWithFactory):
238 archives = list(script.getArchives())
239 self.assertEqual(all_archives[2:5], archives)
240
241+ def test_fetch_archives_with_reference(self):
242+ all_archives = list(self.makeArchives())
243+ script = self.makeScript(["--archive", all_archives[0].reference])
244+ archives = list(script.getArchives())
245+ self.assertEqual([all_archives[0]], archives)
246+
247+ def test_get_key_types(self):
248+ script = self.makeScript([])
249+ key_types = script.getKeyTypes()
250+ self.assertEqual(SigningKeyType.items, key_types)
251+
252+ def test_get_key_types_with_selection(self):
253+ script = self.makeScript(["--type", "UEFI"])
254+ key_types = script.getKeyTypes()
255+ self.assertEqual([SigningKeyType.UEFI], key_types)
256+
257 def test_get_keys_per_type(self):
258 keys_dir = self.signing_root_dir
259
260@@ -178,8 +199,7 @@ class TestSyncSigningKeysScript(TestCaseWithFactory):
261 'wb') as fd:
262 fd.write(b"Series 1 %s" % filename)
263
264- script = self.makeScript([])
265- script.getArchives = mock.Mock(return_value=[archive])
266+ script = self.makeScript(["--archive", archive.reference])
267 script.inject = mock.Mock()
268 script.main()
269
270@@ -234,6 +254,103 @@ class TestSyncSigningKeysScript(TestCaseWithFactory):
271 SigningKeyType.UEFI, None),
272 content)
273
274+ def test_process_archive_dry_run(self):
275+ signing_service_client = self.useFixture(
276+ SigningServiceClientFixture(self.factory))
277+
278+ distro = self.factory.makeDistribution()
279+ series1 = self.factory.makeDistroSeries(distribution=distro)
280+ series2 = self.factory.makeDistroSeries(distribution=distro)
281+
282+ archive = self.factory.makeArchive(distribution=distro)
283+ key_dirs = self.makeArchiveSigningDir(archive, [series1, series2])
284+
285+ archive_root = key_dirs[None]
286+
287+ archive_signing_key_fit = self.factory.makeArchiveSigningKey(
288+ archive=archive, distro_series=series1,
289+ signing_key=self.factory.makeSigningKey(
290+ key_type=SigningKeyType.FIT))
291+ fingerprint_fit = archive_signing_key_fit.signing_key.fingerprint
292+
293+ transaction.commit()
294+
295+ # Create fake UEFI keys for the root
296+ for filename in ("uefi.key", "uefi.crt"):
297+ with open(os.path.join(archive_root, filename), 'wb') as fd:
298+ fd.write(b"Root %s" % filename)
299+
300+ # Create fake OPAL and Kmod keys for series1
301+ for filename in ("opal.pem", "opal.x509", "kmod.pem", "kmod.x509"):
302+ with open(os.path.join(key_dirs[series1], filename), 'wb') as fd:
303+ fd.write(b"Series 1 %s" % filename)
304+
305+ # Create fake FIT keys for series1
306+ os.makedirs(os.path.join(key_dirs[series1], "fit"))
307+ for filename in ("fit.key", "fit.crt"):
308+ with open(os.path.join(key_dirs[series1], "fit", filename),
309+ 'wb') as fd:
310+ fd.write(b"Series 1 %s" % filename)
311+
312+ script = self.makeScript(
313+ ["--archive", archive.reference, "--overwrite", "--dry-run"])
314+ script.main()
315+
316+ self.assertEqual(0, signing_service_client.inject.call_count)
317+
318+ # No changes are committed to the database.
319+ archive_signing_key_set = getUtility(IArchiveSigningKeySet)
320+ self.assertIsNone(
321+ archive_signing_key_set.getSigningKey(
322+ SigningKeyType.UEFI, archive, None, exact_match=True))
323+ self.assertIsNone(
324+ archive_signing_key_set.getSigningKey(
325+ SigningKeyType.KMOD, archive, series1, exact_match=True))
326+ self.assertIsNone(
327+ archive_signing_key_set.getSigningKey(
328+ SigningKeyType.OPAL, archive, series1, exact_match=True))
329+ self.assertThat(
330+ archive_signing_key_set.getSigningKey(
331+ SigningKeyType.FIT, archive, series1, exact_match=True),
332+ MatchesStructure.byEquality(fingerprint=fingerprint_fit))
333+
334+ # Check the log messages.
335+ found_tpl = "INFO Found key files %s / %s (type=%s, series=%s)."
336+ overwrite_tpl = (
337+ "INFO Overwriting existing signing key for %s / %s / %s")
338+ inject_tpl = "INFO Would inject signing key for %s / %s / %s"
339+ self.assertThat(
340+ script.logger.content.as_text().splitlines(),
341+ ContainsAll([
342+ "INFO #0 - Processing keys for archive %s." %
343+ archive.reference,
344+ found_tpl % (
345+ os.path.join(archive_root, "uefi.key"),
346+ os.path.join(archive_root, "uefi.crt"),
347+ SigningKeyType.UEFI, None),
348+ inject_tpl % (SigningKeyType.UEFI, archive.reference, None),
349+ found_tpl % (
350+ os.path.join(key_dirs[series1], "kmod.pem"),
351+ os.path.join(key_dirs[series1], "kmod.x509"),
352+ SigningKeyType.KMOD, series1.name),
353+ inject_tpl % (
354+ SigningKeyType.KMOD, archive.reference, series1.name),
355+ found_tpl % (
356+ os.path.join(key_dirs[series1], "opal.pem"),
357+ os.path.join(key_dirs[series1], "opal.x509"),
358+ SigningKeyType.OPAL, series1.name),
359+ inject_tpl % (
360+ SigningKeyType.OPAL, archive.reference, series1.name),
361+ found_tpl % (
362+ os.path.join(key_dirs[series1], "fit", "fit.key"),
363+ os.path.join(key_dirs[series1], "fit", "fit.crt"),
364+ SigningKeyType.FIT, series1.name),
365+ overwrite_tpl % (
366+ SigningKeyType.FIT, archive.reference, series1.name),
367+ inject_tpl % (
368+ SigningKeyType.FIT, archive.reference, series1.name),
369+ ]))
370+
371 def test_inject(self):
372 signing_service_client = self.useFixture(
373 SigningServiceClientFixture(self.factory))
374@@ -258,8 +375,10 @@ class TestSyncSigningKeysScript(TestCaseWithFactory):
375
376 script = self.makeScript([])
377
378- result_with_series = script.inject(
379- archive, SigningKeyType.UEFI, series, priv_key_path, pub_key_path)
380+ with dbuser(config.archivepublisher.dbuser):
381+ result_with_series = script.inject(
382+ archive, SigningKeyType.UEFI, series,
383+ priv_key_path, pub_key_path)
384
385 self.assertThat(result_with_series, MatchesStructure.byEquality(
386 archive=archive,
387@@ -278,8 +397,10 @@ class TestSyncSigningKeysScript(TestCaseWithFactory):
388 u"UEFI key for %s" % archive.reference, now.replace(tzinfo=utc)),
389 signing_service_client.inject.call_args[0])
390
391- result_no_series = script.inject(
392- archive, SigningKeyType.UEFI, None, priv_key_path, pub_key_path)
393+ with dbuser(config.archivepublisher.dbuser):
394+ result_no_series = script.inject(
395+ archive, SigningKeyType.UEFI, None,
396+ priv_key_path, pub_key_path)
397
398 self.assertThat(result_no_series, MatchesStructure.byEquality(
399 archive=archive,
400@@ -312,12 +433,13 @@ class TestSyncSigningKeysScript(TestCaseWithFactory):
401 fd.write(b"Public key content")
402
403 expected_arch_signing_key = self.factory.makeArchiveSigningKey(
404- archive=archive, distro_series=series).signing_key
405+ archive=archive, distro_series=series)
406 key_type = expected_arch_signing_key.key_type
407
408 script = self.makeScript([])
409- got_arch_key = script.inject(
410- archive, key_type, series, priv_key_path, pub_key_path)
411+ with dbuser(config.archivepublisher.dbuser):
412+ got_arch_key = script.inject(
413+ archive, key_type, series, priv_key_path, pub_key_path)
414 self.assertEqual(expected_arch_signing_key, got_arch_key)
415
416 self.assertIn(
417@@ -325,6 +447,55 @@ class TestSyncSigningKeysScript(TestCaseWithFactory):
418 (key_type, archive.reference, series.name),
419 script.logger.content.as_text())
420
421+ def test_inject_existing_key_with_overwrite(self):
422+ signing_service_client = self.useFixture(
423+ SigningServiceClientFixture(self.factory))
424+
425+ now = datetime.now()
426+ mock_datetime = self.useFixture(MockPatch(
427+ 'lp.archivepublisher.scripts.sync_signingkeys.datetime')).mock
428+ mock_datetime.now = lambda: now
429+
430+ distro = self.factory.makeDistribution()
431+ series = self.factory.makeDistroSeries(distribution=distro)
432+ archive = self.factory.makeArchive(distribution=distro)
433+
434+ tmpdir = self.useFixture(TempDir()).path
435+ priv_key_path = os.path.join(tmpdir, "priv.key")
436+ pub_key_path = os.path.join(tmpdir, "pub.crt")
437+ with open(priv_key_path, 'wb') as fd:
438+ fd.write(b"Private key content")
439+ with open(pub_key_path, 'wb') as fd:
440+ fd.write(b"Public key content")
441+
442+ self.factory.makeArchiveSigningKey(
443+ archive=archive, distro_series=series,
444+ signing_key=self.factory.makeSigningKey(
445+ key_type=SigningKeyType.UEFI))
446+
447+ script = self.makeScript(["--overwrite"])
448+ with dbuser(config.archivepublisher.dbuser):
449+ result = script.inject(
450+ archive, SigningKeyType.UEFI, series,
451+ priv_key_path, pub_key_path)
452+
453+ self.assertThat(result, MatchesStructure(
454+ archive=Equals(archive),
455+ earliest_distro_series=Equals(series),
456+ key_type=Equals(SigningKeyType.UEFI),
457+ signing_key=MatchesStructure.byEquality(
458+ key_type=SigningKeyType.UEFI,
459+ public_key=b"Public key content")))
460+ self.assertEqual(
461+ [(SigningKeyType.UEFI, b"Private key content",
462+ b"Public key content",
463+ "UEFI key for %s" % archive.reference, now.replace(tzinfo=utc))],
464+ signing_service_client.inject.call_args)
465+ self.assertIn(
466+ "Overwriting existing signing key for %s / %s / %s" %
467+ (SigningKeyType.UEFI, archive.reference, series.name),
468+ script.logger.content.as_text())
469+
470 def runScript(self):
471 transaction.commit()
472 ret, out, err = run_script("scripts/sync-signingkeys.py")
473diff --git a/lib/lp/services/signing/interfaces/signingkey.py b/lib/lp/services/signing/interfaces/signingkey.py
474index eccf2a0..77814b8 100644
475--- a/lib/lp/services/signing/interfaces/signingkey.py
476+++ b/lib/lp/services/signing/interfaces/signingkey.py
477@@ -117,14 +117,22 @@ class IArchiveSigningKeySet(Interface):
478 False if it was updated).
479 """
480
481- def getSigningKey(key_type, archive, distro_series, exact_match=False):
482- """Get the most suitable key for a given archive / distro series
483- pair.
484+ def get(key_type, archive, distro_series, exact_match=False):
485+ """Get the most suitable ArchiveSigningKey for a given context.
486
487 :param exact_match: If True, returns the ArchiveSigningKey matching
488- exactly the given key_type, archive and
489- distro_series. If False, gets the best match.
490- :return: The most suitable key
491+ exactly the given key_type, archive and distro_series. If False,
492+ gets the best match.
493+ :return: The most suitable key, or None.
494+ """
495+
496+ def getSigningKey(key_type, archive, distro_series, exact_match=False):
497+ """Get the most suitable SigningKey for a given context.
498+
499+ :param exact_match: If True, returns the SigningKey matching exactly
500+ the given key_type, archive and distro_series. If False, gets
501+ the best match.
502+ :return: The most suitable key, or None.
503 """
504
505 def generate(key_type, description, archive, earliest_distro_series=None):
506diff --git a/lib/lp/services/signing/model/signingkey.py b/lib/lp/services/signing/model/signingkey.py
507index 5557e7e..0c754f9 100644
508--- a/lib/lp/services/signing/model/signingkey.py
509+++ b/lib/lp/services/signing/model/signingkey.py
510@@ -12,8 +12,6 @@ __all__ = [
511 'SigningKey',
512 ]
513
514-from collections import defaultdict
515-
516 import pytz
517 from storm.locals import (
518 Bytes,
519@@ -175,46 +173,48 @@ class ArchiveSigningKeySet:
520 return obj
521
522 @classmethod
523- def getSigningKey(cls, key_type, archive, distro_series,
524- exact_match=False):
525+ def get(cls, key_type, archive, distro_series, exact_match=False):
526 store = IStore(ArchiveSigningKey)
527- # Gets all the keys of the given key_type available for the archive
528- rs = store.find(ArchiveSigningKey,
529- SigningKey.id == ArchiveSigningKey.signing_key_id,
530- SigningKey.key_type == key_type,
531- ArchiveSigningKey.key_type == key_type,
532- ArchiveSigningKey.archive == archive)
533+
534+ # Get all the keys of the given key_type available for the archive.
535+ archive_signing_keys = store.find(
536+ ArchiveSigningKey,
537+ ArchiveSigningKey.key_type == key_type,
538+ ArchiveSigningKey.archive == archive)
539
540 if exact_match:
541- rs = rs.find(
542+ archive_signing_keys = archive_signing_keys.find(
543 ArchiveSigningKey.earliest_distro_series == distro_series)
544
545- # prefetch related signing keys to avoid extra queries.
546- signing_keys = store.find(SigningKey, [
547- SigningKey.id.is_in([i.signing_key_id for i in rs])])
548- signing_keys_by_id = {i.id: i for i in signing_keys}
549-
550- # Group keys per type, and per distro series
551- keys_per_series = defaultdict(dict)
552- for i in rs:
553- signing_key = signing_keys_by_id[i.signing_key_id]
554- keys_per_series[i.earliest_distro_series] = signing_key
555+ # Group keys per distro series.
556+ keys_per_series = {
557+ archive_signing_key.earliest_distro_series_id: archive_signing_key
558+ for archive_signing_key in archive_signing_keys}
559
560- # Let's search the most suitable per key type.
561+ # Find the most suitable per key type.
562 found_series = False
563 # Note that archive.distribution.series is, by default, sorted by
564 # "version", reversed.
565 for series in archive.distribution.series:
566 if series == distro_series:
567 found_series = True
568- if found_series and series in keys_per_series:
569- return keys_per_series[series]
570+ if found_series and series.id in keys_per_series:
571+ return keys_per_series[series.id]
572 # If no specific key for distro_series was found, returns
573 # the keys for the archive itself (or None if no key is
574 # available for the archive either).
575 return keys_per_series.get(None)
576
577 @classmethod
578+ def getSigningKey(cls, key_type, archive, distro_series,
579+ exact_match=False):
580+ archive_signing_key = cls.get(
581+ key_type, archive, distro_series, exact_match=exact_match)
582+ return (
583+ None if archive_signing_key is None
584+ else archive_signing_key.signing_key)
585+
586+ @classmethod
587 def generate(cls, key_type, description, archive,
588 earliest_distro_series=None):
589 signing_key = SigningKey.generate(key_type, description)
590diff --git a/lib/lp/services/signing/tests/test_signingkey.py b/lib/lp/services/signing/tests/test_signingkey.py
591index 81376b1..a7d7360 100644
592--- a/lib/lp/services/signing/tests/test_signingkey.py
593+++ b/lib/lp/services/signing/tests/test_signingkey.py
594@@ -141,6 +141,22 @@ class TestArchiveSigningKey(TestCaseWithFactory):
595 client = removeSecurityProxy(getUtility(ISigningServiceClient))
596 self.addCleanup(client._cleanCaches)
597
598+ def assertGet(self, expected_archive_signing_key,
599+ key_type, archive, distro_series, exact_match=False):
600+ # get and getSigningKey return the expected results.
601+ arch_signing_key_set = getUtility(IArchiveSigningKeySet)
602+ expected_signing_key = (
603+ None if expected_archive_signing_key is None
604+ else expected_archive_signing_key.signing_key)
605+ self.assertEqual(
606+ expected_archive_signing_key,
607+ arch_signing_key_set.get(
608+ key_type, archive, distro_series, exact_match=exact_match))
609+ self.assertEqual(
610+ expected_signing_key,
611+ arch_signing_key_set.getSigningKey(
612+ key_type, archive, distro_series, exact_match=exact_match))
613+
614 @responses.activate
615 def test_generate_saves_correctly(self):
616 self.signing_service.addResponses(self)
617@@ -280,9 +296,6 @@ class TestArchiveSigningKey(TestCaseWithFactory):
618 self.assertEqual(2, store.find(ArchiveSigningKey).count())
619
620 def test_get_signing_keys_without_distro_series_configured(self):
621- UEFI = SigningKeyType.UEFI
622- KMOD = SigningKeyType.KMOD
623-
624 archive = self.factory.makeArchive()
625 distro_series = archive.distribution.series[0]
626 uefi_key = self.factory.makeSigningKey(
627@@ -304,25 +317,16 @@ class TestArchiveSigningKey(TestCaseWithFactory):
628 archive, None, kmod_key)
629
630 # Should find the keys if we ask for the archive key
631- self.assertEqual(
632- arch_uefi_key.signing_key,
633- arch_signing_key_set.getSigningKey(UEFI, archive, None))
634- self.assertEqual(
635- arch_kmod_key.signing_key,
636- arch_signing_key_set.getSigningKey(KMOD, archive, None))
637+ self.assertGet(arch_uefi_key, SigningKeyType.UEFI, archive, None)
638+ self.assertGet(arch_kmod_key, SigningKeyType.KMOD, archive, None)
639
640 # Should find the key if we ask for archive + distro_series key
641- self.assertEqual(
642- arch_uefi_key.signing_key,
643- arch_signing_key_set.getSigningKey(UEFI, archive, distro_series))
644- self.assertEqual(
645- arch_kmod_key.signing_key,
646- arch_signing_key_set.getSigningKey(KMOD, archive, distro_series))
647+ self.assertGet(
648+ arch_uefi_key, SigningKeyType.UEFI, archive, distro_series)
649+ self.assertGet(
650+ arch_kmod_key, SigningKeyType.KMOD, archive, distro_series)
651
652 def test_get_signing_key_exact_match(self):
653- UEFI = SigningKeyType.UEFI
654- KMOD = SigningKeyType.KMOD
655-
656 archive = self.factory.makeArchive()
657 distro_series1 = archive.distribution.series[0]
658 distro_series2 = archive.distribution.series[1]
659@@ -342,38 +346,27 @@ class TestArchiveSigningKey(TestCaseWithFactory):
660 archive, None, kmod_key)
661
662 # Should get the UEFI key for distro_series1
663- self.assertEqual(
664- series1_uefi_key.signing_key,
665- arch_signing_key_set.getSigningKey(
666- UEFI, archive, distro_series1, exact_match=True)
667- )
668+ self.assertGet(
669+ series1_uefi_key,
670+ SigningKeyType.UEFI, archive, distro_series1, exact_match=True)
671 # Should get the archive's KMOD key.
672- self.assertEqual(
673- arch_kmod_key.signing_key,
674- arch_signing_key_set.getSigningKey(
675- KMOD, archive, None, exact_match=True)
676- )
677+ self.assertGet(
678+ arch_kmod_key,
679+ SigningKeyType.KMOD, archive, None, exact_match=True)
680 # distro_series1 has no KMOD key.
681- self.assertEqual(
682+ self.assertGet(
683 None,
684- arch_signing_key_set.getSigningKey(
685- KMOD, archive, distro_series1, exact_match=True)
686- )
687+ SigningKeyType.KMOD, archive, distro_series1, exact_match=True)
688 # distro_series2 has no key at all.
689- self.assertEqual(
690+ self.assertGet(
691 None,
692- arch_signing_key_set.getSigningKey(
693- KMOD, archive, distro_series2, exact_match=True)
694- )
695+ SigningKeyType.KMOD, archive, distro_series2, exact_match=True)
696
697 def test_get_signing_keys_with_distro_series_configured(self):
698- UEFI = SigningKeyType.UEFI
699- KMOD = SigningKeyType.KMOD
700-
701 archive = self.factory.makeArchive()
702 series = archive.distribution.series
703- uefi_key = self.factory.makeSigningKey(key_type=UEFI)
704- kmod_key = self.factory.makeSigningKey(key_type=KMOD)
705+ uefi_key = self.factory.makeSigningKey(key_type=SigningKeyType.UEFI)
706+ kmod_key = self.factory.makeSigningKey(key_type=SigningKeyType.KMOD)
707
708 # Fill the database with keys from other archives to make sure we
709 # are filtering it out
710@@ -395,34 +388,19 @@ class TestArchiveSigningKey(TestCaseWithFactory):
711
712 # If no distroseries is specified, it should give back no KMOD key,
713 # since we don't have a default
714- self.assertEqual(
715- arch_uefi_key.signing_key,
716- arch_signing_key_set.getSigningKey(UEFI, archive, None))
717- self.assertEqual(
718- None,
719- arch_signing_key_set.getSigningKey(KMOD, archive, None))
720+ self.assertGet(arch_uefi_key, SigningKeyType.UEFI, archive, None)
721+ self.assertGet(None, SigningKeyType.KMOD, archive, None)
722
723 # For the most recent series, use the KMOD key we've set for the
724 # previous one
725- self.assertEqual(
726- arch_uefi_key.signing_key,
727- arch_signing_key_set.getSigningKey(UEFI, archive, series[0]))
728- self.assertEqual(
729- arch_kmod_key.signing_key,
730- arch_signing_key_set.getSigningKey(KMOD, archive, series[0]))
731+ self.assertGet(arch_uefi_key, SigningKeyType.UEFI, archive, series[0])
732+ self.assertGet(arch_kmod_key, SigningKeyType.KMOD, archive, series[0])
733
734 # For the previous series, we have a KMOD key configured
735- self.assertEqual(
736- arch_uefi_key.signing_key,
737- arch_signing_key_set.getSigningKey(UEFI, archive, series[1]))
738- self.assertEqual(
739- arch_kmod_key.signing_key,
740- arch_signing_key_set.getSigningKey(KMOD, archive, series[1]))
741+ self.assertGet(arch_uefi_key, SigningKeyType.UEFI, archive, series[1])
742+ self.assertGet(arch_kmod_key, SigningKeyType.KMOD, archive, series[1])
743
744 # For the old series, we have an old KMOD key configured
745- self.assertEqual(
746- arch_uefi_key.signing_key,
747- arch_signing_key_set.getSigningKey(UEFI, archive, series[2]))
748- self.assertEqual(
749- old_arch_kmod_key.signing_key,
750- arch_signing_key_set.getSigningKey(KMOD, archive, series[2]))
751+ self.assertGet(arch_uefi_key, SigningKeyType.UEFI, archive, series[2])
752+ self.assertGet(
753+ old_arch_kmod_key, SigningKeyType.KMOD, archive, series[2])
754diff --git a/scripts/sync-signingkeys.py b/scripts/sync-signingkeys.py
755index 4cdb5cb..84310b0 100755
756--- a/scripts/sync-signingkeys.py
757+++ b/scripts/sync-signingkeys.py
758@@ -3,9 +3,14 @@
759 # GNU Affero General Public License version 3 (see the file LICENSE).
760
761 """Script to inject archive keys into signing service."""
762+
763 import _pythonpath
764+
765 from lp.archivepublisher.scripts.sync_signingkeys import SyncSigningKeysScript
766+from lp.services.config import config
767+
768
769 if __name__ == '__main__':
770- script = SyncSigningKeysScript('sync-signingkeys')
771+ script = SyncSigningKeysScript(
772+ 'sync-signingkeys', dbuser=config.archivepublisher.dbuser)
773 script.lock_and_run()

Subscribers

People subscribed via source and target branches

to status/vote changes: