blob: 2527728112e734cc365152d6716631fe2e676a99 [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
198def BundleFirmware(input_proto, output_proto):
199 """Tar the firmware images for a build target.
200
201 Args:
202 input_proto (BundleRequest): The input proto.
Alex Klein6504eca2019-04-18 15:37:56 -0600203 output_proto (BundleResponse): The output proto.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600204 """
205 target = input_proto.build_target.name
206 output_dir = input_proto.output_dir
207 build_root = constants.SOURCE_ROOT
208
Alex Kleine2612a02019-04-18 13:51:06 -0600209 # TODO(crbug.com/954300): Replace with a chromite/service implementation.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600210 archive = commands.BuildFirmwareArchive(build_root, target, output_dir)
211
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600212 if archive is None:
213 cros_build_lib.Die(
214 'Could not create firmware archive. No firmware found for %s.', target)
215
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600216 output_proto.artifacts.add().path = os.path.join(output_dir, archive)
217
218
219def BundleEbuildLogs(input_proto, output_proto):
220 """Tar the ebuild logs for a build target.
221
222 Args:
223 input_proto (BundleRequest): The input proto.
Alex Klein6504eca2019-04-18 15:37:56 -0600224 output_proto (BundleResponse): The output proto.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600225 """
226 target = input_proto.build_target.name
227 output_dir = input_proto.output_dir
Evan Hernandeza478d802019-04-08 15:08:24 -0600228
229 # commands.BuildEbuildLogsTarball conflates "buildroot" with sysroot.
230 # Adjust accordingly...
231 build_root = os.path.join(constants.SOURCE_ROOT, 'chroot', 'build')
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600232
Alex Kleine2612a02019-04-18 13:51:06 -0600233 # TODO(crbug.com/954303): Replace with a chromite/service implementation.
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600234 archive = commands.BuildEbuildLogsTarball(build_root, target, output_dir)
235
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600236 if archive is None:
237 cros_build_lib.Die(
238 'Could not create ebuild logs archive. No logs found for %s.', target)
239
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600240 output_proto.artifacts.add().path = os.path.join(output_dir, archive)
Alex Klein6504eca2019-04-18 15:37:56 -0600241
242
Alex Klein2275d692019-04-23 16:04:12 -0600243def BundleSimpleChromeArtifacts(input_proto, output_proto):
244 """Create the simple chrome artifacts."""
245 # Required args.
246 sysroot_path = input_proto.sysroot.path
247 build_target_name = input_proto.sysroot.build_target.name
248 output_dir = input_proto.output_dir
249
250 if not build_target_name:
251 cros_build_lib.Die('build_target.name is required')
252 if not output_dir:
253 cros_build_lib.Die('output_dir is required.')
254 if not os.path.exists(output_dir):
255 cros_build_lib.Die('output_dir (%s) does not exist.', output_dir)
256 if not sysroot_path:
257 cros_build_lib.Die('sysroot.path is required.')
258
259 # Optional args.
260 chroot_path = input_proto.chroot.path or constants.DEFAULT_CHROOT_PATH
261 cache_dir = input_proto.chroot.cache_dir
262
263 # Build out the argument instances.
264 build_target = build_target_util.BuildTarget(build_target_name)
265 chroot = chroot_lib.Chroot(path=chroot_path, cache_dir=cache_dir)
266 # Sysroot.path needs to be the fully qualified path, including the chroot.
267 full_sysroot_path = os.path.join(chroot.path, sysroot_path.lstrip(os.sep))
268 sysroot = sysroot_lib.Sysroot(full_sysroot_path)
269
270 # Quick sanity check that the sysroot exists before we go on.
271 if not sysroot.Exists():
272 cros_build_lib.Die('The sysroot does not exist.')
273
274 try:
275 results = artifacts.BundleSimpleChromeArtifacts(chroot, sysroot,
276 build_target, output_dir)
277 except artifacts.Error as e:
278 cros_build_lib.Die('Error %s raised in BundleSimpleChromeArtifacts: %s',
279 type(e), e)
280
281 for file_name in results:
282 output_proto.artifacts.add().path = file_name
283
284
Alex Klein6504eca2019-04-18 15:37:56 -0600285def BundleVmFiles(input_proto, output_proto):
286 """Tar VM disk and memory files.
287
288 Args:
289 input_proto (SysrootBundleRequest): The input proto.
290 output_proto (BundleResponse): The output proto.
291 """
292 chroot = input_proto.chroot.path
293 sysroot = input_proto.sysroot.path
294 test_results_dir = input_proto.test_results_dir
295 output_dir = input_proto.output_dir
296
297 if not chroot:
298 cros_build_lib.Die('chroot.path is required.')
299 if not sysroot:
300 cros_build_lib.Die('sysroot.path is required.')
301 if not test_results_dir:
302 cros_build_lib.Die('test_results_dir is required.')
303 if not output_dir:
304 cros_build_lib.Die('output_dir is required.')
305
306 # TODO(crbug.com/954344): Replace with a chromite/service implementation.
307 sysroot = sysroot.lstrip(os.sep)
308 result_dir = test_results_dir.lstrip(os.sep)
309 image_dir = os.path.join(chroot, sysroot, result_dir)
310 archives = vm_test_stages.ArchiveVMFilesFromImageDir(image_dir, output_dir)
311 for archive in archives:
312 output_proto.artifacts.add().path = archive