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
1diff --git a/debian/changelog b/debian/changelog
2index e66175d..0dcf32e 100644
3--- a/debian/changelog
4+++ b/debian/changelog
5@@ -1,6 +1,7 @@
6 launchpad-buildd (198) UNRELEASED; urgency=medium
7
8 * Run charmcraft in verbose mode.
9+ * Honour proxy arguments when building charms.
10
11 -- Colin Watson <cjwatson@ubuntu.com> Fri, 09 Jul 2021 14:08:58 +0100
12
13diff --git a/lpbuildd/charm.py b/lpbuildd/charm.py
14index e5c5fe9..0838fe7 100644
15--- a/lpbuildd/charm.py
16+++ b/lpbuildd/charm.py
17@@ -11,6 +11,7 @@ from lpbuildd.debian import (
18 DebianBuildState,
19 DebianBuildManager,
20 )
21+from lpbuildd.snap import SnapBuildProxyMixin
22
23
24 RETCODE_SUCCESS = 0
25@@ -22,7 +23,7 @@ class CharmBuildState(DebianBuildState):
26 BUILD_CHARM = "BUILD_CHARM"
27
28
29-class CharmBuildManager(DebianBuildManager):
30+class CharmBuildManager(SnapBuildProxyMixin, DebianBuildManager):
31 """Build a charm."""
32
33 backend_name = "lxd"
34@@ -40,6 +41,9 @@ class CharmBuildManager(DebianBuildManager):
35 self.git_path = extra_args.get("git_path")
36 self.build_path = extra_args.get("build_path")
37 self.channels = extra_args.get("channels", {})
38+ self.proxy_url = extra_args.get("proxy_url")
39+ self.revocation_endpoint = extra_args.get("revocation_endpoint")
40+ self.proxy_service = None
41
42 super(CharmBuildManager, self).initiate(files, chroot, extra_args)
43
44diff --git a/lpbuildd/target/build_charm.py b/lpbuildd/target/build_charm.py
45index 95da85e..7610a8b 100644
46--- a/lpbuildd/target/build_charm.py
47+++ b/lpbuildd/target/build_charm.py
48@@ -2,7 +2,6 @@
49 # GNU Affero General Public License version 3 (see the file LICENSE).
50
51 from __future__ import print_function
52-import functools
53
54 __metaclass__ = type
55
56@@ -14,6 +13,7 @@ import sys
57 from lpbuildd.target.backend import check_path_escape
58 from lpbuildd.target.build_snap import SnapChannelsAction
59 from lpbuildd.target.operation import Operation
60+from lpbuildd.target.snapbuildproxy import SnapBuildProxyOperationMixin
61 from lpbuildd.target.snapstore import SnapStoreOperationMixin
62 from lpbuildd.target.vcs import VCSOperationMixin
63
64@@ -25,7 +25,8 @@ RETCODE_FAILURE_BUILD = 201
65 logger = logging.getLogger(__name__)
66
67
68-class BuildCharm(VCSOperationMixin, SnapStoreOperationMixin, Operation):
69+class BuildCharm(SnapBuildProxyOperationMixin, VCSOperationMixin,
70+ SnapStoreOperationMixin, Operation):
71
72 description = "Build a charm."
73
74@@ -69,6 +70,9 @@ class BuildCharm(VCSOperationMixin, SnapStoreOperationMixin, Operation):
75 def install(self):
76 logger.info("Running install phase")
77 deps = []
78+ if self.args.proxy_url:
79+ deps.extend(self.proxy_deps)
80+ self.install_git_proxy()
81 if self.args.backend == "lxd":
82 # udev is installed explicitly to work around
83 # https://bugs.launchpad.net/snapd/+bug/1731519.
84@@ -99,7 +103,8 @@ class BuildCharm(VCSOperationMixin, SnapStoreOperationMixin, Operation):
85 def repo(self):
86 """Collect git or bzr branch."""
87 logger.info("Running repo phase...")
88- self.vcs_fetch(self.args.name, cwd="/home/buildd")
89+ env = self.build_proxy_environment(proxy_url=self.args.proxy_url)
90+ self.vcs_fetch(self.args.name, cwd="/home/buildd", env=env)
91 self.save_status(self.buildd_path)
92
93 def build(self):
94@@ -109,8 +114,13 @@ class BuildCharm(VCSOperationMixin, SnapStoreOperationMixin, Operation):
95 self.args.name,
96 self.args.build_path)
97 check_path_escape(self.buildd_path, build_context_path)
98+ env = OrderedDict()
99+ if self.args.proxy_url:
100+ env["http_proxy"] = self.args.proxy_url
101+ env["https_proxy"] = self.args.proxy_url
102+ env["GIT_PROXY_COMMAND"] = "/usr/local/bin/snap-git-proxy"
103 args = ["charmcraft", "build", "-v", "-f", build_context_path]
104- self.run_build_command(args)
105+ self.run_build_command(args, env=env)
106
107 def run(self):
108 try:
109diff --git a/lpbuildd/target/tests/test_build_charm.py b/lpbuildd/target/tests/test_build_charm.py
110index 4e77943..8be840e 100644
111--- a/lpbuildd/target/tests/test_build_charm.py
112+++ b/lpbuildd/target/tests/test_build_charm.py
113@@ -5,6 +5,7 @@ __metaclass__ = type
114
115 import json
116 import os
117+import stat
118 import subprocess
119 from textwrap import dedent
120
121@@ -13,6 +14,7 @@ from fixtures import (
122 TempDir,
123 )
124 import responses
125+from systemfixtures import FakeFilesystem
126 from testtools.matchers import (
127 AnyMatch,
128 Equals,
129@@ -171,11 +173,11 @@ class TestBuildCharm(TestCase):
130 "--backend=fake", "--series=xenial", "--arch=amd64", "1",
131 "--git-repository", "lp:foo",
132 "--snap-store-proxy-url", "http://snap-store-proxy.example/",
133- "test-snap",
134+ "test-image",
135 ]
136- build_snap = parse_args(args=args).operation
137- build_snap.install()
138- self.assertThat(build_snap.backend.run.calls, MatchesListwise([
139+ build_charm = parse_args(args=args).operation
140+ build_charm.install()
141+ self.assertThat(build_charm.backend.run.calls, MatchesListwise([
142 RanAptGet("install", "git"),
143 RanCommand(
144 ["snap", "ack", "/dev/stdin"], input_text=store_assertion),
145@@ -184,6 +186,31 @@ class TestBuildCharm(TestCase):
146 RanCommand(["mkdir", "-p", "/home/buildd"]),
147 ]))
148
149+ def test_install_proxy(self):
150+ args = [
151+ "build-charm",
152+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
153+ "--git-repository", "lp:foo",
154+ "--proxy-url", "http://proxy.example:3128/",
155+ "test-image",
156+ ]
157+ build_charm = parse_args(args=args).operation
158+ build_charm.bin = "/builderbin"
159+ self.useFixture(FakeFilesystem()).add("/builderbin")
160+ os.mkdir("/builderbin")
161+ with open("/builderbin/snap-git-proxy", "w") as proxy_script:
162+ proxy_script.write("proxy script\n")
163+ os.fchmod(proxy_script.fileno(), 0o755)
164+ build_charm.install()
165+ self.assertThat(build_charm.backend.run.calls, MatchesListwise([
166+ RanAptGet("install", "python3", "socat", "git"),
167+ RanSnap("install", "charmcraft"),
168+ RanCommand(["mkdir", "-p", "/home/buildd"]),
169+ ]))
170+ self.assertEqual(
171+ (b"proxy script\n", stat.S_IFREG | 0o755),
172+ build_charm.backend.backend_fs["/usr/local/bin/snap-git-proxy"])
173+
174 def test_repo_bzr(self):
175 args = [
176 "build-charm",
177@@ -284,6 +311,39 @@ class TestBuildCharm(TestCase):
178 with open(status_path) as status:
179 self.assertEqual({"revision_id": "0" * 40}, json.load(status))
180
181+ def test_repo_proxy(self):
182+ args = [
183+ "build-charm",
184+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
185+ "--git-repository", "lp:foo",
186+ "--proxy-url", "http://proxy.example:3128/",
187+ "test-image",
188+ ]
189+ build_charm = parse_args(args=args).operation
190+ build_charm.backend.build_path = self.useFixture(TempDir()).path
191+ build_charm.backend.run = FakeRevisionID("0" * 40)
192+ build_charm.repo()
193+ env = {
194+ "http_proxy": "http://proxy.example:3128/",
195+ "https_proxy": "http://proxy.example:3128/",
196+ "GIT_PROXY_COMMAND": "/usr/local/bin/snap-git-proxy",
197+ }
198+ self.assertThat(build_charm.backend.run.calls, MatchesListwise([
199+ RanBuildCommand(
200+ ["git", "clone", "lp:foo", "test-image"],
201+ cwd="/home/buildd", **env),
202+ RanBuildCommand(
203+ ["git", "submodule", "update", "--init", "--recursive"],
204+ cwd="/home/buildd/test-image", **env),
205+ RanBuildCommand(
206+ ["git", "rev-parse", "HEAD^{}"],
207+ cwd="/home/buildd/test-image", get_output=True,
208+ universal_newlines=True),
209+ ]))
210+ status_path = os.path.join(build_charm.backend.build_path, "status")
211+ with open(status_path) as status:
212+ self.assertEqual({"revision_id": "0" * 40}, json.load(status))
213+
214 def test_build(self):
215 args = [
216 "build-charm",
217@@ -317,6 +377,27 @@ class TestBuildCharm(TestCase):
218 cwd="/home/buildd/test-image"),
219 ]))
220
221+ def test_build_proxy(self):
222+ args = [
223+ "build-charm",
224+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
225+ "--branch", "lp:foo", "--proxy-url", "http://proxy.example:3128/",
226+ "test-image",
227+ ]
228+ build_charm = parse_args(args=args).operation
229+ build_charm.build()
230+ env = {
231+ "http_proxy": "http://proxy.example:3128/",
232+ "https_proxy": "http://proxy.example:3128/",
233+ "GIT_PROXY_COMMAND": "/usr/local/bin/snap-git-proxy",
234+ }
235+ self.assertThat(build_charm.backend.run.calls, MatchesListwise([
236+ RanBuildCommand(
237+ ["charmcraft", "build", "-v", "-f",
238+ "/home/buildd/test-image/."],
239+ cwd="/home/buildd/test-image", **env),
240+ ]))
241+
242 def test_run_succeeds(self):
243 args = [
244 "build-charm",
245diff --git a/lpbuildd/tests/test_charm.py b/lpbuildd/tests/test_charm.py
246index 407f732..8610bad 100644
247--- a/lpbuildd/tests/test_charm.py
248+++ b/lpbuildd/tests/test_charm.py
249@@ -4,6 +4,10 @@
250 __metaclass__ = type
251
252 import os
253+try:
254+ from unittest import mock
255+except ImportError:
256+ import mock
257
258 from fixtures import (
259 EnvironmentVariable,
260@@ -137,3 +141,15 @@ class TestCharmBuildManagerIteration(TestCase):
261 self.assertEqual(
262 self.buildmanager.iterate, self.buildmanager.iterators[-1])
263 self.assertFalse(self.builder.wasCalled("buildFail"))
264+
265+ @mock.patch('lpbuildd.snap.urlopen')
266+ def test_revokeProxyToken(self, urlopen_mock):
267+ self.buildmanager.revocation_endpoint = "http://revoke_endpoint"
268+ self.buildmanager.proxy_url = "http://username:password@proxy_url"
269+ self.buildmanager.revokeProxyToken()
270+ self.assertEqual(1, urlopen_mock.call_count)
271+ request = urlopen_mock.call_args[0][0]
272+ self.assertEqual(
273+ {'Authorization': "Basic dXNlcm5hbWU6cGFzc3dvcmQ="},
274+ request.headers)
275+ self.assertEqual('http://revoke_endpoint', request.get_full_url())

Subscribers

People subscribed via source and target branches