blob: a510434ee868469d18b3212d169ba8160361dac1 [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
33
Alex Klein2966e302019-01-17 13:29:38 -070034
Kevin Shelton5b21c8b2022-04-04 17:04:13 -070035if TYPE_CHECKING:
Alex Klein1699fab2022-09-08 08:46:06 -060036 from chromite.api import api_config
37 from chromite.api.gen.chromite.api import image_pb2
Kevin Shelton5b21c8b2022-04-04 17:04:13 -070038
Alex Klein56355682019-02-07 10:36:54 -070039# The image.proto ImageType enum ids.
George Engelbrechtc55d6312021-05-05 12:11:13 -060040_BASE_ID = common_pb2.IMAGE_TYPE_BASE
41_DEV_ID = common_pb2.IMAGE_TYPE_DEV
42_TEST_ID = common_pb2.IMAGE_TYPE_TEST
43_BASE_VM_ID = common_pb2.IMAGE_TYPE_BASE_VM
44_TEST_VM_ID = common_pb2.IMAGE_TYPE_TEST_VM
45_RECOVERY_ID = common_pb2.IMAGE_TYPE_RECOVERY
46_FACTORY_ID = common_pb2.IMAGE_TYPE_FACTORY
47_FIRMWARE_ID = common_pb2.IMAGE_TYPE_FIRMWARE
48_BASE_GUEST_VM_ID = common_pb2.IMAGE_TYPE_BASE_GUEST_VM
49_TEST_GUEST_VM_ID = common_pb2.IMAGE_TYPE_TEST_GUEST_VM
Jack Neus70490b52022-09-12 14:41:45 +000050_NETBOOT_ID = common_pb2.IMAGE_TYPE_NETBOOT
Alex Klein56355682019-02-07 10:36:54 -070051
52# Dict to allow easily translating names to enum ids and vice versa.
53_IMAGE_MAPPING = {
54 _BASE_ID: constants.IMAGE_TYPE_BASE,
55 constants.IMAGE_TYPE_BASE: _BASE_ID,
56 _DEV_ID: constants.IMAGE_TYPE_DEV,
57 constants.IMAGE_TYPE_DEV: _DEV_ID,
58 _TEST_ID: constants.IMAGE_TYPE_TEST,
59 constants.IMAGE_TYPE_TEST: _TEST_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060060 _RECOVERY_ID: constants.IMAGE_TYPE_RECOVERY,
61 constants.IMAGE_TYPE_RECOVERY: _RECOVERY_ID,
Alex Klein9039a952021-07-27 13:52:39 -060062 _FACTORY_ID: constants.IMAGE_TYPE_FACTORY_SHIM,
63 constants.IMAGE_TYPE_FACTORY_SHIM: _FACTORY_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060064 _FIRMWARE_ID: constants.IMAGE_TYPE_FIRMWARE,
65 constants.IMAGE_TYPE_FIRMWARE: _FIRMWARE_ID,
Jack Neus70490b52022-09-12 14:41:45 +000066 _NETBOOT_ID: constants.IMAGE_TYPE_NETBOOT,
67 constants.IMAGE_TYPE_NETBOOT: _NETBOOT_ID,
Alex Klein56355682019-02-07 10:36:54 -070068}
69
George Engelbrecht9f4f8322021-03-08 12:04:17 -070070# Dict to describe the prerequisite built images for each VM image type.
Alex Klein21b95022019-05-09 14:14:46 -060071_VM_IMAGE_MAPPING = {
72 _BASE_VM_ID: _IMAGE_MAPPING[_BASE_ID],
73 _TEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Trent Begin008cade2019-10-31 13:40:59 -060074 _BASE_GUEST_VM_ID: _IMAGE_MAPPING[_BASE_ID],
75 _TEST_GUEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Alex Klein21b95022019-05-09 14:14:46 -060076}
77
George Engelbrecht9f4f8322021-03-08 12:04:17 -070078# Dict to describe the prerequisite built images for each mod image type.
79_MOD_IMAGE_MAPPING = {
80 _RECOVERY_ID: _IMAGE_MAPPING[_BASE_ID],
Jack Neus0829c7e2022-09-15 15:41:57 +000081 _NETBOOT_ID: _IMAGE_MAPPING[_FACTORY_ID],
George Engelbrecht9f4f8322021-03-08 12:04:17 -070082}
83
Jack Neus761e1842020-12-01 18:20:11 +000084# Supported image types for PushImage.
85SUPPORTED_IMAGE_TYPES = {
86 common_pb2.IMAGE_TYPE_RECOVERY: constants.IMAGE_TYPE_RECOVERY,
87 common_pb2.IMAGE_TYPE_FACTORY: constants.IMAGE_TYPE_FACTORY,
88 common_pb2.IMAGE_TYPE_FIRMWARE: constants.IMAGE_TYPE_FIRMWARE,
89 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD: constants.IMAGE_TYPE_ACCESSORY_USBPD,
90 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG: constants.IMAGE_TYPE_ACCESSORY_RWSIG,
Evan Benn4d061102022-02-14 12:50:45 +110091 common_pb2.IMAGE_TYPE_HPS_FIRMWARE: constants.IMAGE_TYPE_HPS_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +000092 common_pb2.IMAGE_TYPE_BASE: constants.IMAGE_TYPE_BASE,
George Engelbrecht9f4f8322021-03-08 12:04:17 -070093 common_pb2.IMAGE_TYPE_GSC_FIRMWARE: constants.IMAGE_TYPE_GSC_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +000094}
95
Alex Klein27978a42021-07-27 14:18:10 -060096# Built image directory symlink names. These names allow specifying a static
97# location for creation to simplify later archival stages. In practice, this
98# sets the symlink argument to build_packages.
99# Core are the build/dev/test images.
100# Use "latest" until we do a better job of passing through image directories,
101# e.g. for artifacts.
Alex Klein1699fab2022-09-08 08:46:06 -0600102LOCATION_CORE = "latest"
Alex Klein27978a42021-07-27 14:18:10 -0600103# The factory_install image.
Alex Klein1699fab2022-09-08 08:46:06 -0600104LOCATION_FACTORY = "factory_shim"
Alex Klein27978a42021-07-27 14:18:10 -0600105
106
107class ImageTypes(NamedTuple):
Alex Klein1699fab2022-09-08 08:46:06 -0600108 """Parsed image types."""
Alex Klein27978a42021-07-27 14:18:10 -0600109
Alex Klein1699fab2022-09-08 08:46:06 -0600110 images: Set[str]
111 vms: Set[int]
112 mod_images: Set[int]
Alex Klein27978a42021-07-27 14:18:10 -0600113
Alex Klein1699fab2022-09-08 08:46:06 -0600114 @property
115 def core_images(self) -> List[str]:
116 """The core images (base/dev/test) as a list."""
117 return list(self.images - {_IMAGE_MAPPING[_FACTORY_ID]}) or []
Alex Klein27978a42021-07-27 14:18:10 -0600118
Alex Klein1699fab2022-09-08 08:46:06 -0600119 @property
120 def has_factory(self) -> bool:
121 """Whether the factory image is present."""
122 return _IMAGE_MAPPING[_FACTORY_ID] in self.images
123
124 @property
125 def factory(self) -> List[str]:
126 """A list with the factory type if set."""
127 return [_IMAGE_MAPPING[_FACTORY_ID]] if self.has_factory else []
Alex Klein27978a42021-07-27 14:18:10 -0600128
Alex Klein56355682019-02-07 10:36:54 -0700129
Alex Klein1699fab2022-09-08 08:46:06 -0600130def _add_image_to_proto(
131 output_proto, path: Union["Path", str], image_type: int, board: str
132):
133 """Quick helper function to add a new image to the output proto."""
134 new_image = output_proto.images.add()
135 new_image.path = str(path)
136 new_image.type = image_type
137 new_image.build_target.name = board
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700138
139
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600140def ExampleGetResponse():
Alex Klein1699fab2022-09-08 08:46:06 -0600141 """Give an example response to assemble upstream in caller artifacts."""
142 uabs = common_pb2.UploadedArtifactsByService
143 cabs = common_pb2.ArtifactsByService
144 return uabs.Sysroot(
145 artifacts=[
146 uabs.Image.ArtifactPaths(
147 artifact_type=cabs.Image.ArtifactType.DLC_IMAGE,
148 paths=[
149 common_pb2.Path(
150 path="/tmp/dlc/dlc.img",
151 location=common_pb2.Path.OUTSIDE,
152 )
153 ],
154 )
155 ]
156 )
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600157
158
Alex Klein1699fab2022-09-08 08:46:06 -0600159def GetArtifacts(
160 in_proto: common_pb2.ArtifactsByService.Image,
161 chroot: chroot_lib.Chroot,
162 sysroot_class: sysroot_lib.Sysroot,
163 build_target: build_target_lib.BuildTarget,
164 output_dir,
165) -> list:
166 """Builds and copies images to specified output_dir.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600167
Alex Klein1699fab2022-09-08 08:46:06 -0600168 Copies (after optionally bundling) all required images into the output_dir,
169 returning a mapping of image type to a list of (output_dir) paths to
170 the desired files. Note that currently it is only processing one image (DLC),
171 but the future direction is to process all required images. Required images
172 are located within output_artifact.artifact_type.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600173
Alex Klein1699fab2022-09-08 08:46:06 -0600174 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600175 in_proto: Proto request defining reqs.
176 chroot: The chroot proto used for these artifacts.
177 sysroot_class: The sysroot proto used for these artifacts.
178 build_target: The build target used for these artifacts.
179 output_dir: The path to write artifacts to.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600180
Alex Klein1699fab2022-09-08 08:46:06 -0600181 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600182 A list of dictionary mappings of ArtifactType to list of paths.
Alex Klein1699fab2022-09-08 08:46:06 -0600183 """
184 base_path = chroot.full_path(sysroot_class.path)
185 board = build_target.name
186 factory_shim_location = Path(
187 image_lib.GetLatestImageLink(board, pointer=LOCATION_FACTORY)
188 )
Jack Neus5e56fef2021-06-18 16:57:28 +0000189
Alex Klein1699fab2022-09-08 08:46:06 -0600190 generated = []
191 dlc_func = functools.partial(image.copy_dlc_image, base_path)
192 license_func = functools.partial(
193 image.copy_license_credits, board, symlink=LOCATION_CORE
194 )
195 factory_image_func = functools.partial(
196 image.create_factory_image_zip,
197 chroot,
198 sysroot_class,
199 factory_shim_location,
200 packages_service.determine_full_version(),
201 )
202 stripped_packags_func = functools.partial(
203 image.create_stripped_packages_tar,
204 chroot,
205 build_target,
206 )
207 artifact_types = {
208 in_proto.ArtifactType.DLC_IMAGE: dlc_func,
209 in_proto.ArtifactType.LICENSE_CREDITS: license_func,
210 in_proto.ArtifactType.FACTORY_IMAGE: factory_image_func,
211 in_proto.ArtifactType.STRIPPED_PACKAGES: stripped_packags_func,
212 }
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600213
Alex Klein1699fab2022-09-08 08:46:06 -0600214 for output_artifact in in_proto.output_artifacts:
215 for artifact_type, func in artifact_types.items():
216 if artifact_type in output_artifact.artifact_types:
Josiah Hounyode113e52022-11-30 06:30:33 +0000217 try:
218 result = func(output_dir)
219 except Exception as e:
220 generated.append(
221 {
222 "type": artifact_type,
223 "failed": True,
224 "failure_reason": str(e),
225 }
226 )
227 artifact_name = (
228 common_pb2.ArtifactsByService.Image.ArtifactType.Name(
229 artifact_type
230 )
231 )
232 logging.warning(
233 "%s artifact generation failed with exception %s",
234 artifact_name,
235 e,
236 )
237 logging.warning("traceback:\n%s", traceback.format_exc())
238 continue
Alex Klein1699fab2022-09-08 08:46:06 -0600239 if result:
240 generated.append(
241 {
242 "paths": [result]
243 if isinstance(result, str)
244 else result,
245 "type": artifact_type,
246 }
247 )
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600248
Alex Klein1699fab2022-09-08 08:46:06 -0600249 return generated
Pi-Hsun Shih8d9c8e42021-06-30 03:27:28 +0000250
Alex Kleincaace392021-07-26 14:28:13 -0600251
Michael Mortensen10146cf2019-11-19 19:59:22 -0700252def _CreateResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600253 """Set output_proto success field on a successful Create response."""
254 output_proto.success = True
Michael Mortensen10146cf2019-11-19 19:59:22 -0700255
256
257@faux.success(_CreateResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700258@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600259@validate.require("build_target.name")
Alex Klein231d2da2019-07-22 16:44:45 -0600260@validate.validation_complete
Alex Klein1699fab2022-09-08 08:46:06 -0600261def Create(
262 input_proto: "image_pb2.CreateImageRequest",
263 output_proto: "image_pb2.CreateImageResult",
264 _config: "api_config.ApiConfig",
265):
266 """Build images.
Alex Klein56355682019-02-07 10:36:54 -0700267
Alex Klein1699fab2022-09-08 08:46:06 -0600268 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600269 input_proto: The input message.
270 output_proto: The output message.
271 _config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600272 """
273 board = input_proto.build_target.name
Alex Klein56355682019-02-07 10:36:54 -0700274
Alex Klein1699fab2022-09-08 08:46:06 -0600275 # Build the base image if no images provided.
276 to_build = input_proto.image_types or [_BASE_ID]
Alex Klein56355682019-02-07 10:36:54 -0700277
Alex Klein1699fab2022-09-08 08:46:06 -0600278 image_types = _ParseImagesToCreate(to_build)
279 build_config = _ParseCreateBuildConfig(input_proto)
280 factory_build_config = build_config._replace(
281 symlink=LOCATION_FACTORY, output_dir_suffix=LOCATION_FACTORY
282 )
Alex Klein56355682019-02-07 10:36:54 -0700283
Alex Klein1699fab2022-09-08 08:46:06 -0600284 # Try building the core and factory images.
285 # Sorted isn't really necessary here, but it's much easier to test.
286 core_result = image.Build(
287 board, sorted(image_types.core_images), config=build_config
288 )
289 logging.debug("Core Result Images: %s", core_result.images)
Alex Klein56355682019-02-07 10:36:54 -0700290
Alex Klein1699fab2022-09-08 08:46:06 -0600291 factory_result = image.Build(
292 board, image_types.factory, config=factory_build_config
293 )
294 logging.debug("Factory Result Images: %s", factory_result.images)
Will Bradley29a49c22019-10-21 11:50:08 -0600295
Alex Klein1699fab2022-09-08 08:46:06 -0600296 # A successful run will have no images missing, will have run at least one
297 # of the two image sets, and neither attempt errored. The no error condition
298 # should be redundant with no missing images, but is cheap insurance.
299 all_built = core_result.all_built and factory_result.all_built
300 one_ran = core_result.build_run or factory_result.build_run
301 no_errors = not core_result.run_error and not factory_result.run_error
302 output_proto.success = success = all_built and one_ran and no_errors
Will Bradley29a49c22019-10-21 11:50:08 -0600303
Alex Klein1699fab2022-09-08 08:46:06 -0600304 if success:
305 # Success! We need to record the images we built in the output.
306 all_images = {**core_result.images, **factory_result.images}
307 for img_name, img_path in all_images.items():
308 _add_image_to_proto(
309 output_proto, img_path, _IMAGE_MAPPING[img_name], board
310 )
Alex Klein27978a42021-07-27 14:18:10 -0600311
Alex Klein1699fab2022-09-08 08:46:06 -0600312 # Build and record VMs as necessary.
313 for vm_type in image_types.vms:
314 is_test = vm_type in [_TEST_VM_ID, _TEST_GUEST_VM_ID]
315 img_type = _IMAGE_MAPPING[_TEST_ID if is_test else _BASE_ID]
316 img_dir = core_result.images[img_type].parent.resolve()
317 try:
318 if vm_type in [_BASE_GUEST_VM_ID, _TEST_GUEST_VM_ID]:
319 vm_path = image.CreateGuestVm(
320 board, is_test=is_test, image_dir=img_dir
321 )
322 else:
323 vm_path = image.CreateVm(
324 board,
325 disk_layout=build_config.disk_layout,
326 is_test=is_test,
327 image_dir=img_dir,
328 )
329 except image.ImageToVmError as e:
330 cros_build_lib.Die(e)
Will Bradley29a49c22019-10-21 11:50:08 -0600331
Alex Klein1699fab2022-09-08 08:46:06 -0600332 _add_image_to_proto(output_proto, vm_path, vm_type, board)
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700333
Alex Klein1699fab2022-09-08 08:46:06 -0600334 # Build and record any mod images.
335 for mod_type in image_types.mod_images:
336 if mod_type == _RECOVERY_ID:
337 base_image_path = core_result.images[constants.IMAGE_TYPE_BASE]
338 # For ChromeOS Flex special case.
339 if build_config.base_is_recovery:
340 result = image.CopyBaseToRecovery(
341 board=board, image_path=base_image_path
342 )
343 else:
344 result = image.BuildRecoveryImage(
345 board=board, image_path=base_image_path
346 )
347 if result.all_built:
348 _add_image_to_proto(
349 output_proto,
350 result.images[_IMAGE_MAPPING[mod_type]],
351 mod_type,
352 board,
353 )
354 else:
355 cros_build_lib.Die("Failed to create recovery image.")
Jack Neus70490b52022-09-12 14:41:45 +0000356 elif mod_type == _NETBOOT_ID:
Madeleine Hardtf0a92fc2022-09-19 18:41:12 +0000357 factory_shim_dir = os.path.dirname(
Jack Neus70490b52022-09-12 14:41:45 +0000358 factory_result.images[constants.IMAGE_TYPE_FACTORY_SHIM]
359 )
Alex Klein857b53d2022-10-21 13:53:26 -0600360 try:
361 image.create_netboot_kernel(board, factory_shim_dir)
362 except cros_build_lib.RunCommandError as e:
363 logging.warning(e)
Alex Klein1699fab2022-09-08 08:46:06 -0600364 else:
Jack Neus70490b52022-09-12 14:41:45 +0000365 cros_build_lib.Die(
366 "_RECOVERY_ID and _NETBOOT_ID are the only mod_image_type."
367 )
Will Bradley29a49c22019-10-21 11:50:08 -0600368
Alex Klein1699fab2022-09-08 08:46:06 -0600369 # Read metric events log and pipe them into output_proto.events.
Alex Kleina0421f32022-09-12 14:39:29 -0600370 if core_result.build_run and core_result.output_dir:
371 _parse_img_metrics_to_response(
372 output_proto, board, core_result.output_dir
373 )
Alex Klein1699fab2022-09-08 08:46:06 -0600374 return controller.RETURN_CODE_SUCCESS
Will Bradley29a49c22019-10-21 11:50:08 -0600375
Alex Klein1699fab2022-09-08 08:46:06 -0600376 else:
377 # Failure, include all of the failed packages in the output when available.
378 packages = core_result.failed_packages + factory_result.failed_packages
379 if not packages:
380 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Alex Klein2557b4f2019-07-11 14:34:00 -0600381
Alex Klein1699fab2022-09-08 08:46:06 -0600382 for package in packages:
383 current = output_proto.failed_packages.add()
384 controller_util.serialize_package_info(package, current)
Alex Klein1bcd9882019-03-19 13:25:24 -0600385
Alex Klein1699fab2022-09-08 08:46:06 -0600386 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
Alex Klein1bcd9882019-03-19 13:25:24 -0600387
Alex Kleincaace392021-07-26 14:28:13 -0600388
Alex Kleina0421f32022-09-12 14:39:29 -0600389def _parse_img_metrics_to_response(
390 output: "image_pb2.CreateImageResult", board: str, build_path: Path
391):
392 """Manually translate the package sizes file to the metrics events.
393
394 This is a temporary hack to manually translate the package sizes file to
395 the metrics output because the metrics library does not reliably transmit
396 the data. This can be removed once we switch over to the new metrics
397 pipeline.
398 """
399 filename = build_path / f"{constants.BASE_IMAGE_BIN}-package-sizes.json"
400 if not filename.exists():
401 logging.error("Package sizes file does not exist.")
402 return
403
404 size_data = json.loads(filename.read_text(encoding="utf-8"))
405
406 ts = int(round(time.time() * 1000))
407
408 # Total size event.
409 event = output.events.add()
410 event.gauge = size_data["total_size"]
411 event.timestamp_milliseconds = ts
412 event.name = f"{board}.total_size.base.rootfs"
413
414 # Package sizes.
415 for pkg, size in size_data["package_sizes"].items():
416 event = output.events.add()
417 event.gauge = size
418 event.timestamp_milliseconds = ts
419 event.name = f"{board}.package_size.base.rootfs.{pkg}"
420
421
Alex Klein27978a42021-07-27 14:18:10 -0600422def _ParseImagesToCreate(to_build: List[int]) -> ImageTypes:
Alex Klein1699fab2022-09-08 08:46:06 -0600423 """Helper function to parse the image types to build.
Alex Klein21b95022019-05-09 14:14:46 -0600424
Alex Klein1699fab2022-09-08 08:46:06 -0600425 This function expresses the dependencies of each image type and adds
426 the requisite image types if they're not explicitly defined.
Alex Klein21b95022019-05-09 14:14:46 -0600427
Alex Klein1699fab2022-09-08 08:46:06 -0600428 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600429 to_build: The image type list.
Alex Klein21b95022019-05-09 14:14:46 -0600430
Alex Klein1699fab2022-09-08 08:46:06 -0600431 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600432 ImageTypes: The parsed images to build.
Alex Klein1699fab2022-09-08 08:46:06 -0600433 """
434 image_types = set()
435 vm_types = set()
436 mod_image_types = set()
437 for current in to_build:
438 # Find out if it's a special case (vm, img mod), or just any old image.
439 if current in _VM_IMAGE_MAPPING:
440 vm_types.add(current)
441 # Make sure we build the image required to build the VM.
442 image_types.add(_VM_IMAGE_MAPPING[current])
443 elif current in _MOD_IMAGE_MAPPING:
444 mod_image_types.add(current)
445 image_types.add(_MOD_IMAGE_MAPPING[current])
446 elif current in _IMAGE_MAPPING:
447 image_types.add(_IMAGE_MAPPING[current])
448 else:
449 # Not expected, but at least it will be obvious if this comes up.
450 cros_build_lib.Die(
Alex Kleindf8ee502022-10-18 09:48:15 -0600451 "The service's known image types do not match those in "
452 "image.proto. Unknown Enum ID: %s",
453 current,
Alex Klein1699fab2022-09-08 08:46:06 -0600454 )
Alex Klein21b95022019-05-09 14:14:46 -0600455
Alex Klein1699fab2022-09-08 08:46:06 -0600456 # We can only build one type of these images at a time since image_to_vm.sh
457 # uses the default path if a name is not provided.
458 if vm_types.issuperset({_BASE_VM_ID, _TEST_VM_ID}):
459 cros_build_lib.Die("Cannot create more than one VM.")
Alex Klein21b95022019-05-09 14:14:46 -0600460
Alex Klein1699fab2022-09-08 08:46:06 -0600461 return ImageTypes(
462 images=image_types, vms=vm_types, mod_images=mod_image_types
463 )
Alex Klein21b95022019-05-09 14:14:46 -0600464
465
466def _ParseCreateBuildConfig(input_proto):
Alex Klein1699fab2022-09-08 08:46:06 -0600467 """Helper to parse the image build config for Create."""
468 enable_rootfs_verification = not input_proto.disable_rootfs_verification
469 version = input_proto.version or None
470 disk_layout = input_proto.disk_layout or None
471 builder_path = input_proto.builder_path or None
472 base_is_recovery = input_proto.base_is_recovery or False
473 return image.BuildConfig(
474 enable_rootfs_verification=enable_rootfs_verification,
475 replace=True,
476 version=version,
477 disk_layout=disk_layout,
478 builder_path=builder_path,
479 symlink=LOCATION_CORE,
480 base_is_recovery=base_is_recovery,
481 )
Alex Klein21b95022019-05-09 14:14:46 -0600482
Alex Klein1bcd9882019-03-19 13:25:24 -0600483
Alex Klein857b53d2022-10-21 13:53:26 -0600484@faux.all_empty
485@validate.require("build_target.name")
486@validate.validation_complete
487def CreateNetboot(input_proto, _output_proto, _config):
488 """Create a netboot kernel.
489
490 The netboot kernel currently needs network access because it's not building
491 everything in build_packages like other images. Once that has been remedied,
492 using Create to build the netboot kernel will be the expected workflow, and
493 this endpoint will be deprecated (b/255397725).
494 """
495 build_target = controller_util.ParseBuildTarget(input_proto.build_target)
496 if input_proto.factory_shim_path:
497 factory_shim_location = Path(input_proto.factory_shim_path).parent
498 else:
499 factory_shim_location = Path(
500 image_lib.GetLatestImageLink(
501 build_target.name, pointer=LOCATION_FACTORY
502 )
503 )
504 if not factory_shim_location.exists():
505 logging.warning(
506 "Factory shim directory does not exist. Skipping netboot creation."
507 )
508 return
509
510 image.create_netboot_kernel(build_target.name, str(factory_shim_location))
511
512
Michael Mortensen10146cf2019-11-19 19:59:22 -0700513def _SignerTestResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600514 """Set output_proto success field on a successful SignerTest response."""
515 output_proto.success = True
516 return controller.RETURN_CODE_SUCCESS
Michael Mortensen10146cf2019-11-19 19:59:22 -0700517
518
519@faux.success(_SignerTestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700520@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600521@validate.exists("image.path")
Alex Klein231d2da2019-07-22 16:44:45 -0600522@validate.validation_complete
Alex Klein1699fab2022-09-08 08:46:06 -0600523def SignerTest(
524 input_proto: "image_pb2.ImageTestRequest",
525 output_proto: "image_pb2.ImageTestRequest",
526 _config: "api_config.ApiConfig",
527):
528 """Run image tests.
Michael Mortensenc83c9952019-08-05 12:15:12 -0600529
Alex Klein1699fab2022-09-08 08:46:06 -0600530 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600531 input_proto: The input message.
532 output_proto: The output message.
533 _config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600534 """
535 image_path = input_proto.image.path
Michael Mortensenc83c9952019-08-05 12:15:12 -0600536
Alex Klein1699fab2022-09-08 08:46:06 -0600537 result = image_lib.SecurityTest(image=image_path)
538 output_proto.success = result
539 if result:
540 return controller.RETURN_CODE_SUCCESS
541 else:
542 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Michael Mortensenc83c9952019-08-05 12:15:12 -0600543
Alex Klein076841b2019-08-29 15:19:39 -0600544
Michael Mortensen10146cf2019-11-19 19:59:22 -0700545def _TestResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600546 """Set output_proto success field on a successful Test response."""
547 output_proto.success = True
548 return controller.RETURN_CODE_SUCCESS
Michael Mortensen10146cf2019-11-19 19:59:22 -0700549
550
551@faux.success(_TestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700552@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600553@validate.require("build_target.name", "result.directory")
554@validate.exists("image.path")
555def Test(
556 input_proto: "image_pb2.ImageTestRequest",
557 output_proto: "image_pb2.ImageTestResult",
558 config: "api_config.ApiConfig",
559):
560 """Run image tests.
Alex Klein2966e302019-01-17 13:29:38 -0700561
Alex Klein1699fab2022-09-08 08:46:06 -0600562 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600563 input_proto: The input message.
564 output_proto: The output message.
565 config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600566 """
567 image_path = input_proto.image.path
568 board = input_proto.build_target.name
569 result_directory = input_proto.result.directory
Alex Klein2966e302019-01-17 13:29:38 -0700570
Alex Klein1699fab2022-09-08 08:46:06 -0600571 if not os.path.isfile(image_path) or not image_path.endswith(".bin"):
572 cros_build_lib.Die(
573 "The image.path must be an existing image file with a .bin extension."
574 )
Alex Klein2966e302019-01-17 13:29:38 -0700575
Alex Klein1699fab2022-09-08 08:46:06 -0600576 if config.validate_only:
577 return controller.RETURN_CODE_VALID_INPUT
Alex Klein231d2da2019-07-22 16:44:45 -0600578
Alex Klein1699fab2022-09-08 08:46:06 -0600579 success = image.Test(board, result_directory, image_dir=image_path)
580 output_proto.success = success
Alex Klein8cb365a2019-05-15 16:24:53 -0600581
Alex Klein1699fab2022-09-08 08:46:06 -0600582 if success:
583 return controller.RETURN_CODE_SUCCESS
584 else:
585 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Jack Neus761e1842020-12-01 18:20:11 +0000586
587
588@faux.empty_success
589@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600590@validate.require("gs_image_dir", "sysroot.build_target.name")
591def PushImage(
592 input_proto: "image_pb2.PushImageRequest",
593 _output_proto: "image_pb2.PushImageResponse",
594 config: "api.config.ApiConfig",
595):
596 """Push artifacts from the archive bucket to the release bucket.
Jack Neus761e1842020-12-01 18:20:11 +0000597
Alex Klein1699fab2022-09-08 08:46:06 -0600598 Wraps chromite/scripts/pushimage.py.
Jack Neus761e1842020-12-01 18:20:11 +0000599
Alex Klein1699fab2022-09-08 08:46:06 -0600600 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600601 input_proto: Input proto.
602 _output_proto: Output proto.
603 config: The API call config.
Jack Neus761e1842020-12-01 18:20:11 +0000604
Alex Klein1699fab2022-09-08 08:46:06 -0600605 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600606 A controller return code (e.g. controller.RETURN_CODE_SUCCESS).
Alex Klein1699fab2022-09-08 08:46:06 -0600607 """
608 sign_types = []
609 if input_proto.sign_types:
610 for sign_type in input_proto.sign_types:
611 if sign_type not in SUPPORTED_IMAGE_TYPES:
612 logging.error("unsupported sign type %g", sign_type)
613 return controller.RETURN_CODE_INVALID_INPUT
614 sign_types.append(SUPPORTED_IMAGE_TYPES[sign_type])
Jack Neus761e1842020-12-01 18:20:11 +0000615
Alex Klein1699fab2022-09-08 08:46:06 -0600616 # If configured for validation only we're done here.
617 if config.validate_only:
618 return controller.RETURN_CODE_VALID_INPUT
Jack Neus761e1842020-12-01 18:20:11 +0000619
Alex Klein1699fab2022-09-08 08:46:06 -0600620 kwargs = {}
621 if input_proto.profile.name:
622 kwargs["profile"] = input_proto.profile.name
623 if input_proto.dest_bucket:
624 kwargs["dest_bucket"] = input_proto.dest_bucket
625 if input_proto.channels:
626 kwargs["force_channels"] = [
627 common_pb2.Channel.Name(channel).lower()[len("channel_") :]
628 for channel in input_proto.channels
629 ]
630 try:
631 channel_to_uris = pushimage.PushImage(
632 input_proto.gs_image_dir,
633 input_proto.sysroot.build_target.name,
634 dry_run=input_proto.dryrun,
635 sign_types=sign_types,
636 **kwargs,
637 )
638 except Exception:
639 logging.error("PushImage failed: ", exc_info=True)
640 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
641 if channel_to_uris:
642 for uris in channel_to_uris.values():
643 for uri in uris:
644 _output_proto.instructions.add().instructions_file_path = uri
645 return controller.RETURN_CODE_SUCCESS