blob: df9c0b5aae9dc404cfb92eadb83353dc1986f6be [file] [log] [blame]
Alex Klein146d4772019-06-20 13:48:25 -06001# Copyright 2019 The Chromium OS Authors. All rights reserved.
2# 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
27from chromite.api.gen.chromite.api import depgraph_pb2
Jett Rink17ed0f52020-09-25 17:14:31 -060028from chromite.api.gen.chromite.api import firmware_pb2
Alex Klein146d4772019-06-20 13:48:25 -060029from chromite.api.gen.chromite.api import image_pb2
Alex Kleind4d9caa2021-11-10 15:44:52 -070030from chromite.api.gen.chromite.api import metadata_pb2
Alex Kleineb77ffa2019-05-28 14:47:44 -060031from chromite.api.gen.chromite.api import packages_pb2
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060032from chromite.api.gen.chromite.api import payload_pb2
Alexander Liu008389c2022-06-27 18:30:11 +000033from chromite.api.gen.chromite.api import portage_explorer_pb2
Alex Klein146d4772019-06-20 13:48:25 -060034from chromite.api.gen.chromite.api import sdk_pb2
35from chromite.api.gen.chromite.api import sysroot_pb2
36from chromite.api.gen.chromite.api import test_pb2
Tiancong Wangaf050172019-07-10 11:52:03 -070037from chromite.api.gen.chromite.api import toolchain_pb2
Alex Klein146d4772019-06-20 13:48:25 -060038from chromite.lib import cros_build_lib
Alex Klein146d4772019-06-20 13:48:25 -060039from chromite.lib import osutils
Alex Klein92341cd2020-02-27 14:11:04 -070040from chromite.utils import memoize
Alex Klein146d4772019-06-20 13:48:25 -060041
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040042
Tomasz Tylendab4292302021-08-08 18:59:36 +090043if TYPE_CHECKING:
Alex Klein1699fab2022-09-08 08:46:06 -060044 from chromite.third_party import google
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040045
Alex Klein1699fab2022-09-08 08:46:06 -060046 from chromite.api import api_config
47 from chromite.api import message_util
Mike Frysinger88770ef2021-05-21 11:04:00 -040048
Alex Klein92341cd2020-02-27 14:11:04 -070049MethodData = collections.namedtuple(
Alex Klein1699fab2022-09-08 08:46:06 -060050 "MethodData", ("service_descriptor", "module_name", "method_descriptor")
51)
Alex Klein146d4772019-06-20 13:48:25 -060052
Mike Frysingeref94e4c2020-02-10 23:59:54 -050053
Alex Klein146d4772019-06-20 13:48:25 -060054class Error(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -060055 """Base error class for the module."""
Alex Klein146d4772019-06-20 13:48:25 -060056
57
Alex Kleind3394c22020-06-16 14:05:06 -060058class InvalidSdkError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060059 """Raised when the SDK is invalid or does not exist."""
Alex Kleind3394c22020-06-16 14:05:06 -060060
61
Alex Klein146d4772019-06-20 13:48:25 -060062class CrosSdkNotRunError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060063 """Raised when the cros_sdk command could not be run to enter the chroot."""
Alex Klein146d4772019-06-20 13:48:25 -060064
65
66# API Service Errors.
67class UnknownServiceError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060068 """Error raised when the requested service has not been registered."""
Alex Klein146d4772019-06-20 13:48:25 -060069
70
71class ControllerModuleNotDefinedError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060072 """Error class for when no controller has been defined for a service."""
Alex Klein146d4772019-06-20 13:48:25 -060073
74
75class ServiceControllerNotFoundError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060076 """Error raised when the service's controller cannot be imported."""
Alex Klein146d4772019-06-20 13:48:25 -060077
78
79# API Method Errors.
80class UnknownMethodError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060081 """The service has been defined in the proto, but the method has not."""
Alex Klein146d4772019-06-20 13:48:25 -060082
83
84class MethodNotFoundError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060085 """The method's implementation cannot be found in the service's controller."""
Alex Klein146d4772019-06-20 13:48:25 -060086
87
88class Router(object):
Alex Klein1699fab2022-09-08 08:46:06 -060089 """Encapsulates the request dispatching logic."""
Alex Klein146d4772019-06-20 13:48:25 -060090
Alex Klein1699fab2022-09-08 08:46:06 -060091 REEXEC_INPUT_FILE = "input_proto"
92 REEXEC_OUTPUT_FILE = "output_proto"
93 REEXEC_CONFIG_FILE = "config_proto"
Alex Kleinbd6edf82019-07-18 10:30:49 -060094
Alex Klein1699fab2022-09-08 08:46:06 -060095 def __init__(self):
96 self._services = {}
97 self._aliases = {}
98 # All imported generated messages get added to this symbol db.
99 self._sym_db = symbol_database.Default()
Alex Klein146d4772019-06-20 13:48:25 -0600100
Alex Klein1699fab2022-09-08 08:46:06 -0600101 # Save the service and method extension info for looking up
102 # configured extension data.
103 extensions = build_api_pb2.DESCRIPTOR.extensions_by_name
104 self._svc_options_ext = extensions["service_options"]
105 self._method_options_ext = extensions["method_options"]
Alex Klein92341cd2020-02-27 14:11:04 -0700106
Alex Klein1699fab2022-09-08 08:46:06 -0600107 @memoize.Memoize
108 def _get_method_data(self, service_name, method_name):
109 """Get the descriptors and module name for the given Service/Method."""
110 try:
111 svc, module_name = self._services[service_name]
112 except KeyError:
113 raise UnknownServiceError(
114 "The %s service has not been registered." % service_name
115 )
Alex Klein92341cd2020-02-27 14:11:04 -0700116
Alex Klein1699fab2022-09-08 08:46:06 -0600117 try:
118 method_desc = svc.methods_by_name[method_name]
119 except KeyError:
120 raise UnknownMethodError(
121 "The %s method has not been defined in the %s "
122 "service." % (method_name, service_name)
123 )
Alex Klein92341cd2020-02-27 14:11:04 -0700124
Alex Klein1699fab2022-09-08 08:46:06 -0600125 return MethodData(
126 service_descriptor=svc,
127 module_name=module_name,
128 method_descriptor=method_desc,
129 )
Alex Klein92341cd2020-02-27 14:11:04 -0700130
Alex Klein1699fab2022-09-08 08:46:06 -0600131 def get_input_message_instance(self, service_name, method_name):
132 """Get an empty input message instance for the specified method."""
133 method_data = self._get_method_data(service_name, method_name)
134 return self._sym_db.GetPrototype(
135 method_data.method_descriptor.input_type
136 )()
Alex Klein92341cd2020-02-27 14:11:04 -0700137
Alex Klein1699fab2022-09-08 08:46:06 -0600138 def _get_output_message_instance(self, service_name, method_name):
139 """Get an empty output message instance for the specified method."""
140 method_data = self._get_method_data(service_name, method_name)
141 return self._sym_db.GetPrototype(
142 method_data.method_descriptor.output_type
143 )()
Alex Klein92341cd2020-02-27 14:11:04 -0700144
Alex Klein1699fab2022-09-08 08:46:06 -0600145 def _get_module_name(self, service_name, method_name):
146 """Get the name of the module containing the endpoint implementation."""
147 return self._get_method_data(service_name, method_name).module_name
Alex Klein92341cd2020-02-27 14:11:04 -0700148
Alex Klein1699fab2022-09-08 08:46:06 -0600149 def _get_service_options(self, service_name, method_name):
150 """Get the configured service options for the endpoint."""
151 method_data = self._get_method_data(service_name, method_name)
152 svc_extensions = method_data.service_descriptor.GetOptions().Extensions
153 return svc_extensions[self._svc_options_ext]
Alex Klein92341cd2020-02-27 14:11:04 -0700154
Alex Klein1699fab2022-09-08 08:46:06 -0600155 def _get_method_options(self, service_name, method_name):
156 """Get the configured method options for the endpoint."""
157 method_data = self._get_method_data(service_name, method_name)
158 method_extensions = (
159 method_data.method_descriptor.GetOptions().Extensions
160 )
161 return method_extensions[self._method_options_ext]
Alex Klein146d4772019-06-20 13:48:25 -0600162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 def Register(self, proto_module: ModuleType):
164 """Register the services from a generated proto module.
Alex Klein146d4772019-06-20 13:48:25 -0600165
Alex Klein1699fab2022-09-08 08:46:06 -0600166 Args:
167 proto_module: The generated proto module to register.
Alex Klein146d4772019-06-20 13:48:25 -0600168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 Raises:
170 ServiceModuleNotDefinedError when the service cannot be found in the
171 provided module.
172 """
173 services = proto_module.DESCRIPTOR.services_by_name
174 for service_name, svc in services.items():
175 module_name = (
176 svc.GetOptions().Extensions[self._svc_options_ext].module
177 )
Alex Klein146d4772019-06-20 13:48:25 -0600178
Alex Klein1699fab2022-09-08 08:46:06 -0600179 if not module_name:
180 raise ControllerModuleNotDefinedError(
181 "The module must be defined in the service definition: %s.%s"
182 % (proto_module, service_name)
183 )
Alex Klein146d4772019-06-20 13:48:25 -0600184
Alex Klein1699fab2022-09-08 08:46:06 -0600185 self._services[svc.full_name] = (svc, module_name)
Alex Klein146d4772019-06-20 13:48:25 -0600186
Alex Klein1699fab2022-09-08 08:46:06 -0600187 def ListMethods(self):
188 """List all methods registered with the router."""
189 services = []
190 for service_name, (svc, _module) in self._services.items():
191 svc_visibility = getattr(
192 svc.GetOptions().Extensions[self._svc_options_ext],
193 "service_visibility",
194 build_api_pb2.LV_VISIBLE,
195 )
196 if svc_visibility == build_api_pb2.LV_HIDDEN:
197 continue
Alex Klein6cce6f62021-03-02 14:24:05 -0700198
Alex Klein1699fab2022-09-08 08:46:06 -0600199 for method_name in svc.methods_by_name.keys():
200 method_options = self._get_method_options(
201 service_name, method_name
202 )
203 method_visibility = getattr(
204 method_options,
205 "method_visibility",
206 build_api_pb2.LV_VISIBLE,
207 )
208 if method_visibility == build_api_pb2.LV_HIDDEN:
209 continue
210
211 services.append("%s/%s" % (service_name, method_name))
212
213 return sorted(services)
214
215 def Route(
216 self,
217 service_name: str,
218 method_name: str,
219 config: "api_config.ApiConfig",
220 input_handler: "message_util.MessageHandler",
221 output_handlers: List["message_util.MessageHandler"],
222 config_handler: "message_util.MessageHandler",
223 ) -> int:
224 """Dispatch the request.
225
226 Args:
227 service_name: The fully qualified service name.
228 method_name: The name of the method being called.
229 config: The call configs.
230 input_handler: The request message handler.
231 output_handlers: The response message handlers.
232 config_handler: The config message handler.
233
234 Returns:
235 The return code.
236
237 Raises:
238 InvalidInputFileError when the input file cannot be read.
239 InvalidOutputFileError when the output file cannot be written.
240 ServiceModuleNotFoundError when the service module cannot be imported.
241 MethodNotFoundError when the method cannot be retrieved from the module.
242 """
243 input_msg = self.get_input_message_instance(service_name, method_name)
244 input_handler.read_into(input_msg)
245
246 # Get an empty output message instance.
247 output_msg = self._get_output_message_instance(
248 service_name, method_name
249 )
250
251 # Fetch the method options for chroot and method name overrides.
Alex Klein6cce6f62021-03-02 14:24:05 -0700252 method_options = self._get_method_options(service_name, method_name)
Alex Klein6cce6f62021-03-02 14:24:05 -0700253
Alex Klein1699fab2022-09-08 08:46:06 -0600254 # Check the chroot settings before running.
255 service_options = self._get_service_options(service_name, method_name)
256 if self._ChrootCheck(service_options, method_options, config):
257 # Run inside the chroot instead.
258 logging.info("Re-executing the endpoint inside the chroot.")
259 return self._ReexecuteInside(
260 input_msg,
261 output_msg,
262 config,
263 input_handler,
264 output_handlers,
265 config_handler,
266 service_name,
267 method_name,
268 )
Alex Klein146d4772019-06-20 13:48:25 -0600269
Alex Klein1699fab2022-09-08 08:46:06 -0600270 # Allow proto-based method name override.
271 if method_options.HasField("implementation_name"):
272 implementation_name = method_options.implementation_name
273 else:
274 implementation_name = method_name
Alex Klein146d4772019-06-20 13:48:25 -0600275
Alex Klein1699fab2022-09-08 08:46:06 -0600276 # Import the module and get the method.
277 module_name = self._get_module_name(service_name, method_name)
278 method_impl = self._GetMethod(module_name, implementation_name)
Alex Klein146d4772019-06-20 13:48:25 -0600279
Alex Klein1699fab2022-09-08 08:46:06 -0600280 # Successfully located; call and return.
281 return_code = method_impl(input_msg, output_msg, config)
282 if return_code is None:
283 return_code = controller.RETURN_CODE_SUCCESS
Alex Klein146d4772019-06-20 13:48:25 -0600284
Alex Klein1699fab2022-09-08 08:46:06 -0600285 for h in output_handlers:
286 h.write_from(output_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600287
Alex Klein1699fab2022-09-08 08:46:06 -0600288 return return_code
Alex Klein146d4772019-06-20 13:48:25 -0600289
Alex Klein1699fab2022-09-08 08:46:06 -0600290 def _ChrootCheck(
291 self,
292 service_options: "google.protobuf.Message",
293 method_options: "google.protobuf.Message",
294 config: "api_config.ApiConfig",
295 ) -> bool:
296 """Check the chroot options, and execute assertion or note reexec as needed.
Alex Klein146d4772019-06-20 13:48:25 -0600297
Alex Klein1699fab2022-09-08 08:46:06 -0600298 Args:
299 service_options: The service options.
300 method_options: The method options.
301 config: The Build API call config instance.
Alex Klein146d4772019-06-20 13:48:25 -0600302
Alex Klein1699fab2022-09-08 08:46:06 -0600303 Returns:
304 True iff it needs to be reexeced inside the chroot.
Alex Kleinbd6edf82019-07-18 10:30:49 -0600305
Alex Klein1699fab2022-09-08 08:46:06 -0600306 Raises:
307 cros_build_lib.DieSystemExit when the chroot setting cannot be satisfied.
308 """
309 if not config.run_endpoint:
310 # Do not enter the chroot for validate only and mock calls.
311 return False
Alex Klein146d4772019-06-20 13:48:25 -0600312
Alex Klein1699fab2022-09-08 08:46:06 -0600313 chroot_assert = build_api_pb2.NO_ASSERTION
314 if method_options.HasField("method_chroot_assert"):
315 # Prefer the method option when set.
316 chroot_assert = method_options.method_chroot_assert
317 elif service_options.HasField("service_chroot_assert"):
318 # Fall back to the service option.
319 chroot_assert = service_options.service_chroot_assert
Alex Klein146d4772019-06-20 13:48:25 -0600320
Alex Klein1699fab2022-09-08 08:46:06 -0600321 if chroot_assert == build_api_pb2.INSIDE:
322 return not cros_build_lib.IsInsideChroot()
323 elif chroot_assert == build_api_pb2.OUTSIDE:
324 # If it must be run outside we have to already be outside.
325 cros_build_lib.AssertOutsideChroot()
Alex Klein146d4772019-06-20 13:48:25 -0600326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 return False
Alex Klein146d4772019-06-20 13:48:25 -0600328
Alex Klein1699fab2022-09-08 08:46:06 -0600329 def _ReexecuteInside(
330 self,
331 input_msg: "google.protobuf.Message",
332 output_msg: "google.protobuf.Message",
333 config: "api_config.ApiConfig",
334 input_handler: "message_util.MessageHandler",
335 output_handlers: List["message_util.MessageHandler"],
336 config_handler: "message_util.MessageHandler",
337 service_name: str,
338 method_name: str,
339 ):
340 """Re-execute the service inside the chroot.
Alex Klein146d4772019-06-20 13:48:25 -0600341
Alex Klein1699fab2022-09-08 08:46:06 -0600342 Args:
343 input_msg: The parsed input message.
344 output_msg: The empty output message instance.
345 config: The call configs.
346 input_handler: Input message handler.
347 output_handlers: Output message handlers.
348 config_handler: Config message handler.
349 service_name: The name of the service to run.
350 method_name: The name of the method to run.
351 """
352 # Parse the chroot and clear the chroot field in the input message.
353 chroot = field_handler.handle_chroot(input_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600354
Alex Klein1699fab2022-09-08 08:46:06 -0600355 if not chroot.exists():
356 raise InvalidSdkError("Chroot does not exist.")
Alex Klein146d4772019-06-20 13:48:25 -0600357
Alex Klein1699fab2022-09-08 08:46:06 -0600358 # Use a ContextManagerStack to avoid the deep nesting this many
359 # context managers introduces.
360 with cros_build_lib.ContextManagerStack() as stack:
361 # TempDirs setup.
362 tempdir = stack.Add(chroot.tempdir).tempdir
363 sync_tempdir = stack.Add(chroot.tempdir).tempdir
364 # The copy-paths-in context manager to handle Path messages.
365 stack.Add(
366 field_handler.copy_paths_in,
367 input_msg,
368 chroot.tmp,
369 prefix=chroot.path,
370 )
371 # The sync-directories context manager to handle SyncedDir messages.
372 stack.Add(
373 field_handler.sync_dirs,
374 input_msg,
375 sync_tempdir,
376 prefix=chroot.path,
377 )
Alex Klein146d4772019-06-20 13:48:25 -0600378
Alex Klein1699fab2022-09-08 08:46:06 -0600379 # Parse goma.
380 chroot.goma = field_handler.handle_goma(input_msg, chroot.path)
Alex Kleind1e9e5c2020-12-14 12:32:32 -0700381
Alex Klein1699fab2022-09-08 08:46:06 -0600382 # Parse remoteexec.
383 chroot.remoteexec = field_handler.handle_remoteexec(input_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600384
Alex Klein1699fab2022-09-08 08:46:06 -0600385 # Build inside-chroot paths for the input, output, and config messages.
386 new_input = os.path.join(tempdir, self.REEXEC_INPUT_FILE)
387 chroot_input = "/%s" % os.path.relpath(new_input, chroot.path)
388 new_output = os.path.join(tempdir, self.REEXEC_OUTPUT_FILE)
389 chroot_output = "/%s" % os.path.relpath(new_output, chroot.path)
390 new_config = os.path.join(tempdir, self.REEXEC_CONFIG_FILE)
391 chroot_config = "/%s" % os.path.relpath(new_config, chroot.path)
Alex Klein146d4772019-06-20 13:48:25 -0600392
Alex Klein1699fab2022-09-08 08:46:06 -0600393 # Setup the inside-chroot message files.
394 logging.info("Writing input message to: %s", new_input)
395 input_handler.write_from(input_msg, path=new_input)
396 osutils.Touch(new_output)
397 logging.info("Writing config message to: %s", new_config)
398 config_handler.write_from(config.get_proto(), path=new_config)
Alex Klein146d4772019-06-20 13:48:25 -0600399
Alex Klein1699fab2022-09-08 08:46:06 -0600400 # We can use a single output to write the rest of them. Use the
401 # first one as the reexec output and just translate its output in
402 # the rest of the handlers after.
403 output_handler = output_handlers[0]
Alex Klein146d4772019-06-20 13:48:25 -0600404
Alex Klein1699fab2022-09-08 08:46:06 -0600405 cmd = [
406 "build_api",
407 "%s/%s" % (service_name, method_name),
408 input_handler.input_arg,
409 chroot_input,
410 output_handler.output_arg,
411 chroot_output,
412 config_handler.config_arg,
413 chroot_config,
414 "--debug",
415 ]
Alex Klein915cce92019-12-17 14:19:50 -0700416
Alex Klein1699fab2022-09-08 08:46:06 -0600417 try:
418 result = cros_build_lib.run(
419 cmd,
420 enter_chroot=True,
421 chroot_args=chroot.get_enter_args(),
422 check=False,
423 extra_env=chroot.env,
424 )
425 except cros_build_lib.RunCommandError:
426 # A non-zero return code will not result in an error, but one
427 # is still thrown when the command cannot be run in the first
428 # place. This is known to happen at least when the PATH does
429 # not include the chromite bin dir.
430 raise CrosSdkNotRunError("Unable to enter the chroot.")
Alex Kleind3394c22020-06-16 14:05:06 -0600431
Alex Klein1699fab2022-09-08 08:46:06 -0600432 logging.info(
433 "Endpoint execution completed, return code: %d",
434 result.returncode,
435 )
Alex Klein146d4772019-06-20 13:48:25 -0600436
Alex Klein1699fab2022-09-08 08:46:06 -0600437 # Transfer result files out of the chroot.
438 output_handler.read_into(output_msg, path=new_output)
439 field_handler.extract_results(input_msg, output_msg, chroot)
Alex Klein9b7331e2019-12-30 14:37:21 -0700440
Alex Klein1699fab2022-09-08 08:46:06 -0600441 # Write out all of the response formats.
442 for handler in output_handlers:
443 handler.write_from(output_msg)
Joanna Wang92cad812021-11-03 14:52:08 -0700444
Alex Klein1699fab2022-09-08 08:46:06 -0600445 return result.returncode
Alex Klein9b7331e2019-12-30 14:37:21 -0700446
Alex Klein1699fab2022-09-08 08:46:06 -0600447 def _GetMethod(self, module_name: str, method_name: str) -> Callable:
448 """Get the implementation of the method for the service module.
Alex Klein146d4772019-06-20 13:48:25 -0600449
Alex Klein1699fab2022-09-08 08:46:06 -0600450 Args:
451 module_name: The name of the service module.
452 method_name: The name of the method.
Alex Kleine191ed62020-02-27 15:59:55 -0700453
Alex Klein1699fab2022-09-08 08:46:06 -0600454 Returns:
455 The method.
Alex Klein146d4772019-06-20 13:48:25 -0600456
Alex Klein1699fab2022-09-08 08:46:06 -0600457 Raises:
458 MethodNotFoundError when the method cannot be found in the module.
459 ServiceModuleNotFoundError when the service module cannot be imported.
460 """
461 try:
462 module = importlib.import_module(
463 controller.IMPORT_PATTERN % module_name
464 )
465 except ImportError as e:
466 raise ServiceControllerNotFoundError(str(e))
467 try:
468 return getattr(module, method_name)
469 except AttributeError as e:
470 raise MethodNotFoundError(str(e))
Alex Klein146d4772019-06-20 13:48:25 -0600471
472
Tomasz Tylendab4292302021-08-08 18:59:36 +0900473def RegisterServices(router: Router):
Alex Klein1699fab2022-09-08 08:46:06 -0600474 """Register all the services.
Alex Klein146d4772019-06-20 13:48:25 -0600475
Alex Klein1699fab2022-09-08 08:46:06 -0600476 Args:
477 router: The router.
478 """
479 router.Register(android_pb2)
480 router.Register(api_pb2)
481 router.Register(artifacts_pb2)
482 router.Register(binhost_pb2)
483 router.Register(depgraph_pb2)
484 router.Register(firmware_pb2)
485 router.Register(image_pb2)
486 router.Register(metadata_pb2)
487 router.Register(packages_pb2)
488 router.Register(payload_pb2)
489 router.Register(portage_explorer_pb2)
490 router.Register(sdk_pb2)
491 router.Register(sysroot_pb2)
492 router.Register(test_pb2)
493 router.Register(toolchain_pb2)
494 logging.debug("Services registered successfully.")
Alex Klein146d4772019-06-20 13:48:25 -0600495
496
497def GetRouter():
Alex Klein1699fab2022-09-08 08:46:06 -0600498 """Get a router that has had all of the services registered."""
499 router = Router()
500 RegisterServices(router)
Alex Klein146d4772019-06-20 13:48:25 -0600501
Alex Klein1699fab2022-09-08 08:46:06 -0600502 return router