blob: 4f0a8958ddd0b86bdb912655326f6163a3d20835 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2018 The ChromiumOS Authors
Alex Klein2966e302019-01-17 13:29:38 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Image API Service.
6
7The image related API endpoints should generally be found here.
8"""
9
Jack Neus5e56fef2021-06-18 16:57:28 +000010import functools
Alex Kleina0421f32022-09-12 14:39:29 -060011import json
Chris McDonald1672ddb2021-07-21 11:48:23 -060012import logging
Alex Klein2966e302019-01-17 13:29:38 -070013import os
Jack Neus94a35be2021-09-10 15:18:06 +000014from pathlib import Path
Alex Kleina0421f32022-09-12 14:39:29 -060015import time
Josiah Hounyode113e52022-11-30 06:30:33 +000016import traceback
Kevin Shelton5b21c8b2022-04-04 17:04:13 -070017from typing import List, NamedTuple, Set, TYPE_CHECKING, Union
Alex Klein2966e302019-01-17 13:29:38 -070018
Alex Klein8cb365a2019-05-15 16:24:53 -060019from chromite.api import controller
Alex Klein076841b2019-08-29 15:19:39 -060020from chromite.api import faux
Alex Klein2b236722019-06-19 15:44:26 -060021from chromite.api import validate
Alex Kleine9a7dbf2020-10-06 18:12:12 -060022from chromite.api.controller import controller_util
David Burgerb171d652019-05-13 16:07:00 -060023from chromite.api.gen.chromiumos import common_pb2
George Engelbrechtc9a8e812021-06-16 18:14:17 -060024from chromite.lib import build_target_lib
25from chromite.lib import chroot_lib
Alex Klein56355682019-02-07 10:36:54 -070026from chromite.lib import constants
Chris McDonald1672ddb2021-07-21 11:48:23 -060027from chromite.lib import cros_build_lib
Alex Klein56355682019-02-07 10:36:54 -070028from chromite.lib import image_lib
George Engelbrechtc9a8e812021-06-16 18:14:17 -060029from chromite.lib import sysroot_lib
Jack Neus761e1842020-12-01 18:20:11 +000030from chromite.scripts import pushimage
Alex Kleinb7cdbe62019-02-22 11:41:32 -070031from chromite.service import image
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040032from chromite.service import packages as packages_service
Alex Kleine92bc3f2023-05-10 15:28:29 -060033from chromite.utils import timer
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040034
Alex Klein2966e302019-01-17 13:29:38 -070035
Kevin Shelton5b21c8b2022-04-04 17:04:13 -070036if TYPE_CHECKING:
Alex Klein1699fab2022-09-08 08:46:06 -060037 from chromite.api import api_config
38 from chromite.api.gen.chromite.api import image_pb2
Kevin Shelton5b21c8b2022-04-04 17:04:13 -070039
Alex Klein56355682019-02-07 10:36:54 -070040# The image.proto ImageType enum ids.
George Engelbrechtc55d6312021-05-05 12:11:13 -060041_BASE_ID = common_pb2.IMAGE_TYPE_BASE
42_DEV_ID = common_pb2.IMAGE_TYPE_DEV
43_TEST_ID = common_pb2.IMAGE_TYPE_TEST
44_BASE_VM_ID = common_pb2.IMAGE_TYPE_BASE_VM
45_TEST_VM_ID = common_pb2.IMAGE_TYPE_TEST_VM
46_RECOVERY_ID = common_pb2.IMAGE_TYPE_RECOVERY
47_FACTORY_ID = common_pb2.IMAGE_TYPE_FACTORY
48_FIRMWARE_ID = common_pb2.IMAGE_TYPE_FIRMWARE
49_BASE_GUEST_VM_ID = common_pb2.IMAGE_TYPE_BASE_GUEST_VM
50_TEST_GUEST_VM_ID = common_pb2.IMAGE_TYPE_TEST_GUEST_VM
Jack Neus70490b52022-09-12 14:41:45 +000051_NETBOOT_ID = common_pb2.IMAGE_TYPE_NETBOOT
Alex Klein56355682019-02-07 10:36:54 -070052
53# Dict to allow easily translating names to enum ids and vice versa.
54_IMAGE_MAPPING = {
55 _BASE_ID: constants.IMAGE_TYPE_BASE,
56 constants.IMAGE_TYPE_BASE: _BASE_ID,
57 _DEV_ID: constants.IMAGE_TYPE_DEV,
58 constants.IMAGE_TYPE_DEV: _DEV_ID,
59 _TEST_ID: constants.IMAGE_TYPE_TEST,
60 constants.IMAGE_TYPE_TEST: _TEST_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060061 _RECOVERY_ID: constants.IMAGE_TYPE_RECOVERY,
62 constants.IMAGE_TYPE_RECOVERY: _RECOVERY_ID,
Alex Klein9039a952021-07-27 13:52:39 -060063 _FACTORY_ID: constants.IMAGE_TYPE_FACTORY_SHIM,
64 constants.IMAGE_TYPE_FACTORY_SHIM: _FACTORY_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060065 _FIRMWARE_ID: constants.IMAGE_TYPE_FIRMWARE,
66 constants.IMAGE_TYPE_FIRMWARE: _FIRMWARE_ID,
Jack Neus70490b52022-09-12 14:41:45 +000067 _NETBOOT_ID: constants.IMAGE_TYPE_NETBOOT,
68 constants.IMAGE_TYPE_NETBOOT: _NETBOOT_ID,
Alex Klein56355682019-02-07 10:36:54 -070069}
70
George Engelbrecht9f4f8322021-03-08 12:04:17 -070071# Dict to describe the prerequisite built images for each VM image type.
Alex Klein21b95022019-05-09 14:14:46 -060072_VM_IMAGE_MAPPING = {
73 _BASE_VM_ID: _IMAGE_MAPPING[_BASE_ID],
74 _TEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Trent Begin008cade2019-10-31 13:40:59 -060075 _BASE_GUEST_VM_ID: _IMAGE_MAPPING[_BASE_ID],
76 _TEST_GUEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Alex Klein21b95022019-05-09 14:14:46 -060077}
78
George Engelbrecht9f4f8322021-03-08 12:04:17 -070079# Dict to describe the prerequisite built images for each mod image type.
80_MOD_IMAGE_MAPPING = {
81 _RECOVERY_ID: _IMAGE_MAPPING[_BASE_ID],
Jack Neus0829c7e2022-09-15 15:41:57 +000082 _NETBOOT_ID: _IMAGE_MAPPING[_FACTORY_ID],
George Engelbrecht9f4f8322021-03-08 12:04:17 -070083}
84
Jack Neus761e1842020-12-01 18:20:11 +000085# Supported image types for PushImage.
86SUPPORTED_IMAGE_TYPES = {
87 common_pb2.IMAGE_TYPE_RECOVERY: constants.IMAGE_TYPE_RECOVERY,
88 common_pb2.IMAGE_TYPE_FACTORY: constants.IMAGE_TYPE_FACTORY,
89 common_pb2.IMAGE_TYPE_FIRMWARE: constants.IMAGE_TYPE_FIRMWARE,
90 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD: constants.IMAGE_TYPE_ACCESSORY_USBPD,
91 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG: constants.IMAGE_TYPE_ACCESSORY_RWSIG,
Evan Benn4d061102022-02-14 12:50:45 +110092 common_pb2.IMAGE_TYPE_HPS_FIRMWARE: constants.IMAGE_TYPE_HPS_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +000093 common_pb2.IMAGE_TYPE_BASE: constants.IMAGE_TYPE_BASE,
George Engelbrecht9f4f8322021-03-08 12:04:17 -070094 common_pb2.IMAGE_TYPE_GSC_FIRMWARE: constants.IMAGE_TYPE_GSC_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +000095}
96
Alex Klein27978a42021-07-27 14:18:10 -060097# Built image directory symlink names. These names allow specifying a static
98# location for creation to simplify later archival stages. In practice, this
99# sets the symlink argument to build_packages.
100# Core are the build/dev/test images.
101# Use "latest" until we do a better job of passing through image directories,
102# e.g. for artifacts.
Alex Klein1699fab2022-09-08 08:46:06 -0600103LOCATION_CORE = "latest"
Alex Klein27978a42021-07-27 14:18:10 -0600104# The factory_install image.
Alex Klein1699fab2022-09-08 08:46:06 -0600105LOCATION_FACTORY = "factory_shim"
Alex Klein27978a42021-07-27 14:18:10 -0600106
107
108class ImageTypes(NamedTuple):
Alex Klein1699fab2022-09-08 08:46:06 -0600109 """Parsed image types."""
Alex Klein27978a42021-07-27 14:18:10 -0600110
Alex Klein1699fab2022-09-08 08:46:06 -0600111 images: Set[str]
112 vms: Set[int]
113 mod_images: Set[int]
Alex Klein27978a42021-07-27 14:18:10 -0600114
Alex Klein1699fab2022-09-08 08:46:06 -0600115 @property
116 def core_images(self) -> List[str]:
117 """The core images (base/dev/test) as a list."""
118 return list(self.images - {_IMAGE_MAPPING[_FACTORY_ID]}) or []
Alex Klein27978a42021-07-27 14:18:10 -0600119
Alex Klein1699fab2022-09-08 08:46:06 -0600120 @property
121 def has_factory(self) -> bool:
122 """Whether the factory image is present."""
123 return _IMAGE_MAPPING[_FACTORY_ID] in self.images
124
125 @property
126 def factory(self) -> List[str]:
127 """A list with the factory type if set."""
128 return [_IMAGE_MAPPING[_FACTORY_ID]] if self.has_factory else []
Alex Klein27978a42021-07-27 14:18:10 -0600129
Alex Klein56355682019-02-07 10:36:54 -0700130
Alex Klein1699fab2022-09-08 08:46:06 -0600131def _add_image_to_proto(
132 output_proto, path: Union["Path", str], image_type: int, board: str
133):
134 """Quick helper function to add a new image to the output proto."""
135 new_image = output_proto.images.add()
136 new_image.path = str(path)
137 new_image.type = image_type
138 new_image.build_target.name = board
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700139
140
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600141def ExampleGetResponse():
Alex Klein1699fab2022-09-08 08:46:06 -0600142 """Give an example response to assemble upstream in caller artifacts."""
143 uabs = common_pb2.UploadedArtifactsByService
144 cabs = common_pb2.ArtifactsByService
145 return uabs.Sysroot(
146 artifacts=[
147 uabs.Image.ArtifactPaths(
148 artifact_type=cabs.Image.ArtifactType.DLC_IMAGE,
149 paths=[
150 common_pb2.Path(
151 path="/tmp/dlc/dlc.img",
152 location=common_pb2.Path.OUTSIDE,
153 )
154 ],
155 )
156 ]
157 )
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600158
159
Alex Klein1699fab2022-09-08 08:46:06 -0600160def GetArtifacts(
161 in_proto: common_pb2.ArtifactsByService.Image,
162 chroot: chroot_lib.Chroot,
163 sysroot_class: sysroot_lib.Sysroot,
164 build_target: build_target_lib.BuildTarget,
165 output_dir,
166) -> list:
167 """Builds and copies images to specified output_dir.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 Copies (after optionally bundling) all required images into the output_dir,
170 returning a mapping of image type to a list of (output_dir) paths to
Alex Kleinab87ceb2023-01-24 12:00:51 -0700171 the desired files. Note that currently it is only processing one image
172 (DLC), but the future direction is to process all required images. Required
173 images are located within output_artifact.artifact_type.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600174
Alex Klein1699fab2022-09-08 08:46:06 -0600175 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600176 in_proto: Proto request defining reqs.
177 chroot: The chroot proto used for these artifacts.
178 sysroot_class: The sysroot proto used for these artifacts.
179 build_target: The build target used for these artifacts.
180 output_dir: The path to write artifacts to.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600181
Alex Klein1699fab2022-09-08 08:46:06 -0600182 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600183 A list of dictionary mappings of ArtifactType to list of paths.
Alex Klein1699fab2022-09-08 08:46:06 -0600184 """
185 base_path = chroot.full_path(sysroot_class.path)
186 board = build_target.name
187 factory_shim_location = Path(
188 image_lib.GetLatestImageLink(board, pointer=LOCATION_FACTORY)
189 )
Jack Neus5e56fef2021-06-18 16:57:28 +0000190
Alex Klein1699fab2022-09-08 08:46:06 -0600191 generated = []
192 dlc_func = functools.partial(image.copy_dlc_image, base_path)
193 license_func = functools.partial(
194 image.copy_license_credits, board, symlink=LOCATION_CORE
195 )
196 factory_image_func = functools.partial(
197 image.create_factory_image_zip,
198 chroot,
199 sysroot_class,
200 factory_shim_location,
201 packages_service.determine_full_version(),
202 )
203 stripped_packags_func = functools.partial(
204 image.create_stripped_packages_tar,
205 chroot,
206 build_target,
207 )
Jack Neus91846002022-12-01 23:49:22 +0000208 image_scripts_func = functools.partial(
209 image.create_image_scripts_archive, build_target
210 )
211
Alex Klein1699fab2022-09-08 08:46:06 -0600212 artifact_types = {
213 in_proto.ArtifactType.DLC_IMAGE: dlc_func,
214 in_proto.ArtifactType.LICENSE_CREDITS: license_func,
215 in_proto.ArtifactType.FACTORY_IMAGE: factory_image_func,
216 in_proto.ArtifactType.STRIPPED_PACKAGES: stripped_packags_func,
Jack Neus91846002022-12-01 23:49:22 +0000217 in_proto.ArtifactType.IMAGE_SCRIPTS: image_scripts_func,
Alex Klein1699fab2022-09-08 08:46:06 -0600218 }
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600219
Alex Klein1699fab2022-09-08 08:46:06 -0600220 for output_artifact in in_proto.output_artifacts:
221 for artifact_type, func in artifact_types.items():
222 if artifact_type in output_artifact.artifact_types:
Josiah Hounyode113e52022-11-30 06:30:33 +0000223 try:
224 result = func(output_dir)
225 except Exception as e:
226 generated.append(
227 {
228 "type": artifact_type,
229 "failed": True,
230 "failure_reason": str(e),
231 }
232 )
233 artifact_name = (
234 common_pb2.ArtifactsByService.Image.ArtifactType.Name(
235 artifact_type
236 )
237 )
238 logging.warning(
239 "%s artifact generation failed with exception %s",
240 artifact_name,
241 e,
242 )
243 logging.warning("traceback:\n%s", traceback.format_exc())
244 continue
Alex Klein1699fab2022-09-08 08:46:06 -0600245 if result:
246 generated.append(
247 {
248 "paths": [result]
249 if isinstance(result, str)
250 else result,
251 "type": artifact_type,
252 }
253 )
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600254
Alex Klein1699fab2022-09-08 08:46:06 -0600255 return generated
Pi-Hsun Shih8d9c8e42021-06-30 03:27:28 +0000256
Alex Kleincaace392021-07-26 14:28:13 -0600257
Michael Mortensen10146cf2019-11-19 19:59:22 -0700258def _CreateResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600259 """Set output_proto success field on a successful Create response."""
260 output_proto.success = True
Michael Mortensen10146cf2019-11-19 19:59:22 -0700261
262
263@faux.success(_CreateResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700264@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600265@validate.require("build_target.name")
Alex Klein231d2da2019-07-22 16:44:45 -0600266@validate.validation_complete
Alex Klein1699fab2022-09-08 08:46:06 -0600267def Create(
268 input_proto: "image_pb2.CreateImageRequest",
269 output_proto: "image_pb2.CreateImageResult",
270 _config: "api_config.ApiConfig",
271):
272 """Build images.
Alex Klein56355682019-02-07 10:36:54 -0700273
Alex Klein1699fab2022-09-08 08:46:06 -0600274 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600275 input_proto: The input message.
276 output_proto: The output message.
277 _config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600278 """
279 board = input_proto.build_target.name
Alex Klein56355682019-02-07 10:36:54 -0700280
Alex Klein1699fab2022-09-08 08:46:06 -0600281 # Build the base image if no images provided.
282 to_build = input_proto.image_types or [_BASE_ID]
Alex Klein56355682019-02-07 10:36:54 -0700283
Alex Klein1699fab2022-09-08 08:46:06 -0600284 image_types = _ParseImagesToCreate(to_build)
285 build_config = _ParseCreateBuildConfig(input_proto)
286 factory_build_config = build_config._replace(
287 symlink=LOCATION_FACTORY, output_dir_suffix=LOCATION_FACTORY
288 )
Alex Klein56355682019-02-07 10:36:54 -0700289
Alex Klein1699fab2022-09-08 08:46:06 -0600290 # Try building the core and factory images.
291 # Sorted isn't really necessary here, but it's much easier to test.
Alex Kleine92bc3f2023-05-10 15:28:29 -0600292 with timer.timer("build-base-dev-test-timer"):
293 core_result = image.Build(
294 board, sorted(image_types.core_images), config=build_config
295 )
Alex Klein1699fab2022-09-08 08:46:06 -0600296 logging.debug("Core Result Images: %s", core_result.images)
Alex Klein56355682019-02-07 10:36:54 -0700297
Alex Kleine92bc3f2023-05-10 15:28:29 -0600298 with timer.timer("factory-timer"):
299 factory_result = image.Build(
300 board, image_types.factory, config=factory_build_config
301 )
Alex Klein1699fab2022-09-08 08:46:06 -0600302 logging.debug("Factory Result Images: %s", factory_result.images)
Will Bradley29a49c22019-10-21 11:50:08 -0600303
Alex Klein1699fab2022-09-08 08:46:06 -0600304 # A successful run will have no images missing, will have run at least one
305 # of the two image sets, and neither attempt errored. The no error condition
306 # should be redundant with no missing images, but is cheap insurance.
307 all_built = core_result.all_built and factory_result.all_built
308 one_ran = core_result.build_run or factory_result.build_run
309 no_errors = not core_result.run_error and not factory_result.run_error
310 output_proto.success = success = all_built and one_ran and no_errors
Will Bradley29a49c22019-10-21 11:50:08 -0600311
Alex Klein1699fab2022-09-08 08:46:06 -0600312 if success:
313 # Success! We need to record the images we built in the output.
314 all_images = {**core_result.images, **factory_result.images}
315 for img_name, img_path in all_images.items():
316 _add_image_to_proto(
317 output_proto, img_path, _IMAGE_MAPPING[img_name], board
318 )
Alex Klein27978a42021-07-27 14:18:10 -0600319
Alex Klein1699fab2022-09-08 08:46:06 -0600320 # Build and record VMs as necessary.
321 for vm_type in image_types.vms:
322 is_test = vm_type in [_TEST_VM_ID, _TEST_GUEST_VM_ID]
323 img_type = _IMAGE_MAPPING[_TEST_ID if is_test else _BASE_ID]
324 img_dir = core_result.images[img_type].parent.resolve()
Alex Kleine92bc3f2023-05-10 15:28:29 -0600325 with timer.timer(f"vm-image-{vm_type}-timer"):
326 try:
327 if vm_type in [_BASE_GUEST_VM_ID, _TEST_GUEST_VM_ID]:
328 vm_path = image.CreateGuestVm(
Brian Norrisd0a8e6a2023-07-24 16:00:07 -0700329 image_dir=img_dir, is_test=is_test
Alex Kleine92bc3f2023-05-10 15:28:29 -0600330 )
331 else:
332 vm_path = image.CreateVm(
333 board,
334 disk_layout=build_config.disk_layout,
335 is_test=is_test,
336 image_dir=img_dir,
337 )
338 except image.ImageToVmError as e:
339 cros_build_lib.Die(e)
Will Bradley29a49c22019-10-21 11:50:08 -0600340
Alex Klein1699fab2022-09-08 08:46:06 -0600341 _add_image_to_proto(output_proto, vm_path, vm_type, board)
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700342
Alex Klein1699fab2022-09-08 08:46:06 -0600343 # Build and record any mod images.
344 for mod_type in image_types.mod_images:
345 if mod_type == _RECOVERY_ID:
346 base_image_path = core_result.images[constants.IMAGE_TYPE_BASE]
347 # For ChromeOS Flex special case.
Alex Kleine92bc3f2023-05-10 15:28:29 -0600348 with timer.timer("recovery-image-timer"):
349 if build_config.base_is_recovery:
350 result = image.CopyBaseToRecovery(
351 board=board, image_path=base_image_path
352 )
353 else:
354 result = image.BuildRecoveryImage(
355 board=board, image_path=base_image_path
356 )
Alex Klein1699fab2022-09-08 08:46:06 -0600357 if result.all_built:
358 _add_image_to_proto(
359 output_proto,
360 result.images[_IMAGE_MAPPING[mod_type]],
361 mod_type,
362 board,
363 )
364 else:
365 cros_build_lib.Die("Failed to create recovery image.")
Jack Neus70490b52022-09-12 14:41:45 +0000366 elif mod_type == _NETBOOT_ID:
Madeleine Hardtf0a92fc2022-09-19 18:41:12 +0000367 factory_shim_dir = os.path.dirname(
Jack Neus70490b52022-09-12 14:41:45 +0000368 factory_result.images[constants.IMAGE_TYPE_FACTORY_SHIM]
369 )
Alex Kleine92bc3f2023-05-10 15:28:29 -0600370 with timer.timer("netboot-kernel-timer"):
371 try:
372 image.create_netboot_kernel(board, factory_shim_dir)
373 except cros_build_lib.RunCommandError as e:
374 logging.warning(e)
Alex Klein1699fab2022-09-08 08:46:06 -0600375 else:
Jack Neus70490b52022-09-12 14:41:45 +0000376 cros_build_lib.Die(
377 "_RECOVERY_ID and _NETBOOT_ID are the only mod_image_type."
378 )
Will Bradley29a49c22019-10-21 11:50:08 -0600379
Alex Klein1699fab2022-09-08 08:46:06 -0600380 # Read metric events log and pipe them into output_proto.events.
Alex Kleina0421f32022-09-12 14:39:29 -0600381 if core_result.build_run and core_result.output_dir:
382 _parse_img_metrics_to_response(
383 output_proto, board, core_result.output_dir
384 )
Alex Klein1699fab2022-09-08 08:46:06 -0600385 return controller.RETURN_CODE_SUCCESS
Will Bradley29a49c22019-10-21 11:50:08 -0600386
Alex Klein1699fab2022-09-08 08:46:06 -0600387 else:
Alex Kleinab87ceb2023-01-24 12:00:51 -0700388 # Failure, include all the failed packages in the output when available.
Alex Klein1699fab2022-09-08 08:46:06 -0600389 packages = core_result.failed_packages + factory_result.failed_packages
390 if not packages:
391 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Alex Klein2557b4f2019-07-11 14:34:00 -0600392
Alex Klein1699fab2022-09-08 08:46:06 -0600393 for package in packages:
394 current = output_proto.failed_packages.add()
395 controller_util.serialize_package_info(package, current)
Alex Klein1bcd9882019-03-19 13:25:24 -0600396
Alex Klein1699fab2022-09-08 08:46:06 -0600397 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
Alex Klein1bcd9882019-03-19 13:25:24 -0600398
Alex Kleincaace392021-07-26 14:28:13 -0600399
Alex Kleina0421f32022-09-12 14:39:29 -0600400def _parse_img_metrics_to_response(
401 output: "image_pb2.CreateImageResult", board: str, build_path: Path
402):
403 """Manually translate the package sizes file to the metrics events.
404
405 This is a temporary hack to manually translate the package sizes file to
406 the metrics output because the metrics library does not reliably transmit
407 the data. This can be removed once we switch over to the new metrics
408 pipeline.
409 """
410 filename = build_path / f"{constants.BASE_IMAGE_BIN}-package-sizes.json"
411 if not filename.exists():
412 logging.error("Package sizes file does not exist.")
413 return
414
415 size_data = json.loads(filename.read_text(encoding="utf-8"))
416
417 ts = int(round(time.time() * 1000))
418
419 # Total size event.
420 event = output.events.add()
421 event.gauge = size_data["total_size"]
422 event.timestamp_milliseconds = ts
423 event.name = f"{board}.total_size.base.rootfs"
424
425 # Package sizes.
426 for pkg, size in size_data["package_sizes"].items():
427 event = output.events.add()
428 event.gauge = size
429 event.timestamp_milliseconds = ts
430 event.name = f"{board}.package_size.base.rootfs.{pkg}"
431
432
Alex Klein27978a42021-07-27 14:18:10 -0600433def _ParseImagesToCreate(to_build: List[int]) -> ImageTypes:
Alex Klein1699fab2022-09-08 08:46:06 -0600434 """Helper function to parse the image types to build.
Alex Klein21b95022019-05-09 14:14:46 -0600435
Alex Klein1699fab2022-09-08 08:46:06 -0600436 This function expresses the dependencies of each image type and adds
437 the requisite image types if they're not explicitly defined.
Alex Klein21b95022019-05-09 14:14:46 -0600438
Alex Klein1699fab2022-09-08 08:46:06 -0600439 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600440 to_build: The image type list.
Alex Klein21b95022019-05-09 14:14:46 -0600441
Alex Klein1699fab2022-09-08 08:46:06 -0600442 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600443 ImageTypes: The parsed images to build.
Alex Klein1699fab2022-09-08 08:46:06 -0600444 """
445 image_types = set()
446 vm_types = set()
447 mod_image_types = set()
448 for current in to_build:
449 # Find out if it's a special case (vm, img mod), or just any old image.
450 if current in _VM_IMAGE_MAPPING:
451 vm_types.add(current)
452 # Make sure we build the image required to build the VM.
453 image_types.add(_VM_IMAGE_MAPPING[current])
454 elif current in _MOD_IMAGE_MAPPING:
455 mod_image_types.add(current)
456 image_types.add(_MOD_IMAGE_MAPPING[current])
457 elif current in _IMAGE_MAPPING:
458 image_types.add(_IMAGE_MAPPING[current])
459 else:
460 # Not expected, but at least it will be obvious if this comes up.
461 cros_build_lib.Die(
Alex Kleindf8ee502022-10-18 09:48:15 -0600462 "The service's known image types do not match those in "
463 "image.proto. Unknown Enum ID: %s",
464 current,
Alex Klein1699fab2022-09-08 08:46:06 -0600465 )
Alex Klein21b95022019-05-09 14:14:46 -0600466
Alex Klein1699fab2022-09-08 08:46:06 -0600467 # We can only build one type of these images at a time since image_to_vm.sh
468 # uses the default path if a name is not provided.
469 if vm_types.issuperset({_BASE_VM_ID, _TEST_VM_ID}):
470 cros_build_lib.Die("Cannot create more than one VM.")
Alex Klein21b95022019-05-09 14:14:46 -0600471
Alex Klein1699fab2022-09-08 08:46:06 -0600472 return ImageTypes(
473 images=image_types, vms=vm_types, mod_images=mod_image_types
474 )
Alex Klein21b95022019-05-09 14:14:46 -0600475
476
477def _ParseCreateBuildConfig(input_proto):
Alex Klein1699fab2022-09-08 08:46:06 -0600478 """Helper to parse the image build config for Create."""
479 enable_rootfs_verification = not input_proto.disable_rootfs_verification
480 version = input_proto.version or None
481 disk_layout = input_proto.disk_layout or None
482 builder_path = input_proto.builder_path or None
483 base_is_recovery = input_proto.base_is_recovery or False
484 return image.BuildConfig(
485 enable_rootfs_verification=enable_rootfs_verification,
486 replace=True,
487 version=version,
488 disk_layout=disk_layout,
489 builder_path=builder_path,
490 symlink=LOCATION_CORE,
491 base_is_recovery=base_is_recovery,
492 )
Alex Klein21b95022019-05-09 14:14:46 -0600493
Alex Klein1bcd9882019-03-19 13:25:24 -0600494
Alex Klein857b53d2022-10-21 13:53:26 -0600495@faux.all_empty
496@validate.require("build_target.name")
497@validate.validation_complete
498def CreateNetboot(input_proto, _output_proto, _config):
499 """Create a netboot kernel.
500
501 The netboot kernel currently needs network access because it's not building
502 everything in build_packages like other images. Once that has been remedied,
503 using Create to build the netboot kernel will be the expected workflow, and
504 this endpoint will be deprecated (b/255397725).
505 """
506 build_target = controller_util.ParseBuildTarget(input_proto.build_target)
507 if input_proto.factory_shim_path:
508 factory_shim_location = Path(input_proto.factory_shim_path).parent
509 else:
510 factory_shim_location = Path(
511 image_lib.GetLatestImageLink(
512 build_target.name, pointer=LOCATION_FACTORY
513 )
514 )
515 if not factory_shim_location.exists():
516 logging.warning(
517 "Factory shim directory does not exist. Skipping netboot creation."
518 )
519 return
520
521 image.create_netboot_kernel(build_target.name, str(factory_shim_location))
522
523
Michael Mortensen10146cf2019-11-19 19:59:22 -0700524def _SignerTestResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600525 """Set output_proto success field on a successful SignerTest response."""
526 output_proto.success = True
527 return controller.RETURN_CODE_SUCCESS
Michael Mortensen10146cf2019-11-19 19:59:22 -0700528
529
530@faux.success(_SignerTestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700531@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600532@validate.exists("image.path")
Alex Klein231d2da2019-07-22 16:44:45 -0600533@validate.validation_complete
Alex Klein1699fab2022-09-08 08:46:06 -0600534def SignerTest(
535 input_proto: "image_pb2.ImageTestRequest",
536 output_proto: "image_pb2.ImageTestRequest",
537 _config: "api_config.ApiConfig",
538):
539 """Run image tests.
Michael Mortensenc83c9952019-08-05 12:15:12 -0600540
Alex Klein1699fab2022-09-08 08:46:06 -0600541 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600542 input_proto: The input message.
543 output_proto: The output message.
544 _config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600545 """
546 image_path = input_proto.image.path
Michael Mortensenc83c9952019-08-05 12:15:12 -0600547
Alex Klein1699fab2022-09-08 08:46:06 -0600548 result = image_lib.SecurityTest(image=image_path)
549 output_proto.success = result
550 if result:
551 return controller.RETURN_CODE_SUCCESS
552 else:
553 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Michael Mortensenc83c9952019-08-05 12:15:12 -0600554
Alex Klein076841b2019-08-29 15:19:39 -0600555
Michael Mortensen10146cf2019-11-19 19:59:22 -0700556def _TestResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600557 """Set output_proto success field on a successful Test response."""
558 output_proto.success = True
559 return controller.RETURN_CODE_SUCCESS
Michael Mortensen10146cf2019-11-19 19:59:22 -0700560
561
562@faux.success(_TestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700563@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600564@validate.require("build_target.name", "result.directory")
565@validate.exists("image.path")
566def Test(
567 input_proto: "image_pb2.ImageTestRequest",
568 output_proto: "image_pb2.ImageTestResult",
569 config: "api_config.ApiConfig",
570):
571 """Run image tests.
Alex Klein2966e302019-01-17 13:29:38 -0700572
Alex Klein1699fab2022-09-08 08:46:06 -0600573 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600574 input_proto: The input message.
575 output_proto: The output message.
576 config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600577 """
578 image_path = input_proto.image.path
579 board = input_proto.build_target.name
580 result_directory = input_proto.result.directory
Alex Klein2966e302019-01-17 13:29:38 -0700581
Alex Klein1699fab2022-09-08 08:46:06 -0600582 if not os.path.isfile(image_path) or not image_path.endswith(".bin"):
583 cros_build_lib.Die(
Alex Kleinab87ceb2023-01-24 12:00:51 -0700584 "The image.path must be an existing image file with a .bin "
585 "extension."
Alex Klein1699fab2022-09-08 08:46:06 -0600586 )
Alex Klein2966e302019-01-17 13:29:38 -0700587
Alex Klein1699fab2022-09-08 08:46:06 -0600588 if config.validate_only:
589 return controller.RETURN_CODE_VALID_INPUT
Alex Klein231d2da2019-07-22 16:44:45 -0600590
Alex Klein1699fab2022-09-08 08:46:06 -0600591 success = image.Test(board, result_directory, image_dir=image_path)
592 output_proto.success = success
Alex Klein8cb365a2019-05-15 16:24:53 -0600593
Alex Klein1699fab2022-09-08 08:46:06 -0600594 if success:
595 return controller.RETURN_CODE_SUCCESS
596 else:
597 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Jack Neus761e1842020-12-01 18:20:11 +0000598
599
600@faux.empty_success
601@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600602@validate.require("gs_image_dir", "sysroot.build_target.name")
603def PushImage(
604 input_proto: "image_pb2.PushImageRequest",
605 _output_proto: "image_pb2.PushImageResponse",
606 config: "api.config.ApiConfig",
607):
608 """Push artifacts from the archive bucket to the release bucket.
Jack Neus761e1842020-12-01 18:20:11 +0000609
Alex Klein1699fab2022-09-08 08:46:06 -0600610 Wraps chromite/scripts/pushimage.py.
Jack Neus761e1842020-12-01 18:20:11 +0000611
Alex Klein1699fab2022-09-08 08:46:06 -0600612 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600613 input_proto: Input proto.
614 _output_proto: Output proto.
615 config: The API call config.
Jack Neus761e1842020-12-01 18:20:11 +0000616
Alex Klein1699fab2022-09-08 08:46:06 -0600617 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600618 A controller return code (e.g. controller.RETURN_CODE_SUCCESS).
Alex Klein1699fab2022-09-08 08:46:06 -0600619 """
620 sign_types = []
621 if input_proto.sign_types:
622 for sign_type in input_proto.sign_types:
623 if sign_type not in SUPPORTED_IMAGE_TYPES:
624 logging.error("unsupported sign type %g", sign_type)
625 return controller.RETURN_CODE_INVALID_INPUT
626 sign_types.append(SUPPORTED_IMAGE_TYPES[sign_type])
Jack Neus761e1842020-12-01 18:20:11 +0000627
Alex Klein1699fab2022-09-08 08:46:06 -0600628 # If configured for validation only we're done here.
629 if config.validate_only:
630 return controller.RETURN_CODE_VALID_INPUT
Jack Neus761e1842020-12-01 18:20:11 +0000631
Alex Klein1699fab2022-09-08 08:46:06 -0600632 kwargs = {}
633 if input_proto.profile.name:
634 kwargs["profile"] = input_proto.profile.name
635 if input_proto.dest_bucket:
636 kwargs["dest_bucket"] = input_proto.dest_bucket
637 if input_proto.channels:
638 kwargs["force_channels"] = [
639 common_pb2.Channel.Name(channel).lower()[len("channel_") :]
640 for channel in input_proto.channels
641 ]
642 try:
643 channel_to_uris = pushimage.PushImage(
644 input_proto.gs_image_dir,
645 input_proto.sysroot.build_target.name,
Mike Frysinger9d1253c2023-02-02 09:00:07 -0500646 dryrun=input_proto.dryrun,
Alex Klein1699fab2022-09-08 08:46:06 -0600647 sign_types=sign_types,
648 **kwargs,
649 )
650 except Exception:
651 logging.error("PushImage failed: ", exc_info=True)
652 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
653 if channel_to_uris:
654 for uris in channel_to_uris.values():
655 for uri in uris:
656 _output_proto.instructions.add().instructions_file_path = uri
657 return controller.RETURN_CODE_SUCCESS