blob: 2181173b9b1a17a7fc50a8efbfe95578d217ce0d [file] [log] [blame]
Evan Hernandezf388cbf2019-04-01 11:15:23 -06001# -*- coding: utf-8 -*-
2# Copyright 2019 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Implements ArtifactService."""
7
8from __future__ import print_function
9
10import os
11
Alex Klein238d8862019-05-07 11:32:46 -060012from chromite.api.controller import controller_util
Evan Hernandezf388cbf2019-04-01 11:15:23 -060013from chromite.cbuildbot import commands
Alex Klein6504eca2019-04-18 15:37:56 -060014from chromite.cbuildbot.stages import vm_test_stages
Alex Klein2275d692019-04-23 16:04:12 -060015from chromite.lib import build_target_util
16from chromite.lib import chroot_lib
Evan Hernandezf388cbf2019-04-01 11:15:23 -060017from chromite.lib import constants
18from chromite.lib import cros_build_lib
Evan Hernandezde445982019-04-22 13:42:34 -060019from chromite.lib import cros_logging as logging
Evan Hernandezf388cbf2019-04-01 11:15:23 -060020from chromite.lib import osutils
Alex Klein2275d692019-04-23 16:04:12 -060021from chromite.lib import sysroot_lib
22from chromite.service import artifacts
Evan Hernandezf388cbf2019-04-01 11:15:23 -060023
24
Evan Hernandez9f125ac2019-04-08 17:18:47 -060025def _GetImageDir(build_root, target):
26 """Return path containing images for the given build target.
27
Alex Kleine2612a02019-04-18 13:51:06 -060028 TODO(saklein) Expand image_lib.GetLatestImageLink to support this use case.
29
Evan Hernandez9f125ac2019-04-08 17:18:47 -060030 Args:
31 build_root (str): Path to checkout where build occurs.
32 target (str): Name of the build target.
33
34 Returns:
35 Path to the directory containing target images.
36
37 Raises:
38 DieSystemExit: If the image dir does not exist.
39 """
40 image_dir = os.path.join(build_root, 'src/build/images', target, 'latest')
41 if not os.path.exists(image_dir):
42 cros_build_lib.Die('Expected to find image output for target %s at %s, '
43 'but path does not exist' % (target, image_dir))
44 return image_dir
45
46
47def BundleImageZip(input_proto, output_proto):
48 """Bundle image.zip.
49
50 Args:
51 input_proto (BundleRequest): The input proto.
Alex Klein6504eca2019-04-18 15:37:56 -060052 output_proto (BundleResponse): The output proto.
Evan Hernandez9f125ac2019-04-08 17:18:47 -060053 """
54 target = input_proto.build_target.name
55 output_dir = input_proto.output_dir
56 image_dir = _GetImageDir(constants.SOURCE_ROOT, target)
57 archive = commands.BuildImageZip(output_dir, image_dir)
58 output_proto.artifacts.add().path = os.path.join(output_dir, archive)
59
60
Evan Hernandezf388cbf2019-04-01 11:15:23 -060061def BundleTestUpdatePayloads(input_proto, output_proto):
62 """Generate minimal update payloads for the build target for testing.
63
64 Args:
65 input_proto (BundleRequest): The input proto.
Alex Klein6504eca2019-04-18 15:37:56 -060066 output_proto (BundleResponse): The output proto.
Evan Hernandezf388cbf2019-04-01 11:15:23 -060067 """
68 target = input_proto.build_target.name
69 output_dir = input_proto.output_dir
70 build_root = constants.SOURCE_ROOT
71
72 # Use the first available image to create the update payload.
Evan Hernandez9f125ac2019-04-08 17:18:47 -060073 img_dir = _GetImageDir(build_root, target)
Evan Hernandezf388cbf2019-04-01 11:15:23 -060074 img_types = [
75 constants.IMAGE_TYPE_TEST, constants.IMAGE_TYPE_DEV,
76 constants.IMAGE_TYPE_BASE
77 ]
78 img_paths = []
79 for img_type in img_types:
Evan Hernandez9f125ac2019-04-08 17:18:47 -060080 img_path = os.path.join(img_dir, constants.IMAGE_TYPE_TO_NAME[img_type])
Evan Hernandezf388cbf2019-04-01 11:15:23 -060081 if os.path.exists(img_path):
82 img_paths.append(img_path)
83
84 if not img_paths:
85 cros_build_lib.Die(
86 'Expected to find an image of type among %r for target "%s" '
Evan Hernandez9f125ac2019-04-08 17:18:47 -060087 'at path %s.', img_types, target, img_dir)
Evan Hernandezf388cbf2019-04-01 11:15:23 -060088 img = img_paths[0]
89
90 # Unfortunately, the relevant commands.py functions do not return
91 # a list of generated files. As a workaround, we have commands.py
92 # put the files in a separate temporary directory so we can catalog them,
93 # then move them to the output dir.
Alex Kleine2612a02019-04-18 13:51:06 -060094 # TODO(crbug.com/954283): Replace with a chromite/service implementation.
Evan Hernandezf388cbf2019-04-01 11:15:23 -060095 with osutils.TempDir() as temp:
96 commands.GeneratePayloads(img, temp, full=True, stateful=True, delta=True)
97 commands.GenerateQuickProvisionPayloads(img, temp)
98 for path in osutils.DirectoryIterator(temp):
99 if os.path.isfile(path):
100 rel_path = os.path.relpath(path, temp)
101 output_proto.artifacts.add().path = os.path.join(output_dir, rel_path)
Evan Hernandezb04e2aa2019-04-08 16:55:54 -0600102 osutils.CopyDirContents(temp, output_dir, allow_nonempty=True)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600103
104
105def BundleAutotestFiles(input_proto, output_proto):
106 """Tar the autotest files for a build target.
107
108 Args:
109 input_proto (BundleRequest): The input proto.
Alex Klein6504eca2019-04-18 15:37:56 -0600110 output_proto (BundleResponse): The output proto.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600111 """
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600112 output_dir = input_proto.output_dir
Alex Klein238d8862019-05-07 11:32:46 -0600113 if not output_dir:
114 cros_build_lib.Die('output_dir is required.')
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600115
Alex Klein238d8862019-05-07 11:32:46 -0600116 target = input_proto.build_target.name
117 if target:
118 # Legacy call, build out sysroot path from default source root and the
119 # build target.
120 target = input_proto.build_target.name
121 build_root = constants.SOURCE_ROOT
122 sysroot_path = os.path.join(build_root, constants.DEFAULT_CHROOT_DIR,
123 'build', target)
124 sysroot = sysroot_lib.Sysroot(sysroot_path)
125 else:
126 # New style call, use chroot and sysroot.
127 chroot = controller_util.ParseChroot(input_proto.chroot)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600128
Alex Klein238d8862019-05-07 11:32:46 -0600129 sysroot_path = input_proto.sysroot.path
130 if not sysroot_path:
131 cros_build_lib.Die('sysroot.path is required.')
132
133 # Since we're staying outside the chroot, prepend the chroot path to the
134 # sysroot path so we have a valid full path to the sysroot.
135 sysroot = sysroot_lib.Sysroot(os.path.join(chroot.path,
136 sysroot_path.lstrip(os.sep)))
137
138 if not sysroot.Exists():
139 cros_build_lib.Die('Sysroot path must exist: %s', sysroot.path)
140
141 try:
142 # Note that this returns the full path to *multiple* tarballs.
143 archives = artifacts.BundleAutotestFiles(sysroot, output_dir)
144 except artifacts.Error as e:
145 cros_build_lib.Die(e.message)
146
147 for archive in archives.values():
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600148 output_proto.artifacts.add().path = archive
149
150
151def BundleTastFiles(input_proto, output_proto):
152 """Tar the tast files for a build target.
153
154 Args:
155 input_proto (BundleRequest): The input proto.
Alex Klein6504eca2019-04-18 15:37:56 -0600156 output_proto (BundleResponse): The output proto.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600157 """
158 target = input_proto.build_target.name
159 output_dir = input_proto.output_dir
160 build_root = constants.SOURCE_ROOT
Evan Hernandez36589c62019-04-05 18:14:42 -0600161 cwd = os.path.join(build_root, 'chroot/build', target, 'build')
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600162
163 # Note that unlike the functions below, this returns the full path
164 # to the tarball.
Alex Kleine2612a02019-04-18 13:51:06 -0600165 # TODO(crbug.com/954294): Replace with a chromite/service implementation.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600166 archive = commands.BuildTastBundleTarball(build_root, cwd, output_dir)
167
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600168 if archive is None:
169 cros_build_lib.Die(
170 'Could not bundle Tast files. '
171 'No Tast directories found for %s.', target)
172
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600173 output_proto.artifacts.add().path = archive
174
175
176def BundlePinnedGuestImages(input_proto, output_proto):
177 """Tar the pinned guest images for a build target.
178
179 Args:
180 input_proto (BundleRequest): The input proto.
Alex Klein6504eca2019-04-18 15:37:56 -0600181 output_proto (BundleResponse): The output proto.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600182 """
183 target = input_proto.build_target.name
184 output_dir = input_proto.output_dir
185 build_root = constants.SOURCE_ROOT
186
Alex Kleine2612a02019-04-18 13:51:06 -0600187 # TODO(crbug.com/954299): Replace with a chromite/service implementation.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600188 archive = commands.BuildPinnedGuestImagesTarball(build_root, target,
189 output_dir)
190
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600191 if archive is None:
Evan Hernandezde445982019-04-22 13:42:34 -0600192 logging.warning('Found no pinned guest images for %s.', target)
193 return
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600194
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600195 output_proto.artifacts.add().path = os.path.join(output_dir, archive)
196
197
Alex Klein7bf0ecb2019-06-25 10:04:15 -0600198def FetchPinnedGuestImages(input_proto, output_proto):
199 """Get the pinned guest image information."""
200 sysroot_path = input_proto.sysroot.path
201 if not sysroot_path:
202 cros_build_lib.Die('sysroot.path is required.')
203
204 chroot = controller_util.ParseChroot(input_proto.chroot)
205 sysroot = sysroot_lib.Sysroot(sysroot_path)
206
207 if not chroot.exists():
208 cros_build_lib.Die('Chroot does not exist: %s', chroot.path)
209
210 if not sysroot.Exists(chroot=chroot):
211 cros_build_lib.Die('Sysroot does not exist: %s',
212 chroot.full_path(sysroot.path))
213
214 pins = artifacts.FetchPinnedGuestImages(chroot, sysroot)
215
216 for pin in pins:
217 pinned_image = output_proto.pinned_images.add()
218 pinned_image.filename = pin.filename
219 pinned_image.uri = pin.uri
220
221
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600222def BundleFirmware(input_proto, output_proto):
223 """Tar the firmware images for a build target.
224
225 Args:
226 input_proto (BundleRequest): The input proto.
Alex Klein6504eca2019-04-18 15:37:56 -0600227 output_proto (BundleResponse): The output proto.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600228 """
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600229 output_dir = input_proto.output_dir
Michael Mortensen3df9c762019-06-19 07:50:09 -0600230 if not output_dir:
231 cros_build_lib.Die('output_dir is required.')
232 chroot = controller_util.ParseChroot(input_proto.chroot)
233 sysroot_path = input_proto.sysroot.path
234 if not sysroot_path:
235 cros_build_lib.Die('sysroot.path is required.')
236 sysroot = sysroot_lib.Sysroot(sysroot_path)
237 archive = artifacts.BuildFirmwareArchive(chroot, sysroot, output_dir)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600238
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600239 if archive is None:
240 cros_build_lib.Die(
Michael Mortensen3df9c762019-06-19 07:50:09 -0600241 'Could not create firmware archive. No firmware found for %s.',
242 sysroot_path)
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600243
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600244 output_proto.artifacts.add().path = os.path.join(output_dir, archive)
245
246
247def BundleEbuildLogs(input_proto, output_proto):
248 """Tar the ebuild logs for a build target.
249
250 Args:
251 input_proto (BundleRequest): The input proto.
Alex Klein6504eca2019-04-18 15:37:56 -0600252 output_proto (BundleResponse): The output proto.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600253 """
254 target = input_proto.build_target.name
255 output_dir = input_proto.output_dir
Evan Hernandeza478d802019-04-08 15:08:24 -0600256
257 # commands.BuildEbuildLogsTarball conflates "buildroot" with sysroot.
258 # Adjust accordingly...
259 build_root = os.path.join(constants.SOURCE_ROOT, 'chroot', 'build')
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600260
Alex Kleine2612a02019-04-18 13:51:06 -0600261 # TODO(crbug.com/954303): Replace with a chromite/service implementation.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600262 archive = commands.BuildEbuildLogsTarball(build_root, target, output_dir)
263
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600264 if archive is None:
265 cros_build_lib.Die(
266 'Could not create ebuild logs archive. No logs found for %s.', target)
267
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600268 output_proto.artifacts.add().path = os.path.join(output_dir, archive)
Alex Klein6504eca2019-04-18 15:37:56 -0600269
270
Alex Klein2275d692019-04-23 16:04:12 -0600271def BundleSimpleChromeArtifacts(input_proto, output_proto):
272 """Create the simple chrome artifacts."""
273 # Required args.
274 sysroot_path = input_proto.sysroot.path
275 build_target_name = input_proto.sysroot.build_target.name
276 output_dir = input_proto.output_dir
277
278 if not build_target_name:
279 cros_build_lib.Die('build_target.name is required')
280 if not output_dir:
281 cros_build_lib.Die('output_dir is required.')
282 if not os.path.exists(output_dir):
283 cros_build_lib.Die('output_dir (%s) does not exist.', output_dir)
284 if not sysroot_path:
285 cros_build_lib.Die('sysroot.path is required.')
286
287 # Optional args.
288 chroot_path = input_proto.chroot.path or constants.DEFAULT_CHROOT_PATH
289 cache_dir = input_proto.chroot.cache_dir
290
291 # Build out the argument instances.
292 build_target = build_target_util.BuildTarget(build_target_name)
293 chroot = chroot_lib.Chroot(path=chroot_path, cache_dir=cache_dir)
294 # Sysroot.path needs to be the fully qualified path, including the chroot.
295 full_sysroot_path = os.path.join(chroot.path, sysroot_path.lstrip(os.sep))
296 sysroot = sysroot_lib.Sysroot(full_sysroot_path)
297
298 # Quick sanity check that the sysroot exists before we go on.
299 if not sysroot.Exists():
300 cros_build_lib.Die('The sysroot does not exist.')
301
302 try:
303 results = artifacts.BundleSimpleChromeArtifacts(chroot, sysroot,
304 build_target, output_dir)
305 except artifacts.Error as e:
306 cros_build_lib.Die('Error %s raised in BundleSimpleChromeArtifacts: %s',
307 type(e), e)
308
309 for file_name in results:
310 output_proto.artifacts.add().path = file_name
311
312
Alex Klein6504eca2019-04-18 15:37:56 -0600313def BundleVmFiles(input_proto, output_proto):
314 """Tar VM disk and memory files.
315
316 Args:
317 input_proto (SysrootBundleRequest): The input proto.
318 output_proto (BundleResponse): The output proto.
319 """
320 chroot = input_proto.chroot.path
321 sysroot = input_proto.sysroot.path
322 test_results_dir = input_proto.test_results_dir
323 output_dir = input_proto.output_dir
324
325 if not chroot:
326 cros_build_lib.Die('chroot.path is required.')
327 if not sysroot:
328 cros_build_lib.Die('sysroot.path is required.')
329 if not test_results_dir:
330 cros_build_lib.Die('test_results_dir is required.')
331 if not output_dir:
332 cros_build_lib.Die('output_dir is required.')
333
334 # TODO(crbug.com/954344): Replace with a chromite/service implementation.
335 sysroot = sysroot.lstrip(os.sep)
336 result_dir = test_results_dir.lstrip(os.sep)
337 image_dir = os.path.join(chroot, sysroot, result_dir)
338 archives = vm_test_stages.ArchiveVMFilesFromImageDir(image_dir, output_dir)
339 for archive in archives:
340 output_proto.artifacts.add().path = archive
Tiancong Wangc4805b72019-06-11 12:12:03 -0700341
342def BundleOrderfileGenerationArtifacts(input_proto, output_proto):
343 """Create tarballs of all the artifacts of orderfile_generate builder.
344
345 Args:
346 input_proto (BundleRequest): The input proto.
347 output_proto (BundleResponse): The output proto.
348 """
349 # Required args.
350 build_target_name = input_proto.build_target.name
351 output_dir = input_proto.output_dir
352 chrome_version = input_proto.chrome_version
353
354 if not build_target_name:
355 cros_build_lib.Die('build_target.name is required.')
356 if not chrome_version:
357 cros_build_lib.Die('chrome_version is required.')
358 if not output_dir:
359 cros_build_lib.Die('output_dir is required.')
360 elif not os.path.isdir(output_dir):
361 cros_build_lib.Die('output_dir does not exist.')
362
363 chroot = controller_util.ParseChroot(input_proto.chroot)
364
365 try:
366 results = artifacts.BundleOrderfileGenerationArtifacts(
367 chroot, input_proto.build_target, chrome_version, output_dir)
368 except artifacts.Error as e:
369 cros_build_lib.Die('Error %s raised in BundleSimpleChromeArtifacts: %s',
370 type(e), e)
371
372 for file_name in results:
373 output_proto.artifacts.add().path = file_name