Merge ~cjwatson/launchpad-buildd:charm-snap-build-proxy into launchpad-buildd:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 7166ab3c8b0d105079d2f2c08863eddbb4a6681f
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad-buildd:charm-snap-build-proxy
Merge into: launchpad-buildd:master
Diff against target: 275 lines (+121/-9)
5 files modified
debian/changelog (+1/-0)
lpbuildd/charm.py (+5/-1)
lpbuildd/target/build_charm.py (+14/-4)
lpbuildd/target/tests/test_build_charm.py (+85/-4)
lpbuildd/tests/test_charm.py (+16/-0)
Reviewer Review Type Date Requested Status
Cristian Gonzalez (community) Approve
Review via email: mp+405623@code.launchpad.net

Commit message

Honour proxy arguments when building charms

Description of the change

charmcraft essentially always runs pip3 as part of building the charm, and has no obvious way to provide a local package index. It looks as though we'll need to regard charms as requiring internet access to build.

To post a comment you must log in.
Revision history for this message
Cristian Gonzalez (cristiangsp) wrote :

Looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/debian/changelog b/debian/changelog
index e66175d..0dcf32e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,6 +1,7 @@
1launchpad-buildd (198) UNRELEASED; urgency=medium1launchpad-buildd (198) UNRELEASED; urgency=medium
22
3 * Run charmcraft in verbose mode.3 * Run charmcraft in verbose mode.
4 * Honour proxy arguments when building charms.
45
5 -- Colin Watson <cjwatson@ubuntu.com> Fri, 09 Jul 2021 14:08:58 +01006 -- Colin Watson <cjwatson@ubuntu.com> Fri, 09 Jul 2021 14:08:58 +0100
67
diff --git a/lpbuildd/charm.py b/lpbuildd/charm.py
index e5c5fe9..0838fe7 100644
--- a/lpbuildd/charm.py
+++ b/lpbuildd/charm.py
@@ -11,6 +11,7 @@ from lpbuildd.debian import (
11 DebianBuildState,11 DebianBuildState,
12 DebianBuildManager,12 DebianBuildManager,
13 )13 )
14from lpbuildd.snap import SnapBuildProxyMixin
1415
1516
16RETCODE_SUCCESS = 017RETCODE_SUCCESS = 0
@@ -22,7 +23,7 @@ class CharmBuildState(DebianBuildState):
22 BUILD_CHARM = "BUILD_CHARM"23 BUILD_CHARM = "BUILD_CHARM"
2324
2425
25class CharmBuildManager(DebianBuildManager):26class CharmBuildManager(SnapBuildProxyMixin, DebianBuildManager):
26 """Build a charm."""27 """Build a charm."""
2728
28 backend_name = "lxd"29 backend_name = "lxd"
@@ -40,6 +41,9 @@ class CharmBuildManager(DebianBuildManager):
40 self.git_path = extra_args.get("git_path")41 self.git_path = extra_args.get("git_path")
41 self.build_path = extra_args.get("build_path")42 self.build_path = extra_args.get("build_path")
42 self.channels = extra_args.get("channels", {})43 self.channels = extra_args.get("channels", {})
44 self.proxy_url = extra_args.get("proxy_url")
45 self.revocation_endpoint = extra_args.get("revocation_endpoint")
46 self.proxy_service = None
4347
44 super(CharmBuildManager, self).initiate(files, chroot, extra_args)48 super(CharmBuildManager, self).initiate(files, chroot, extra_args)
4549
diff --git a/lpbuildd/target/build_charm.py b/lpbuildd/target/build_charm.py
index 95da85e..7610a8b 100644
--- a/lpbuildd/target/build_charm.py
+++ b/lpbuildd/target/build_charm.py
@@ -2,7 +2,6 @@
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4from __future__ import print_function4from __future__ import print_function
5import functools
65
7__metaclass__ = type6__metaclass__ = type
87
@@ -14,6 +13,7 @@ import sys
14from lpbuildd.target.backend import check_path_escape13from lpbuildd.target.backend import check_path_escape
15from lpbuildd.target.build_snap import SnapChannelsAction14from lpbuildd.target.build_snap import SnapChannelsAction
16from lpbuildd.target.operation import Operation15from lpbuildd.target.operation import Operation
16from lpbuildd.target.snapbuildproxy import SnapBuildProxyOperationMixin
17from lpbuildd.target.snapstore import SnapStoreOperationMixin17from lpbuildd.target.snapstore import SnapStoreOperationMixin
18from lpbuildd.target.vcs import VCSOperationMixin18from lpbuildd.target.vcs import VCSOperationMixin
1919
@@ -25,7 +25,8 @@ RETCODE_FAILURE_BUILD = 201
25logger = logging.getLogger(__name__)25logger = logging.getLogger(__name__)
2626
2727
28class BuildCharm(VCSOperationMixin, SnapStoreOperationMixin, Operation):28class BuildCharm(SnapBuildProxyOperationMixin, VCSOperationMixin,
29 SnapStoreOperationMixin, Operation):
2930
30 description = "Build a charm."31 description = "Build a charm."
3132
@@ -69,6 +70,9 @@ class BuildCharm(VCSOperationMixin, SnapStoreOperationMixin, Operation):
69 def install(self):70 def install(self):
70 logger.info("Running install phase")71 logger.info("Running install phase")
71 deps = []72 deps = []
73 if self.args.proxy_url:
74 deps.extend(self.proxy_deps)
75 self.install_git_proxy()
72 if self.args.backend == "lxd":76 if self.args.backend == "lxd":
73 # udev is installed explicitly to work around77 # udev is installed explicitly to work around
74 # https://bugs.launchpad.net/snapd/+bug/1731519.78 # https://bugs.launchpad.net/snapd/+bug/1731519.
@@ -99,7 +103,8 @@ class BuildCharm(VCSOperationMixin, SnapStoreOperationMixin, Operation):
99 def repo(self):103 def repo(self):
100 """Collect git or bzr branch."""104 """Collect git or bzr branch."""
101 logger.info("Running repo phase...")105 logger.info("Running repo phase...")
102 self.vcs_fetch(self.args.name, cwd="/home/buildd")106 env = self.build_proxy_environment(proxy_url=self.args.proxy_url)
107 self.vcs_fetch(self.args.name, cwd="/home/buildd", env=env)
103 self.save_status(self.buildd_path)108 self.save_status(self.buildd_path)
104109
105 def build(self):110 def build(self):
@@ -109,8 +114,13 @@ class BuildCharm(VCSOperationMixin, SnapStoreOperationMixin, Operation):
109 self.args.name,114 self.args.name,
110 self.args.build_path)115 self.args.build_path)
111 check_path_escape(self.buildd_path, build_context_path)116 check_path_escape(self.buildd_path, build_context_path)
117 env = OrderedDict()
118 if self.args.proxy_url:
119 env["http_proxy"] = self.args.proxy_url
120 env["https_proxy"] = self.args.proxy_url
121 env["GIT_PROXY_COMMAND"] = "/usr/local/bin/snap-git-proxy"
112 args = ["charmcraft", "build", "-v", "-f", build_context_path]122 args = ["charmcraft", "build", "-v", "-f", build_context_path]
113 self.run_build_command(args)123 self.run_build_command(args, env=env)
114124
115 def run(self):125 def run(self):
116 try:126 try:
diff --git a/lpbuildd/target/tests/test_build_charm.py b/lpbuildd/target/tests/test_build_charm.py
index 4e77943..8be840e 100644
--- a/lpbuildd/target/tests/test_build_charm.py
+++ b/lpbuildd/target/tests/test_build_charm.py
@@ -5,6 +5,7 @@ __metaclass__ = type
55
6import json6import json
7import os7import os
8import stat
8import subprocess9import subprocess
9from textwrap import dedent10from textwrap import dedent
1011
@@ -13,6 +14,7 @@ from fixtures import (
13 TempDir,14 TempDir,
14 )15 )
15import responses16import responses
17from systemfixtures import FakeFilesystem
16from testtools.matchers import (18from testtools.matchers import (
17 AnyMatch,19 AnyMatch,
18 Equals,20 Equals,
@@ -171,11 +173,11 @@ class TestBuildCharm(TestCase):
171 "--backend=fake", "--series=xenial", "--arch=amd64", "1",173 "--backend=fake", "--series=xenial", "--arch=amd64", "1",
172 "--git-repository", "lp:foo",174 "--git-repository", "lp:foo",
173 "--snap-store-proxy-url", "http://snap-store-proxy.example/",175 "--snap-store-proxy-url", "http://snap-store-proxy.example/",
174 "test-snap",176 "test-image",
175 ]177 ]
176 build_snap = parse_args(args=args).operation178 build_charm = parse_args(args=args).operation
177 build_snap.install()179 build_charm.install()
178 self.assertThat(build_snap.backend.run.calls, MatchesListwise([180 self.assertThat(build_charm.backend.run.calls, MatchesListwise([
179 RanAptGet("install", "git"),181 RanAptGet("install", "git"),
180 RanCommand(182 RanCommand(
181 ["snap", "ack", "/dev/stdin"], input_text=store_assertion),183 ["snap", "ack", "/dev/stdin"], input_text=store_assertion),
@@ -184,6 +186,31 @@ class TestBuildCharm(TestCase):
184 RanCommand(["mkdir", "-p", "/home/buildd"]),186 RanCommand(["mkdir", "-p", "/home/buildd"]),
185 ]))187 ]))
186188
189 def test_install_proxy(self):
190 args = [
191 "build-charm",
192 "--backend=fake", "--series=xenial", "--arch=amd64", "1",
193 "--git-repository", "lp:foo",
194 "--proxy-url", "http://proxy.example:3128/",
195 "test-image",
196 ]
197 build_charm = parse_args(args=args).operation
198 build_charm.bin = "/builderbin"
199 self.useFixture(FakeFilesystem()).add("/builderbin")
200 os.mkdir("/builderbin")
201 with open("/builderbin/snap-git-proxy", "w") as proxy_script:
202 proxy_script.write("proxy script\n")
203 os.fchmod(proxy_script.fileno(), 0o755)
204 build_charm.install()
205 self.assertThat(build_charm.backend.run.calls, MatchesListwise([
206 RanAptGet("install", "python3", "socat", "git"),
207 RanSnap("install", "charmcraft"),
208 RanCommand(["mkdir", "-p", "/home/buildd"]),
209 ]))
210 self.assertEqual(
211 (b"proxy script\n", stat.S_IFREG | 0o755),
212 build_charm.backend.backend_fs["/usr/local/bin/snap-git-proxy"])
213
187 def test_repo_bzr(self):214 def test_repo_bzr(self):
188 args = [215 args = [
189 "build-charm",216 "build-charm",
@@ -284,6 +311,39 @@ class TestBuildCharm(TestCase):
284 with open(status_path) as status:311 with open(status_path) as status:
285 self.assertEqual({"revision_id": "0" * 40}, json.load(status))312 self.assertEqual({"revision_id": "0" * 40}, json.load(status))
286313
314 def test_repo_proxy(self):
315 args = [
316 "build-charm",
317 "--backend=fake", "--series=xenial", "--arch=amd64", "1",
318 "--git-repository", "lp:foo",
319 "--proxy-url", "http://proxy.example:3128/",
320 "test-image",
321 ]
322 build_charm = parse_args(args=args).operation
323 build_charm.backend.build_path = self.useFixture(TempDir()).path
324 build_charm.backend.run = FakeRevisionID("0" * 40)
325 build_charm.repo()
326 env = {
327 "http_proxy": "http://proxy.example:3128/",
328 "https_proxy": "http://proxy.example:3128/",
329 "GIT_PROXY_COMMAND": "/usr/local/bin/snap-git-proxy",
330 }
331 self.assertThat(build_charm.backend.run.calls, MatchesListwise([
332 RanBuildCommand(
333 ["git", "clone", "lp:foo", "test-image"],
334 cwd="/home/buildd", **env),
335 RanBuildCommand(
336 ["git", "submodule", "update", "--init", "--recursive"],
337 cwd="/home/buildd/test-image", **env),
338 RanBuildCommand(
339 ["git", "rev-parse", "HEAD^{}"],
340 cwd="/home/buildd/test-image", get_output=True,
341 universal_newlines=True),
342 ]))
343 status_path = os.path.join(build_charm.backend.build_path, "status")
344 with open(status_path) as status:
345 self.assertEqual({"revision_id": "0" * 40}, json.load(status))
346
287 def test_build(self):347 def test_build(self):
288 args = [348 args = [
289 "build-charm",349 "build-charm",
@@ -317,6 +377,27 @@ class TestBuildCharm(TestCase):
317 cwd="/home/buildd/test-image"),377 cwd="/home/buildd/test-image"),
318 ]))378 ]))
319379
380 def test_build_proxy(self):
381 args = [
382 "build-charm",
383 "--backend=fake", "--series=xenial", "--arch=amd64", "1",
384 "--branch", "lp:foo", "--proxy-url", "http://proxy.example:3128/",
385 "test-image",
386 ]
387 build_charm = parse_args(args=args).operation
388 build_charm.build()
389 env = {
390 "http_proxy": "http://proxy.example:3128/",
391 "https_proxy": "http://proxy.example:3128/",
392 "GIT_PROXY_COMMAND": "/usr/local/bin/snap-git-proxy",
393 }
394 self.assertThat(build_charm.backend.run.calls, MatchesListwise([
395 RanBuildCommand(
396 ["charmcraft", "build", "-v", "-f",
397 "/home/buildd/test-image/."],
398 cwd="/home/buildd/test-image", **env),
399 ]))
400
320 def test_run_succeeds(self):401 def test_run_succeeds(self):
321 args = [402 args = [
322 "build-charm",403 "build-charm",
diff --git a/lpbuildd/tests/test_charm.py b/lpbuildd/tests/test_charm.py
index 407f732..8610bad 100644
--- a/lpbuildd/tests/test_charm.py
+++ b/lpbuildd/tests/test_charm.py
@@ -4,6 +4,10 @@
4__metaclass__ = type4__metaclass__ = type
55
6import os6import os
7try:
8 from unittest import mock
9except ImportError:
10 import mock
711
8from fixtures import (12from fixtures import (
9 EnvironmentVariable,13 EnvironmentVariable,
@@ -137,3 +141,15 @@ class TestCharmBuildManagerIteration(TestCase):
137 self.assertEqual(141 self.assertEqual(
138 self.buildmanager.iterate, self.buildmanager.iterators[-1])142 self.buildmanager.iterate, self.buildmanager.iterators[-1])
139 self.assertFalse(self.builder.wasCalled("buildFail"))143 self.assertFalse(self.builder.wasCalled("buildFail"))
144
145 @mock.patch('lpbuildd.snap.urlopen')
146 def test_revokeProxyToken(self, urlopen_mock):
147 self.buildmanager.revocation_endpoint = "http://revoke_endpoint"
148 self.buildmanager.proxy_url = "http://username:password@proxy_url"
149 self.buildmanager.revokeProxyToken()
150 self.assertEqual(1, urlopen_mock.call_count)
151 request = urlopen_mock.call_args[0][0]
152 self.assertEqual(
153 {'Authorization': "Basic dXNlcm5hbWU6cGFzc3dvcmQ="},
154 request.headers)
155 self.assertEqual('http://revoke_endpoint', request.get_full_url())

Subscribers

People subscribed via source and target branches