blob: 5a7b0c137273b20ecaace0f281f4aa870f3af514 [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
Alex Klein56355682019-02-07 10:36:54 -070049
50# Dict to allow easily translating names to enum ids and vice versa.
51_IMAGE_MAPPING = {
52 _BASE_ID: constants.IMAGE_TYPE_BASE,
53 constants.IMAGE_TYPE_BASE: _BASE_ID,
54 _DEV_ID: constants.IMAGE_TYPE_DEV,
55 constants.IMAGE_TYPE_DEV: _DEV_ID,
56 _TEST_ID: constants.IMAGE_TYPE_TEST,
57 constants.IMAGE_TYPE_TEST: _TEST_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060058 _RECOVERY_ID: constants.IMAGE_TYPE_RECOVERY,
59 constants.IMAGE_TYPE_RECOVERY: _RECOVERY_ID,
Alex Klein9039a952021-07-27 13:52:39 -060060 _FACTORY_ID: constants.IMAGE_TYPE_FACTORY_SHIM,
61 constants.IMAGE_TYPE_FACTORY_SHIM: _FACTORY_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060062 _FIRMWARE_ID: constants.IMAGE_TYPE_FIRMWARE,
63 constants.IMAGE_TYPE_FIRMWARE: _FIRMWARE_ID,
Alex Klein56355682019-02-07 10:36:54 -070064}
65
George Engelbrecht9f4f8322021-03-08 12:04:17 -070066# Dict to describe the prerequisite built images for each VM image type.
Alex Klein21b95022019-05-09 14:14:46 -060067_VM_IMAGE_MAPPING = {
68 _BASE_VM_ID: _IMAGE_MAPPING[_BASE_ID],
69 _TEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Trent Begin008cade2019-10-31 13:40:59 -060070 _BASE_GUEST_VM_ID: _IMAGE_MAPPING[_BASE_ID],
71 _TEST_GUEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Alex Klein21b95022019-05-09 14:14:46 -060072}
73
George Engelbrecht9f4f8322021-03-08 12:04:17 -070074# Dict to describe the prerequisite built images for each mod image type.
75_MOD_IMAGE_MAPPING = {
76 _RECOVERY_ID: _IMAGE_MAPPING[_BASE_ID],
77}
78
Jack Neus761e1842020-12-01 18:20:11 +000079# Supported image types for PushImage.
80SUPPORTED_IMAGE_TYPES = {
81 common_pb2.IMAGE_TYPE_RECOVERY: constants.IMAGE_TYPE_RECOVERY,
82 common_pb2.IMAGE_TYPE_FACTORY: constants.IMAGE_TYPE_FACTORY,
83 common_pb2.IMAGE_TYPE_FIRMWARE: constants.IMAGE_TYPE_FIRMWARE,
84 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD: constants.IMAGE_TYPE_ACCESSORY_USBPD,
85 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG: constants.IMAGE_TYPE_ACCESSORY_RWSIG,
Evan Benn4d061102022-02-14 12:50:45 +110086 common_pb2.IMAGE_TYPE_HPS_FIRMWARE: constants.IMAGE_TYPE_HPS_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +000087 common_pb2.IMAGE_TYPE_BASE: constants.IMAGE_TYPE_BASE,
George Engelbrecht9f4f8322021-03-08 12:04:17 -070088 common_pb2.IMAGE_TYPE_GSC_FIRMWARE: constants.IMAGE_TYPE_GSC_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +000089}
90
Alex Klein27978a42021-07-27 14:18:10 -060091# Built image directory symlink names. These names allow specifying a static
92# location for creation to simplify later archival stages. In practice, this
93# sets the symlink argument to build_packages.
94# Core are the build/dev/test images.
95# Use "latest" until we do a better job of passing through image directories,
96# e.g. for artifacts.
Alex Klein1699fab2022-09-08 08:46:06 -060097LOCATION_CORE = "latest"
Alex Klein27978a42021-07-27 14:18:10 -060098# The factory_install image.
Alex Klein1699fab2022-09-08 08:46:06 -060099LOCATION_FACTORY = "factory_shim"
Alex Klein27978a42021-07-27 14:18:10 -0600100
101
102class ImageTypes(NamedTuple):
Alex Klein1699fab2022-09-08 08:46:06 -0600103 """Parsed image types."""
Alex Klein27978a42021-07-27 14:18:10 -0600104
Alex Klein1699fab2022-09-08 08:46:06 -0600105 images: Set[str]
106 vms: Set[int]
107 mod_images: Set[int]
Alex Klein27978a42021-07-27 14:18:10 -0600108
Alex Klein1699fab2022-09-08 08:46:06 -0600109 @property
110 def core_images(self) -> List[str]:
111 """The core images (base/dev/test) as a list."""
112 return list(self.images - {_IMAGE_MAPPING[_FACTORY_ID]}) or []
Alex Klein27978a42021-07-27 14:18:10 -0600113
Alex Klein1699fab2022-09-08 08:46:06 -0600114 @property
115 def has_factory(self) -> bool:
116 """Whether the factory image is present."""
117 return _IMAGE_MAPPING[_FACTORY_ID] in self.images
118
119 @property
120 def factory(self) -> List[str]:
121 """A list with the factory type if set."""
122 return [_IMAGE_MAPPING[_FACTORY_ID]] if self.has_factory else []
Alex Klein27978a42021-07-27 14:18:10 -0600123
Alex Klein56355682019-02-07 10:36:54 -0700124
Alex Klein1699fab2022-09-08 08:46:06 -0600125def _add_image_to_proto(
126 output_proto, path: Union["Path", str], image_type: int, board: str
127):
128 """Quick helper function to add a new image to the output proto."""
129 new_image = output_proto.images.add()
130 new_image.path = str(path)
131 new_image.type = image_type
132 new_image.build_target.name = board
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700133
134
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600135def ExampleGetResponse():
Alex Klein1699fab2022-09-08 08:46:06 -0600136 """Give an example response to assemble upstream in caller artifacts."""
137 uabs = common_pb2.UploadedArtifactsByService
138 cabs = common_pb2.ArtifactsByService
139 return uabs.Sysroot(
140 artifacts=[
141 uabs.Image.ArtifactPaths(
142 artifact_type=cabs.Image.ArtifactType.DLC_IMAGE,
143 paths=[
144 common_pb2.Path(
145 path="/tmp/dlc/dlc.img",
146 location=common_pb2.Path.OUTSIDE,
147 )
148 ],
149 )
150 ]
151 )
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600152
153
Alex Klein1699fab2022-09-08 08:46:06 -0600154def GetArtifacts(
155 in_proto: common_pb2.ArtifactsByService.Image,
156 chroot: chroot_lib.Chroot,
157 sysroot_class: sysroot_lib.Sysroot,
158 build_target: build_target_lib.BuildTarget,
159 output_dir,
160) -> list:
161 """Builds and copies images to specified output_dir.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 Copies (after optionally bundling) all required images into the output_dir,
164 returning a mapping of image type to a list of (output_dir) paths to
165 the desired files. Note that currently it is only processing one image (DLC),
166 but the future direction is to process all required images. Required images
167 are located within output_artifact.artifact_type.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 Args:
170 in_proto: Proto request defining reqs.
171 chroot: The chroot proto used for these artifacts.
172 sysroot_class: The sysroot proto used for these artifacts.
173 build_target: The build target used for these artifacts.
174 output_dir: The path to write artifacts to.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600175
Alex Klein1699fab2022-09-08 08:46:06 -0600176 Returns:
177 A list of dictionary mappings of ArtifactType to list of paths.
178 """
179 base_path = chroot.full_path(sysroot_class.path)
180 board = build_target.name
181 factory_shim_location = Path(
182 image_lib.GetLatestImageLink(board, pointer=LOCATION_FACTORY)
183 )
Jack Neus5e56fef2021-06-18 16:57:28 +0000184
Alex Klein1699fab2022-09-08 08:46:06 -0600185 generated = []
186 dlc_func = functools.partial(image.copy_dlc_image, base_path)
187 license_func = functools.partial(
188 image.copy_license_credits, board, symlink=LOCATION_CORE
189 )
190 factory_image_func = functools.partial(
191 image.create_factory_image_zip,
192 chroot,
193 sysroot_class,
194 factory_shim_location,
195 packages_service.determine_full_version(),
196 )
197 stripped_packags_func = functools.partial(
198 image.create_stripped_packages_tar,
199 chroot,
200 build_target,
201 )
202 artifact_types = {
203 in_proto.ArtifactType.DLC_IMAGE: dlc_func,
204 in_proto.ArtifactType.LICENSE_CREDITS: license_func,
205 in_proto.ArtifactType.FACTORY_IMAGE: factory_image_func,
206 in_proto.ArtifactType.STRIPPED_PACKAGES: stripped_packags_func,
207 }
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600208
Alex Klein1699fab2022-09-08 08:46:06 -0600209 for output_artifact in in_proto.output_artifacts:
210 for artifact_type, func in artifact_types.items():
211 if artifact_type in output_artifact.artifact_types:
212 result = func(output_dir)
213 if result:
214 generated.append(
215 {
216 "paths": [result]
217 if isinstance(result, str)
218 else result,
219 "type": artifact_type,
220 }
221 )
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600222
Alex Klein1699fab2022-09-08 08:46:06 -0600223 return generated
Pi-Hsun Shih8d9c8e42021-06-30 03:27:28 +0000224
Alex Kleincaace392021-07-26 14:28:13 -0600225
Michael Mortensen10146cf2019-11-19 19:59:22 -0700226def _CreateResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600227 """Set output_proto success field on a successful Create response."""
228 output_proto.success = True
Michael Mortensen10146cf2019-11-19 19:59:22 -0700229
230
231@faux.success(_CreateResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700232@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600233@validate.require("build_target.name")
Alex Klein231d2da2019-07-22 16:44:45 -0600234@validate.validation_complete
Alex Klein1699fab2022-09-08 08:46:06 -0600235def Create(
236 input_proto: "image_pb2.CreateImageRequest",
237 output_proto: "image_pb2.CreateImageResult",
238 _config: "api_config.ApiConfig",
239):
240 """Build images.
Alex Klein56355682019-02-07 10:36:54 -0700241
Alex Klein1699fab2022-09-08 08:46:06 -0600242 Args:
243 input_proto: The input message.
244 output_proto: The output message.
245 _config: The API call config.
246 """
247 board = input_proto.build_target.name
Alex Klein56355682019-02-07 10:36:54 -0700248
Alex Klein1699fab2022-09-08 08:46:06 -0600249 # Build the base image if no images provided.
250 to_build = input_proto.image_types or [_BASE_ID]
Alex Klein56355682019-02-07 10:36:54 -0700251
Alex Klein1699fab2022-09-08 08:46:06 -0600252 image_types = _ParseImagesToCreate(to_build)
253 build_config = _ParseCreateBuildConfig(input_proto)
254 factory_build_config = build_config._replace(
255 symlink=LOCATION_FACTORY, output_dir_suffix=LOCATION_FACTORY
256 )
Alex Klein56355682019-02-07 10:36:54 -0700257
Alex Klein1699fab2022-09-08 08:46:06 -0600258 # Try building the core and factory images.
259 # Sorted isn't really necessary here, but it's much easier to test.
260 core_result = image.Build(
261 board, sorted(image_types.core_images), config=build_config
262 )
263 logging.debug("Core Result Images: %s", core_result.images)
Alex Klein56355682019-02-07 10:36:54 -0700264
Alex Klein1699fab2022-09-08 08:46:06 -0600265 factory_result = image.Build(
266 board, image_types.factory, config=factory_build_config
267 )
268 logging.debug("Factory Result Images: %s", factory_result.images)
Will Bradley29a49c22019-10-21 11:50:08 -0600269
Alex Klein1699fab2022-09-08 08:46:06 -0600270 # A successful run will have no images missing, will have run at least one
271 # of the two image sets, and neither attempt errored. The no error condition
272 # should be redundant with no missing images, but is cheap insurance.
273 all_built = core_result.all_built and factory_result.all_built
274 one_ran = core_result.build_run or factory_result.build_run
275 no_errors = not core_result.run_error and not factory_result.run_error
276 output_proto.success = success = all_built and one_ran and no_errors
Will Bradley29a49c22019-10-21 11:50:08 -0600277
Alex Klein1699fab2022-09-08 08:46:06 -0600278 if success:
279 # Success! We need to record the images we built in the output.
280 all_images = {**core_result.images, **factory_result.images}
281 for img_name, img_path in all_images.items():
282 _add_image_to_proto(
283 output_proto, img_path, _IMAGE_MAPPING[img_name], board
284 )
Alex Klein27978a42021-07-27 14:18:10 -0600285
Alex Klein1699fab2022-09-08 08:46:06 -0600286 # Build and record VMs as necessary.
287 for vm_type in image_types.vms:
288 is_test = vm_type in [_TEST_VM_ID, _TEST_GUEST_VM_ID]
289 img_type = _IMAGE_MAPPING[_TEST_ID if is_test else _BASE_ID]
290 img_dir = core_result.images[img_type].parent.resolve()
291 try:
292 if vm_type in [_BASE_GUEST_VM_ID, _TEST_GUEST_VM_ID]:
293 vm_path = image.CreateGuestVm(
294 board, is_test=is_test, image_dir=img_dir
295 )
296 else:
297 vm_path = image.CreateVm(
298 board,
299 disk_layout=build_config.disk_layout,
300 is_test=is_test,
301 image_dir=img_dir,
302 )
303 except image.ImageToVmError as e:
304 cros_build_lib.Die(e)
Will Bradley29a49c22019-10-21 11:50:08 -0600305
Alex Klein1699fab2022-09-08 08:46:06 -0600306 _add_image_to_proto(output_proto, vm_path, vm_type, board)
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700307
Alex Klein1699fab2022-09-08 08:46:06 -0600308 # Build and record any mod images.
309 for mod_type in image_types.mod_images:
310 if mod_type == _RECOVERY_ID:
311 base_image_path = core_result.images[constants.IMAGE_TYPE_BASE]
312 # For ChromeOS Flex special case.
313 if build_config.base_is_recovery:
314 result = image.CopyBaseToRecovery(
315 board=board, image_path=base_image_path
316 )
317 else:
318 result = image.BuildRecoveryImage(
319 board=board, image_path=base_image_path
320 )
321 if result.all_built:
322 _add_image_to_proto(
323 output_proto,
324 result.images[_IMAGE_MAPPING[mod_type]],
325 mod_type,
326 board,
327 )
328 else:
329 cros_build_lib.Die("Failed to create recovery image.")
330 else:
331 cros_build_lib.Die("_RECOVERY_ID is the only mod_image_type.")
Will Bradley29a49c22019-10-21 11:50:08 -0600332
Alex Klein1699fab2022-09-08 08:46:06 -0600333 # Read metric events log and pipe them into output_proto.events.
Alex Kleina0421f32022-09-12 14:39:29 -0600334 if core_result.build_run and core_result.output_dir:
335 _parse_img_metrics_to_response(
336 output_proto, board, core_result.output_dir
337 )
Alex Klein1699fab2022-09-08 08:46:06 -0600338 return controller.RETURN_CODE_SUCCESS
Will Bradley29a49c22019-10-21 11:50:08 -0600339
Alex Klein1699fab2022-09-08 08:46:06 -0600340 else:
341 # Failure, include all of the failed packages in the output when available.
342 packages = core_result.failed_packages + factory_result.failed_packages
343 if not packages:
344 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Alex Klein2557b4f2019-07-11 14:34:00 -0600345
Alex Klein1699fab2022-09-08 08:46:06 -0600346 for package in packages:
347 current = output_proto.failed_packages.add()
348 controller_util.serialize_package_info(package, current)
Alex Klein1bcd9882019-03-19 13:25:24 -0600349
Alex Klein1699fab2022-09-08 08:46:06 -0600350 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
Alex Klein1bcd9882019-03-19 13:25:24 -0600351
Alex Kleincaace392021-07-26 14:28:13 -0600352
Alex Kleina0421f32022-09-12 14:39:29 -0600353def _parse_img_metrics_to_response(
354 output: "image_pb2.CreateImageResult", board: str, build_path: Path
355):
356 """Manually translate the package sizes file to the metrics events.
357
358 This is a temporary hack to manually translate the package sizes file to
359 the metrics output because the metrics library does not reliably transmit
360 the data. This can be removed once we switch over to the new metrics
361 pipeline.
362 """
363 filename = build_path / f"{constants.BASE_IMAGE_BIN}-package-sizes.json"
364 if not filename.exists():
365 logging.error("Package sizes file does not exist.")
366 return
367
368 size_data = json.loads(filename.read_text(encoding="utf-8"))
369
370 ts = int(round(time.time() * 1000))
371
372 # Total size event.
373 event = output.events.add()
374 event.gauge = size_data["total_size"]
375 event.timestamp_milliseconds = ts
376 event.name = f"{board}.total_size.base.rootfs"
377
378 # Package sizes.
379 for pkg, size in size_data["package_sizes"].items():
380 event = output.events.add()
381 event.gauge = size
382 event.timestamp_milliseconds = ts
383 event.name = f"{board}.package_size.base.rootfs.{pkg}"
384
385
Alex Klein27978a42021-07-27 14:18:10 -0600386def _ParseImagesToCreate(to_build: List[int]) -> ImageTypes:
Alex Klein1699fab2022-09-08 08:46:06 -0600387 """Helper function to parse the image types to build.
Alex Klein21b95022019-05-09 14:14:46 -0600388
Alex Klein1699fab2022-09-08 08:46:06 -0600389 This function expresses the dependencies of each image type and adds
390 the requisite image types if they're not explicitly defined.
Alex Klein21b95022019-05-09 14:14:46 -0600391
Alex Klein1699fab2022-09-08 08:46:06 -0600392 Args:
393 to_build: The image type list.
Alex Klein21b95022019-05-09 14:14:46 -0600394
Alex Klein1699fab2022-09-08 08:46:06 -0600395 Returns:
396 ImageTypes: The parsed images to build.
397 """
398 image_types = set()
399 vm_types = set()
400 mod_image_types = set()
401 for current in to_build:
402 # Find out if it's a special case (vm, img mod), or just any old image.
403 if current in _VM_IMAGE_MAPPING:
404 vm_types.add(current)
405 # Make sure we build the image required to build the VM.
406 image_types.add(_VM_IMAGE_MAPPING[current])
407 elif current in _MOD_IMAGE_MAPPING:
408 mod_image_types.add(current)
409 image_types.add(_MOD_IMAGE_MAPPING[current])
410 elif current in _IMAGE_MAPPING:
411 image_types.add(_IMAGE_MAPPING[current])
412 else:
413 # Not expected, but at least it will be obvious if this comes up.
414 cros_build_lib.Die(
415 "The service's known image types do not match those in image.proto. "
416 "Unknown Enum ID: %s" % current
417 )
Alex Klein21b95022019-05-09 14:14:46 -0600418
Alex Klein1699fab2022-09-08 08:46:06 -0600419 # We can only build one type of these images at a time since image_to_vm.sh
420 # uses the default path if a name is not provided.
421 if vm_types.issuperset({_BASE_VM_ID, _TEST_VM_ID}):
422 cros_build_lib.Die("Cannot create more than one VM.")
Alex Klein21b95022019-05-09 14:14:46 -0600423
Alex Klein1699fab2022-09-08 08:46:06 -0600424 return ImageTypes(
425 images=image_types, vms=vm_types, mod_images=mod_image_types
426 )
Alex Klein21b95022019-05-09 14:14:46 -0600427
428
429def _ParseCreateBuildConfig(input_proto):
Alex Klein1699fab2022-09-08 08:46:06 -0600430 """Helper to parse the image build config for Create."""
431 enable_rootfs_verification = not input_proto.disable_rootfs_verification
432 version = input_proto.version or None
433 disk_layout = input_proto.disk_layout or None
434 builder_path = input_proto.builder_path or None
435 base_is_recovery = input_proto.base_is_recovery or False
436 return image.BuildConfig(
437 enable_rootfs_verification=enable_rootfs_verification,
438 replace=True,
439 version=version,
440 disk_layout=disk_layout,
441 builder_path=builder_path,
442 symlink=LOCATION_CORE,
443 base_is_recovery=base_is_recovery,
444 )
Alex Klein21b95022019-05-09 14:14:46 -0600445
Alex Klein1bcd9882019-03-19 13:25:24 -0600446
Michael Mortensen10146cf2019-11-19 19:59:22 -0700447def _SignerTestResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600448 """Set output_proto success field on a successful SignerTest response."""
449 output_proto.success = True
450 return controller.RETURN_CODE_SUCCESS
Michael Mortensen10146cf2019-11-19 19:59:22 -0700451
452
453@faux.success(_SignerTestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700454@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600455@validate.exists("image.path")
Alex Klein231d2da2019-07-22 16:44:45 -0600456@validate.validation_complete
Alex Klein1699fab2022-09-08 08:46:06 -0600457def SignerTest(
458 input_proto: "image_pb2.ImageTestRequest",
459 output_proto: "image_pb2.ImageTestRequest",
460 _config: "api_config.ApiConfig",
461):
462 """Run image tests.
Michael Mortensenc83c9952019-08-05 12:15:12 -0600463
Alex Klein1699fab2022-09-08 08:46:06 -0600464 Args:
465 input_proto: The input message.
466 output_proto: The output message.
467 _config: The API call config.
468 """
469 image_path = input_proto.image.path
Michael Mortensenc83c9952019-08-05 12:15:12 -0600470
Alex Klein1699fab2022-09-08 08:46:06 -0600471 result = image_lib.SecurityTest(image=image_path)
472 output_proto.success = result
473 if result:
474 return controller.RETURN_CODE_SUCCESS
475 else:
476 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Michael Mortensenc83c9952019-08-05 12:15:12 -0600477
Alex Klein076841b2019-08-29 15:19:39 -0600478
Michael Mortensen10146cf2019-11-19 19:59:22 -0700479def _TestResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600480 """Set output_proto success field on a successful Test response."""
481 output_proto.success = True
482 return controller.RETURN_CODE_SUCCESS
Michael Mortensen10146cf2019-11-19 19:59:22 -0700483
484
485@faux.success(_TestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700486@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600487@validate.require("build_target.name", "result.directory")
488@validate.exists("image.path")
489def Test(
490 input_proto: "image_pb2.ImageTestRequest",
491 output_proto: "image_pb2.ImageTestResult",
492 config: "api_config.ApiConfig",
493):
494 """Run image tests.
Alex Klein2966e302019-01-17 13:29:38 -0700495
Alex Klein1699fab2022-09-08 08:46:06 -0600496 Args:
497 input_proto: The input message.
498 output_proto: The output message.
499 config: The API call config.
500 """
501 image_path = input_proto.image.path
502 board = input_proto.build_target.name
503 result_directory = input_proto.result.directory
Alex Klein2966e302019-01-17 13:29:38 -0700504
Alex Klein1699fab2022-09-08 08:46:06 -0600505 if not os.path.isfile(image_path) or not image_path.endswith(".bin"):
506 cros_build_lib.Die(
507 "The image.path must be an existing image file with a .bin extension."
508 )
Alex Klein2966e302019-01-17 13:29:38 -0700509
Alex Klein1699fab2022-09-08 08:46:06 -0600510 if config.validate_only:
511 return controller.RETURN_CODE_VALID_INPUT
Alex Klein231d2da2019-07-22 16:44:45 -0600512
Alex Klein1699fab2022-09-08 08:46:06 -0600513 success = image.Test(board, result_directory, image_dir=image_path)
514 output_proto.success = success
Alex Klein8cb365a2019-05-15 16:24:53 -0600515
Alex Klein1699fab2022-09-08 08:46:06 -0600516 if success:
517 return controller.RETURN_CODE_SUCCESS
518 else:
519 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Jack Neus761e1842020-12-01 18:20:11 +0000520
521
522@faux.empty_success
523@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600524@validate.require("gs_image_dir", "sysroot.build_target.name")
525def PushImage(
526 input_proto: "image_pb2.PushImageRequest",
527 _output_proto: "image_pb2.PushImageResponse",
528 config: "api.config.ApiConfig",
529):
530 """Push artifacts from the archive bucket to the release bucket.
Jack Neus761e1842020-12-01 18:20:11 +0000531
Alex Klein1699fab2022-09-08 08:46:06 -0600532 Wraps chromite/scripts/pushimage.py.
Jack Neus761e1842020-12-01 18:20:11 +0000533
Alex Klein1699fab2022-09-08 08:46:06 -0600534 Args:
535 input_proto: Input proto.
536 _output_proto: Output proto.
537 config: The API call config.
Jack Neus761e1842020-12-01 18:20:11 +0000538
Alex Klein1699fab2022-09-08 08:46:06 -0600539 Returns:
540 A controller return code (e.g. controller.RETURN_CODE_SUCCESS).
541 """
542 sign_types = []
543 if input_proto.sign_types:
544 for sign_type in input_proto.sign_types:
545 if sign_type not in SUPPORTED_IMAGE_TYPES:
546 logging.error("unsupported sign type %g", sign_type)
547 return controller.RETURN_CODE_INVALID_INPUT
548 sign_types.append(SUPPORTED_IMAGE_TYPES[sign_type])
Jack Neus761e1842020-12-01 18:20:11 +0000549
Alex Klein1699fab2022-09-08 08:46:06 -0600550 # If configured for validation only we're done here.
551 if config.validate_only:
552 return controller.RETURN_CODE_VALID_INPUT
Jack Neus761e1842020-12-01 18:20:11 +0000553
Alex Klein1699fab2022-09-08 08:46:06 -0600554 kwargs = {}
555 if input_proto.profile.name:
556 kwargs["profile"] = input_proto.profile.name
557 if input_proto.dest_bucket:
558 kwargs["dest_bucket"] = input_proto.dest_bucket
559 if input_proto.channels:
560 kwargs["force_channels"] = [
561 common_pb2.Channel.Name(channel).lower()[len("channel_") :]
562 for channel in input_proto.channels
563 ]
564 try:
565 channel_to_uris = pushimage.PushImage(
566 input_proto.gs_image_dir,
567 input_proto.sysroot.build_target.name,
568 dry_run=input_proto.dryrun,
569 sign_types=sign_types,
570 **kwargs,
571 )
572 except Exception:
573 logging.error("PushImage failed: ", exc_info=True)
574 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
575 if channel_to_uris:
576 for uris in channel_to_uris.values():
577 for uri in uris:
578 _output_proto.instructions.add().instructions_file_path = uri
579 return controller.RETURN_CODE_SUCCESS