Colin Watson has proposed merging lp:~cjwatson/launchpad/snap-build-behaviour-macaroon into lp:launchpad with lp:~cjwatson/launchpad/authserver-issue-macaroon as a prerequisite.
Commit message: Issue an appropriate macaroon when dispatching a snap build that uses a private Git repository. Requested reviews: Launchpad code reviewers (launchpad-reviewers) Related bugs: Bug #1639975 in Launchpad itself: "support for building private snaps" https://bugs.launchpad.net/launchpad/+bug/1639975 For more details, see: https://code.launchpad.net/~cjwatson/launchpad/snap-build-behaviour-macaroon/+merge/364387 -- Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/snap-build-behaviour-macaroon into lp:launchpad.
=== modified file 'configs/development/launchpad-lazr.conf' --- configs/development/launchpad-lazr.conf 2019-03-13 17:47:21 +0000 +++ configs/development/launchpad-lazr.conf 2019-03-13 17:47:21 +0000 @@ -12,6 +12,7 @@ root: /var/tmp/builddmaster/ uploader: scripts/process-upload.py -Mvv bzr_builder_sources_list: None +authentication_endpoint: http://xmlrpc-private.launchpad.dev:8087/authserver [canonical] show_tracebacks: True === modified file 'lib/lp/buildmaster/model/buildfarmjobbehaviour.py' --- lib/lp/buildmaster/model/buildfarmjobbehaviour.py 2019-01-14 13:43:06 +0000 +++ lib/lp/buildmaster/model/buildfarmjobbehaviour.py 2019-03-13 17:47:21 +0000 @@ -17,6 +17,7 @@ import transaction from twisted.internet import defer +from twisted.web import xmlrpc from zope.component import getUtility from lp.buildmaster.enums import ( @@ -52,6 +53,9 @@ """Store a reference to the job_type with which we were created.""" self.build = build self._builder = None + self._authserver = xmlrpc.Proxy( + config.builddmaster.authentication_endpoint, + connectTimeout=config.builddmaster.authentication_timeout) @property def archive(self): === modified file 'lib/lp/services/config/schema-lazr.conf' --- lib/lp/services/config/schema-lazr.conf 2019-03-13 17:47:21 +0000 +++ lib/lp/services/config/schema-lazr.conf 2019-03-13 17:47:21 +0000 @@ -112,6 +112,16 @@ # datatype: string bzr_builder_sources_list: none +# The URL of the XML-RPC endpoint that handles issuing macaroons. This +# should implement IAuthServer. +# +# datatype: string +authentication_endpoint: none + +# The time in seconds that the builddmaster will wait for a reply from the +# authserver. +authentication_timeout: 15 + [canonical] # datatype: boolean === modified file 'lib/lp/snappy/model/snapbuildbehaviour.py' --- lib/lp/snappy/model/snapbuildbehaviour.py 2019-03-06 23:00:49 +0000 +++ lib/lp/snappy/model/snapbuildbehaviour.py 2019-03-13 17:47:21 +0000 @@ -14,6 +14,10 @@ import base64 import time +from six.moves.urllib.parse import ( + urlsplit, + urlunsplit, + ) import treq from twisted.internet import defer from zope.component import adapter @@ -31,6 +35,7 @@ from lp.registry.interfaces.series import SeriesStatus from lp.services.config import config from lp.services.features import getFeatureFlag +from lp.services.twistedsupport import cancel_on_timeout from lp.services.twistedsupport.treq import check_status from lp.snappy.interfaces.snap import ( SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG, @@ -128,6 +133,21 @@ elif build.snap.git_ref is not None: if build.snap.git_ref.repository_url is not None: args["git_repository"] = build.snap.git_ref.repository_url + elif build.snap.git_repository.private: + macaroon_raw = yield cancel_on_timeout( + self._authserver.callRemote( + "issueMacaroon", "snap-build", build.id), + config.builddmaster.authentication_timeout) + # XXX cjwatson 2019-03-07: This is ugly and needs + # refactoring once we support more general HTTPS + # authentication; see also comment in + # GitRepository.git_https_url. + split = urlsplit(build.snap.git_repository.getCodebrowseUrl()) + netloc = ":%s@%s" % (macaroon_raw, split.hostname) + if split.port: + netloc += ":%s" % split.port + args["git_repository"] = urlunsplit([ + split.scheme, netloc, split.path, "", ""]) else: args["git_repository"] = ( build.snap.git_repository.git_https_url) === modified file 'lib/lp/snappy/tests/test_snapbuildbehaviour.py' --- lib/lp/snappy/tests/test_snapbuildbehaviour.py 2019-03-06 22:27:11 +0000 +++ lib/lp/snappy/tests/test_snapbuildbehaviour.py 2019-03-13 17:47:21 +0000 @@ -45,11 +45,14 @@ from twisted.web import ( resource, server, + xmlrpc, ) from zope.component import getUtility from zope.proxy import isProxy +from zope.publisher.xmlrpc import TestRequest from zope.security.proxy import removeSecurityProxy +from lp.app.enums import InformationType from lp.archivepublisher.interfaces.archivesigningkey import ( IArchiveSigningKey, ) @@ -74,6 +77,7 @@ ) from lp.registry.interfaces.pocket import PackagePublishingPocket from lp.registry.interfaces.series import SeriesStatus +from lp.services.authserver.xmlrpc import AuthServerAPIView from lp.services.config import config from lp.services.features.testing import FeatureFixture from lp.services.log.logger import ( @@ -97,6 +101,7 @@ from lp.testing.gpgkeys import gpgkeysdir from lp.testing.keyserver import InProcessKeyServerFixture from lp.testing.layers import LaunchpadZopelessLayer +from lp.xmlrpc.interfaces import IPrivateApplication class ProxyAuthAPITokensResource(resource.Resource): @@ -162,6 +167,34 @@ self.addCleanup(config.pop, "in-process-proxy-auth-api-fixture") +class InProcessAuthServer(xmlrpc.XMLRPC): + + def __init__(self, *args, **kwargs): + xmlrpc.XMLRPC.__init__(self, *args, **kwargs) + private_root = getUtility(IPrivateApplication) + self.authserver = AuthServerAPIView( + private_root.authserver, TestRequest()) + + def __getattr__(self, name): + if name.startswith("xmlrpc_"): + return getattr(self.authserver, name[len("xmlrpc_"):]) + else: + raise AttributeError("%r has no attribute '%s'" % name) + + +class InProcessAuthServerFixture(fixtures.Fixture, xmlrpc.XMLRPC): + """A fixture that runs an in-process authserver.""" + + def _setUp(self): + listener = reactor.listenTCP(0, server.Site(InProcessAuthServer())) + self.addCleanup(listener.stopListening) + config.push("in-process-auth-server-fixture", (dedent(""" + [builddmaster] + authentication_endpoint: http://localhost:%d/ + """) % listener.getHost().port).encode("UTF-8")) + self.addCleanup(config.pop, "in-process-auth-server-fixture") + + class TestSnapBuildBehaviourBase(TestCaseWithFactory): layer = LaunchpadZopelessLayer @@ -459,6 +492,51 @@ })) @defer.inlineCallbacks + def test_extraBuildArgs_git_private(self): + # extraBuildArgs returns appropriate arguments if asked to build a + # job for a private Git branch. + self.useFixture(FeatureFixture({SNAP_PRIVATE_FEATURE_FLAG: "on"})) + self.useFixture(InProcessAuthServerFixture()) + self.pushConfig( + "launchpad", internal_macaroon_secret_key="some-secret") + [ref] = self.factory.makeGitRefs( + information_type=InformationType.USERDATA) + job = self.makeJob(git_ref=ref, private=True) + expected_archives, expected_trusted_keys = ( + yield get_sources_list_for_building( + job.build, job.build.distro_arch_series, None)) + args = yield job.extraBuildArgs() + split_browse_root = urlsplit(config.codehosting.git_browse_root) + self.assertThat(args, MatchesDict({ + "archive_private": Is(False), + "archives": Equals(expected_archives), + "arch_tag": Equals("i386"), + "build_source_tarball": Is(False), + "build_url": Equals(canonical_url(job.build)), + "fast_cleanup": Is(True), + "git_repository": AfterPreprocessing(urlsplit, MatchesStructure( + scheme=Equals(split_browse_root.scheme), + username=Equals(""), + password=AfterPreprocessing( + Macaroon.deserialize, MatchesStructure( + location=Equals(config.vhost.mainsite.hostname), + identifier=Equals("snap-build"), + caveats=MatchesListwise([ + MatchesStructure.byEquality( + caveat_id="lp.snap-build %s" % job.build.id), + ]))), + hostname=Equals(split_browse_root.hostname), + port=Equals(split_browse_root.port))), + "git_path": Equals(ref.name), + "name": Equals("test-snap"), + "private": Is(True), + "proxy_url": self.getProxyURLMatcher(job), + "revocation_endpoint": self.getRevocationEndpointMatcher(job), + "series": Equals("unstable"), + "trusted_keys": Equals(expected_trusted_keys), + })) + + @defer.inlineCallbacks def test_extraBuildArgs_git_url(self): # extraBuildArgs returns appropriate arguments if asked to build a # job for a Git branch backed by a URL for an external repository.
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : [email protected] Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp

