launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #21527
[Merge] lp:~cjwatson/launchpad/send-keys-to-builders into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/send-keys-to-builders into lp:launchpad with lp:~cjwatson/launchpad/faster-archive-signing-key-tests as a prerequisite.
Commit message:
Send the necessary set of archive signing keys to builders.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1626739 in Launchpad itself: "Snapcraft build failing in Yakkety for unauthenticated stage-packages"
https://bugs.launchpad.net/launchpad/+bug/1626739
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/send-keys-to-builders/+merge/323438
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/send-keys-to-builders into lp:launchpad.
=== modified file 'lib/lp/code/model/recipebuilder.py'
--- lib/lp/code/model/recipebuilder.py 2017-04-29 23:54:40 +0000
+++ lib/lp/code/model/recipebuilder.py 2017-04-29 23:54:40 +0000
@@ -67,10 +67,11 @@
args["ogrecomponent"] = get_primary_current_component(
self.build.archive, self.build.distroseries,
None).name
- args['archives'] = yield get_sources_list_for_building(
- self.build, distroarchseries, None,
- tools_source=config.builddmaster.bzr_builder_sources_list,
- logger=logger)
+ args['archives'], args['trusted_keys'] = (
+ yield get_sources_list_for_building(
+ self.build, distroarchseries, None,
+ tools_source=config.builddmaster.bzr_builder_sources_list,
+ logger=logger))
args['archive_private'] = self.build.archive.private
args['distroseries_name'] = self.build.distroseries.name
if self.build.recipe.base_git_repository is not None:
=== modified file 'lib/lp/code/model/tests/test_recipebuilder.py'
--- lib/lp/code/model/tests/test_recipebuilder.py 2017-04-29 23:54:40 +0000
+++ lib/lp/code/model/tests/test_recipebuilder.py 2017-04-29 23:54:40 +0000
@@ -5,16 +5,21 @@
__metaclass__ = type
+import os.path
import shutil
import tempfile
from testtools.deferredruntest import AsynchronousDeferredRunTest
+from testtools.matchers import MatchesListwise
import transaction
from twisted.internet import defer
from twisted.trial.unittest import TestCase as TrialTestCase
from zope.component import getUtility
from zope.security.proxy import removeSecurityProxy
+from lp.archivepublisher.interfaces.archivesigningkey import (
+ IArchiveSigningKey,
+ )
from lp.buildmaster.enums import BuildStatus
from lp.buildmaster.interactor import BuilderInteractor
from lp.buildmaster.interfaces.buildfarmjobbehaviour import (
@@ -40,13 +45,19 @@
from lp.soyuz.adapters.archivedependencies import (
get_sources_list_for_building,
)
-from lp.soyuz.enums import ArchivePurpose
+from lp.soyuz.enums import (
+ ArchivePurpose,
+ PackagePublishingStatus,
+ )
+from lp.soyuz.tests.soyuz import Base64KeyMatches
from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
from lp.testing import (
person_logged_in,
TestCaseWithFactory,
)
from lp.testing.fakemethod import FakeMethod
+from lp.testing.gpgkeys import gpgkeysdir
+from lp.testing.keyserver import InProcessKeyServerFixture
from lp.testing.layers import LaunchpadZopelessLayer
from lp.testing.mail_helpers import pop_notifications
@@ -59,7 +70,10 @@
archive=None, git=False):
"""Create a sample `ISourcePackageRecipeBuild`."""
spn = self.factory.makeSourcePackageName("apackage")
- distro = self.factory.makeDistribution(name="distro")
+ if archive is None:
+ distro = self.factory.makeDistribution(name="distro")
+ else:
+ distro = archive.distribution
distroseries = self.factory.makeDistroSeries(
name="mydistro", distribution=distro)
processor = getUtility(IProcessorSet).getByName('386')
@@ -144,7 +158,7 @@
class TestAsyncRecipeBuilder(TestRecipeBuilderBase):
- run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=30)
+ run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=10)
def _setBuilderConfig(self):
"""Setup a temporary builder config."""
@@ -158,8 +172,9 @@
self._setBuilderConfig()
job = self.makeJob()
distroarchseries = job.build.distroseries.architectures[0]
- expected_archives = yield get_sources_list_for_building(
- job.build, distroarchseries, None)
+ expected_archives, expected_trusted_keys = (
+ yield get_sources_list_for_building(
+ job.build, distroarchseries, None))
expected_archives.insert(
0, "deb http://foo %s main" % job.build.distroseries.name)
args = yield job._extraBuildArgs(distroarchseries)
@@ -175,6 +190,7 @@
'# bzr-builder format 0.3 deb-version {debupstream}-0~{revno}\n'
'lp://dev/~joe/someapp/pkg\n',
'archives': expected_archives,
+ 'trusted_keys': expected_trusted_keys,
'distroseries_name': job.build.distroseries.name,
}, args)
@@ -249,8 +265,9 @@
# (note the missing 's' in %(series)
job = self.makeJob()
distroarchseries = job.build.distroseries.architectures[0]
- expected_archives = yield get_sources_list_for_building(
- job.build, distroarchseries, None)
+ expected_archives, expected_trusted_keys = (
+ yield get_sources_list_for_building(
+ job.build, distroarchseries, None))
logger = BufferLogger()
extra_args = yield job._extraBuildArgs(distroarchseries, logger)
self.assertEqual({
@@ -265,6 +282,7 @@
'# bzr-builder format 0.3 deb-version {debupstream}-0~{revno}\n'
'lp://dev/~joe/someapp/pkg\n',
'archives': expected_archives,
+ 'trusted_keys': expected_trusted_keys,
'distroseries_name': job.build.distroseries.name,
}, extra_args)
self.assertIn(
@@ -278,16 +296,19 @@
job = self.makeJob()
distroarchseries = job.build.distroseries.architectures[0]
args = yield job._extraBuildArgs(distroarchseries)
- expected_archives = yield get_sources_list_for_building(
- job.build, distroarchseries, None)
+ expected_archives, expected_trusted_keys = (
+ yield get_sources_list_for_building(
+ job.build, distroarchseries, None))
self.assertEqual(args["archives"], expected_archives)
+ self.assertEqual(args["trusted_keys"], expected_trusted_keys)
@defer.inlineCallbacks
def test_extraBuildArgs_git(self):
job = self.makeJob(git=True)
distroarchseries = job.build.distroseries.architectures[0]
- expected_archives = yield get_sources_list_for_building(
- job.build, distroarchseries, None)
+ expected_archives, expected_trusted_keys = (
+ yield get_sources_list_for_building(
+ job.build, distroarchseries, None))
extra_args = yield job._extraBuildArgs(distroarchseries)
self.assertEqual({
'archive_private': False,
@@ -302,11 +323,30 @@
'{debupstream}-0~{revtime}\n'
'lp:~joe/someapp/+git/pkg packaging\n',
'archives': expected_archives,
+ 'trusted_keys': expected_trusted_keys,
'distroseries_name': job.build.distroseries.name,
'git': True,
}, extra_args)
@defer.inlineCallbacks
+ def test_extraBuildArgs_archive_trusted_keys(self):
+ # If the archive has a signing key, _extraBuildArgs sends it.
+ yield self.useFixture(InProcessKeyServerFixture()).start()
+ archive = self.factory.makeArchive()
+ key_path = os.path.join(gpgkeysdir, "ppa-sample@xxxxxxxxxxxxxxxxx")
+ yield IArchiveSigningKey(archive).setSigningKey(
+ key_path, async_keyserver=True)
+ job = self.makeJob(archive=archive)
+ distroarchseries = job.build.distroseries.architectures[0]
+ self.factory.makeBinaryPackagePublishingHistory(
+ distroarchseries=distroarchseries, pocket=job.build.pocket,
+ archive=archive, status=PackagePublishingStatus.PUBLISHED)
+ args = yield job._extraBuildArgs(distroarchseries)
+ self.assertThat(args["trusted_keys"], MatchesListwise([
+ Base64KeyMatches("0D57E99656BEFB0897606EE9A022DD1F5001B46D"),
+ ]))
+
+ @defer.inlineCallbacks
def test_composeBuildRequest(self):
job = self.makeJob()
test_publisher = SoyuzTestPublisher()
=== modified file 'lib/lp/services/tests/test_timeout.py'
--- lib/lp/services/tests/test_timeout.py 2016-07-01 20:07:09 +0000
+++ lib/lp/services/tests/test_timeout.py 2017-04-29 23:54:40 +0000
@@ -22,6 +22,7 @@
from testtools.matchers import MatchesStructure
from lp.services.timeout import (
+ default_timeout,
get_default_timeout_function,
reduced_timeout,
set_default_timeout_function,
@@ -207,6 +208,15 @@
no_default_timeout()
self.assertEqual([True], using_default)
+ def test_default_timeout(self):
+ """default_timeout sets the default timeout if none is set."""
+ self.addCleanup(set_default_timeout_function, None)
+ with default_timeout(1.0):
+ self.assertEqual(1.0, get_default_timeout_function()())
+ set_default_timeout_function(lambda: 5.0)
+ with default_timeout(1.0):
+ self.assertEqual(5.0, get_default_timeout_function()())
+
def test_reduced_timeout(self):
"""reduced_timeout caps the available timeout in various ways."""
self.addCleanup(set_default_timeout_function, None)
=== modified file 'lib/lp/services/timeout.py'
--- lib/lp/services/timeout.py 2016-12-22 16:32:38 +0000
+++ lib/lp/services/timeout.py 2017-04-29 23:54:40 +0000
@@ -1,10 +1,11 @@
-# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Helpers to time out external operations."""
__metaclass__ = type
__all__ = [
+ "default_timeout",
"get_default_timeout_function",
"reduced_timeout",
"SafeTransportWithTimeout",
@@ -57,6 +58,23 @@
@contextmanager
+def default_timeout(default):
+ """A context manager that sets the default timeout if none is set.
+
+ :param default: The default timeout to use if none is set.
+ """
+ original_timeout_function = get_default_timeout_function()
+
+ if original_timeout_function is None:
+ set_default_timeout_function(lambda: default)
+ try:
+ yield
+ finally:
+ if original_timeout_function is None:
+ set_default_timeout_function(None)
+
+
+@contextmanager
def reduced_timeout(clearance, webapp_max=None, default=None):
"""A context manager that reduces the default timeout.
=== modified file 'lib/lp/snappy/model/snapbuildbehaviour.py'
--- lib/lp/snappy/model/snapbuildbehaviour.py 2017-04-29 23:54:40 +0000
+++ lib/lp/snappy/model/snapbuildbehaviour.py 2017-04-29 23:54:40 +0000
@@ -99,9 +99,10 @@
args["arch_tag"] = build.distro_arch_series.architecturetag
# XXX cjwatson 2015-08-03: Allow tools_source to be overridden at
# some more fine-grained level.
- args["archives"] = yield get_sources_list_for_building(
- build, build.distro_arch_series, None,
- tools_source=config.snappy.tools_source, logger=logger)
+ args["archives"], args["trusted_keys"] = (
+ yield get_sources_list_for_building(
+ build, build.distro_arch_series, None,
+ tools_source=config.snappy.tools_source, logger=logger))
args["archive_private"] = build.archive.private
if build.snap.branch is not None:
args["branch"] = build.snap.branch.bzr_identity
=== modified file 'lib/lp/snappy/tests/test_snapbuildbehaviour.py'
--- lib/lp/snappy/tests/test_snapbuildbehaviour.py 2017-04-29 23:54:40 +0000
+++ lib/lp/snappy/tests/test_snapbuildbehaviour.py 2017-04-29 23:54:40 +0000
@@ -6,6 +6,7 @@
__metaclass__ = type
from datetime import datetime
+import os.path
from textwrap import dedent
import uuid
@@ -17,12 +18,18 @@
from pymacaroons import Macaroon
from testtools import ExpectedException
from testtools.deferredruntest import AsynchronousDeferredRunTest
-from testtools.matchers import IsInstance
+from testtools.matchers import (
+ IsInstance,
+ MatchesListwise,
+ )
import transaction
from twisted.internet import defer
from twisted.trial.unittest import TestCase as TrialTestCase
from zope.component import getUtility
+from lp.archivepublisher.interfaces.archivesigningkey import (
+ IArchiveSigningKey,
+ )
from lp.buildmaster.enums import BuildStatus
from lp.buildmaster.interfaces.builder import CannotBuild
from lp.buildmaster.interfaces.buildfarmjobbehaviour import (
@@ -47,8 +54,12 @@
from lp.soyuz.adapters.archivedependencies import (
get_sources_list_for_building,
)
+from lp.soyuz.enums import PackagePublishingStatus
from lp.soyuz.interfaces.archive import ArchiveDisabled
+from lp.soyuz.tests.soyuz import Base64KeyMatches
from lp.testing import TestCaseWithFactory
+from lp.testing.gpgkeys import gpgkeysdir
+from lp.testing.keyserver import InProcessKeyServerFixture
from lp.testing.layers import LaunchpadZopelessLayer
@@ -59,9 +70,13 @@
super(TestSnapBuildBehaviourBase, self).setUp()
self.pushConfig("snappy", tools_source=None)
- def makeJob(self, pocket=PackagePublishingPocket.UPDATES, **kwargs):
+ def makeJob(self, archive=None, pocket=PackagePublishingPocket.UPDATES,
+ **kwargs):
"""Create a sample `ISnapBuildBehaviour`."""
- distribution = self.factory.makeDistribution(name="distro")
+ if archive is None:
+ distribution = self.factory.makeDistribution(name="distro")
+ else:
+ distribution = archive.distribution
distroseries = self.factory.makeDistroSeries(
distribution=distribution, name="unstable")
processor = getUtility(IProcessorSet).getByName("386")
@@ -69,7 +84,7 @@
distroseries=distroseries, architecturetag="i386",
processor=processor)
build = self.factory.makeSnapBuild(
- distroarchseries=distroarchseries, pocket=pocket,
+ archive=archive, distroarchseries=distroarchseries, pocket=pocket,
name=u"test-snap", **kwargs)
return IBuildFarmJobBehaviour(build)
@@ -168,7 +183,7 @@
class TestAsyncSnapBuildBehaviour(TestSnapBuildBehaviourBase):
- run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=30)
+ run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=10)
def setUp(self):
super(TestAsyncSnapBuildBehaviour, self).setUp()
@@ -211,8 +226,9 @@
# job for a Bazaar branch.
branch = self.factory.makeBranch()
job = self.makeJob(branch=branch)
- expected_archives = yield get_sources_list_for_building(
- job.build, job.build.distro_arch_series, None)
+ expected_archives, expected_trusted_keys = (
+ yield get_sources_list_for_building(
+ job.build, job.build.distro_arch_series, None))
args = yield job._extraBuildArgs()
self.assertEqual({
"archive_private": False,
@@ -222,6 +238,7 @@
"name": u"test-snap",
"proxy_url": self.proxy_url,
"revocation_endpoint": self.revocation_endpoint,
+ "trusted_keys": expected_trusted_keys,
}, args)
@defer.inlineCallbacks
@@ -230,8 +247,9 @@
# job for a Git branch.
[ref] = self.factory.makeGitRefs()
job = self.makeJob(git_ref=ref)
- expected_archives = yield get_sources_list_for_building(
- job.build, job.build.distro_arch_series, None)
+ expected_archives, expected_trusted_keys = (
+ yield get_sources_list_for_building(
+ job.build, job.build.distro_arch_series, None))
args = yield job._extraBuildArgs()
self.assertEqual({
"archive_private": False,
@@ -242,6 +260,7 @@
"name": u"test-snap",
"proxy_url": self.proxy_url,
"revocation_endpoint": self.revocation_endpoint,
+ "trusted_keys": expected_trusted_keys,
}, args)
@defer.inlineCallbacks
@@ -252,8 +271,9 @@
ref = self.factory.makeGitRefRemote(
repository_url=url, path=u"refs/heads/master")
job = self.makeJob(git_ref=ref)
- expected_archives = yield get_sources_list_for_building(
- job.build, job.build.distro_arch_series, None)
+ expected_archives, expected_trusted_keys = (
+ yield get_sources_list_for_building(
+ job.build, job.build.distro_arch_series, None))
args = yield job._extraBuildArgs()
self.assertEqual({
"archive_private": False,
@@ -264,10 +284,29 @@
"name": u"test-snap",
"proxy_url": self.proxy_url,
"revocation_endpoint": self.revocation_endpoint,
+ "trusted_keys": expected_trusted_keys,
}, args)
@defer.inlineCallbacks
- def test_extraBuildArgs_proxy_url_set(self):
+ def test_extraBuildArgs_archive_trusted_keys(self):
+ # If the archive has a signing key, _extraBuildArgs sends it.
+ yield self.useFixture(InProcessKeyServerFixture()).start()
+ archive = self.factory.makeArchive()
+ key_path = os.path.join(gpgkeysdir, "ppa-sample@xxxxxxxxxxxxxxxxx")
+ yield IArchiveSigningKey(archive).setSigningKey(
+ key_path, async_keyserver=True)
+ job = self.makeJob(archive=archive)
+ self.factory.makeBinaryPackagePublishingHistory(
+ distroarchseries=job.build.distro_arch_series,
+ pocket=job.build.pocket, archive=archive,
+ status=PackagePublishingStatus.PUBLISHED)
+ args = yield job._extraBuildArgs()
+ self.assertThat(args["trusted_keys"], MatchesListwise([
+ Base64KeyMatches("0D57E99656BEFB0897606EE9A022DD1F5001B46D"),
+ ]))
+
+ @defer.inlineCallbacks
+ def test_composeBuildRequest_proxy_url_set(self):
job = self.makeJob()
build_request = yield job.composeBuildRequest(None)
proxy_url = ("http://{username}:{password}"
=== modified file 'lib/lp/soyuz/adapters/archivedependencies.py'
--- lib/lp/soyuz/adapters/archivedependencies.py 2016-04-07 00:04:42 +0000
+++ lib/lp/soyuz/adapters/archivedependencies.py 2017-04-29 23:54:40 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Archive dependencies helper function.
@@ -36,11 +36,15 @@
'pocket_dependencies',
]
+import base64
import logging
import traceback
from lazr.uri import URI
+from twisted.internet import defer
+from twisted.internet.threads import deferToThread
from zope.component import getUtility
+from zope.security.proxy import removeSecurityProxy
from lp.app.errors import NotFoundError
from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
@@ -48,6 +52,11 @@
PackagePublishingPocket,
pocketsuffix,
)
+from lp.services.gpg.interfaces import (
+ GPGKeyNotFoundError,
+ IGPGHandler,
+ )
+from lp.services.timeout import default_timeout
from lp.soyuz.enums import (
ArchivePurpose,
PackagePublishingStatus,
@@ -229,16 +238,19 @@
return deps
+@defer.inlineCallbacks
def get_sources_list_for_building(build, distroarchseries, sourcepackagename,
tools_source=None, logger=None):
- """Return the sources_list entries required to build the given item.
+ """Return sources.list entries and keys required to build the given item.
- The entries are returned in the order that is most useful;
+ The sources.list entries are returned in the order that is most useful:
1. the context archive itself
2. external dependencies
3. user-selected archive dependencies
4. the default primary archive
+ The keys are in an arbitrary order.
+
:param build: a context `IBuild`.
:param distroarchseries: A `IDistroArchSeries`
:param sourcepackagename: A source package name (as text)
@@ -246,14 +258,16 @@
additional dependency for build tools, just before the default
primary archive.
:param logger: an optional logger.
- :return: a deb sources_list entries (lines).
+ :return: a Deferred resolving to a tuple containing a list of deb
+ sources.list entries (lines) and a list of base64-encoded public
+ keys.
"""
deps = expand_dependencies(
build.archive, distroarchseries, build.pocket,
build.current_component, sourcepackagename,
tools_source=tools_source, logger=logger)
- sources_list_lines = \
- _get_sources_list_for_dependencies(deps)
+ sources_list_lines, trusted_keys = (
+ yield _get_sources_list_for_dependencies(deps, logger=logger))
external_dep_lines = []
# Append external sources.list lines for this build if specified. No
@@ -289,8 +303,9 @@
# binaries that need to override primary binaries of the same
# version), we want the external dependency lines to show up second:
# after the archive itself, but before any other dependencies.
- return [sources_list_lines[0]] + external_dep_lines + \
- sources_list_lines[1:]
+ defer.returnValue(
+ ([sources_list_lines[0]] + external_dep_lines + sources_list_lines[1:],
+ trusted_keys))
def _has_published_binaries(archive, distroarchseries, pocket):
@@ -323,8 +338,9 @@
return 'deb %s %s %s' % (url, suite, ' '.join(components))
-def _get_sources_list_for_dependencies(dependencies):
- """Return a list of sources_list lines.
+@defer.inlineCallbacks
+def _get_sources_list_for_dependencies(dependencies, logger=None):
+ """Return sources.list entries and keys.
Process the given list of dependency tuples for the given
`DistroArchseries`.
@@ -334,9 +350,15 @@
list of `IComponent` names)
:param distroarchseries: target `IDistroArchSeries`;
- :return: a list of sources_list formatted lines.
+ :return: a tuple containing a list of sources.list formatted lines and a
+ list of base64-encoded public keys.
"""
sources_list_lines = []
+ trusted_keys = {}
+ # The handler's security proxying doesn't protect anything useful here,
+ # and the thread that we defer key retrieval to doesn't have an
+ # interaction.
+ gpghandler = removeSecurityProxy(getUtility(IGPGHandler))
for dep in dependencies:
if isinstance(dep, basestring):
sources_list_lines.append(dep)
@@ -356,8 +378,27 @@
sources_list_line = _get_binary_sources_list_line(
archive, distro_arch_series, pocket, components)
sources_list_lines.append(sources_list_line)
-
- return sources_list_lines
+ fingerprint = archive.signing_key_fingerprint
+ if fingerprint is not None and fingerprint not in trusted_keys:
+ def get_key():
+ with default_timeout(15.0):
+ try:
+ return gpghandler.retrieveKey(fingerprint)
+ except GPGKeyNotFoundError as e:
+ # For now, just log this and proceed without the
+ # key. We'll have to fix any outstanding cases
+ # of this before we can switch to requiring
+ # authentication across the board.
+ if logger is not None:
+ logger.warning(str(e))
+ return None
+
+ key = yield deferToThread(get_key)
+ if key is not None:
+ trusted_keys[fingerprint] = base64.b64encode(key.export())
+
+ defer.returnValue(
+ (sources_list_lines, [v for k, v in sorted(trusted_keys.items())]))
def _get_default_primary_dependencies(archive, distro_arch_series, component,
=== modified file 'lib/lp/soyuz/adapters/tests/test_archivedependencies.py'
--- lib/lp/soyuz/adapters/tests/test_archivedependencies.py 2017-04-29 23:54:40 +0000
+++ lib/lp/soyuz/adapters/tests/test_archivedependencies.py 2017-04-29 23:54:40 +0000
@@ -7,10 +7,20 @@
__metaclass__ = type
-from testtools.matchers import StartsWith
+import os.path
+
+from testtools.deferredruntest import AsynchronousDeferredRunTest
+from testtools.matchers import (
+ MatchesSetwise,
+ StartsWith,
+ )
import transaction
+from twisted.internet import defer
from zope.component import getUtility
+from lp.archivepublisher.interfaces.archivesigningkey import (
+ IArchiveSigningKey,
+ )
from lp.registry.interfaces.distribution import IDistributionSet
from lp.registry.interfaces.pocket import PackagePublishingPocket
from lp.services.log.logger import BufferLogger
@@ -25,8 +35,11 @@
from lp.soyuz.enums import PackagePublishingStatus
from lp.soyuz.interfaces.archive import IArchive
from lp.soyuz.interfaces.component import IComponentSet
+from lp.soyuz.tests.soyuz import Base64KeyMatches
from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
from lp.testing import TestCaseWithFactory
+from lp.testing.gpgkeys import gpgkeysdir
+from lp.testing.keyserver import InProcessKeyServerFixture
from lp.testing.layers import (
LaunchpadZopelessLayer,
ZopelessDatabaseLayer,
@@ -111,10 +124,18 @@
"""Test sources.list contents for building, and related mechanisms."""
layer = LaunchpadZopelessLayer
+ run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=10)
ubuntu_components = [
"main", "restricted", "universe", "multiverse", "partner"]
+ fingerprints = {
+ "ppa-sample@xxxxxxxxxxxxx": "0D57E99656BEFB0897606EE9A022DD1F5001B46D",
+ "ppa-sample-4096@xxxxxxxxxxxxx": (
+ "B7B1966662BA8D3F5A6ED89BD640F4A593B2CF67"),
+ }
+
+ @defer.inlineCallbacks
def setUp(self):
super(TestSourcesList, self).setUp()
self.publisher = SoyuzTestPublisher()
@@ -126,6 +147,7 @@
component = getUtility(IComponentSet)[component_name]
if component not in self.hoary.components:
self.factory.makeComponentSelection(self.hoary, component)
+ yield self.useFixture(InProcessKeyServerFixture()).start()
def test_defaults(self):
# Non-primary archives by default use the Release, Security and
@@ -140,12 +162,18 @@
PackagePublishingPocket.UPDATES),
pocket_dependencies[default_pocket_dependency])
- def makeArchive(self, publish_binary=False, **kwargs):
+ @defer.inlineCallbacks
+ def makeArchive(self, signing_key_name="ppa-sample@xxxxxxxxxxxxx",
+ publish_binary=False, **kwargs):
archive = self.factory.makeArchive(distribution=self.ubuntu, **kwargs)
+ if signing_key_name is not None:
+ key_path = os.path.join(gpgkeysdir, "%s.sec" % signing_key_name)
+ yield IArchiveSigningKey(archive).setSigningKey(
+ key_path, async_keyserver=True)
if publish_binary:
self.publisher.getPubBinaries(
archive=archive, status=PackagePublishingStatus.PUBLISHED)
- return archive
+ defer.returnValue(archive)
def makeBuild(self, **kwargs):
pub_source = self.publisher.getPubSource(**kwargs)
@@ -159,77 +187,90 @@
build.archive, build.distro_series,
build.source_package_release.name).name)
- def assertSourcesList(self, expected, build, **kwargs):
+ @defer.inlineCallbacks
+ def assertSourcesListAndKeys(self, expected_sources_list,
+ expected_key_names, build, **kwargs):
expected_lines = []
- for archive_or_prefix, suffixes in expected:
+ for archive_or_prefix, suffixes in expected_sources_list:
if IArchive.providedBy(archive_or_prefix):
prefix = "deb %s " % archive_or_prefix.archive_url
else:
prefix = archive_or_prefix + " "
expected_lines.extend([prefix + suffix for suffix in suffixes])
- sources_list = get_sources_list_for_building(
+ sources_list, trusted_keys = yield get_sources_list_for_building(
build, build.distro_arch_series, build.source_package_release.name,
**kwargs)
self.assertEqual(expected_lines, sources_list)
+ key_matchers = [
+ Base64KeyMatches(self.fingerprints[key_name])
+ for key_name in expected_key_names]
+ self.assertThat(trusted_keys, MatchesSetwise(*key_matchers))
+ @defer.inlineCallbacks
def test_ppa_with_no_binaries(self):
# If there are no published binaries in a PPA, only its primary
# archive dependencies need to be considered.
- ppa = self.makeArchive()
+ ppa = yield self.makeArchive()
build = self.makeBuild(archive=ppa)
self.assertEqual(
0, ppa.getAllPublishedBinaries(
distroarchseries=build.distro_arch_series,
status=PackagePublishingStatus.PUBLISHED).count())
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(self.ubuntu.main_archive, [
"hoary main restricted universe multiverse",
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build)
+ ], [], build)
+ @defer.inlineCallbacks
def test_ppa_with_binaries(self):
# If there are binaries published in a PPA, then the PPA is
# considered as well as its primary dependencies.
- ppa = self.makeArchive(publish_binary=True)
+ ppa = yield self.makeArchive(publish_binary=True)
build = self.makeBuild(archive=ppa)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(ppa, ["hoary main"]),
(self.ubuntu.main_archive, [
"hoary main restricted universe multiverse",
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build)
+ ], ["ppa-sample@xxxxxxxxxxxxx"], build)
+ @defer.inlineCallbacks
def test_dependent_ppa_with_no_binaries(self):
# A depended-upon PPA is not considered if it has no published
# binaries.
- lower_ppa = self.makeArchive()
- upper_ppa = self.makeArchive(publish_binary=True)
+ lower_ppa = yield self.makeArchive(
+ signing_key_name="ppa-sample-4096@xxxxxxxxxxxxx")
+ upper_ppa = yield self.makeArchive(publish_binary=True)
upper_ppa.addArchiveDependency(
lower_ppa, PackagePublishingPocket.RELEASE,
getUtility(IComponentSet)["main"])
build = self.makeBuild(archive=upper_ppa)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(upper_ppa, ["hoary main"]),
(self.ubuntu.main_archive, [
"hoary main restricted universe multiverse",
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build)
+ ], ["ppa-sample@xxxxxxxxxxxxx"], build)
+ @defer.inlineCallbacks
def test_dependent_ppa_with_binaries(self):
# A depended-upon PPA is considered if it has published binaries.
- lower_ppa = self.makeArchive(publish_binary=True)
- upper_ppa = self.makeArchive(publish_binary=True)
+ lower_ppa = yield self.makeArchive(
+ signing_key_name="ppa-sample-4096@xxxxxxxxxxxxx",
+ publish_binary=True)
+ upper_ppa = yield self.makeArchive(publish_binary=True)
upper_ppa.addArchiveDependency(
lower_ppa, PackagePublishingPocket.RELEASE,
getUtility(IComponentSet)["main"])
build = self.makeBuild(archive=upper_ppa)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(upper_ppa, ["hoary main"]),
(lower_ppa, ["hoary main"]),
(self.ubuntu.main_archive, [
@@ -237,14 +278,19 @@
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build)
+ ],
+ ["ppa-sample@xxxxxxxxxxxxx", "ppa-sample-4096@xxxxxxxxxxxxx"],
+ build)
+ @defer.inlineCallbacks
def test_lax_supported_component_dependencies(self):
# Dependencies for series with
# strict_supported_component_dependencies=False are reasonable.
# PPAs only have the "main" component.
- lower_ppa = self.makeArchive(publish_binary=True)
- upper_ppa = self.makeArchive(publish_binary=True)
+ lower_ppa = yield self.makeArchive(
+ signing_key_name="ppa-sample-4096@xxxxxxxxxxxxx",
+ publish_binary=True)
+ upper_ppa = yield self.makeArchive(publish_binary=True)
upper_ppa.addArchiveDependency(
lower_ppa, PackagePublishingPocket.RELEASE,
getUtility(IComponentSet)["main"])
@@ -252,7 +298,7 @@
self.ubuntu.main_archive, PackagePublishingPocket.UPDATES,
getUtility(IComponentSet)["restricted"])
build = self.makeBuild(archive=upper_ppa)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(upper_ppa, ["hoary main"]),
(lower_ppa, ["hoary main"]),
(self.ubuntu.main_archive, [
@@ -260,10 +306,12 @@
"hoary-security main restricted",
"hoary-updates main restricted",
]),
- ], build)
+ ],
+ ["ppa-sample@xxxxxxxxxxxxx", "ppa-sample-4096@xxxxxxxxxxxxx"],
+ build)
self.hoary.strict_supported_component_dependencies = False
transaction.commit()
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(upper_ppa, ["hoary main"]),
(lower_ppa, ["hoary main"]),
(self.ubuntu.main_archive, [
@@ -271,41 +319,45 @@
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build)
+ ],
+ ["ppa-sample@xxxxxxxxxxxxx", "ppa-sample-4096@xxxxxxxxxxxxx"],
+ build)
+ @defer.inlineCallbacks
def test_no_op_primary_archive_dependency(self):
# Overriding the default primary archive dependencies with exactly
# the same values has no effect.
- ppa = self.makeArchive()
+ ppa = yield self.makeArchive()
ppa.addArchiveDependency(
self.ubuntu.main_archive, PackagePublishingPocket.UPDATES,
getUtility(IComponentSet)["multiverse"])
build = self.makeBuild(archive=ppa)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(self.ubuntu.main_archive, [
"hoary main restricted universe multiverse",
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build)
+ ], [], build)
+ @defer.inlineCallbacks
def test_primary_archive_dependency_security(self):
# The primary archive dependency can be modified to behave as an
# embargoed archive that builds security updates. This is done by
# setting the SECURITY pocket dependencies (RELEASE and SECURITY)
# and following the component dependencies of the component where
# the source was last published in the primary archive.
- ppa = self.makeArchive()
+ ppa = yield self.makeArchive()
ppa.addArchiveDependency(
self.ubuntu.main_archive, PackagePublishingPocket.SECURITY)
build = self.makeBuild(archive=ppa)
self.assertPrimaryCurrentComponent("universe", build)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(self.ubuntu.main_archive, [
"hoary main universe",
"hoary-security main universe",
]),
- ], build)
+ ], [], build)
self.publisher.getPubSource(
sourcename="with-ancestry", version="1.0",
archive=self.ubuntu.main_archive)
@@ -313,59 +365,63 @@
sourcename="with-ancestry", version="1.1",
archive=ppa).createMissingBuilds()
self.assertPrimaryCurrentComponent("main", build_with_ancestry)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(self.ubuntu.main_archive, [
"hoary main",
"hoary-security main",
]),
- ], build_with_ancestry)
+ ], [], build_with_ancestry)
+ @defer.inlineCallbacks
def test_primary_archive_dependency_release(self):
# The primary archive dependency can be modified to behave as a
# pristine build environment based only on what was included in the
# original release of the corresponding series.
- ppa = self.makeArchive()
+ ppa = yield self.makeArchive()
ppa.addArchiveDependency(
self.ubuntu.main_archive, PackagePublishingPocket.RELEASE,
getUtility(IComponentSet)["restricted"])
build = self.makeBuild(archive=ppa)
- self.assertSourcesList(
- [(self.ubuntu.main_archive, ["hoary main restricted"])], build)
+ yield self.assertSourcesListAndKeys(
+ [(self.ubuntu.main_archive, ["hoary main restricted"])], [], build)
+ @defer.inlineCallbacks
def test_primary_archive_dependency_proposed(self):
# The primary archive dependency can be modified to extend the build
# environment for PROPOSED.
- ppa = self.makeArchive()
+ ppa = yield self.makeArchive()
ppa.addArchiveDependency(
self.ubuntu.main_archive, PackagePublishingPocket.PROPOSED,
getUtility(IComponentSet)["multiverse"])
build = self.makeBuild(archive=ppa)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(self.ubuntu.main_archive, [
"hoary main restricted universe multiverse",
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
"hoary-proposed main restricted universe multiverse",
]),
- ], build)
+ ], [], build)
+ @defer.inlineCallbacks
def test_primary_archive_dependency_backports(self):
# The primary archive dependency can be modified to extend the build
# environment for PROPOSED.
- ppa = self.makeArchive()
+ ppa = yield self.makeArchive()
ppa.addArchiveDependency(
self.ubuntu.main_archive, PackagePublishingPocket.BACKPORTS,
getUtility(IComponentSet)["multiverse"])
build = self.makeBuild(archive=ppa)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(self.ubuntu.main_archive, [
"hoary main restricted universe multiverse",
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
"hoary-backports main restricted universe multiverse",
]),
- ], build)
+ ], [], build)
+ @defer.inlineCallbacks
def test_partner(self):
# Similarly to what happens with PPA builds, partner builds may
# depend on any component in the primary archive. This behaviour
@@ -377,15 +433,16 @@
archive=partner, component="partner",
status=PackagePublishingStatus.PUBLISHED)
build = self.makeBuild(archive=partner, component="partner")
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(partner, ["hoary partner"]),
(primary, [
"hoary main restricted universe multiverse",
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build)
+ ], [], build)
+ @defer.inlineCallbacks
def test_partner_proposed(self):
# The partner archive's PROPOSED pocket builds against itself, but
# still uses the default UPDATES dependency for the primary archive
@@ -401,7 +458,7 @@
build = self.makeBuild(
archive=partner, component="partner",
pocket=PackagePublishingPocket.PROPOSED)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(partner, [
"hoary partner",
"hoary-proposed partner",
@@ -411,19 +468,20 @@
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build)
+ ], [], build)
+ @defer.inlineCallbacks
def test_archive_external_dependencies(self):
# An archive can be manually given additional external dependencies.
# If present, "%(series)s" is replaced with the series name for the
# build being dispatched.
- ppa = self.makeArchive(publish_binary=True)
+ ppa = yield self.makeArchive(publish_binary=True)
ppa.external_dependencies = (
"deb http://user:pass@repository zoing everything\n"
"deb http://user:pass@repository %(series)s public private\n"
"deb http://user:pass@repository %(series)s-extra public")
build = self.makeBuild(archive=ppa)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(ppa, ["hoary main"]),
("deb http://user:pass@repository", [
"zoing everything",
@@ -435,16 +493,17 @@
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build)
+ ], ["ppa-sample@xxxxxxxxxxxxx"], build)
+ @defer.inlineCallbacks
def test_build_external_dependencies(self):
# A single build can be manually given additional external
# dependencies.
- ppa = self.makeArchive(publish_binary=True)
+ ppa = yield self.makeArchive(publish_binary=True)
build = self.makeBuild(archive=ppa)
build.api_external_dependencies = (
"deb http://user:pass@repository foo bar")
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(ppa, ["hoary main"]),
("deb http://user:pass@repository", ["foo bar"]),
(self.ubuntu.main_archive, [
@@ -452,14 +511,15 @@
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build)
+ ], ["ppa-sample@xxxxxxxxxxxxx"], build)
+ @defer.inlineCallbacks
def test_build_tools(self):
# We can force an extra build tools line to be added to
# sources.list, which is useful for specialised build types.
- ppa = self.makeArchive(publish_binary=True)
+ ppa = yield self.makeArchive(publish_binary=True)
build = self.makeBuild(archive=ppa)
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(ppa, ["hoary main"]),
("deb http://example.org", ["hoary main"]),
(self.ubuntu.main_archive, [
@@ -467,15 +527,18 @@
"hoary-security main restricted universe multiverse",
"hoary-updates main restricted universe multiverse",
]),
- ], build, tools_source="deb http://example.org %(series)s main")
+ ],
+ ["ppa-sample@xxxxxxxxxxxxx"], build,
+ tools_source="deb http://example.org %(series)s main")
+ @defer.inlineCallbacks
def test_build_tools_bad_formatting(self):
# If tools_source is badly formatted, we log the error but don't
# blow up. (Note the missing "s" at the end of "%(series)".)
- ppa = self.makeArchive(publish_binary=True)
+ ppa = yield self.makeArchive(publish_binary=True)
build = self.makeBuild(archive=ppa)
logger = BufferLogger()
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(ppa, ["hoary main"]),
(self.ubuntu.main_archive, [
"hoary main restricted universe multiverse",
@@ -483,11 +546,13 @@
"hoary-updates main restricted universe multiverse",
]),
],
- build, tools_source="deb http://example.org %(series) main",
+ ["ppa-sample@xxxxxxxxxxxxx"], build,
+ tools_source="deb http://example.org %(series) main",
logger=logger)
self.assertThat(logger.getLogBuffer(), StartsWith(
"ERROR Exception processing build tools sources.list entry:\n"))
+ @defer.inlineCallbacks
def test_overlay(self):
# An overlay distroseries is a derived distribution which works like
# a PPA. This means that the parent's details gets added to the
@@ -508,10 +573,10 @@
pocket=PackagePublishingPocket.SECURITY,
component=getUtility(IComponentSet)["universe"])
build = self.makeBuild()
- self.assertSourcesList(
+ yield self.assertSourcesListAndKeys(
[(self.ubuntu.main_archive, ["hoary main"]),
(depdistro.main_archive, [
"depseries main universe",
"depseries-security main universe",
]),
- ], build)
+ ], [], build)
=== modified file 'lib/lp/soyuz/model/binarypackagebuildbehaviour.py'
--- lib/lp/soyuz/model/binarypackagebuildbehaviour.py 2017-04-29 23:54:40 +0000
+++ lib/lp/soyuz/model/binarypackagebuildbehaviour.py 2017-04-29 23:54:40 +0000
@@ -164,8 +164,9 @@
args["ogrecomponent"] = (
build.current_component.name)
- args['archives'] = yield get_sources_list_for_building(
- build, das, build.source_package_release.name, logger=logger)
+ args['archives'], args['trusted_keys'] = (
+ yield get_sources_list_for_building(
+ build, das, build.source_package_release.name, logger=logger))
args['archive_private'] = build.archive.private
args['build_debug_symbols'] = build.archive.build_debug_symbols
=== modified file 'lib/lp/soyuz/model/livefsbuildbehaviour.py'
--- lib/lp/soyuz/model/livefsbuildbehaviour.py 2017-04-29 23:54:40 +0000
+++ lib/lp/soyuz/model/livefsbuildbehaviour.py 2017-04-29 23:54:40 +0000
@@ -90,8 +90,9 @@
args["pocket"] = build.pocket.name.lower()
args["arch_tag"] = build.distro_arch_series.architecturetag
args["datestamp"] = build.version
- args["archives"] = yield get_sources_list_for_building(
- build, build.distro_arch_series, None, logger=logger)
+ args["archives"], args["trusted_keys"] = (
+ yield get_sources_list_for_building(
+ build, build.distro_arch_series, None, logger=logger))
args["archive_private"] = build.archive.private
defer.returnValue(args)
=== modified file 'lib/lp/soyuz/tests/soyuz.py'
--- lib/lp/soyuz/tests/soyuz.py 2015-09-28 17:38:45 +0000
+++ lib/lp/soyuz/tests/soyuz.py 2017-04-29 23:54:40 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Helper functions/classes for Soyuz tests."""
@@ -6,14 +6,22 @@
__metaclass__ = type
__all__ = [
+ 'Base64KeyMatches',
'SoyuzTestHelper',
]
+import base64
+
+from testtools.matchers import (
+ Equals,
+ Matcher,
+ )
from zope.component import getUtility
from lp.registry.interfaces.distribution import IDistributionSet
from lp.registry.interfaces.person import IPersonSet
from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.services.gpg.interfaces import IGPGHandler
from lp.soyuz.enums import PackagePublishingStatus
from lp.soyuz.model.publishing import SourcePackagePublishingHistory
from lp.testing.sampledata import (
@@ -94,3 +102,15 @@
Return True if the lists matches, otherwise False.
"""
return [p.id for p in expected] == [r.id for r in given]
+
+
+class Base64KeyMatches(Matcher):
+ """Matches if base64-encoded key material has a given fingerprint."""
+
+ def __init__(self, fingerprint):
+ self.fingerprint = fingerprint
+
+ def match(self, encoded_key):
+ key = base64.b64decode(encoded_key)
+ return Equals(self.fingerprint).match(
+ getUtility(IGPGHandler).importPublicKey(key).fingerprint)
=== modified file 'lib/lp/soyuz/tests/test_archive.py'
--- lib/lp/soyuz/tests/test_archive.py 2017-04-29 23:54:40 +0000
+++ lib/lp/soyuz/tests/test_archive.py 2017-04-29 23:54:40 +0000
@@ -9,6 +9,7 @@
timedelta,
)
import doctest
+import os.path
from pytz import UTC
from testtools.deferredruntest import AsynchronousDeferredRunTest
@@ -16,6 +17,7 @@
AllMatch,
DocTestMatches,
LessThan,
+ MatchesListwise,
MatchesPredicate,
MatchesRegex,
MatchesStructure,
@@ -29,6 +31,9 @@
from lp.app.errors import NotFoundError
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
+from lp.archivepublisher.interfaces.archivesigningkey import (
+ IArchiveSigningKey,
+ )
from lp.buildmaster.enums import (
BuildQueueStatus,
BuildStatus,
@@ -108,6 +113,7 @@
BinaryPackageReleaseDownloadCount,
)
from lp.soyuz.model.component import ComponentSelection
+from lp.soyuz.tests.soyuz import Base64KeyMatches
from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
from lp.testing import (
admin_logged_in,
@@ -120,6 +126,8 @@
StormStatementRecorder,
TestCaseWithFactory,
)
+from lp.testing.gpgkeys import gpgkeysdir
+from lp.testing.keyserver import InProcessKeyServerFixture
from lp.testing.layers import (
DatabaseFunctionalLayer,
LaunchpadFunctionalLayer,
@@ -1748,12 +1756,17 @@
class TestArchiveDependencies(TestCaseWithFactory):
layer = LaunchpadZopelessLayer
- run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=30)
+ run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=10)
@defer.inlineCallbacks
def test_private_sources_list(self):
"""Entries for private dependencies include credentials."""
p3a = self.factory.makeArchive(name='p3a', private=True)
+ with InProcessKeyServerFixture() as keyserver:
+ yield keyserver.start()
+ key_path = os.path.join(gpgkeysdir, 'ppa-sample@xxxxxxxxxxxxxxxxx')
+ yield IArchiveSigningKey(p3a).setSigningKey(
+ key_path, async_keyserver=True)
dependency = self.factory.makeArchive(
name='dependency', private=True, owner=p3a.owner)
with person_logged_in(p3a.owner):
@@ -1764,13 +1777,16 @@
PackagePublishingPocket.RELEASE)
build = self.factory.makeBinaryPackageBuild(archive=p3a,
distroarchseries=bpph.distroarchseries)
- sources_list = yield get_sources_list_for_building(
+ sources_list, trusted_keys = yield get_sources_list_for_building(
build, build.distro_arch_series,
build.source_package_release.name)
matches = MatchesRegex(
"deb http://buildd:sekrit@xxxxxxxxxxxxxxxxxxxxxxxxx/"
"person-name-.*/dependency/ubuntu distroseries-.* main")
self.assertThat(sources_list[0], matches)
+ self.assertThat(trusted_keys, MatchesListwise([
+ Base64KeyMatches("0D57E99656BEFB0897606EE9A022DD1F5001B46D"),
+ ]))
def test_invalid_external_dependencies(self):
"""Trying to set invalid external dependencies raises an exception."""
@@ -2018,7 +2034,7 @@
self._createDep(
test_publisher, series11, 'series12', 'depdistro4', 'multiverse',
PackagePublishingPocket.UPDATES)
- sources_list = yield get_sources_list_for_building(
+ sources_list, trusted_keys = yield get_sources_list_for_building(
build, build.distro_arch_series, build.source_package_release.name)
self.assertThat(
@@ -2040,6 +2056,7 @@
".../depdistro4 series12-updates "
"main restricted universe multiverse\n",
doctest.ELLIPSIS))
+ self.assertEqual([], trusted_keys)
class TestComponents(TestCaseWithFactory):
=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py'
--- lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py 2017-04-29 23:54:40 +0000
+++ lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py 2017-04-29 23:54:40 +0000
@@ -12,6 +12,7 @@
from storm.store import Store
from testtools.deferredruntest import AsynchronousDeferredRunTest
+from testtools.matchers import MatchesListwise
import transaction
from twisted.internet import defer
from twisted.trial.unittest import TestCase as TrialTestCase
@@ -19,6 +20,9 @@
from zope.security.proxy import removeSecurityProxy
from lp.archivepublisher.diskpool import poolify
+from lp.archivepublisher.interfaces.archivesigningkey import (
+ IArchiveSigningKey,
+ )
from lp.buildmaster.enums import (
BuilderCleanStatus,
BuildStatus,
@@ -55,9 +59,15 @@
from lp.soyuz.adapters.archivedependencies import (
get_sources_list_for_building,
)
-from lp.soyuz.enums import ArchivePurpose
+from lp.soyuz.enums import (
+ ArchivePurpose,
+ PackagePublishingStatus,
+ )
+from lp.soyuz.tests.soyuz import Base64KeyMatches
from lp.testing import TestCaseWithFactory
from lp.testing.dbuser import switch_dbuser
+from lp.testing.gpgkeys import gpgkeysdir
+from lp.testing.keyserver import InProcessKeyServerFixture
from lp.testing.layers import LaunchpadZopelessLayer
@@ -107,7 +117,7 @@
das = build.distro_arch_series
ds_name = das.distroseries.name
suite = ds_name + pocketsuffix[build.pocket]
- archives = yield get_sources_list_for_building(
+ archives, trusted_keys = yield get_sources_list_for_building(
build, das, build.source_package_release.name)
arch_indep = das.isNominatedArchIndep
if component is None:
@@ -131,6 +141,7 @@
'ogrecomponent': component,
'distribution': das.distroseries.distribution.name,
'suite': suite,
+ 'trusted_keys': trusted_keys,
}
build_log = [
('build', build.build_cookie, 'binarypackage',
@@ -324,6 +335,23 @@
extra_args = yield IBuildFarmJobBehaviour(build)._extraBuildArgs(build)
self.assertTrue(extra_args['arch_indep'])
+ @defer.inlineCallbacks
+ def test_extraBuildArgs_archive_trusted_keys(self):
+ # If the archive has a signing key, _extraBuildArgs sends it.
+ yield self.useFixture(InProcessKeyServerFixture()).start()
+ archive = self.factory.makeArchive()
+ key_path = os.path.join(gpgkeysdir, "ppa-sample@xxxxxxxxxxxxxxxxx")
+ yield IArchiveSigningKey(archive).setSigningKey(
+ key_path, async_keyserver=True)
+ build = self.factory.makeBinaryPackageBuild(archive=archive)
+ self.factory.makeBinaryPackagePublishingHistory(
+ distroarchseries=build.distro_arch_series, pocket=build.pocket,
+ archive=archive, status=PackagePublishingStatus.PUBLISHED)
+ args = yield IBuildFarmJobBehaviour(build)._extraBuildArgs(build)
+ self.assertThat(args["trusted_keys"], MatchesListwise([
+ Base64KeyMatches("0D57E99656BEFB0897606EE9A022DD1F5001B46D"),
+ ]))
+
def test_verifyBuildRequest(self):
# Don't allow a virtual build on a non-virtual builder.
archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
=== modified file 'lib/lp/soyuz/tests/test_livefsbuildbehaviour.py'
--- lib/lp/soyuz/tests/test_livefsbuildbehaviour.py 2017-04-29 23:54:40 +0000
+++ lib/lp/soyuz/tests/test_livefsbuildbehaviour.py 2017-04-29 23:54:40 +0000
@@ -6,16 +6,21 @@
__metaclass__ = type
from datetime import datetime
+import os.path
import fixtures
import pytz
from testtools.deferredruntest import AsynchronousDeferredRunTest
+from testtools.matchers import MatchesListwise
import transaction
from twisted.internet import defer
from twisted.trial.unittest import TestCase as TrialTestCase
from zope.component import getUtility
from zope.security.proxy import Proxy
+from lp.archivepublisher.interfaces.archivesigningkey import (
+ IArchiveSigningKey,
+ )
from lp.buildmaster.enums import BuildStatus
from lp.buildmaster.interfaces.builder import CannotBuild
from lp.buildmaster.interfaces.buildfarmjobbehaviour import (
@@ -38,13 +43,17 @@
from lp.soyuz.adapters.archivedependencies import (
get_sources_list_for_building,
)
+from lp.soyuz.enums import PackagePublishingStatus
from lp.soyuz.interfaces.archive import ArchiveDisabled
from lp.soyuz.interfaces.livefs import (
LIVEFS_FEATURE_FLAG,
LiveFSBuildArchiveOwnerMismatch,
)
from lp.soyuz.model.livefsbuildbehaviour import LiveFSBuildBehaviour
+from lp.soyuz.tests.soyuz import Base64KeyMatches
from lp.testing import TestCaseWithFactory
+from lp.testing.gpgkeys import gpgkeysdir
+from lp.testing.keyserver import InProcessKeyServerFixture
from lp.testing.layers import LaunchpadZopelessLayer
@@ -56,9 +65,13 @@
super(TestLiveFSBuildBehaviourBase, self).setUp()
self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
- def makeJob(self, pocket=PackagePublishingPocket.RELEASE, **kwargs):
+ def makeJob(self, archive=None, pocket=PackagePublishingPocket.RELEASE,
+ **kwargs):
"""Create a sample `ILiveFSBuildBehaviour`."""
- distribution = self.factory.makeDistribution(name="distro")
+ if archive is None:
+ distribution = self.factory.makeDistribution(name="distro")
+ else:
+ distribution = archive.distribution
distroseries = self.factory.makeDistroSeries(
distribution=distribution, name="unstable")
processor = getUtility(IProcessorSet).getByName("386")
@@ -66,7 +79,7 @@
distroseries=distroseries, architecturetag="i386",
processor=processor)
build = self.factory.makeLiveFSBuild(
- distroarchseries=distroarchseries, pocket=pocket,
+ archive=archive, distroarchseries=distroarchseries, pocket=pocket,
name=u"test-livefs", **kwargs)
return IBuildFarmJobBehaviour(build)
@@ -173,8 +186,9 @@
job = self.makeJob(
date_created=datetime(2014, 4, 25, 10, 38, 0, tzinfo=pytz.UTC),
metadata={"project": "distro", "subproject": "special"})
- expected_archives = yield get_sources_list_for_building(
- job.build, job.build.distro_arch_series, None)
+ expected_archives, expected_trusted_keys = (
+ yield get_sources_list_for_building(
+ job.build, job.build.distro_arch_series, None))
extra_args = yield job._extraBuildArgs()
self.assertEqual({
"archive_private": False,
@@ -185,6 +199,7 @@
"project": "distro",
"subproject": "special",
"series": "unstable",
+ "trusted_keys": expected_trusted_keys,
}, extra_args)
@defer.inlineCallbacks
@@ -209,6 +224,24 @@
self.assertIsNot(Proxy, type(args["lb_args"]))
@defer.inlineCallbacks
+ def test_extraBuildArgs_archive_trusted_keys(self):
+ # If the archive has a signing key, _extraBuildArgs sends it.
+ yield self.useFixture(InProcessKeyServerFixture()).start()
+ archive = self.factory.makeArchive()
+ key_path = os.path.join(gpgkeysdir, "ppa-sample@xxxxxxxxxxxxxxxxx")
+ yield IArchiveSigningKey(archive).setSigningKey(
+ key_path, async_keyserver=True)
+ job = self.makeJob(archive=archive)
+ self.factory.makeBinaryPackagePublishingHistory(
+ distroarchseries=job.build.distro_arch_series,
+ pocket=job.build.pocket, archive=archive,
+ status=PackagePublishingStatus.PUBLISHED)
+ args = yield job._extraBuildArgs()
+ self.assertThat(args["trusted_keys"], MatchesListwise([
+ Base64KeyMatches("0D57E99656BEFB0897606EE9A022DD1F5001B46D"),
+ ]))
+
+ @defer.inlineCallbacks
def test_composeBuildRequest(self):
job = self.makeJob()
lfa = self.factory.makeLibraryFileAlias(db_only=True)
Follow ups