Merge ~twom/launchpad-buildd:oci-build-path into launchpad-buildd:master

Proposed by Tom Wardill
Status: Merged
Approved by: Tom Wardill
Approved revision: 5d972a3983cb430e7f8bb09ad31b129afff34924
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~twom/launchpad-buildd:oci-build-path
Merge into: launchpad-buildd:master
Diff against target: 196 lines (+94/-10)
4 files modified
debian/changelog (+1/-0)
lpbuildd/oci.py (+3/-0)
lpbuildd/target/build_oci.py (+13/-5)
lpbuildd/target/tests/test_build_oci.py (+77/-5)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+389258@code.launchpad.net

Commit message

Add build path handling to OCI builds

Description of the change

A user may wish to use a subdirectory as the build context, we should allow this.
This does enforce that the Dockerfile will live in the given directory.

To post a comment you must log in.
5d972a3... by Tom Wardill

Add changelog entry

Revision history for this message
Colin Watson (cjwatson) :
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 e6caf70..3a3704c 100644
3--- a/debian/changelog
4+++ b/debian/changelog
5@@ -37,6 +37,7 @@ launchpad-buildd (190) UNRELEASED; urgency=medium
6
7 [ Tom Wardill ]
8 * Improve error logging in OCI post-build
9+ * Add build_path directory context for OCI builds
10
11 -- Colin Watson <cjwatson@ubuntu.com> Tue, 28 Apr 2020 10:19:27 +0100
12
13diff --git a/lpbuildd/oci.py b/lpbuildd/oci.py
14index c89a7c0..1fccada 100644
15--- a/lpbuildd/oci.py
16+++ b/lpbuildd/oci.py
17@@ -49,6 +49,7 @@ class OCIBuildManager(SnapBuildProxyMixin, DebianBuildManager):
18 self.git_repository = extra_args.get("git_repository")
19 self.git_path = extra_args.get("git_path")
20 self.build_file = extra_args.get("build_file")
21+ self.build_path = extra_args.get("build_path")
22 self.proxy_url = extra_args.get("proxy_url")
23 self.revocation_endpoint = extra_args.get("revocation_endpoint")
24 self.proxy_service = None
25@@ -69,6 +70,8 @@ class OCIBuildManager(SnapBuildProxyMixin, DebianBuildManager):
26 args.extend(["--git-path", self.git_path])
27 if self.build_file is not None:
28 args.extend(["--build-file", self.build_file])
29+ if self.build_path is not None:
30+ args.extend(["--build-path", self.build_path])
31 try:
32 snap_store_proxy_url = self._builder._config.get(
33 "proxy", "snapstore")
34diff --git a/lpbuildd/target/build_oci.py b/lpbuildd/target/build_oci.py
35index 1f220ab..377fc68 100644
36--- a/lpbuildd/target/build_oci.py
37+++ b/lpbuildd/target/build_oci.py
38@@ -39,6 +39,9 @@ class BuildOCI(SnapBuildProxyOperationMixin, VCSOperationMixin,
39 super(BuildOCI, cls).add_arguments(parser)
40 parser.add_argument(
41 "--build-file", help="path to Dockerfile in branch")
42+ parser.add_argument(
43+ "--build-path", default=".",
44+ help="context directory for docker build")
45 parser.add_argument("name", help="name of snap to build")
46
47 def __init__(self, args, parser):
48@@ -64,10 +67,10 @@ class BuildOCI(SnapBuildProxyOperationMixin, VCSOperationMixin,
49 systemd_file.flush()
50 self.backend.copy_in(systemd_file.name, file_path)
51
52- def _check_build_file_escape(self):
53+ def _check_path_escape(self, path_to_check):
54 """Check the build file path doesn't escape the build directory."""
55 build_file_path = os.path.realpath(
56- os.path.join(self.buildd_path, self.args.build_file))
57+ os.path.join(self.buildd_path, path_to_check))
58 common_path = os.path.commonprefix((build_file_path, self.buildd_path))
59 if common_path != self.buildd_path:
60 raise InvalidBuildFilePath("Invalid build file path.")
61@@ -120,9 +123,14 @@ class BuildOCI(SnapBuildProxyOperationMixin, VCSOperationMixin,
62 ["--build-arg", "{}={}".format(var, self.args.proxy_url)])
63 args.extend(["--tag", self.args.name])
64 if self.args.build_file is not None:
65- self._check_build_file_escape()
66- args.extend(["--file", self.args.build_file])
67- args.append(self.buildd_path)
68+ build_file_path = os.path.join(
69+ self.args.build_path, self.args.build_file)
70+ self._check_path_escape(build_file_path)
71+ args.extend(["--file", build_file_path])
72+ build_context_path = os.path.join(
73+ self.buildd_path, self.args.build_path)
74+ self._check_path_escape(build_context_path)
75+ args.append(build_context_path)
76 self.run_build_command(args)
77
78 def run(self):
79diff --git a/lpbuildd/target/tests/test_build_oci.py b/lpbuildd/target/tests/test_build_oci.py
80index 47fa1a5..a2c67d7 100644
81--- a/lpbuildd/target/tests/test_build_oci.py
82+++ b/lpbuildd/target/tests/test_build_oci.py
83@@ -302,7 +302,7 @@ class TestBuildOCI(TestCase):
84 self.assertThat(build_oci.backend.run.calls, MatchesListwise([
85 RanBuildCommand(
86 ["docker", "build", "--no-cache", "--tag", "test-image",
87- "/home/buildd/test-image"],
88+ "/home/buildd/test-image/."],
89 cwd="/home/buildd/test-image"),
90 ]))
91
92@@ -319,8 +319,44 @@ class TestBuildOCI(TestCase):
93 self.assertThat(build_oci.backend.run.calls, MatchesListwise([
94 RanBuildCommand(
95 ["docker", "build", "--no-cache", "--tag", "test-image",
96- "--file", "build-aux/Dockerfile",
97- "/home/buildd/test-image"],
98+ "--file", "./build-aux/Dockerfile",
99+ "/home/buildd/test-image/."],
100+ cwd="/home/buildd/test-image"),
101+ ]))
102+
103+ def test_build_with_path(self):
104+ args = [
105+ "build-oci",
106+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
107+ "--branch", "lp:foo", "--build-path", "a-sub-directory/",
108+ "test-image",
109+ ]
110+ build_oci = parse_args(args=args).operation
111+ build_oci.backend.add_dir('/build/test-directory')
112+ build_oci.build()
113+ self.assertThat(build_oci.backend.run.calls, MatchesListwise([
114+ RanBuildCommand(
115+ ["docker", "build", "--no-cache", "--tag", "test-image",
116+ "/home/buildd/test-image/a-sub-directory/"],
117+ cwd="/home/buildd/test-image"),
118+ ]))
119+
120+ def test_build_with_file_and_path(self):
121+ args = [
122+ "build-oci",
123+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
124+ "--branch", "lp:foo", "--build-file", "build-aux/Dockerfile",
125+ "--build-path", "test-build-path",
126+ "test-image",
127+ ]
128+ build_oci = parse_args(args=args).operation
129+ build_oci.backend.add_dir('/build/test-directory')
130+ build_oci.build()
131+ self.assertThat(build_oci.backend.run.calls, MatchesListwise([
132+ RanBuildCommand(
133+ ["docker", "build", "--no-cache", "--tag", "test-image",
134+ "--file", "test-build-path/build-aux/Dockerfile",
135+ "/home/buildd/test-image/test-build-path"],
136 cwd="/home/buildd/test-image"),
137 ]))
138
139@@ -339,7 +375,7 @@ class TestBuildOCI(TestCase):
140 ["docker", "build", "--no-cache",
141 "--build-arg", "http_proxy=http://proxy.example:3128/",
142 "--build-arg", "https_proxy=http://proxy.example:3128/",
143- "--tag", "test-image", "/home/buildd/test-image"],
144+ "--tag", "test-image", "/home/buildd/test-image/."],
145 cwd="/home/buildd/test-image"),
146 ]))
147
148@@ -360,7 +396,7 @@ class TestBuildOCI(TestCase):
149 cwd="/home/buildd")),
150 AnyMatch(RanBuildCommand(
151 ["docker", "build", "--no-cache", "--tag", "test-image",
152- "/home/buildd/test-image"],
153+ "/home/buildd/test-image/."],
154 cwd="/home/buildd/test-image")),
155 ))
156
157@@ -451,3 +487,39 @@ class TestBuildOCI(TestCase):
158 '/etc/hosts',
159 os.path.join(build_oci.buildd_path, 'Dockerfile'))
160 self.assertRaises(InvalidBuildFilePath, build_oci.build)
161+
162+ def test_build_with_invalid_build_path_parent(self):
163+ args = [
164+ "build-oci",
165+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
166+ "--branch", "lp:foo", "--build-path", "../",
167+ "test-image",
168+ ]
169+ build_oci = parse_args(args=args).operation
170+ build_oci.backend.add_dir('/build/test-directory')
171+ self.assertRaises(InvalidBuildFilePath, build_oci.build)
172+
173+ def test_build_with_invalid_build_path_absolute(self):
174+ args = [
175+ "build-oci",
176+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
177+ "--branch", "lp:foo", "--build-path", "/etc",
178+ "test-image",
179+ ]
180+ build_oci = parse_args(args=args).operation
181+ build_oci.backend.add_dir('/build/test-directory')
182+ self.assertRaises(InvalidBuildFilePath, build_oci.build)
183+
184+ def test_build_with_invalid_build_path_symlink(self):
185+ args = [
186+ "build-oci",
187+ "--backend=fake", "--series=xenial", "--arch=amd64", "1",
188+ "--branch", "lp:foo", "--build-path", "build/",
189+ "test-image",
190+ ]
191+ build_oci = parse_args(args=args).operation
192+ build_oci.buildd_path = self.useFixture(TempDir()).path
193+ os.symlink(
194+ '/etc/hosts',
195+ os.path.join(build_oci.buildd_path, 'build'))
196+ self.assertRaises(InvalidBuildFilePath, build_oci.build)

Subscribers

People subscribed via source and target branches