Merge lp:~twom/launchpad-buildd/include-base-in-digests into lp:launchpad-buildd

Proposed by Tom Wardill
Status: Needs review
Proposed branch: lp:~twom/launchpad-buildd/include-base-in-digests
Merge into: lp:launchpad-buildd
Diff against target: 288 lines (+124/-37)
5 files modified
debian/changelog (+6/-0)
lpbuildd/oci.py (+37/-17)
lpbuildd/target/build_oci.py (+18/-0)
lpbuildd/target/tests/test_build_oci.py (+32/-0)
lpbuildd/tests/test_oci.py (+31/-20)
To merge this branch: bzr merge lp:~twom/launchpad-buildd/include-base-in-digests
Reviewer Review Type Date Requested Status
Thiago F. Pappacena (community) Approve
Colin Watson (community) Approve
Review via email: mp+383584@code.launchpad.net

Commit message

Include base OS information in digests.json

Description of the change

For the final push to a registry, we need to tag the image with the base OS that it is built on.
Grab this information from `/etc/os-release` inside the image, parse it and save it to an appropriate place in the digests.json for processing downstream in Launchpad.

This is a breaking change in the format of digests.json, so needs the equivalent LP branch first.

To post a comment you must log in.
421. By Tom Wardill

Comment fix

422. By Tom Wardill

Import ordering

Revision history for this message
Thiago F. Pappacena (pappacena) wrote :

LGTM. Thanks!

review: Approve
Revision history for this message
Colin Watson (cjwatson) :
Revision history for this message
Colin Watson (cjwatson) :
423. By Tom Wardill

Tidy up error handling

Revision history for this message
Colin Watson (cjwatson) :
review: Approve
424. By Tom Wardill

More exception cleanup

Revision history for this message
Thiago F. Pappacena (pappacena) wrote :

LGTM

review: Approve

Unmerged revisions

424. By Tom Wardill

More exception cleanup

423. By Tom Wardill

Tidy up error handling

422. By Tom Wardill

Import ordering

421. By Tom Wardill

Comment fix

420. By Tom Wardill

Add some debug

419. By Tom Wardill

