blob: 6a2dcf0b0056b811900edcf8d0c8eaa7d35e2a12 [file] [log] [blame]
Alex Klein2966e302019-01-17 13:29:38 -07001# -*- coding: utf-8 -*-
2# Copyright 2018 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Image API Service.
7
8The image related API endpoints should generally be found here.
9"""
10
11from __future__ import print_function
12
13import os
14
Alex Klein8cb365a2019-05-15 16:24:53 -060015from chromite.api import controller
Alex Klein076841b2019-08-29 15:19:39 -060016from chromite.api import faux
Alex Klein2b236722019-06-19 15:44:26 -060017from chromite.api import validate
David Burgerb171d652019-05-13 16:07:00 -060018from chromite.api.gen.chromiumos import common_pb2
Alex Klein4f0eb432019-05-02 13:56:04 -060019from chromite.lib import cros_build_lib
Alex Klein56355682019-02-07 10:36:54 -070020from chromite.lib import constants
21from chromite.lib import image_lib
Alex Kleinb7cdbe62019-02-22 11:41:32 -070022from chromite.service import image
Alex Klein2966e302019-01-17 13:29:38 -070023
Alex Klein56355682019-02-07 10:36:54 -070024# The image.proto ImageType enum ids.
David Burgerb171d652019-05-13 16:07:00 -060025_BASE_ID = common_pb2.BASE
26_DEV_ID = common_pb2.DEV
27_TEST_ID = common_pb2.TEST
Alex Klein21b95022019-05-09 14:14:46 -060028_BASE_VM_ID = common_pb2.BASE_VM
29_TEST_VM_ID = common_pb2.TEST_VM
Michael Mortenseneefe8952019-08-12 15:37:15 -060030_RECOVERY_ID = common_pb2.RECOVERY
31_FACTORY_ID = common_pb2.FACTORY
32_FIRMWARE_ID = common_pb2.FIRMWARE
Alex Klein56355682019-02-07 10:36:54 -070033
34# Dict to allow easily translating names to enum ids and vice versa.
35_IMAGE_MAPPING = {
36 _BASE_ID: constants.IMAGE_TYPE_BASE,
37 constants.IMAGE_TYPE_BASE: _BASE_ID,
38 _DEV_ID: constants.IMAGE_TYPE_DEV,
39 constants.IMAGE_TYPE_DEV: _DEV_ID,
40 _TEST_ID: constants.IMAGE_TYPE_TEST,
41 constants.IMAGE_TYPE_TEST: _TEST_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060042 _RECOVERY_ID: constants.IMAGE_TYPE_RECOVERY,
43 constants.IMAGE_TYPE_RECOVERY: _RECOVERY_ID,
44 _FACTORY_ID: constants.IMAGE_TYPE_FACTORY,
45 constants.IMAGE_TYPE_FACTORY: _FACTORY_ID,
46 _FIRMWARE_ID: constants.IMAGE_TYPE_FIRMWARE,
47 constants.IMAGE_TYPE_FIRMWARE: _FIRMWARE_ID,
Alex Klein56355682019-02-07 10:36:54 -070048}
49
Alex Klein21b95022019-05-09 14:14:46 -060050_VM_IMAGE_MAPPING = {
51 _BASE_VM_ID: _IMAGE_MAPPING[_BASE_ID],
52 _TEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
53}
54
Alex Klein56355682019-02-07 10:36:54 -070055
Alex Klein076841b2019-08-29 15:19:39 -060056@faux.all_empty
Alex Klein2b236722019-06-19 15:44:26 -060057@validate.require('build_target.name')
Alex Klein231d2da2019-07-22 16:44:45 -060058@validate.validation_complete
59def Create(input_proto, output_proto, _config):
Alex Klein56355682019-02-07 10:36:54 -070060 """Build an image.
61
62 Args:
63 input_proto (image_pb2.CreateImageRequest): The input message.
64 output_proto (image_pb2.CreateImageResult): The output message.
Alex Klein231d2da2019-07-22 16:44:45 -060065 _config (api_config.ApiConfig): The API call config.
Alex Klein56355682019-02-07 10:36:54 -070066 """
67 board = input_proto.build_target.name
Alex Klein56355682019-02-07 10:36:54 -070068
Alex Klein56355682019-02-07 10:36:54 -070069 # Build the base image if no images provided.
70 to_build = input_proto.image_types or [_BASE_ID]
Alex Klein56355682019-02-07 10:36:54 -070071
Alex Klein21b95022019-05-09 14:14:46 -060072 image_types, vm_types = _ParseImagesToCreate(to_build)
73 build_config = _ParseCreateBuildConfig(input_proto)
Alex Klein56355682019-02-07 10:36:54 -070074
75 # Sorted isn't really necessary here, but it's much easier to test.
Alex Klein1bcd9882019-03-19 13:25:24 -060076 result = image.Build(board=board, images=sorted(list(image_types)),
77 config=build_config)
Alex Klein56355682019-02-07 10:36:54 -070078
Alex Klein1bcd9882019-03-19 13:25:24 -060079 output_proto.success = result.success
80 if result.success:
81 # Success -- we need to list out the images we built in the output.
82 _PopulateBuiltImages(board, image_types, output_proto)
83 else:
Alex Klein2557b4f2019-07-11 14:34:00 -060084 # Failure, include all of the failed packages in the output when available.
85 if not result.failed_packages:
86 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
87
Alex Klein1bcd9882019-03-19 13:25:24 -060088 for package in result.failed_packages:
89 current = output_proto.failed_packages.add()
90 current.category = package.category
91 current.package_name = package.package
92 if package.version:
93 current.version = package.version
94
Alex Klein8cb365a2019-05-15 16:24:53 -060095 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
Alex Klein1bcd9882019-03-19 13:25:24 -060096
Alex Klein21b95022019-05-09 14:14:46 -060097 if not vm_types:
98 # No VMs to build, we can exit now.
Alex Klein231d2da2019-07-22 16:44:45 -060099 return controller.RETURN_CODE_SUCCESS
Alex Klein21b95022019-05-09 14:14:46 -0600100
101 # There can be only one.
102 vm_type = vm_types.pop()
103 is_test = vm_type == _TEST_VM_ID
104 try:
105 vm_path = image.CreateVm(board, is_test=is_test)
106 except image.ImageToVmError as e:
Mike Frysinger6b5c3cd2019-08-27 16:51:00 -0400107 cros_build_lib.Die(e)
Alex Klein21b95022019-05-09 14:14:46 -0600108
109 new_image = output_proto.images.add()
110 new_image.path = vm_path
111 new_image.type = vm_type
112 new_image.build_target.name = board
113
114
115def _ParseImagesToCreate(to_build):
116 """Helper function to parse the image types to build.
117
118 This function exists just to clean up the Create function.
119
120 Args:
121 to_build (list[int]): The image type list.
122
123 Returns:
124 (set, set): The image and vm types, respectively, that need to be built.
125 """
126 image_types = set()
127 vm_types = set()
128 for current in to_build:
129 if current in _IMAGE_MAPPING:
130 image_types.add(_IMAGE_MAPPING[current])
131 elif current in _VM_IMAGE_MAPPING:
132 vm_types.add(current)
133 # Make sure we build the image required to build the VM.
134 image_types.add(_VM_IMAGE_MAPPING[current])
135 else:
136 # Not expected, but at least it will be obvious if this comes up.
137 cros_build_lib.Die(
138 "The service's known image types do not match those in image.proto. "
139 'Unknown Enum ID: %s' % current)
140
141 if len(vm_types) > 1:
142 cros_build_lib.Die('Cannot create more than one VM.')
143
144 return image_types, vm_types
145
146
147def _ParseCreateBuildConfig(input_proto):
148 """Helper to parse the image build config for Create."""
149 enable_rootfs_verification = not input_proto.disable_rootfs_verification
150 version = input_proto.version or None
151 disk_layout = input_proto.disk_layout or None
152 builder_path = input_proto.builder_path or None
153 return image.BuildConfig(
154 enable_rootfs_verification=enable_rootfs_verification, replace=True,
155 version=version, disk_layout=disk_layout, builder_path=builder_path,
156 )
157
Alex Klein1bcd9882019-03-19 13:25:24 -0600158
159def _PopulateBuiltImages(board, image_types, output_proto):
160 """Helper to list out built images for Create."""
Alex Klein56355682019-02-07 10:36:54 -0700161 # Build out the ImageType->ImagePath mapping in the output.
162 # We're using the default path, so just fetch that, but read the symlink so
163 # the path we're returning is somewhat more permanent.
164 latest_link = image_lib.GetLatestImageLink(board)
Alex Klein4f0eb432019-05-02 13:56:04 -0600165 base_path = os.path.realpath(latest_link)
Alex Klein56355682019-02-07 10:36:54 -0700166
167 for current in image_types:
168 type_id = _IMAGE_MAPPING[current]
169 path = os.path.join(base_path, constants.IMAGE_TYPE_TO_NAME[current])
170
171 new_image = output_proto.images.add()
172 new_image.path = path
173 new_image.type = type_id
Alex Klein4f0eb432019-05-02 13:56:04 -0600174 new_image.build_target.name = board
175
176
Alex Klein076841b2019-08-29 15:19:39 -0600177@faux.all_empty
Michael Mortensenc83c9952019-08-05 12:15:12 -0600178@validate.exists('image.path')
Alex Klein231d2da2019-07-22 16:44:45 -0600179@validate.validation_complete
180def SignerTest(input_proto, output_proto, _config):
Michael Mortensenc83c9952019-08-05 12:15:12 -0600181 """Run image tests.
182
183 Args:
184 input_proto (image_pb2.ImageTestRequest): The input message.
185 output_proto (image_pb2.ImageTestResult): The output message.
Alex Klein231d2da2019-07-22 16:44:45 -0600186 _config (api_config.ApiConfig): The API call config.
Michael Mortensenc83c9952019-08-05 12:15:12 -0600187 """
Michael Mortensenc83c9952019-08-05 12:15:12 -0600188 image_path = input_proto.image.path
189
Alex Klein231d2da2019-07-22 16:44:45 -0600190 result = image_lib.SecurityTest(image=image_path)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600191 output_proto.success = result
192 if result:
193 return controller.RETURN_CODE_SUCCESS
194 else:
195 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
196
Alex Klein076841b2019-08-29 15:19:39 -0600197
198@faux.all_empty
Alex Klein2b236722019-06-19 15:44:26 -0600199@validate.require('build_target.name', 'result.directory')
200@validate.exists('image.path')
Alex Klein231d2da2019-07-22 16:44:45 -0600201def Test(input_proto, output_proto, config):
Alex Klein2966e302019-01-17 13:29:38 -0700202 """Run image tests.
203
204 Args:
205 input_proto (image_pb2.ImageTestRequest): The input message.
206 output_proto (image_pb2.ImageTestResult): The output message.
Alex Klein231d2da2019-07-22 16:44:45 -0600207 config (api_config.ApiConfig): The API call config.
Alex Klein2966e302019-01-17 13:29:38 -0700208 """
209 image_path = input_proto.image.path
210 board = input_proto.build_target.name
211 result_directory = input_proto.result.directory
212
Alex Klein2966e302019-01-17 13:29:38 -0700213 if not os.path.isfile(image_path) or not image_path.endswith('.bin'):
Alex Klein4f0eb432019-05-02 13:56:04 -0600214 cros_build_lib.Die(
Alex Klein2966e302019-01-17 13:29:38 -0700215 'The image.path must be an existing image file with a .bin extension.')
216
Alex Klein231d2da2019-07-22 16:44:45 -0600217 if config.validate_only:
218 return controller.RETURN_CODE_VALID_INPUT
219
Alex Klein8cb365a2019-05-15 16:24:53 -0600220 success = image.Test(board, result_directory, image_dir=image_path)
221 output_proto.success = success
222
223 if success:
224 return controller.RETURN_CODE_SUCCESS
225 else:
226 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY