Merge lp:~cjwatson/launchpad/snap-build-behaviour-macaroon into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18968
Proposed branch: lp:~cjwatson/launchpad/snap-build-behaviour-macaroon
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/authserver-issue-macaroon
Diff against target: 221 lines (+113/-0)
5 files modified
configs/development/launchpad-lazr.conf (+1/-0)
lib/lp/buildmaster/model/buildfarmjobbehaviour.py (+4/-0)
lib/lp/services/config/schema-lazr.conf (+10/-0)
lib/lp/snappy/model/snapbuildbehaviour.py (+20/-0)
lib/lp/snappy/tests/test_snapbuildbehaviour.py (+78/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-build-behaviour-macaroon
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+364387@code.launchpad.net

Commit message

Issue an appropriate macaroon when dispatching a snap build that uses a private Git repository.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configs/development/launchpad-lazr.conf'
2--- configs/development/launchpad-lazr.conf 2019-04-27 08:35:51 +0000
3+++ configs/development/launchpad-lazr.conf 2019-05-22 16:55:01 +0000
4@@ -12,6 +12,7 @@
5 root: /var/tmp/builddmaster/
6 uploader: scripts/process-upload.py -Mvv
7 bzr_builder_sources_list: None
8+authentication_endpoint: http://xmlrpc-private.launchpad.dev:8087/authserver
9
10 [canonical]
11 show_tracebacks: True
12
13=== modified file 'lib/lp/buildmaster/model/buildfarmjobbehaviour.py'
14--- lib/lp/buildmaster/model/buildfarmjobbehaviour.py 2019-01-14 13:43:06 +0000
15+++ lib/lp/buildmaster/model/buildfarmjobbehaviour.py 2019-05-22 16:55:01 +0000
16@@ -17,6 +17,7 @@
17
18 import transaction
19 from twisted.internet import defer
20+from twisted.web import xmlrpc
21 from zope.component import getUtility
22
23 from lp.buildmaster.enums import (
24@@ -52,6 +53,9 @@
25 """Store a reference to the job_type with which we were created."""
26 self.build = build
27 self._builder = None
28+ self._authserver = xmlrpc.Proxy(
29+ config.builddmaster.authentication_endpoint,
30+ connectTimeout=config.builddmaster.authentication_timeout)
31
32 @property
33 def archive(self):
34
35=== modified file 'lib/lp/services/config/schema-lazr.conf'
36--- lib/lp/services/config/schema-lazr.conf 2019-05-03 13:18:52 +0000
37+++ lib/lp/services/config/schema-lazr.conf 2019-05-22 16:55:01 +0000
38@@ -112,6 +112,16 @@
39 # datatype: string
40 bzr_builder_sources_list: none
41
42+# The URL of the XML-RPC endpoint that handles issuing macaroons. This
43+# should implement IAuthServer.
44+#
45+# datatype: string
46+authentication_endpoint: none
47+
48+# The time in seconds that the builddmaster will wait for a reply from the
49+# authserver.
50+authentication_timeout: 15
51+
52
53 [canonical]
54 # datatype: boolean
55
56=== modified file 'lib/lp/snappy/model/snapbuildbehaviour.py'
57--- lib/lp/snappy/model/snapbuildbehaviour.py 2019-05-03 13:16:52 +0000
58+++ lib/lp/snappy/model/snapbuildbehaviour.py 2019-05-22 16:55:01 +0000
59@@ -14,6 +14,10 @@
60 import base64
61 import time
62
63+from six.moves.urllib.parse import (
64+ urlsplit,
65+ urlunsplit,
66+ )
67 import treq
68 from twisted.internet import defer
69 from zope.component import adapter
70@@ -31,6 +35,7 @@
71 from lp.registry.interfaces.series import SeriesStatus
72 from lp.services.config import config
73 from lp.services.features import getFeatureFlag
74+from lp.services.twistedsupport import cancel_on_timeout
75 from lp.services.twistedsupport.treq import check_status
76 from lp.snappy.interfaces.snap import (
77 SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG,
78@@ -139,6 +144,21 @@
79 elif build.snap.git_ref is not None:
80 if build.snap.git_ref.repository_url is not None:
81 args["git_repository"] = build.snap.git_ref.repository_url
82+ elif build.snap.git_repository.private:
83+ macaroon_raw = yield cancel_on_timeout(
84+ self._authserver.callRemote(
85+ "issueMacaroon", "snap-build", "SnapBuild", build.id),
86+ config.builddmaster.authentication_timeout)
87+ # XXX cjwatson 2019-03-07: This is ugly and needs
88+ # refactoring once we support more general HTTPS
89+ # authentication; see also comment in
90+ # GitRepository.git_https_url.
91+ split = urlsplit(build.snap.git_repository.getCodebrowseUrl())
92+ netloc = ":%s@%s" % (macaroon_raw, split.hostname)
93+ if split.port:
94+ netloc += ":%s" % split.port
95+ args["git_repository"] = urlunsplit([
96+ split.scheme, netloc, split.path, "", ""])
97 else:
98 args["git_repository"] = (
99 build.snap.git_repository.git_https_url)
100
101=== modified file 'lib/lp/snappy/tests/test_snapbuildbehaviour.py'
102--- lib/lp/snappy/tests/test_snapbuildbehaviour.py 2019-05-07 17:33:45 +0000
103+++ lib/lp/snappy/tests/test_snapbuildbehaviour.py 2019-05-22 16:55:01 +0000
104@@ -46,11 +46,14 @@
105 from twisted.web import (
106 resource,
107 server,
108+ xmlrpc,
109 )
110 from zope.component import getUtility
111 from zope.proxy import isProxy
112+from zope.publisher.xmlrpc import TestRequest
113 from zope.security.proxy import removeSecurityProxy
114
115+from lp.app.enums import InformationType
116 from lp.archivepublisher.interfaces.archivesigningkey import (
117 IArchiveSigningKey,
118 )
119@@ -75,6 +78,7 @@
120 )
121 from lp.registry.interfaces.pocket import PackagePublishingPocket
122 from lp.registry.interfaces.series import SeriesStatus
123+from lp.services.authserver.xmlrpc import AuthServerAPIView
124 from lp.services.config import config
125 from lp.services.features.testing import FeatureFixture
126 from lp.services.log.logger import (
127@@ -105,6 +109,7 @@
128 from lp.testing.gpgkeys import gpgkeysdir
129 from lp.testing.keyserver import InProcessKeyServerFixture
130 from lp.testing.layers import LaunchpadZopelessLayer
131+from lp.xmlrpc.interfaces import IPrivateApplication
132
133
134 class ProxyAuthAPITokensResource(resource.Resource):
135@@ -170,6 +175,34 @@
136 self.addCleanup(config.pop, "in-process-proxy-auth-api-fixture")
137
138
139+class InProcessAuthServer(xmlrpc.XMLRPC):
140+
141+ def __init__(self, *args, **kwargs):
142+ xmlrpc.XMLRPC.__init__(self, *args, **kwargs)
143+ private_root = getUtility(IPrivateApplication)
144+ self.authserver = AuthServerAPIView(
145+ private_root.authserver, TestRequest())
146+
147+ def __getattr__(self, name):
148+ if name.startswith("xmlrpc_"):
149+ return getattr(self.authserver, name[len("xmlrpc_"):])
150+ else:
151+ raise AttributeError("%r has no attribute '%s'" % name)
152+
153+
154+class InProcessAuthServerFixture(fixtures.Fixture, xmlrpc.XMLRPC):
155+ """A fixture that runs an in-process authserver."""
156+
157+ def _setUp(self):
158+ listener = reactor.listenTCP(0, server.Site(InProcessAuthServer()))
159+ self.addCleanup(listener.stopListening)
160+ config.push("in-process-auth-server-fixture", (dedent("""
161+ [builddmaster]
162+ authentication_endpoint: http://localhost:%d/
163+ """) % listener.getHost().port).encode("UTF-8"))
164+ self.addCleanup(config.pop, "in-process-auth-server-fixture")
165+
166+
167 class FormatAsRfc3339TestCase(TestCase):
168
169 def test_simple(self):
170@@ -496,6 +529,51 @@
171 }))
172
173 @defer.inlineCallbacks
174+ def test_extraBuildArgs_git_private(self):
175+ # extraBuildArgs returns appropriate arguments if asked to build a
176+ # job for a private Git branch.
177+ self.useFixture(FeatureFixture({SNAP_PRIVATE_FEATURE_FLAG: "on"}))
178+ self.useFixture(InProcessAuthServerFixture())
179+ self.pushConfig(
180+ "launchpad", internal_macaroon_secret_key="some-secret")
181+ [ref] = self.factory.makeGitRefs(
182+ information_type=InformationType.USERDATA)
183+ job = self.makeJob(git_ref=ref, private=True)
184+ expected_archives, expected_trusted_keys = (
185+ yield get_sources_list_for_building(
186+ job.build, job.build.distro_arch_series, None))
187+ args = yield job.extraBuildArgs()
188+ split_browse_root = urlsplit(config.codehosting.git_browse_root)
189+ self.assertThat(args, MatchesDict({
190+ "archive_private": Is(False),
191+ "archives": Equals(expected_archives),
192+ "arch_tag": Equals("i386"),
193+ "build_source_tarball": Is(False),
194+ "build_url": Equals(canonical_url(job.build)),
195+ "fast_cleanup": Is(True),
196+ "git_repository": AfterPreprocessing(urlsplit, MatchesStructure(
197+ scheme=Equals(split_browse_root.scheme),
198+ username=Equals(""),
199+ password=AfterPreprocessing(
200+ Macaroon.deserialize, MatchesStructure(
201+ location=Equals(config.vhost.mainsite.hostname),
202+ identifier=Equals("snap-build"),
203+ caveats=MatchesListwise([
204+ MatchesStructure.byEquality(
205+ caveat_id="lp.snap-build %s" % job.build.id),
206+ ]))),
207+ hostname=Equals(split_browse_root.hostname),
208+ port=Equals(split_browse_root.port))),
209+ "git_path": Equals(ref.name),
210+ "name": Equals("test-snap"),
211+ "private": Is(True),
212+ "proxy_url": self.getProxyURLMatcher(job),
213+ "revocation_endpoint": self.getRevocationEndpointMatcher(job),
214+ "series": Equals("unstable"),
215+ "trusted_keys": Equals(expected_trusted_keys),
216+ }))
217+
218+ @defer.inlineCallbacks
219 def test_extraBuildArgs_git_url(self):
220 # extraBuildArgs returns appropriate arguments if asked to build a
221 # job for a Git branch backed by a URL for an external repository.