Include base in the digests file

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/changelog'
--- debian/changelog 2020-04-09 10:42:50 +0000
+++ debian/changelog 2020-05-07 16:12:43 +0000
@@ -1,3 +1,9 @@
1launchpad-buildd (190) UNRELEASED; urgency=medium
2
3 * Include base in OCI digests
4
5 -- Tom Wardill <tom.wardill@canonical.com> Thu, 07 May 2020 11:21:58 +0100
6
1launchpad-buildd (189) xenial; urgency=medium7launchpad-buildd (189) xenial; urgency=medium
28
3 * Fix closing tar files in OCI builds9 * Fix closing tar files in OCI builds
410
=== modified file 'lpbuildd/oci.py'
--- lpbuildd/oci.py 2020-04-09 07:51:23 +0000
+++ lpbuildd/oci.py 2020-05-07 16:12:43 +0000
@@ -8,6 +8,7 @@
8import hashlib8import hashlib
9import json9import json
10import os10import os
11from subprocess import CalledProcessError
11import tarfile12import tarfile
12import tempfile13import tempfile
1314
@@ -21,6 +22,7 @@
21 DebianBuildState,22 DebianBuildState,
22 )23 )
23from lpbuildd.snap import SnapBuildProxyMixin24from lpbuildd.snap import SnapBuildProxyMixin
25from lpbuildd.target.lxd import LXDException
2426
2527
26RETCODE_SUCCESS = 028RETCODE_SUCCESS = 0
@@ -147,16 +149,36 @@
147149
148 return digest_diff_map150 return digest_diff_map
149151
152 def _extractBase(self):
153 """Extract the base from the os-release file"""
154 os_file = tempfile.NamedTemporaryFile(mode='w+')
155 try:
156 self.backend.copy_out(
157 '/home/buildd/os-release',
158 os_file.name)
159 except (CalledProcessError, KeyError, LXDException) as e:
160 print("Error getting os-release from image: {}".format(e))
161 return ""
162 id = ""
163 version_id = ""
164 os_file.seek(0, os.SEEK_SET)
165 contents = [x.strip() for x in os_file.readlines()]
166 for line in contents:
167 key, value = line.split('=', 1)
168 if key == "ID":
169 id = value
170 elif key == "VERSION_ID":
171 version_id = value.replace("\"", "")
172 return "{}{}".format(id, version_id)
173
174
150 def gatherResults(self):175 def gatherResults(self):
151 """Gather the results of the build and add them to the file cache."""176 """Gather the results of the build and add them to the file cache."""
152 extract_path = tempfile.mkdtemp(prefix=self.name)177 extract_path = tempfile.mkdtemp(prefix=self.name)
153 proc = self.backend.run(178 proc = self.backend.run(
154 ['docker', 'save', self.name],179 ['docker', 'save', self.name],
155 get_output=True, universal_newlines=False, return_process=True)180 get_output=True, universal_newlines=False, return_process=True)
156 try:181 tar = tarfile.open(fileobj=proc.stdout, mode="r|")
157 tar = tarfile.open(fileobj=proc.stdout, mode="r|")
158 except Exception as e:
159 print(e)
160182
161 current_dir = ''183 current_dir = ''
162 directory_tar = None184 directory_tar = None
@@ -187,8 +209,6 @@
187 else:209 else:
188 # If it's not in a directory, we need that210 # If it's not in a directory, we need that
189 tar.extract(file, extract_path)211 tar.extract(file, extract_path)
190 except Exception as e:
191 print(e)
192 finally:212 finally:
193 if directory_tar is not None:213 if directory_tar is not None:
194 directory_tar.close()214 directory_tar.close()
@@ -212,14 +232,14 @@
212 manifest = json.load(manifest_fp)232 manifest = json.load(manifest_fp)
213233
214 digest_maps = []234 digest_maps = []
215 try:235 for section in manifest:
216 for section in manifest:236 digests = self._gatherManifestSection(
217 digest_maps.append(237 section, extract_path, sha_directory)
218 self._gatherManifestSection(section, extract_path,238 base = self._extractBase()
219 sha_directory))239 digest_maps.append({
220 digest_map_file = os.path.join(extract_path, 'digests.json')240 "digest_mappings": digests,
221 with open(digest_map_file, 'w') as digest_map_fp:241 "base": base})
222 json.dump(digest_maps, digest_map_fp)242 digest_map_file = os.path.join(extract_path, 'digests.json')
223 self._builder.addWaitingFile(digest_map_file)243 with open(digest_map_file, 'w') as digest_map_fp:
224 except Exception as e:244 json.dump(digest_maps, digest_map_fp)
225 print(e)245 self._builder.addWaitingFile(digest_map_file)
226246
=== modified file 'lpbuildd/target/build_oci.py'
--- lpbuildd/target/build_oci.py 2020-03-31 13:54:56 +0000
+++ lpbuildd/target/build_oci.py 2020-05-07 16:12:43 +0000
@@ -8,10 +8,12 @@
8from collections import OrderedDict8from collections import OrderedDict
9import logging9import logging
10import os.path10import os.path
11import subprocess
11import sys12import sys
12import tempfile13import tempfile
13from textwrap import dedent14from textwrap import dedent
1415
16from lpbuildd.target.lxd import LXDException
15from lpbuildd.target.operation import Operation17from lpbuildd.target.operation import Operation
16from lpbuildd.target.snapbuildproxy import SnapBuildProxyOperationMixin18from lpbuildd.target.snapbuildproxy import SnapBuildProxyOperationMixin
17from lpbuildd.target.snapstore import SnapStoreOperationMixin19from lpbuildd.target.snapstore import SnapStoreOperationMixin
@@ -125,6 +127,22 @@
125 args.append(self.buildd_path)127 args.append(self.buildd_path)
126 self.run_build_command(args)128 self.run_build_command(args)
127129
130 # Copy os-release file out, if it's present
131 args = ["docker", "container", "create",
132 "--name", "os-release-extraction", self.args.name]
133 self.run_build_command(args)
134 # docker cp test_lsb_release:/usr/lib/os-release os-release
135 args = ["docker", "cp", "-L", "os-release-extraction:/etc/os-release",
136 "/home/buildd/os-release"]
137 try:
138 self.run_build_command(args)
139 logger.info("os-release file extracted")
140 except (LXDException, subprocess.CalledProcessError) as e:
141 # The os-release file may not exist causing this to error,
142 # we don't care.
143 logger.info("os-release file extraction failed: {}".format(e))
144 pass
145
128 def run(self):146 def run(self):
129 try:147 try:
130 self.install()148 self.install()
131149
=== modified file 'lpbuildd/target/tests/test_build_oci.py'
--- lpbuildd/target/tests/test_build_oci.py 2020-03-31 14:12:59 +0000
+++ lpbuildd/target/tests/test_build_oci.py 2020-05-07 16:12:43 +0000
@@ -304,6 +304,14 @@
304 ["docker", "build", "--no-cache", "--tag", "test-image",304 ["docker", "build", "--no-cache", "--tag", "test-image",
305 "/home/buildd/test-image"],305 "/home/buildd/test-image"],
306 cwd="/home/buildd/test-image"),306 cwd="/home/buildd/test-image"),
307 RanBuildCommand(
308 ["docker", "container", "create", "--name",
309 "os-release-extraction", "test-image"],
310 cwd="/home/buildd/test-image"),
311 RanBuildCommand(
312 ["docker", "cp", "-L", "os-release-extraction:/etc/os-release",
313 "/home/buildd/os-release"],
314 cwd="/home/buildd/test-image"),
307 ]))315 ]))
308316
309 def test_build_with_file(self):317 def test_build_with_file(self):
@@ -322,6 +330,14 @@
322 "--file", "build-aux/Dockerfile",330 "--file", "build-aux/Dockerfile",
323 "/home/buildd/test-image"],331 "/home/buildd/test-image"],
324 cwd="/home/buildd/test-image"),332 cwd="/home/buildd/test-image"),
333 RanBuildCommand(
334 ["docker", "container", "create", "--name",
335 "os-release-extraction", "test-image"],
336 cwd="/home/buildd/test-image"),
337 RanBuildCommand(
338 ["docker", "cp", "-L", "os-release-extraction:/etc/os-release",
339 "/home/buildd/os-release"],
340 cwd="/home/buildd/test-image"),
325 ]))341 ]))
326342
327 def test_build_proxy(self):343 def test_build_proxy(self):
@@ -341,6 +357,14 @@
341 "--build-arg", "https_proxy=http://proxy.example:3128/",357 "--build-arg", "https_proxy=http://proxy.example:3128/",
342 "--tag", "test-image", "/home/buildd/test-image"],358 "--tag", "test-image", "/home/buildd/test-image"],
343 cwd="/home/buildd/test-image"),359 cwd="/home/buildd/test-image"),
360 RanBuildCommand(
361 ["docker", "container", "create", "--name",
362 "os-release-extraction", "test-image"],
363 cwd="/home/buildd/test-image"),
364 RanBuildCommand(
365 ["docker", "cp", "-L", "os-release-extraction:/etc/os-release",
366 "/home/buildd/os-release"],
367 cwd="/home/buildd/test-image"),
344 ]))368 ]))
345369
346 def test_run_succeeds(self):370 def test_run_succeeds(self):
@@ -362,6 +386,14 @@
362 ["docker", "build", "--no-cache", "--tag", "test-image",386 ["docker", "build", "--no-cache", "--tag", "test-image",
363 "/home/buildd/test-image"],387 "/home/buildd/test-image"],
364 cwd="/home/buildd/test-image")),388 cwd="/home/buildd/test-image")),
389 AnyMatch(RanBuildCommand(
390 ["docker", "container", "create", "--name",
391 "os-release-extraction", "test-image"],
392 cwd="/home/buildd/test-image")),
393 AnyMatch(RanBuildCommand(
394 ["docker", "cp", "-L", "os-release-extraction:/etc/os-release",
395 "/home/buildd/os-release"],
396 cwd="/home/buildd/test-image")),
365 ))397 ))
366398
367 def test_run_install_fails(self):399 def test_run_install_fails(self):
368400
=== modified file 'lpbuildd/tests/test_oci.py'
--- lpbuildd/tests/test_oci.py 2020-02-26 10:52:29 +0000
+++ lpbuildd/tests/test_oci.py 2020-05-07 16:12:43 +0000
@@ -135,6 +135,13 @@
135 'vfs/distribution/v2metadata-by-diffid/sha256/diff1',135 'vfs/distribution/v2metadata-by-diffid/sha256/diff1',
136 b"""[{"Digest": "test_digest", "SourceRepository": "test"}]"""136 b"""[{"Digest": "test_digest", "SourceRepository": "test"}]"""
137 )137 )
138 self.buildmanager.backend.add_file(
139 '/home/buildd/os-release',
140 b'ID=ubuntu\n'
141 b'ID_LIKE=debian\n'
142 b'PRETTY_NAME="Ubuntu 16.04.6 LTS"\n'
143 b'VERSION_ID="16.04"\n'
144 )
138145
139 # After building the package, reap processes.146 # After building the package, reap processes.
140 yield self.buildmanager.iterate(0)147 yield self.buildmanager.iterate(0)
@@ -162,16 +169,18 @@
162 with open(cache_path, "rb") as f:169 with open(cache_path, "rb") as f:
163 digests_contents = f.read()170 digests_contents = f.read()
164 digests_expected = [{171 digests_expected = [{
165 "sha256:diff1": {172 "digest_mappings": {
166 "source": "test",173 "sha256:diff1": {
167 "digest": "test_digest",174 "source": "test",
168 "layer_id": "layer-1"175 "digest": "test_digest",
169 },176 "layer_id": "layer-1"
170 "sha256:diff2": {177 },
171 "source": "",178 "sha256:diff2": {
172 "digest": "testsha",179 "source": "",
173 "layer_id": "layer-2"180 "digest": "testsha",
174 }181 "layer_id": "layer-2"
182 }},
183 "base": "ubuntu16.04"
175 }]184 }]
176 self.assertEqual(digests_contents, json.dumps(digests_expected))185 self.assertEqual(digests_contents, json.dumps(digests_expected))
177 # Control returns to the DebianBuildManager in the UMOUNT state.186 # Control returns to the DebianBuildManager in the UMOUNT state.
@@ -245,16 +254,18 @@
245 with open(cache_path, "rb") as f:254 with open(cache_path, "rb") as f:
246 digests_contents = f.read()255 digests_contents = f.read()
247 digests_expected = [{256 digests_expected = [{
248 "sha256:diff1": {257 "digest_mappings": {
249 "source": "test",258 "sha256:diff1": {
250 "digest": "test_digest",259 "source": "test",
251 "layer_id": "layer-1"260 "digest": "test_digest",
252 },261 "layer_id": "layer-1"
253 "sha256:diff2": {262 },
254 "source": "",263 "sha256:diff2": {
255 "digest": "testsha",264 "source": "",
256 "layer_id": "layer-2"265 "digest": "testsha",
257 }266 "layer_id": "layer-2"
267 }},
268 "base": ""
258 }]269 }]
259 self.assertEqual(digests_contents, json.dumps(digests_expected))270 self.assertEqual(digests_contents, json.dumps(digests_expected))
260271

Subscribers

People subscribed via source and target branches