blob: e3aa62d0d5e5135b12639554558689c0adaee44a [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2019 The ChromiumOS Authors
Alex Klein146d4772019-06-20 13:48:25 -06002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Alex Kleine0fa6422019-06-21 12:01:39 -06005"""Router class for the Build API.
6
7Handles routing requests to the appropriate controller and handles service
8registration.
9"""
Alex Klein146d4772019-06-20 13:48:25 -060010
Alex Klein92341cd2020-02-27 14:11:04 -070011import collections
Mike Frysingerc2e72b42023-03-03 22:34:04 -050012import contextlib
Alex Klein146d4772019-06-20 13:48:25 -060013import importlib
Chris McDonald1672ddb2021-07-21 11:48:23 -060014import logging
Alex Klein146d4772019-06-20 13:48:25 -060015import os
Tomasz Tylendab4292302021-08-08 18:59:36 +090016from types import ModuleType
17from typing import Callable, List, TYPE_CHECKING
Alex Klein146d4772019-06-20 13:48:25 -060018
Mike Frysinger2c024062021-05-22 15:43:22 -040019from chromite.third_party.google.protobuf import symbol_database
Alex Klein146d4772019-06-20 13:48:25 -060020
21from chromite.api import controller
22from chromite.api import field_handler
Alex Klein4de25e82019-08-05 15:58:39 -060023from chromite.api.gen.chromite.api import android_pb2
Alex Klein54e38e32019-06-21 14:54:17 -060024from chromite.api.gen.chromite.api import api_pb2
Alex Klein146d4772019-06-20 13:48:25 -060025from chromite.api.gen.chromite.api import artifacts_pb2
26from chromite.api.gen.chromite.api import binhost_pb2
27from chromite.api.gen.chromite.api import build_api_pb2
Tristan Honscheid52ba4d22023-02-09 11:59:29 -070028from chromite.api.gen.chromite.api import copybot_pb2
Alex Klein146d4772019-06-20 13:48:25 -060029from chromite.api.gen.chromite.api import depgraph_pb2
Jett Rink17ed0f52020-09-25 17:14:31 -060030from chromite.api.gen.chromite.api import firmware_pb2
Alex Klein146d4772019-06-20 13:48:25 -060031from chromite.api.gen.chromite.api import image_pb2
Alex Kleind4d9caa2021-11-10 15:44:52 -070032from chromite.api.gen.chromite.api import metadata_pb2
Lizzy Preslande723c012022-06-10 05:05:37 +000033from chromite.api.gen.chromite.api import observability_pb2
Alex Kleineb77ffa2019-05-28 14:47:44 -060034from chromite.api.gen.chromite.api import packages_pb2
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060035from chromite.api.gen.chromite.api import payload_pb2
Alexander Liu008389c2022-06-27 18:30:11 +000036from chromite.api.gen.chromite.api import portage_explorer_pb2
Alex Klein146d4772019-06-20 13:48:25 -060037from chromite.api.gen.chromite.api import sdk_pb2
38from chromite.api.gen.chromite.api import sysroot_pb2
39from chromite.api.gen.chromite.api import test_pb2
Tiancong Wangaf050172019-07-10 11:52:03 -070040from chromite.api.gen.chromite.api import toolchain_pb2
Alex Klein146d4772019-06-20 13:48:25 -060041from chromite.lib import cros_build_lib
Alex Klein146d4772019-06-20 13:48:25 -060042from chromite.lib import osutils
Alex Klein92341cd2020-02-27 14:11:04 -070043from chromite.utils import memoize
Alex Klein146d4772019-06-20 13:48:25 -060044
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040045
Tomasz Tylendab4292302021-08-08 18:59:36 +090046if TYPE_CHECKING:
Alex Klein1699fab2022-09-08 08:46:06 -060047 from chromite.third_party import google
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040048
Alex Klein1699fab2022-09-08 08:46:06 -060049 from chromite.api import api_config
50 from chromite.api import message_util
Mike Frysinger88770ef2021-05-21 11:04:00 -040051
Alex Klein92341cd2020-02-27 14:11:04 -070052MethodData = collections.namedtuple(
Alex Klein1699fab2022-09-08 08:46:06 -060053 "MethodData", ("service_descriptor", "module_name", "method_descriptor")
54)
Alex Klein146d4772019-06-20 13:48:25 -060055
Mike Frysingeref94e4c2020-02-10 23:59:54 -050056
Alex Klein146d4772019-06-20 13:48:25 -060057class Error(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -060058 """Base error class for the module."""
Alex Klein146d4772019-06-20 13:48:25 -060059
60
Alex Kleind3394c22020-06-16 14:05:06 -060061class InvalidSdkError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060062 """Raised when the SDK is invalid or does not exist."""
Alex Kleind3394c22020-06-16 14:05:06 -060063
64
Alex Klein146d4772019-06-20 13:48:25 -060065class CrosSdkNotRunError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060066 """Raised when the cros_sdk command could not be run to enter the chroot."""
Alex Klein146d4772019-06-20 13:48:25 -060067
68
69# API Service Errors.
70class UnknownServiceError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060071 """Error raised when the requested service has not been registered."""
Alex Klein146d4772019-06-20 13:48:25 -060072
73
74class ControllerModuleNotDefinedError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060075 """Error class for when no controller has been defined for a service."""
Alex Klein146d4772019-06-20 13:48:25 -060076
77
78class ServiceControllerNotFoundError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060079 """Error raised when the service's controller cannot be imported."""
Alex Klein146d4772019-06-20 13:48:25 -060080
81
82# API Method Errors.
83class UnknownMethodError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060084 """The service has been defined in the proto, but the method has not."""
Alex Klein146d4772019-06-20 13:48:25 -060085
86
87class MethodNotFoundError(Error):
Alex Klein54c891a2023-01-24 10:45:41 -070088 """The method's implementation cannot be found in the controller."""
Alex Klein146d4772019-06-20 13:48:25 -060089
90
91class Router(object):
Alex Klein1699fab2022-09-08 08:46:06 -060092 """Encapsulates the request dispatching logic."""
Alex Klein146d4772019-06-20 13:48:25 -060093
Alex Klein1699fab2022-09-08 08:46:06 -060094 REEXEC_INPUT_FILE = "input_proto"
95 REEXEC_OUTPUT_FILE = "output_proto"
96 REEXEC_CONFIG_FILE = "config_proto"
Alex Kleinbd6edf82019-07-18 10:30:49 -060097
Alex Klein1699fab2022-09-08 08:46:06 -060098 def __init__(self):
99 self._services = {}
100 self._aliases = {}
101 # All imported generated messages get added to this symbol db.
102 self._sym_db = symbol_database.Default()
Alex Klein146d4772019-06-20 13:48:25 -0600103
Alex Klein1699fab2022-09-08 08:46:06 -0600104 # Save the service and method extension info for looking up
105 # configured extension data.
106 extensions = build_api_pb2.DESCRIPTOR.extensions_by_name
107 self._svc_options_ext = extensions["service_options"]
108 self._method_options_ext = extensions["method_options"]
Alex Klein92341cd2020-02-27 14:11:04 -0700109
Alex Klein1699fab2022-09-08 08:46:06 -0600110 @memoize.Memoize
111 def _get_method_data(self, service_name, method_name):
112 """Get the descriptors and module name for the given Service/Method."""
113 try:
114 svc, module_name = self._services[service_name]
115 except KeyError:
116 raise UnknownServiceError(
117 "The %s service has not been registered." % service_name
118 )
Alex Klein92341cd2020-02-27 14:11:04 -0700119
Alex Klein1699fab2022-09-08 08:46:06 -0600120 try:
121 method_desc = svc.methods_by_name[method_name]
122 except KeyError:
123 raise UnknownMethodError(
124 "The %s method has not been defined in the %s "
125 "service." % (method_name, service_name)
126 )
Alex Klein92341cd2020-02-27 14:11:04 -0700127
Alex Klein1699fab2022-09-08 08:46:06 -0600128 return MethodData(
129 service_descriptor=svc,
130 module_name=module_name,
131 method_descriptor=method_desc,
132 )
Alex Klein92341cd2020-02-27 14:11:04 -0700133
Alex Klein1699fab2022-09-08 08:46:06 -0600134 def get_input_message_instance(self, service_name, method_name):
135 """Get an empty input message instance for the specified method."""
136 method_data = self._get_method_data(service_name, method_name)
137 return self._sym_db.GetPrototype(
138 method_data.method_descriptor.input_type
139 )()
Alex Klein92341cd2020-02-27 14:11:04 -0700140
Alex Klein1699fab2022-09-08 08:46:06 -0600141 def _get_output_message_instance(self, service_name, method_name):
142 """Get an empty output message instance for the specified method."""
143 method_data = self._get_method_data(service_name, method_name)
144 return self._sym_db.GetPrototype(
145 method_data.method_descriptor.output_type
146 )()
Alex Klein92341cd2020-02-27 14:11:04 -0700147
Alex Klein1699fab2022-09-08 08:46:06 -0600148 def _get_module_name(self, service_name, method_name):
149 """Get the name of the module containing the endpoint implementation."""
150 return self._get_method_data(service_name, method_name).module_name
Alex Klein92341cd2020-02-27 14:11:04 -0700151
Alex Klein1699fab2022-09-08 08:46:06 -0600152 def _get_service_options(self, service_name, method_name):
153 """Get the configured service options for the endpoint."""
154 method_data = self._get_method_data(service_name, method_name)
155 svc_extensions = method_data.service_descriptor.GetOptions().Extensions
156 return svc_extensions[self._svc_options_ext]
Alex Klein92341cd2020-02-27 14:11:04 -0700157
Alex Klein1699fab2022-09-08 08:46:06 -0600158 def _get_method_options(self, service_name, method_name):
159 """Get the configured method options for the endpoint."""
160 method_data = self._get_method_data(service_name, method_name)
161 method_extensions = (
162 method_data.method_descriptor.GetOptions().Extensions
163 )
164 return method_extensions[self._method_options_ext]
Alex Klein146d4772019-06-20 13:48:25 -0600165
Alex Klein1699fab2022-09-08 08:46:06 -0600166 def Register(self, proto_module: ModuleType):
167 """Register the services from a generated proto module.
Alex Klein146d4772019-06-20 13:48:25 -0600168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600170 proto_module: The generated proto module to register.
Alex Klein146d4772019-06-20 13:48:25 -0600171
Alex Klein1699fab2022-09-08 08:46:06 -0600172 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600173 ServiceModuleNotDefinedError when the service cannot be found in the
174 provided module.
Alex Klein1699fab2022-09-08 08:46:06 -0600175 """
176 services = proto_module.DESCRIPTOR.services_by_name
177 for service_name, svc in services.items():
178 module_name = (
179 svc.GetOptions().Extensions[self._svc_options_ext].module
180 )
Alex Klein146d4772019-06-20 13:48:25 -0600181
Alex Klein1699fab2022-09-08 08:46:06 -0600182 if not module_name:
183 raise ControllerModuleNotDefinedError(
Alex Klein54c891a2023-01-24 10:45:41 -0700184 "The module must be defined in the service definition: "
185 f"{proto_module}.{service_name}"
Alex Klein1699fab2022-09-08 08:46:06 -0600186 )
Alex Klein146d4772019-06-20 13:48:25 -0600187
Alex Klein1699fab2022-09-08 08:46:06 -0600188 self._services[svc.full_name] = (svc, module_name)
Alex Klein146d4772019-06-20 13:48:25 -0600189
Alex Klein1699fab2022-09-08 08:46:06 -0600190 def ListMethods(self):
191 """List all methods registered with the router."""
192 services = []
193 for service_name, (svc, _module) in self._services.items():
194 svc_visibility = getattr(
195 svc.GetOptions().Extensions[self._svc_options_ext],
196 "service_visibility",
197 build_api_pb2.LV_VISIBLE,
198 )
199 if svc_visibility == build_api_pb2.LV_HIDDEN:
200 continue
Alex Klein6cce6f62021-03-02 14:24:05 -0700201
Alex Klein1699fab2022-09-08 08:46:06 -0600202 for method_name in svc.methods_by_name.keys():
203 method_options = self._get_method_options(
204 service_name, method_name
205 )
206 method_visibility = getattr(
207 method_options,
208 "method_visibility",
209 build_api_pb2.LV_VISIBLE,
210 )
211 if method_visibility == build_api_pb2.LV_HIDDEN:
212 continue
213
214 services.append("%s/%s" % (service_name, method_name))
215
216 return sorted(services)
217
218 def Route(
219 self,
220 service_name: str,
221 method_name: str,
222 config: "api_config.ApiConfig",
223 input_handler: "message_util.MessageHandler",
224 output_handlers: List["message_util.MessageHandler"],
225 config_handler: "message_util.MessageHandler",
226 ) -> int:
227 """Dispatch the request.
228
229 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600230 service_name: The fully qualified service name.
231 method_name: The name of the method being called.
232 config: The call configs.
233 input_handler: The request message handler.
234 output_handlers: The response message handlers.
235 config_handler: The config message handler.
Alex Klein1699fab2022-09-08 08:46:06 -0600236
237 Returns:
Alex Kleina0442682022-10-10 13:47:38 -0600238 The return code.
Alex Klein1699fab2022-09-08 08:46:06 -0600239
240 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600241 InvalidInputFileError when the input file cannot be read.
242 InvalidOutputFileError when the output file cannot be written.
243 ServiceModuleNotFoundError when the service module cannot be
244 imported.
245 MethodNotFoundError when the method cannot be retrieved from the
246 module.
Alex Klein1699fab2022-09-08 08:46:06 -0600247 """
248 input_msg = self.get_input_message_instance(service_name, method_name)
249 input_handler.read_into(input_msg)
250
251 # Get an empty output message instance.
252 output_msg = self._get_output_message_instance(
253 service_name, method_name
254 )
255
256 # Fetch the method options for chroot and method name overrides.
Alex Klein6cce6f62021-03-02 14:24:05 -0700257 method_options = self._get_method_options(service_name, method_name)
Alex Klein6cce6f62021-03-02 14:24:05 -0700258
Alex Klein1699fab2022-09-08 08:46:06 -0600259 # Check the chroot settings before running.
260 service_options = self._get_service_options(service_name, method_name)
261 if self._ChrootCheck(service_options, method_options, config):
262 # Run inside the chroot instead.
263 logging.info("Re-executing the endpoint inside the chroot.")
264 return self._ReexecuteInside(
265 input_msg,
266 output_msg,
267 config,
268 input_handler,
269 output_handlers,
270 config_handler,
271 service_name,
272 method_name,
273 )
Alex Klein146d4772019-06-20 13:48:25 -0600274
Alex Klein1699fab2022-09-08 08:46:06 -0600275 # Allow proto-based method name override.
276 if method_options.HasField("implementation_name"):
277 implementation_name = method_options.implementation_name
278 else:
279 implementation_name = method_name
Alex Klein146d4772019-06-20 13:48:25 -0600280
Alex Klein1699fab2022-09-08 08:46:06 -0600281 # Import the module and get the method.
282 module_name = self._get_module_name(service_name, method_name)
283 method_impl = self._GetMethod(module_name, implementation_name)
Alex Klein146d4772019-06-20 13:48:25 -0600284
Alex Klein1699fab2022-09-08 08:46:06 -0600285 # Successfully located; call and return.
286 return_code = method_impl(input_msg, output_msg, config)
287 if return_code is None:
288 return_code = controller.RETURN_CODE_SUCCESS
Alex Klein146d4772019-06-20 13:48:25 -0600289
Alex Klein1699fab2022-09-08 08:46:06 -0600290 for h in output_handlers:
291 h.write_from(output_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600292
Alex Klein1699fab2022-09-08 08:46:06 -0600293 return return_code
Alex Klein146d4772019-06-20 13:48:25 -0600294
Alex Klein1699fab2022-09-08 08:46:06 -0600295 def _ChrootCheck(
296 self,
297 service_options: "google.protobuf.Message",
298 method_options: "google.protobuf.Message",
299 config: "api_config.ApiConfig",
300 ) -> bool:
Alex Kleina0442682022-10-10 13:47:38 -0600301 """Check the chroot options; execute assertion or note reexec as needed.
Alex Klein146d4772019-06-20 13:48:25 -0600302
Alex Klein1699fab2022-09-08 08:46:06 -0600303 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600304 service_options: The service options.
305 method_options: The method options.
306 config: The Build API call config instance.
Alex Klein146d4772019-06-20 13:48:25 -0600307
Alex Klein1699fab2022-09-08 08:46:06 -0600308 Returns:
Alex Kleina0442682022-10-10 13:47:38 -0600309 True iff it needs to be reexeced inside the chroot.
Alex Kleinbd6edf82019-07-18 10:30:49 -0600310
Alex Klein1699fab2022-09-08 08:46:06 -0600311 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600312 cros_build_lib.DieSystemExit when the chroot setting cannot be
313 satisfied.
Alex Klein1699fab2022-09-08 08:46:06 -0600314 """
315 if not config.run_endpoint:
316 # Do not enter the chroot for validate only and mock calls.
317 return False
Alex Klein146d4772019-06-20 13:48:25 -0600318
Alex Klein1699fab2022-09-08 08:46:06 -0600319 chroot_assert = build_api_pb2.NO_ASSERTION
320 if method_options.HasField("method_chroot_assert"):
321 # Prefer the method option when set.
322 chroot_assert = method_options.method_chroot_assert
323 elif service_options.HasField("service_chroot_assert"):
324 # Fall back to the service option.
325 chroot_assert = service_options.service_chroot_assert
Alex Klein146d4772019-06-20 13:48:25 -0600326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 if chroot_assert == build_api_pb2.INSIDE:
328 return not cros_build_lib.IsInsideChroot()
329 elif chroot_assert == build_api_pb2.OUTSIDE:
330 # If it must be run outside we have to already be outside.
331 cros_build_lib.AssertOutsideChroot()
Alex Klein146d4772019-06-20 13:48:25 -0600332
Alex Klein1699fab2022-09-08 08:46:06 -0600333 return False
Alex Klein146d4772019-06-20 13:48:25 -0600334
Alex Klein1699fab2022-09-08 08:46:06 -0600335 def _ReexecuteInside(
336 self,
337 input_msg: "google.protobuf.Message",
338 output_msg: "google.protobuf.Message",
339 config: "api_config.ApiConfig",
340 input_handler: "message_util.MessageHandler",
341 output_handlers: List["message_util.MessageHandler"],
342 config_handler: "message_util.MessageHandler",
343 service_name: str,
344 method_name: str,
345 ):
346 """Re-execute the service inside the chroot.
Alex Klein146d4772019-06-20 13:48:25 -0600347
Alex Klein1699fab2022-09-08 08:46:06 -0600348 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600349 input_msg: The parsed input message.
350 output_msg: The empty output message instance.
351 config: The call configs.
352 input_handler: Input message handler.
353 output_handlers: Output message handlers.
354 config_handler: Config message handler.
355 service_name: The name of the service to run.
356 method_name: The name of the method to run.
Alex Klein1699fab2022-09-08 08:46:06 -0600357 """
358 # Parse the chroot and clear the chroot field in the input message.
359 chroot = field_handler.handle_chroot(input_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600360
Alex Klein1699fab2022-09-08 08:46:06 -0600361 if not chroot.exists():
362 raise InvalidSdkError("Chroot does not exist.")
Alex Klein146d4772019-06-20 13:48:25 -0600363
Mike Frysingerc2e72b42023-03-03 22:34:04 -0500364 # Use a ExitStack to avoid the deep nesting this many context managers
365 # introduces.
366 with contextlib.ExitStack() as stack:
Alex Klein1699fab2022-09-08 08:46:06 -0600367 # TempDirs setup.
Mike Frysingerc2e72b42023-03-03 22:34:04 -0500368 tempdir = stack.enter_context(chroot.tempdir())
369 sync_tempdir = stack.enter_context(chroot.tempdir())
Alex Klein1699fab2022-09-08 08:46:06 -0600370 # The copy-paths-in context manager to handle Path messages.
Mike Frysingerc2e72b42023-03-03 22:34:04 -0500371 stack.enter_context(
372 field_handler.copy_paths_in(
373 input_msg,
374 chroot.tmp,
375 prefix=chroot.path,
376 )
Alex Klein1699fab2022-09-08 08:46:06 -0600377 )
378 # The sync-directories context manager to handle SyncedDir messages.
Mike Frysingerc2e72b42023-03-03 22:34:04 -0500379 stack.enter_context(
380 field_handler.sync_dirs(
381 input_msg,
382 sync_tempdir,
383 prefix=chroot.path,
384 )
Alex Klein1699fab2022-09-08 08:46:06 -0600385 )
Alex Klein146d4772019-06-20 13:48:25 -0600386
Alex Klein1699fab2022-09-08 08:46:06 -0600387 # Parse goma.
Brian Norrisd0dfeae2023-03-09 13:06:47 -0800388 chroot.goma = field_handler.handle_goma(
389 input_msg, chroot.path, chroot.out_path
390 )
Alex Kleind1e9e5c2020-12-14 12:32:32 -0700391
Alex Klein1699fab2022-09-08 08:46:06 -0600392 # Parse remoteexec.
393 chroot.remoteexec = field_handler.handle_remoteexec(input_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600394
Alex Klein54c891a2023-01-24 10:45:41 -0700395 # Build inside-chroot paths for the input, output, and config
396 # messages.
Alex Klein1699fab2022-09-08 08:46:06 -0600397 new_input = os.path.join(tempdir, self.REEXEC_INPUT_FILE)
Brian Norris8faf47b2023-03-03 16:19:08 -0800398 chroot_input = chroot.chroot_path(new_input)
Alex Klein1699fab2022-09-08 08:46:06 -0600399 new_output = os.path.join(tempdir, self.REEXEC_OUTPUT_FILE)
Brian Norris8faf47b2023-03-03 16:19:08 -0800400 chroot_output = chroot.chroot_path(new_output)
Alex Klein1699fab2022-09-08 08:46:06 -0600401 new_config = os.path.join(tempdir, self.REEXEC_CONFIG_FILE)
Brian Norris8faf47b2023-03-03 16:19:08 -0800402 chroot_config = chroot.chroot_path(new_config)
Alex Klein146d4772019-06-20 13:48:25 -0600403
Alex Klein1699fab2022-09-08 08:46:06 -0600404 # Setup the inside-chroot message files.
405 logging.info("Writing input message to: %s", new_input)
406 input_handler.write_from(input_msg, path=new_input)
407 osutils.Touch(new_output)
408 logging.info("Writing config message to: %s", new_config)
409 config_handler.write_from(config.get_proto(), path=new_config)
Alex Klein146d4772019-06-20 13:48:25 -0600410
Alex Klein1699fab2022-09-08 08:46:06 -0600411 # We can use a single output to write the rest of them. Use the
412 # first one as the reexec output and just translate its output in
413 # the rest of the handlers after.
414 output_handler = output_handlers[0]
Alex Klein146d4772019-06-20 13:48:25 -0600415
Alex Klein1699fab2022-09-08 08:46:06 -0600416 cmd = [
417 "build_api",
418 "%s/%s" % (service_name, method_name),
419 input_handler.input_arg,
420 chroot_input,
421 output_handler.output_arg,
422 chroot_output,
423 config_handler.config_arg,
424 chroot_config,
425 "--debug",
426 ]
Alex Klein915cce92019-12-17 14:19:50 -0700427
Alex Klein1699fab2022-09-08 08:46:06 -0600428 try:
429 result = cros_build_lib.run(
430 cmd,
431 enter_chroot=True,
432 chroot_args=chroot.get_enter_args(),
433 check=False,
434 extra_env=chroot.env,
435 )
436 except cros_build_lib.RunCommandError:
437 # A non-zero return code will not result in an error, but one
438 # is still thrown when the command cannot be run in the first
439 # place. This is known to happen at least when the PATH does
440 # not include the chromite bin dir.
441 raise CrosSdkNotRunError("Unable to enter the chroot.")
Alex Kleind3394c22020-06-16 14:05:06 -0600442
Alex Klein1699fab2022-09-08 08:46:06 -0600443 logging.info(
444 "Endpoint execution completed, return code: %d",
445 result.returncode,
446 )
Alex Klein146d4772019-06-20 13:48:25 -0600447
Alex Klein1699fab2022-09-08 08:46:06 -0600448 # Transfer result files out of the chroot.
449 output_handler.read_into(output_msg, path=new_output)
450 field_handler.extract_results(input_msg, output_msg, chroot)
Alex Klein9b7331e2019-12-30 14:37:21 -0700451
Alex Klein54c891a2023-01-24 10:45:41 -0700452 # Write out all the response formats.
Alex Klein1699fab2022-09-08 08:46:06 -0600453 for handler in output_handlers:
454 handler.write_from(output_msg)
Joanna Wang92cad812021-11-03 14:52:08 -0700455
Alex Klein1699fab2022-09-08 08:46:06 -0600456 return result.returncode
Alex Klein9b7331e2019-12-30 14:37:21 -0700457
Alex Klein1699fab2022-09-08 08:46:06 -0600458 def _GetMethod(self, module_name: str, method_name: str) -> Callable:
459 """Get the implementation of the method for the service module.
Alex Klein146d4772019-06-20 13:48:25 -0600460
Alex Klein1699fab2022-09-08 08:46:06 -0600461 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600462 module_name: The name of the service module.
463 method_name: The name of the method.
Alex Kleine191ed62020-02-27 15:59:55 -0700464
Alex Klein1699fab2022-09-08 08:46:06 -0600465 Returns:
Alex Kleina0442682022-10-10 13:47:38 -0600466 The method.
Alex Klein146d4772019-06-20 13:48:25 -0600467
Alex Klein1699fab2022-09-08 08:46:06 -0600468 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600469 MethodNotFoundError when the method cannot be found in the module.
470 ServiceModuleNotFoundError when the service module cannot be
471 imported.
Alex Klein1699fab2022-09-08 08:46:06 -0600472 """
473 try:
474 module = importlib.import_module(
475 controller.IMPORT_PATTERN % module_name
476 )
477 except ImportError as e:
478 raise ServiceControllerNotFoundError(str(e))
479 try:
480 return getattr(module, method_name)
481 except AttributeError as e:
482 raise MethodNotFoundError(str(e))
Alex Klein146d4772019-06-20 13:48:25 -0600483
484
Tomasz Tylendab4292302021-08-08 18:59:36 +0900485def RegisterServices(router: Router):
Alex Klein1699fab2022-09-08 08:46:06 -0600486 """Register all the services.
Alex Klein146d4772019-06-20 13:48:25 -0600487
Alex Klein1699fab2022-09-08 08:46:06 -0600488 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600489 router: The router.
Alex Klein1699fab2022-09-08 08:46:06 -0600490 """
491 router.Register(android_pb2)
492 router.Register(api_pb2)
493 router.Register(artifacts_pb2)
494 router.Register(binhost_pb2)
Tristan Honscheid52ba4d22023-02-09 11:59:29 -0700495 router.Register(copybot_pb2)
Alex Klein1699fab2022-09-08 08:46:06 -0600496 router.Register(depgraph_pb2)
497 router.Register(firmware_pb2)
498 router.Register(image_pb2)
499 router.Register(metadata_pb2)
Lizzy Preslande723c012022-06-10 05:05:37 +0000500 router.Register(observability_pb2)
Alex Klein1699fab2022-09-08 08:46:06 -0600501 router.Register(packages_pb2)
502 router.Register(payload_pb2)
503 router.Register(portage_explorer_pb2)
504 router.Register(sdk_pb2)
505 router.Register(sysroot_pb2)
506 router.Register(test_pb2)
507 router.Register(toolchain_pb2)
508 logging.debug("Services registered successfully.")
Alex Klein146d4772019-06-20 13:48:25 -0600509
510
511def GetRouter():
Alex Klein54c891a2023-01-24 10:45:41 -0700512 """Get a router that has had all the services registered."""
Alex Klein1699fab2022-09-08 08:46:06 -0600513 router = Router()
514 RegisterServices(router)
Alex Klein146d4772019-06-20 13:48:25 -0600515
Alex Klein1699fab2022-09-08 08:46:06 -0600516 return router