launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #23410
[Merge] lp:~cjwatson/launchpad/snap-build-behaviour-macaroon into lp:launchpad
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.
Follow ups