blob: a47544a14fe047529b6978a60b47195a5d288eb9 [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
Alex Klein146d4772019-06-20 13:48:25 -060012import importlib
Chris McDonald1672ddb2021-07-21 11:48:23 -060013import logging
Alex Klein146d4772019-06-20 13:48:25 -060014import os
Tomasz Tylendab4292302021-08-08 18:59:36 +090015from types import ModuleType
16from typing import Callable, List, TYPE_CHECKING
Alex Klein146d4772019-06-20 13:48:25 -060017
Mike Frysinger2c024062021-05-22 15:43:22 -040018from chromite.third_party.google.protobuf import symbol_database
Alex Klein146d4772019-06-20 13:48:25 -060019
20from chromite.api import controller
21from chromite.api import field_handler
Alex Klein4de25e82019-08-05 15:58:39 -060022from chromite.api.gen.chromite.api import android_pb2
Alex Klein54e38e32019-06-21 14:54:17 -060023from chromite.api.gen.chromite.api import api_pb2
Alex Klein146d4772019-06-20 13:48:25 -060024from chromite.api.gen.chromite.api import artifacts_pb2
25from chromite.api.gen.chromite.api import binhost_pb2
26from chromite.api.gen.chromite.api import build_api_pb2
Tristan Honscheid52ba4d22023-02-09 11:59:29 -070027from chromite.api.gen.chromite.api import copybot_pb2
Alex Klein146d4772019-06-20 13:48:25 -060028from chromite.api.gen.chromite.api import depgraph_pb2
Jett Rink17ed0f52020-09-25 17:14:31 -060029from chromite.api.gen.chromite.api import firmware_pb2
Alex Klein146d4772019-06-20 13:48:25 -060030from chromite.api.gen.chromite.api import image_pb2
Alex Kleind4d9caa2021-11-10 15:44:52 -070031from chromite.api.gen.chromite.api import metadata_pb2
Lizzy Preslande723c012022-06-10 05:05:37 +000032from chromite.api.gen.chromite.api import observability_pb2
Alex Kleineb77ffa2019-05-28 14:47:44 -060033from chromite.api.gen.chromite.api import packages_pb2
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060034from chromite.api.gen.chromite.api import payload_pb2
Alexander Liu008389c2022-06-27 18:30:11 +000035from chromite.api.gen.chromite.api import portage_explorer_pb2
Alex Klein146d4772019-06-20 13:48:25 -060036from chromite.api.gen.chromite.api import sdk_pb2
37from chromite.api.gen.chromite.api import sysroot_pb2
38from chromite.api.gen.chromite.api import test_pb2
Tiancong Wangaf050172019-07-10 11:52:03 -070039from chromite.api.gen.chromite.api import toolchain_pb2
Alex Klein146d4772019-06-20 13:48:25 -060040from chromite.lib import cros_build_lib
Alex Klein146d4772019-06-20 13:48:25 -060041from chromite.lib import osutils
Alex Klein92341cd2020-02-27 14:11:04 -070042from chromite.utils import memoize
Alex Klein146d4772019-06-20 13:48:25 -060043
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040044
Tomasz Tylendab4292302021-08-08 18:59:36 +090045if TYPE_CHECKING:
Alex Klein1699fab2022-09-08 08:46:06 -060046 from chromite.third_party import google
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040047
Alex Klein1699fab2022-09-08 08:46:06 -060048 from chromite.api import api_config
49 from chromite.api import message_util
Mike Frysinger88770ef2021-05-21 11:04:00 -040050
Alex Klein92341cd2020-02-27 14:11:04 -070051MethodData = collections.namedtuple(
Alex Klein1699fab2022-09-08 08:46:06 -060052 "MethodData", ("service_descriptor", "module_name", "method_descriptor")
53)
Alex Klein146d4772019-06-20 13:48:25 -060054
Mike Frysingeref94e4c2020-02-10 23:59:54 -050055
Alex Klein146d4772019-06-20 13:48:25 -060056class Error(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -060057 """Base error class for the module."""
Alex Klein146d4772019-06-20 13:48:25 -060058
59
Alex Kleind3394c22020-06-16 14:05:06 -060060class InvalidSdkError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060061 """Raised when the SDK is invalid or does not exist."""
Alex Kleind3394c22020-06-16 14:05:06 -060062
63
Alex Klein146d4772019-06-20 13:48:25 -060064class CrosSdkNotRunError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060065 """Raised when the cros_sdk command could not be run to enter the chroot."""
Alex Klein146d4772019-06-20 13:48:25 -060066
67
68# API Service Errors.
69class UnknownServiceError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060070 """Error raised when the requested service has not been registered."""
Alex Klein146d4772019-06-20 13:48:25 -060071
72
73class ControllerModuleNotDefinedError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060074 """Error class for when no controller has been defined for a service."""
Alex Klein146d4772019-06-20 13:48:25 -060075
76
77class ServiceControllerNotFoundError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060078 """Error raised when the service's controller cannot be imported."""
Alex Klein146d4772019-06-20 13:48:25 -060079
80
81# API Method Errors.
82class UnknownMethodError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060083 """The service has been defined in the proto, but the method has not."""
Alex Klein146d4772019-06-20 13:48:25 -060084
85
86class MethodNotFoundError(Error):
Alex Klein54c891a2023-01-24 10:45:41 -070087 """The method's implementation cannot be found in the controller."""
Alex Klein146d4772019-06-20 13:48:25 -060088
89
90class Router(object):
Alex Klein1699fab2022-09-08 08:46:06 -060091 """Encapsulates the request dispatching logic."""
Alex Klein146d4772019-06-20 13:48:25 -060092
Alex Klein1699fab2022-09-08 08:46:06 -060093 REEXEC_INPUT_FILE = "input_proto"
94 REEXEC_OUTPUT_FILE = "output_proto"
95 REEXEC_CONFIG_FILE = "config_proto"
Alex Kleinbd6edf82019-07-18 10:30:49 -060096
Alex Klein1699fab2022-09-08 08:46:06 -060097 def __init__(self):
98 self._services = {}
99 self._aliases = {}
100 # All imported generated messages get added to this symbol db.
101 self._sym_db = symbol_database.Default()
Alex Klein146d4772019-06-20 13:48:25 -0600102
Alex Klein1699fab2022-09-08 08:46:06 -0600103 # Save the service and method extension info for looking up
104 # configured extension data.
105 extensions = build_api_pb2.DESCRIPTOR.extensions_by_name
106 self._svc_options_ext = extensions["service_options"]
107 self._method_options_ext = extensions["method_options"]
Alex Klein92341cd2020-02-27 14:11:04 -0700108
Alex Klein1699fab2022-09-08 08:46:06 -0600109 @memoize.Memoize
110 def _get_method_data(self, service_name, method_name):
111 """Get the descriptors and module name for the given Service/Method."""
112 try:
113 svc, module_name = self._services[service_name]
114 except KeyError:
115 raise UnknownServiceError(
116 "The %s service has not been registered." % service_name
117 )
Alex Klein92341cd2020-02-27 14:11:04 -0700118
Alex Klein1699fab2022-09-08 08:46:06 -0600119 try:
120 method_desc = svc.methods_by_name[method_name]
121 except KeyError:
122 raise UnknownMethodError(
123 "The %s method has not been defined in the %s "
124 "service." % (method_name, service_name)
125 )
Alex Klein92341cd2020-02-27 14:11:04 -0700126
Alex Klein1699fab2022-09-08 08:46:06 -0600127 return MethodData(
128 service_descriptor=svc,
129 module_name=module_name,
130 method_descriptor=method_desc,
131 )
Alex Klein92341cd2020-02-27 14:11:04 -0700132
Alex Klein1699fab2022-09-08 08:46:06 -0600133 def get_input_message_instance(self, service_name, method_name):
134 """Get an empty input message instance for the specified method."""
135 method_data = self._get_method_data(service_name, method_name)
136 return self._sym_db.GetPrototype(
137 method_data.method_descriptor.input_type
138 )()
Alex Klein92341cd2020-02-27 14:11:04 -0700139
Alex Klein1699fab2022-09-08 08:46:06 -0600140 def _get_output_message_instance(self, service_name, method_name):
141 """Get an empty output message instance for the specified method."""
142 method_data = self._get_method_data(service_name, method_name)
143 return self._sym_db.GetPrototype(
144 method_data.method_descriptor.output_type
145 )()
Alex Klein92341cd2020-02-27 14:11:04 -0700146
Alex Klein1699fab2022-09-08 08:46:06 -0600147 def _get_module_name(self, service_name, method_name):
148 """Get the name of the module containing the endpoint implementation."""
149 return self._get_method_data(service_name, method_name).module_name
Alex Klein92341cd2020-02-27 14:11:04 -0700150
Alex Klein1699fab2022-09-08 08:46:06 -0600151 def _get_service_options(self, service_name, method_name):
152 """Get the configured service options for the endpoint."""
153 method_data = self._get_method_data(service_name, method_name)
154 svc_extensions = method_data.service_descriptor.GetOptions().Extensions
155 return svc_extensions[self._svc_options_ext]
Alex Klein92341cd2020-02-27 14:11:04 -0700156
Alex Klein1699fab2022-09-08 08:46:06 -0600157 def _get_method_options(self, service_name, method_name):
158 """Get the configured method options for the endpoint."""
159 method_data = self._get_method_data(service_name, method_name)
160 method_extensions = (
161 method_data.method_descriptor.GetOptions().Extensions
162 )
163 return method_extensions[self._method_options_ext]
Alex Klein146d4772019-06-20 13:48:25 -0600164
Alex Klein1699fab2022-09-08 08:46:06 -0600165 def Register(self, proto_module: ModuleType):
166 """Register the services from a generated proto module.
Alex Klein146d4772019-06-20 13:48:25 -0600167
Alex Klein1699fab2022-09-08 08:46:06 -0600168 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600169 proto_module: The generated proto module to register.
Alex Klein146d4772019-06-20 13:48:25 -0600170
Alex Klein1699fab2022-09-08 08:46:06 -0600171 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600172 ServiceModuleNotDefinedError when the service cannot be found in the
173 provided module.
Alex Klein1699fab2022-09-08 08:46:06 -0600174 """
175 services = proto_module.DESCRIPTOR.services_by_name
176 for service_name, svc in services.items():
177 module_name = (
178 svc.GetOptions().Extensions[self._svc_options_ext].module
179 )
Alex Klein146d4772019-06-20 13:48:25 -0600180
Alex Klein1699fab2022-09-08 08:46:06 -0600181 if not module_name:
182 raise ControllerModuleNotDefinedError(
Alex Klein54c891a2023-01-24 10:45:41 -0700183 "The module must be defined in the service definition: "
184 f"{proto_module}.{service_name}"
Alex Klein1699fab2022-09-08 08:46:06 -0600185 )
Alex Klein146d4772019-06-20 13:48:25 -0600186
Alex Klein1699fab2022-09-08 08:46:06 -0600187 self._services[svc.full_name] = (svc, module_name)
Alex Klein146d4772019-06-20 13:48:25 -0600188
Alex Klein1699fab2022-09-08 08:46:06 -0600189 def ListMethods(self):
190 """List all methods registered with the router."""
191 services = []
192 for service_name, (svc, _module) in self._services.items():
193 svc_visibility = getattr(
194 svc.GetOptions().Extensions[self._svc_options_ext],
195 "service_visibility",
196 build_api_pb2.LV_VISIBLE,
197 )
198 if svc_visibility == build_api_pb2.LV_HIDDEN:
199 continue
Alex Klein6cce6f62021-03-02 14:24:05 -0700200
Alex Klein1699fab2022-09-08 08:46:06 -0600201 for method_name in svc.methods_by_name.keys():
202 method_options = self._get_method_options(
203 service_name, method_name
204 )
205 method_visibility = getattr(
206 method_options,
207 "method_visibility",
208 build_api_pb2.LV_VISIBLE,
209 )
210 if method_visibility == build_api_pb2.LV_HIDDEN:
211 continue
212
213 services.append("%s/%s" % (service_name, method_name))
214
215 return sorted(services)
216
217 def Route(
218 self,
219 service_name: str,
220 method_name: str,
221 config: "api_config.ApiConfig",
222 input_handler: "message_util.MessageHandler",
223 output_handlers: List["message_util.MessageHandler"],
224 config_handler: "message_util.MessageHandler",
225 ) -> int:
226 """Dispatch the request.
227
228 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600229 service_name: The fully qualified service name.
230 method_name: The name of the method being called.
231 config: The call configs.
232 input_handler: The request message handler.
233 output_handlers: The response message handlers.
234 config_handler: The config message handler.
Alex Klein1699fab2022-09-08 08:46:06 -0600235
236 Returns:
Alex Kleina0442682022-10-10 13:47:38 -0600237 The return code.
Alex Klein1699fab2022-09-08 08:46:06 -0600238
239 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600240 InvalidInputFileError when the input file cannot be read.
241 InvalidOutputFileError when the output file cannot be written.
242 ServiceModuleNotFoundError when the service module cannot be
243 imported.
244 MethodNotFoundError when the method cannot be retrieved from the
245 module.
Alex Klein1699fab2022-09-08 08:46:06 -0600246 """
247 input_msg = self.get_input_message_instance(service_name, method_name)
248 input_handler.read_into(input_msg)
249
250 # Get an empty output message instance.
251 output_msg = self._get_output_message_instance(
252 service_name, method_name
253 )
254
255 # Fetch the method options for chroot and method name overrides.
Alex Klein6cce6f62021-03-02 14:24:05 -0700256 method_options = self._get_method_options(service_name, method_name)
Alex Klein6cce6f62021-03-02 14:24:05 -0700257
Alex Klein1699fab2022-09-08 08:46:06 -0600258 # Check the chroot settings before running.
259 service_options = self._get_service_options(service_name, method_name)
260 if self._ChrootCheck(service_options, method_options, config):
261 # Run inside the chroot instead.
262 logging.info("Re-executing the endpoint inside the chroot.")
263 return self._ReexecuteInside(
264 input_msg,
265 output_msg,
266 config,
267 input_handler,
268 output_handlers,
269 config_handler,
270 service_name,
271 method_name,
272 )
Alex Klein146d4772019-06-20 13:48:25 -0600273
Alex Klein1699fab2022-09-08 08:46:06 -0600274 # Allow proto-based method name override.
275 if method_options.HasField("implementation_name"):
276 implementation_name = method_options.implementation_name
277 else:
278 implementation_name = method_name
Alex Klein146d4772019-06-20 13:48:25 -0600279
Alex Klein1699fab2022-09-08 08:46:06 -0600280 # Import the module and get the method.
281 module_name = self._get_module_name(service_name, method_name)
282 method_impl = self._GetMethod(module_name, implementation_name)
Alex Klein146d4772019-06-20 13:48:25 -0600283
Alex Klein1699fab2022-09-08 08:46:06 -0600284 # Successfully located; call and return.
285 return_code = method_impl(input_msg, output_msg, config)
286 if return_code is None:
287 return_code = controller.RETURN_CODE_SUCCESS
Alex Klein146d4772019-06-20 13:48:25 -0600288
Alex Klein1699fab2022-09-08 08:46:06 -0600289 for h in output_handlers:
290 h.write_from(output_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600291
Alex Klein1699fab2022-09-08 08:46:06 -0600292 return return_code
Alex Klein146d4772019-06-20 13:48:25 -0600293
Alex Klein1699fab2022-09-08 08:46:06 -0600294 def _ChrootCheck(
295 self,
296 service_options: "google.protobuf.Message",
297 method_options: "google.protobuf.Message",
298 config: "api_config.ApiConfig",
299 ) -> bool:
Alex Kleina0442682022-10-10 13:47:38 -0600300 """Check the chroot options; execute assertion or note reexec as needed.
Alex Klein146d4772019-06-20 13:48:25 -0600301
Alex Klein1699fab2022-09-08 08:46:06 -0600302 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600303 service_options: The service options.
304 method_options: The method options.
305 config: The Build API call config instance.
Alex Klein146d4772019-06-20 13:48:25 -0600306
Alex Klein1699fab2022-09-08 08:46:06 -0600307 Returns:
Alex Kleina0442682022-10-10 13:47:38 -0600308 True iff it needs to be reexeced inside the chroot.
Alex Kleinbd6edf82019-07-18 10:30:49 -0600309
Alex Klein1699fab2022-09-08 08:46:06 -0600310 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600311 cros_build_lib.DieSystemExit when the chroot setting cannot be
312 satisfied.
Alex Klein1699fab2022-09-08 08:46:06 -0600313 """
314 if not config.run_endpoint:
315 # Do not enter the chroot for validate only and mock calls.
316 return False
Alex Klein146d4772019-06-20 13:48:25 -0600317
Alex Klein1699fab2022-09-08 08:46:06 -0600318 chroot_assert = build_api_pb2.NO_ASSERTION
319 if method_options.HasField("method_chroot_assert"):
320 # Prefer the method option when set.
321 chroot_assert = method_options.method_chroot_assert
322 elif service_options.HasField("service_chroot_assert"):
323 # Fall back to the service option.
324 chroot_assert = service_options.service_chroot_assert
Alex Klein146d4772019-06-20 13:48:25 -0600325
Alex Klein1699fab2022-09-08 08:46:06 -0600326 if chroot_assert == build_api_pb2.INSIDE:
327 return not cros_build_lib.IsInsideChroot()
328 elif chroot_assert == build_api_pb2.OUTSIDE:
329 # If it must be run outside we have to already be outside.
330 cros_build_lib.AssertOutsideChroot()
Alex Klein146d4772019-06-20 13:48:25 -0600331
Alex Klein1699fab2022-09-08 08:46:06 -0600332 return False
Alex Klein146d4772019-06-20 13:48:25 -0600333
Alex Klein1699fab2022-09-08 08:46:06 -0600334 def _ReexecuteInside(
335 self,
336 input_msg: "google.protobuf.Message",
337 output_msg: "google.protobuf.Message",
338 config: "api_config.ApiConfig",
339 input_handler: "message_util.MessageHandler",
340 output_handlers: List["message_util.MessageHandler"],
341 config_handler: "message_util.MessageHandler",
342 service_name: str,
343 method_name: str,
344 ):
345 """Re-execute the service inside the chroot.
Alex Klein146d4772019-06-20 13:48:25 -0600346
Alex Klein1699fab2022-09-08 08:46:06 -0600347 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600348 input_msg: The parsed input message.
349 output_msg: The empty output message instance.
350 config: The call configs.
351 input_handler: Input message handler.
352 output_handlers: Output message handlers.
353 config_handler: Config message handler.
354 service_name: The name of the service to run.
355 method_name: The name of the method to run.
Alex Klein1699fab2022-09-08 08:46:06 -0600356 """
357 # Parse the chroot and clear the chroot field in the input message.
358 chroot = field_handler.handle_chroot(input_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600359
Alex Klein1699fab2022-09-08 08:46:06 -0600360 if not chroot.exists():
361 raise InvalidSdkError("Chroot does not exist.")
Alex Klein146d4772019-06-20 13:48:25 -0600362
Alex Klein1699fab2022-09-08 08:46:06 -0600363 # Use a ContextManagerStack to avoid the deep nesting this many
364 # context managers introduces.
365 with cros_build_lib.ContextManagerStack() as stack:
366 # TempDirs setup.
367 tempdir = stack.Add(chroot.tempdir).tempdir
368 sync_tempdir = stack.Add(chroot.tempdir).tempdir
369 # The copy-paths-in context manager to handle Path messages.
370 stack.Add(
371 field_handler.copy_paths_in,
372 input_msg,
373 chroot.tmp,
374 prefix=chroot.path,
375 )
376 # The sync-directories context manager to handle SyncedDir messages.
377 stack.Add(
378 field_handler.sync_dirs,
379 input_msg,
380 sync_tempdir,
381 prefix=chroot.path,
382 )
Alex Klein146d4772019-06-20 13:48:25 -0600383
Alex Klein1699fab2022-09-08 08:46:06 -0600384 # Parse goma.
385 chroot.goma = field_handler.handle_goma(input_msg, chroot.path)
Alex Kleind1e9e5c2020-12-14 12:32:32 -0700386
Alex Klein1699fab2022-09-08 08:46:06 -0600387 # Parse remoteexec.
388 chroot.remoteexec = field_handler.handle_remoteexec(input_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600389
Alex Klein54c891a2023-01-24 10:45:41 -0700390 # Build inside-chroot paths for the input, output, and config
391 # messages.
Alex Klein1699fab2022-09-08 08:46:06 -0600392 new_input = os.path.join(tempdir, self.REEXEC_INPUT_FILE)
393 chroot_input = "/%s" % os.path.relpath(new_input, chroot.path)
394 new_output = os.path.join(tempdir, self.REEXEC_OUTPUT_FILE)
395 chroot_output = "/%s" % os.path.relpath(new_output, chroot.path)
396 new_config = os.path.join(tempdir, self.REEXEC_CONFIG_FILE)
397 chroot_config = "/%s" % os.path.relpath(new_config, chroot.path)
Alex Klein146d4772019-06-20 13:48:25 -0600398
Alex Klein1699fab2022-09-08 08:46:06 -0600399 # Setup the inside-chroot message files.
400 logging.info("Writing input message to: %s", new_input)
401 input_handler.write_from(input_msg, path=new_input)
402 osutils.Touch(new_output)
403 logging.info("Writing config message to: %s", new_config)
404 config_handler.write_from(config.get_proto(), path=new_config)
Alex Klein146d4772019-06-20 13:48:25 -0600405
Alex Klein1699fab2022-09-08 08:46:06 -0600406 # We can use a single output to write the rest of them. Use the
407 # first one as the reexec output and just translate its output in
408 # the rest of the handlers after.
409 output_handler = output_handlers[0]
Alex Klein146d4772019-06-20 13:48:25 -0600410
Alex Klein1699fab2022-09-08 08:46:06 -0600411 cmd = [
412 "build_api",
413 "%s/%s" % (service_name, method_name),
414 input_handler.input_arg,
415 chroot_input,
416 output_handler.output_arg,
417 chroot_output,
418 config_handler.config_arg,
419 chroot_config,
420 "--debug",
421 ]
Alex Klein915cce92019-12-17 14:19:50 -0700422
Alex Klein1699fab2022-09-08 08:46:06 -0600423 try:
424 result = cros_build_lib.run(
425 cmd,
426 enter_chroot=True,
427 chroot_args=chroot.get_enter_args(),
428 check=False,
429 extra_env=chroot.env,
430 )
431 except cros_build_lib.RunCommandError:
432 # A non-zero return code will not result in an error, but one
433 # is still thrown when the command cannot be run in the first
434 # place. This is known to happen at least when the PATH does
435 # not include the chromite bin dir.
436 raise CrosSdkNotRunError("Unable to enter the chroot.")
Alex Kleind3394c22020-06-16 14:05:06 -0600437
Alex Klein1699fab2022-09-08 08:46:06 -0600438 logging.info(
439 "Endpoint execution completed, return code: %d",
440 result.returncode,
441 )
Alex Klein146d4772019-06-20 13:48:25 -0600442
Alex Klein1699fab2022-09-08 08:46:06 -0600443 # Transfer result files out of the chroot.
444 output_handler.read_into(output_msg, path=new_output)
445 field_handler.extract_results(input_msg, output_msg, chroot)
Alex Klein9b7331e2019-12-30 14:37:21 -0700446
Alex Klein54c891a2023-01-24 10:45:41 -0700447 # Write out all the response formats.
Alex Klein1699fab2022-09-08 08:46:06 -0600448 for handler in output_handlers:
449 handler.write_from(output_msg)
Joanna Wang92cad812021-11-03 14:52:08 -0700450
Alex Klein1699fab2022-09-08 08:46:06 -0600451 return result.returncode
Alex Klein9b7331e2019-12-30 14:37:21 -0700452
Alex Klein1699fab2022-09-08 08:46:06 -0600453 def _GetMethod(self, module_name: str, method_name: str) -> Callable:
454 """Get the implementation of the method for the service module.
Alex Klein146d4772019-06-20 13:48:25 -0600455
Alex Klein1699fab2022-09-08 08:46:06 -0600456 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600457 module_name: The name of the service module.
458 method_name: The name of the method.
Alex Kleine191ed62020-02-27 15:59:55 -0700459
Alex Klein1699fab2022-09-08 08:46:06 -0600460 Returns:
Alex Kleina0442682022-10-10 13:47:38 -0600461 The method.
Alex Klein146d4772019-06-20 13:48:25 -0600462
Alex Klein1699fab2022-09-08 08:46:06 -0600463 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600464 MethodNotFoundError when the method cannot be found in the module.
465 ServiceModuleNotFoundError when the service module cannot be
466 imported.
Alex Klein1699fab2022-09-08 08:46:06 -0600467 """
468 try:
469 module = importlib.import_module(
470 controller.IMPORT_PATTERN % module_name
471 )
472 except ImportError as e:
473 raise ServiceControllerNotFoundError(str(e))
474 try:
475 return getattr(module, method_name)
476 except AttributeError as e:
477 raise MethodNotFoundError(str(e))
Alex Klein146d4772019-06-20 13:48:25 -0600478
479
Tomasz Tylendab4292302021-08-08 18:59:36 +0900480def RegisterServices(router: Router):
Alex Klein1699fab2022-09-08 08:46:06 -0600481 """Register all the services.
Alex Klein146d4772019-06-20 13:48:25 -0600482
Alex Klein1699fab2022-09-08 08:46:06 -0600483 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600484 router: The router.
Alex Klein1699fab2022-09-08 08:46:06 -0600485 """
486 router.Register(android_pb2)
487 router.Register(api_pb2)
488 router.Register(artifacts_pb2)
489 router.Register(binhost_pb2)
Tristan Honscheid52ba4d22023-02-09 11:59:29 -0700490 router.Register(copybot_pb2)
Alex Klein1699fab2022-09-08 08:46:06 -0600491 router.Register(depgraph_pb2)
492 router.Register(firmware_pb2)
493 router.Register(image_pb2)
494 router.Register(metadata_pb2)
Lizzy Preslande723c012022-06-10 05:05:37 +0000495 router.Register(observability_pb2)
Alex Klein1699fab2022-09-08 08:46:06 -0600496 router.Register(packages_pb2)
497 router.Register(payload_pb2)
498 router.Register(portage_explorer_pb2)
499 router.Register(sdk_pb2)
500 router.Register(sysroot_pb2)
501 router.Register(test_pb2)
502 router.Register(toolchain_pb2)
503 logging.debug("Services registered successfully.")
Alex Klein146d4772019-06-20 13:48:25 -0600504
505
506def GetRouter():
Alex Klein54c891a2023-01-24 10:45:41 -0700507 """Get a router that has had all the services registered."""
Alex Klein1699fab2022-09-08 08:46:06 -0600508 router = Router()
509 RegisterServices(router)
Alex Klein146d4772019-06-20 13:48:25 -0600510
Alex Klein1699fab2022-09-08 08:46:06 -0600511 return router