blob: ef69e73de41140d179c484299da515081785b83a [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
Kevin Shelton5b21c8b2022-04-04 17:04:13 -070016from typing import List, NamedTuple, Set, TYPE_CHECKING, Union
Alex Klein2966e302019-01-17 13:29:38 -070017
Alex Klein8cb365a2019-05-15 16:24:53 -060018from chromite.api import controller
Alex Klein076841b2019-08-29 15:19:39 -060019from chromite.api import faux
Alex Klein2b236722019-06-19 15:44:26 -060020from chromite.api import validate
Alex Kleine9a7dbf2020-10-06 18:12:12 -060021from chromite.api.controller import controller_util
David Burgerb171d652019-05-13 16:07:00 -060022from chromite.api.gen.chromiumos import common_pb2
George Engelbrechtc9a8e812021-06-16 18:14:17 -060023from chromite.lib import build_target_lib
24from chromite.lib import chroot_lib
Alex Klein56355682019-02-07 10:36:54 -070025from chromite.lib import constants
Chris McDonald1672ddb2021-07-21 11:48:23 -060026from chromite.lib import cros_build_lib
Alex Klein56355682019-02-07 10:36:54 -070027from chromite.lib import image_lib
George Engelbrechtc9a8e812021-06-16 18:14:17 -060028from chromite.lib import sysroot_lib
Jack Neus761e1842020-12-01 18:20:11 +000029from chromite.scripts import pushimage
Alex Kleinb7cdbe62019-02-22 11:41:32 -070030from chromite.service import image
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040031from chromite.service import packages as packages_service
32
Alex Klein2966e302019-01-17 13:29:38 -070033
Kevin Shelton5b21c8b2022-04-04 17:04:13 -070034if TYPE_CHECKING:
Alex Klein1699fab2022-09-08 08:46:06 -060035 from chromite.api import api_config
36 from chromite.api.gen.chromite.api import image_pb2
Kevin Shelton5b21c8b2022-04-04 17:04:13 -070037
Alex Klein56355682019-02-07 10:36:54 -070038# The image.proto ImageType enum ids.
George Engelbrechtc55d6312021-05-05 12:11:13 -060039_BASE_ID = common_pb2.IMAGE_TYPE_BASE
40_DEV_ID = common_pb2.IMAGE_TYPE_DEV
41_TEST_ID = common_pb2.IMAGE_TYPE_TEST
42_BASE_VM_ID = common_pb2.IMAGE_TYPE_BASE_VM
43_TEST_VM_ID = common_pb2.IMAGE_TYPE_TEST_VM
44_RECOVERY_ID = common_pb2.IMAGE_TYPE_RECOVERY
45_FACTORY_ID = common_pb2.IMAGE_TYPE_FACTORY
46_FIRMWARE_ID = common_pb2.IMAGE_TYPE_FIRMWARE
47_BASE_GUEST_VM_ID = common_pb2.IMAGE_TYPE_BASE_GUEST_VM
48_TEST_GUEST_VM_ID = common_pb2.IMAGE_TYPE_TEST_GUEST_VM
Jack Neus70490b52022-09-12 14:41:45 +000049_NETBOOT_ID = common_pb2.IMAGE_TYPE_NETBOOT
Alex Klein56355682019-02-07 10:36:54 -070050
51# Dict to allow easily translating names to enum ids and vice versa.
52_IMAGE_MAPPING = {
53 _BASE_ID: constants.IMAGE_TYPE_BASE,
54 constants.IMAGE_TYPE_BASE: _BASE_ID,
55 _DEV_ID: constants.IMAGE_TYPE_DEV,
56 constants.IMAGE_TYPE_DEV: _DEV_ID,
57 _TEST_ID: constants.IMAGE_TYPE_TEST,
58 constants.IMAGE_TYPE_TEST: _TEST_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060059 _RECOVERY_ID: constants.IMAGE_TYPE_RECOVERY,
60 constants.IMAGE_TYPE_RECOVERY: _RECOVERY_ID,
Alex Klein9039a952021-07-27 13:52:39 -060061 _FACTORY_ID: constants.IMAGE_TYPE_FACTORY_SHIM,
62 constants.IMAGE_TYPE_FACTORY_SHIM: _FACTORY_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060063 _FIRMWARE_ID: constants.IMAGE_TYPE_FIRMWARE,
64 constants.IMAGE_TYPE_FIRMWARE: _FIRMWARE_ID,
Jack Neus70490b52022-09-12 14:41:45 +000065 _NETBOOT_ID: constants.IMAGE_TYPE_NETBOOT,
66 constants.IMAGE_TYPE_NETBOOT: _NETBOOT_ID,
Alex Klein56355682019-02-07 10:36:54 -070067}
68
George Engelbrecht9f4f8322021-03-08 12:04:17 -070069# Dict to describe the prerequisite built images for each VM image type.
Alex Klein21b95022019-05-09 14:14:46 -060070_VM_IMAGE_MAPPING = {
71 _BASE_VM_ID: _IMAGE_MAPPING[_BASE_ID],
72 _TEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Trent Begin008cade2019-10-31 13:40:59 -060073 _BASE_GUEST_VM_ID: _IMAGE_MAPPING[_BASE_ID],
74 _TEST_GUEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Alex Klein21b95022019-05-09 14:14:46 -060075}
76
George Engelbrecht9f4f8322021-03-08 12:04:17 -070077# Dict to describe the prerequisite built images for each mod image type.
78_MOD_IMAGE_MAPPING = {
79 _RECOVERY_ID: _IMAGE_MAPPING[_BASE_ID],
Jack Neus0829c7e2022-09-15 15:41:57 +000080 _NETBOOT_ID: _IMAGE_MAPPING[_FACTORY_ID],
George Engelbrecht9f4f8322021-03-08 12:04:17 -070081}
82
Jack Neus761e1842020-12-01 18:20:11 +000083# Supported image types for PushImage.
84SUPPORTED_IMAGE_TYPES = {
85 common_pb2.IMAGE_TYPE_RECOVERY: constants.IMAGE_TYPE_RECOVERY,
86 common_pb2.IMAGE_TYPE_FACTORY: constants.IMAGE_TYPE_FACTORY,
87 common_pb2.IMAGE_TYPE_FIRMWARE: constants.IMAGE_TYPE_FIRMWARE,
88 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD: constants.IMAGE_TYPE_ACCESSORY_USBPD,
89 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG: constants.IMAGE_TYPE_ACCESSORY_RWSIG,
Evan Benn4d061102022-02-14 12:50:45 +110090 common_pb2.IMAGE_TYPE_HPS_FIRMWARE: constants.IMAGE_TYPE_HPS_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +000091 common_pb2.IMAGE_TYPE_BASE: constants.IMAGE_TYPE_BASE,
George Engelbrecht9f4f8322021-03-08 12:04:17 -070092 common_pb2.IMAGE_TYPE_GSC_FIRMWARE: constants.IMAGE_TYPE_GSC_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +000093}
94
Alex Klein27978a42021-07-27 14:18:10 -060095# Built image directory symlink names. These names allow specifying a static
96# location for creation to simplify later archival stages. In practice, this
97# sets the symlink argument to build_packages.
98# Core are the build/dev/test images.
99# Use "latest" until we do a better job of passing through image directories,
100# e.g. for artifacts.
Alex Klein1699fab2022-09-08 08:46:06 -0600101LOCATION_CORE = "latest"
Alex Klein27978a42021-07-27 14:18:10 -0600102# The factory_install image.
Alex Klein1699fab2022-09-08 08:46:06 -0600103LOCATION_FACTORY = "factory_shim"
Alex Klein27978a42021-07-27 14:18:10 -0600104
105
106class ImageTypes(NamedTuple):
Alex Klein1699fab2022-09-08 08:46:06 -0600107 """Parsed image types."""
Alex Klein27978a42021-07-27 14:18:10 -0600108
Alex Klein1699fab2022-09-08 08:46:06 -0600109 images: Set[str]
110 vms: Set[int]
111 mod_images: Set[int]
Alex Klein27978a42021-07-27 14:18:10 -0600112
Alex Klein1699fab2022-09-08 08:46:06 -0600113 @property
114 def core_images(self) -> List[str]:
115 """The core images (base/dev/test) as a list."""
116 return list(self.images - {_IMAGE_MAPPING[_FACTORY_ID]}) or []
Alex Klein27978a42021-07-27 14:18:10 -0600117
Alex Klein1699fab2022-09-08 08:46:06 -0600118 @property
119 def has_factory(self) -> bool:
120 """Whether the factory image is present."""
121 return _IMAGE_MAPPING[_FACTORY_ID] in self.images
122
123 @property
124 def factory(self) -> List[str]:
125 """A list with the factory type if set."""
126 return [_IMAGE_MAPPING[_FACTORY_ID]] if self.has_factory else []
Alex Klein27978a42021-07-27 14:18:10 -0600127
Alex Klein56355682019-02-07 10:36:54 -0700128
Alex Klein1699fab2022-09-08 08:46:06 -0600129def _add_image_to_proto(
130 output_proto, path: Union["Path", str], image_type: int, board: str
131):
132 """Quick helper function to add a new image to the output proto."""
133 new_image = output_proto.images.add()
134 new_image.path = str(path)
135 new_image.type = image_type
136 new_image.build_target.name = board
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700137
138
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600139def ExampleGetResponse():
Alex Klein1699fab2022-09-08 08:46:06 -0600140 """Give an example response to assemble upstream in caller artifacts."""
141 uabs = common_pb2.UploadedArtifactsByService
142 cabs = common_pb2.ArtifactsByService
143 return uabs.Sysroot(
144 artifacts=[
145 uabs.Image.ArtifactPaths(
146 artifact_type=cabs.Image.ArtifactType.DLC_IMAGE,
147 paths=[
148 common_pb2.Path(
149 path="/tmp/dlc/dlc.img",
150 location=common_pb2.Path.OUTSIDE,
151 )
152 ],
153 )
154 ]
155 )
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600156
157
Alex Klein1699fab2022-09-08 08:46:06 -0600158def GetArtifacts(
159 in_proto: common_pb2.ArtifactsByService.Image,
160 chroot: chroot_lib.Chroot,
161 sysroot_class: sysroot_lib.Sysroot,
162 build_target: build_target_lib.BuildTarget,
163 output_dir,
164) -> list:
165 """Builds and copies images to specified output_dir.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600166
Alex Klein1699fab2022-09-08 08:46:06 -0600167 Copies (after optionally bundling) all required images into the output_dir,
168 returning a mapping of image type to a list of (output_dir) paths to
169 the desired files. Note that currently it is only processing one image (DLC),
170 but the future direction is to process all required images. Required images
171 are located within output_artifact.artifact_type.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600172
Alex Klein1699fab2022-09-08 08:46:06 -0600173 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600174 in_proto: Proto request defining reqs.
175 chroot: The chroot proto used for these artifacts.
176 sysroot_class: The sysroot proto used for these artifacts.
177 build_target: The build target used for these artifacts.
178 output_dir: The path to write artifacts to.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600179
Alex Klein1699fab2022-09-08 08:46:06 -0600180 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600181 A list of dictionary mappings of ArtifactType to list of paths.
Alex Klein1699fab2022-09-08 08:46:06 -0600182 """
183 base_path = chroot.full_path(sysroot_class.path)
184 board = build_target.name
185 factory_shim_location = Path(
186 image_lib.GetLatestImageLink(board, pointer=LOCATION_FACTORY)
187 )
Jack Neus5e56fef2021-06-18 16:57:28 +0000188
Alex Klein1699fab2022-09-08 08:46:06 -0600189 generated = []
190 dlc_func = functools.partial(image.copy_dlc_image, base_path)
191 license_func = functools.partial(
192 image.copy_license_credits, board, symlink=LOCATION_CORE
193 )
194 factory_image_func = functools.partial(
195 image.create_factory_image_zip,
196 chroot,
197 sysroot_class,
198 factory_shim_location,
199 packages_service.determine_full_version(),
200 )
201 stripped_packags_func = functools.partial(
202 image.create_stripped_packages_tar,
203 chroot,
204 build_target,
205 )
206 artifact_types = {
207 in_proto.ArtifactType.DLC_IMAGE: dlc_func,
208 in_proto.ArtifactType.LICENSE_CREDITS: license_func,
209 in_proto.ArtifactType.FACTORY_IMAGE: factory_image_func,
210 in_proto.ArtifactType.STRIPPED_PACKAGES: stripped_packags_func,
211 }
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600212
Alex Klein1699fab2022-09-08 08:46:06 -0600213 for output_artifact in in_proto.output_artifacts:
214 for artifact_type, func in artifact_types.items():
215 if artifact_type in output_artifact.artifact_types:
216 result = func(output_dir)
217 if result:
218 generated.append(
219 {
220 "paths": [result]
221 if isinstance(result, str)
222 else result,
223 "type": artifact_type,
224 }
225 )
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600226
Alex Klein1699fab2022-09-08 08:46:06 -0600227 return generated
Pi-Hsun Shih8d9c8e42021-06-30 03:27:28 +0000228
Alex Kleincaace392021-07-26 14:28:13 -0600229
Michael Mortensen10146cf2019-11-19 19:59:22 -0700230def _CreateResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600231 """Set output_proto success field on a successful Create response."""
232 output_proto.success = True
Michael Mortensen10146cf2019-11-19 19:59:22 -0700233
234
235@faux.success(_CreateResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700236@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600237@validate.require("build_target.name")
Alex Klein231d2da2019-07-22 16:44:45 -0600238@validate.validation_complete
Alex Klein1699fab2022-09-08 08:46:06 -0600239def Create(
240 input_proto: "image_pb2.CreateImageRequest",
241 output_proto: "image_pb2.CreateImageResult",
242 _config: "api_config.ApiConfig",
243):
244 """Build images.
Alex Klein56355682019-02-07 10:36:54 -0700245
Alex Klein1699fab2022-09-08 08:46:06 -0600246 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600247 input_proto: The input message.
248 output_proto: The output message.
249 _config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600250 """
251 board = input_proto.build_target.name
Alex Klein56355682019-02-07 10:36:54 -0700252
Alex Klein1699fab2022-09-08 08:46:06 -0600253 # Build the base image if no images provided.
254 to_build = input_proto.image_types or [_BASE_ID]
Alex Klein56355682019-02-07 10:36:54 -0700255
Alex Klein1699fab2022-09-08 08:46:06 -0600256 image_types = _ParseImagesToCreate(to_build)
257 build_config = _ParseCreateBuildConfig(input_proto)
258 factory_build_config = build_config._replace(
259 symlink=LOCATION_FACTORY, output_dir_suffix=LOCATION_FACTORY
260 )
Alex Klein56355682019-02-07 10:36:54 -0700261
Alex Klein1699fab2022-09-08 08:46:06 -0600262 # Try building the core and factory images.
263 # Sorted isn't really necessary here, but it's much easier to test.
264 core_result = image.Build(
265 board, sorted(image_types.core_images), config=build_config
266 )
267 logging.debug("Core Result Images: %s", core_result.images)
Alex Klein56355682019-02-07 10:36:54 -0700268
Alex Klein1699fab2022-09-08 08:46:06 -0600269 factory_result = image.Build(
270 board, image_types.factory, config=factory_build_config
271 )
272 logging.debug("Factory Result Images: %s", factory_result.images)
Will Bradley29a49c22019-10-21 11:50:08 -0600273
Alex Klein1699fab2022-09-08 08:46:06 -0600274 # A successful run will have no images missing, will have run at least one
275 # of the two image sets, and neither attempt errored. The no error condition
276 # should be redundant with no missing images, but is cheap insurance.
277 all_built = core_result.all_built and factory_result.all_built
278 one_ran = core_result.build_run or factory_result.build_run
279 no_errors = not core_result.run_error and not factory_result.run_error
280 output_proto.success = success = all_built and one_ran and no_errors
Will Bradley29a49c22019-10-21 11:50:08 -0600281
Alex Klein1699fab2022-09-08 08:46:06 -0600282 if success:
283 # Success! We need to record the images we built in the output.
284 all_images = {**core_result.images, **factory_result.images}
285 for img_name, img_path in all_images.items():
286 _add_image_to_proto(
287 output_proto, img_path, _IMAGE_MAPPING[img_name], board
288 )
Alex Klein27978a42021-07-27 14:18:10 -0600289
Alex Klein1699fab2022-09-08 08:46:06 -0600290 # Build and record VMs as necessary.
291 for vm_type in image_types.vms:
292 is_test = vm_type in [_TEST_VM_ID, _TEST_GUEST_VM_ID]
293 img_type = _IMAGE_MAPPING[_TEST_ID if is_test else _BASE_ID]
294 img_dir = core_result.images[img_type].parent.resolve()
295 try:
296 if vm_type in [_BASE_GUEST_VM_ID, _TEST_GUEST_VM_ID]:
297 vm_path = image.CreateGuestVm(
298 board, is_test=is_test, image_dir=img_dir
299 )
300 else:
301 vm_path = image.CreateVm(
302 board,
303 disk_layout=build_config.disk_layout,
304 is_test=is_test,
305 image_dir=img_dir,
306 )
307 except image.ImageToVmError as e:
308 cros_build_lib.Die(e)
Will Bradley29a49c22019-10-21 11:50:08 -0600309
Alex Klein1699fab2022-09-08 08:46:06 -0600310 _add_image_to_proto(output_proto, vm_path, vm_type, board)
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700311
Alex Klein1699fab2022-09-08 08:46:06 -0600312 # Build and record any mod images.
313 for mod_type in image_types.mod_images:
314 if mod_type == _RECOVERY_ID:
315 base_image_path = core_result.images[constants.IMAGE_TYPE_BASE]
316 # For ChromeOS Flex special case.
317 if build_config.base_is_recovery:
318 result = image.CopyBaseToRecovery(
319 board=board, image_path=base_image_path
320 )
321 else:
322 result = image.BuildRecoveryImage(
323 board=board, image_path=base_image_path
324 )
325 if result.all_built:
326 _add_image_to_proto(
327 output_proto,
328 result.images[_IMAGE_MAPPING[mod_type]],
329 mod_type,
330 board,
331 )
332 else:
333 cros_build_lib.Die("Failed to create recovery image.")
Jack Neus70490b52022-09-12 14:41:45 +0000334 elif mod_type == _NETBOOT_ID:
Madeleine Hardtf0a92fc2022-09-19 18:41:12 +0000335 factory_shim_dir = os.path.dirname(
Jack Neus70490b52022-09-12 14:41:45 +0000336 factory_result.images[constants.IMAGE_TYPE_FACTORY_SHIM]
337 )
338 image.create_netboot_kernel(board, factory_shim_dir)
Alex Klein1699fab2022-09-08 08:46:06 -0600339 else:
Jack Neus70490b52022-09-12 14:41:45 +0000340 cros_build_lib.Die(
341 "_RECOVERY_ID and _NETBOOT_ID are the only mod_image_type."
342 )
Will Bradley29a49c22019-10-21 11:50:08 -0600343
Alex Klein1699fab2022-09-08 08:46:06 -0600344 # Read metric events log and pipe them into output_proto.events.
Alex Kleina0421f32022-09-12 14:39:29 -0600345 if core_result.build_run and core_result.output_dir:
346 _parse_img_metrics_to_response(
347 output_proto, board, core_result.output_dir
348 )
Alex Klein1699fab2022-09-08 08:46:06 -0600349 return controller.RETURN_CODE_SUCCESS
Will Bradley29a49c22019-10-21 11:50:08 -0600350
Alex Klein1699fab2022-09-08 08:46:06 -0600351 else:
352 # Failure, include all of the failed packages in the output when available.
353 packages = core_result.failed_packages + factory_result.failed_packages
354 if not packages:
355 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Alex Klein2557b4f2019-07-11 14:34:00 -0600356
Alex Klein1699fab2022-09-08 08:46:06 -0600357 for package in packages:
358 current = output_proto.failed_packages.add()
359 controller_util.serialize_package_info(package, current)
Alex Klein1bcd9882019-03-19 13:25:24 -0600360
Alex Klein1699fab2022-09-08 08:46:06 -0600361 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
Alex Klein1bcd9882019-03-19 13:25:24 -0600362
Alex Kleincaace392021-07-26 14:28:13 -0600363
Alex Kleina0421f32022-09-12 14:39:29 -0600364def _parse_img_metrics_to_response(
365 output: "image_pb2.CreateImageResult", board: str, build_path: Path
366):
367 """Manually translate the package sizes file to the metrics events.
368
369 This is a temporary hack to manually translate the package sizes file to
370 the metrics output because the metrics library does not reliably transmit
371 the data. This can be removed once we switch over to the new metrics
372 pipeline.
373 """
374 filename = build_path / f"{constants.BASE_IMAGE_BIN}-package-sizes.json"
375 if not filename.exists():
376 logging.error("Package sizes file does not exist.")
377 return
378
379 size_data = json.loads(filename.read_text(encoding="utf-8"))
380
381 ts = int(round(time.time() * 1000))
382
383 # Total size event.
384 event = output.events.add()
385 event.gauge = size_data["total_size"]
386 event.timestamp_milliseconds = ts
387 event.name = f"{board}.total_size.base.rootfs"
388
389 # Package sizes.
390 for pkg, size in size_data["package_sizes"].items():
391 event = output.events.add()
392 event.gauge = size
393 event.timestamp_milliseconds = ts
394 event.name = f"{board}.package_size.base.rootfs.{pkg}"
395
396
Alex Klein27978a42021-07-27 14:18:10 -0600397def _ParseImagesToCreate(to_build: List[int]) -> ImageTypes:
Alex Klein1699fab2022-09-08 08:46:06 -0600398 """Helper function to parse the image types to build.
Alex Klein21b95022019-05-09 14:14:46 -0600399
Alex Klein1699fab2022-09-08 08:46:06 -0600400 This function expresses the dependencies of each image type and adds
401 the requisite image types if they're not explicitly defined.
Alex Klein21b95022019-05-09 14:14:46 -0600402
Alex Klein1699fab2022-09-08 08:46:06 -0600403 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600404 to_build: The image type list.
Alex Klein21b95022019-05-09 14:14:46 -0600405
Alex Klein1699fab2022-09-08 08:46:06 -0600406 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600407 ImageTypes: The parsed images to build.
Alex Klein1699fab2022-09-08 08:46:06 -0600408 """
409 image_types = set()
410 vm_types = set()
411 mod_image_types = set()
412 for current in to_build:
413 # Find out if it's a special case (vm, img mod), or just any old image.
414 if current in _VM_IMAGE_MAPPING:
415 vm_types.add(current)
416 # Make sure we build the image required to build the VM.
417 image_types.add(_VM_IMAGE_MAPPING[current])
418 elif current in _MOD_IMAGE_MAPPING:
419 mod_image_types.add(current)
420 image_types.add(_MOD_IMAGE_MAPPING[current])
421 elif current in _IMAGE_MAPPING:
422 image_types.add(_IMAGE_MAPPING[current])
423 else:
424 # Not expected, but at least it will be obvious if this comes up.
425 cros_build_lib.Die(
Alex Kleindf8ee502022-10-18 09:48:15 -0600426 "The service's known image types do not match those in "
427 "image.proto. Unknown Enum ID: %s",
428 current,
Alex Klein1699fab2022-09-08 08:46:06 -0600429 )
Alex Klein21b95022019-05-09 14:14:46 -0600430
Alex Klein1699fab2022-09-08 08:46:06 -0600431 # We can only build one type of these images at a time since image_to_vm.sh
432 # uses the default path if a name is not provided.
433 if vm_types.issuperset({_BASE_VM_ID, _TEST_VM_ID}):
434 cros_build_lib.Die("Cannot create more than one VM.")
Alex Klein21b95022019-05-09 14:14:46 -0600435
Alex Klein1699fab2022-09-08 08:46:06 -0600436 return ImageTypes(
437 images=image_types, vms=vm_types, mod_images=mod_image_types
438 )
Alex Klein21b95022019-05-09 14:14:46 -0600439
440
441def _ParseCreateBuildConfig(input_proto):
Alex Klein1699fab2022-09-08 08:46:06 -0600442 """Helper to parse the image build config for Create."""
443 enable_rootfs_verification = not input_proto.disable_rootfs_verification
444 version = input_proto.version or None
445 disk_layout = input_proto.disk_layout or None
446 builder_path = input_proto.builder_path or None
447 base_is_recovery = input_proto.base_is_recovery or False
448 return image.BuildConfig(
449 enable_rootfs_verification=enable_rootfs_verification,
450 replace=True,
451 version=version,
452 disk_layout=disk_layout,
453 builder_path=builder_path,
454 symlink=LOCATION_CORE,
455 base_is_recovery=base_is_recovery,
456 )
Alex Klein21b95022019-05-09 14:14:46 -0600457
Alex Klein1bcd9882019-03-19 13:25:24 -0600458
Michael Mortensen10146cf2019-11-19 19:59:22 -0700459def _SignerTestResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600460 """Set output_proto success field on a successful SignerTest response."""
461 output_proto.success = True
462 return controller.RETURN_CODE_SUCCESS
Michael Mortensen10146cf2019-11-19 19:59:22 -0700463
464
465@faux.success(_SignerTestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700466@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600467@validate.exists("image.path")
Alex Klein231d2da2019-07-22 16:44:45 -0600468@validate.validation_complete
Alex Klein1699fab2022-09-08 08:46:06 -0600469def SignerTest(
470 input_proto: "image_pb2.ImageTestRequest",
471 output_proto: "image_pb2.ImageTestRequest",
472 _config: "api_config.ApiConfig",
473):
474 """Run image tests.
Michael Mortensenc83c9952019-08-05 12:15:12 -0600475
Alex Klein1699fab2022-09-08 08:46:06 -0600476 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600477 input_proto: The input message.
478 output_proto: The output message.
479 _config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600480 """
481 image_path = input_proto.image.path
Michael Mortensenc83c9952019-08-05 12:15:12 -0600482
Alex Klein1699fab2022-09-08 08:46:06 -0600483 result = image_lib.SecurityTest(image=image_path)
484 output_proto.success = result
485 if result:
486 return controller.RETURN_CODE_SUCCESS
487 else:
488 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Michael Mortensenc83c9952019-08-05 12:15:12 -0600489
Alex Klein076841b2019-08-29 15:19:39 -0600490
Michael Mortensen10146cf2019-11-19 19:59:22 -0700491def _TestResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600492 """Set output_proto success field on a successful Test response."""
493 output_proto.success = True
494 return controller.RETURN_CODE_SUCCESS
Michael Mortensen10146cf2019-11-19 19:59:22 -0700495
496
497@faux.success(_TestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700498@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600499@validate.require("build_target.name", "result.directory")
500@validate.exists("image.path")
501def Test(
502 input_proto: "image_pb2.ImageTestRequest",
503 output_proto: "image_pb2.ImageTestResult",
504 config: "api_config.ApiConfig",
505):
506 """Run image tests.
Alex Klein2966e302019-01-17 13:29:38 -0700507
Alex Klein1699fab2022-09-08 08:46:06 -0600508 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600509 input_proto: The input message.
510 output_proto: The output message.
511 config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600512 """
513 image_path = input_proto.image.path
514 board = input_proto.build_target.name
515 result_directory = input_proto.result.directory
Alex Klein2966e302019-01-17 13:29:38 -0700516
Alex Klein1699fab2022-09-08 08:46:06 -0600517 if not os.path.isfile(image_path) or not image_path.endswith(".bin"):
518 cros_build_lib.Die(
519 "The image.path must be an existing image file with a .bin extension."
520 )
Alex Klein2966e302019-01-17 13:29:38 -0700521
Alex Klein1699fab2022-09-08 08:46:06 -0600522 if config.validate_only:
523 return controller.RETURN_CODE_VALID_INPUT
Alex Klein231d2da2019-07-22 16:44:45 -0600524
Alex Klein1699fab2022-09-08 08:46:06 -0600525 success = image.Test(board, result_directory, image_dir=image_path)
526 output_proto.success = success
Alex Klein8cb365a2019-05-15 16:24:53 -0600527
Alex Klein1699fab2022-09-08 08:46:06 -0600528 if success:
529 return controller.RETURN_CODE_SUCCESS
530 else:
531 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Jack Neus761e1842020-12-01 18:20:11 +0000532
533
534@faux.empty_success
535@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600536@validate.require("gs_image_dir", "sysroot.build_target.name")
537def PushImage(
538 input_proto: "image_pb2.PushImageRequest",
539 _output_proto: "image_pb2.PushImageResponse",
540 config: "api.config.ApiConfig",
541):
542 """Push artifacts from the archive bucket to the release bucket.
Jack Neus761e1842020-12-01 18:20:11 +0000543
Alex Klein1699fab2022-09-08 08:46:06 -0600544 Wraps chromite/scripts/pushimage.py.
Jack Neus761e1842020-12-01 18:20:11 +0000545
Alex Klein1699fab2022-09-08 08:46:06 -0600546 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600547 input_proto: Input proto.
548 _output_proto: Output proto.
549 config: The API call config.
Jack Neus761e1842020-12-01 18:20:11 +0000550
Alex Klein1699fab2022-09-08 08:46:06 -0600551 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600552 A controller return code (e.g. controller.RETURN_CODE_SUCCESS).
Alex Klein1699fab2022-09-08 08:46:06 -0600553 """
554 sign_types = []
555 if input_proto.sign_types:
556 for sign_type in input_proto.sign_types:
557 if sign_type not in SUPPORTED_IMAGE_TYPES:
558 logging.error("unsupported sign type %g", sign_type)
559 return controller.RETURN_CODE_INVALID_INPUT
560 sign_types.append(SUPPORTED_IMAGE_TYPES[sign_type])
Jack Neus761e1842020-12-01 18:20:11 +0000561
Alex Klein1699fab2022-09-08 08:46:06 -0600562 # If configured for validation only we're done here.
563 if config.validate_only:
564 return controller.RETURN_CODE_VALID_INPUT
Jack Neus761e1842020-12-01 18:20:11 +0000565
Alex Klein1699fab2022-09-08 08:46:06 -0600566 kwargs = {}
567 if input_proto.profile.name:
568 kwargs["profile"] = input_proto.profile.name
569 if input_proto.dest_bucket:
570 kwargs["dest_bucket"] = input_proto.dest_bucket
571 if input_proto.channels:
572 kwargs["force_channels"] = [
573 common_pb2.Channel.Name(channel).lower()[len("channel_") :]
574 for channel in input_proto.channels
575 ]
576 try:
577 channel_to_uris = pushimage.PushImage(
578 input_proto.gs_image_dir,
579 input_proto.sysroot.build_target.name,
580 dry_run=input_proto.dryrun,
581 sign_types=sign_types,
582 **kwargs,
583 )
584 except Exception:
585 logging.error("PushImage failed: ", exc_info=True)
586 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
587 if channel_to_uris:
588 for uris in channel_to_uris.values():
589 for uri in uris:
590 _output_proto.instructions.add().instructions_file_path = uri
591 return controller.RETURN_CODE_SUCCESS