blob: f730a38e3297ad8db0468aefaf65da4a2b9eb43e [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
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
Lizzy Preslande723c012022-06-10 05:05:37 +000031from chromite.api.gen.chromite.api import observability_pb2
Alex Kleineb77ffa2019-05-28 14:47:44 -060032from chromite.api.gen.chromite.api import packages_pb2
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060033from chromite.api.gen.chromite.api import payload_pb2
Alexander Liu008389c2022-06-27 18:30:11 +000034from chromite.api.gen.chromite.api import portage_explorer_pb2
Alex Klein146d4772019-06-20 13:48:25 -060035from chromite.api.gen.chromite.api import sdk_pb2
36from chromite.api.gen.chromite.api import sysroot_pb2
37from chromite.api.gen.chromite.api import test_pb2
Tiancong Wangaf050172019-07-10 11:52:03 -070038from chromite.api.gen.chromite.api import toolchain_pb2
Alex Klein146d4772019-06-20 13:48:25 -060039from chromite.lib import cros_build_lib
Alex Klein146d4772019-06-20 13:48:25 -060040from chromite.lib import osutils
Alex Klein92341cd2020-02-27 14:11:04 -070041from chromite.utils import memoize
Alex Klein146d4772019-06-20 13:48:25 -060042
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040043
Tomasz Tylendab4292302021-08-08 18:59:36 +090044if TYPE_CHECKING:
Alex Klein1699fab2022-09-08 08:46:06 -060045 from chromite.third_party import google
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040046
Alex Klein1699fab2022-09-08 08:46:06 -060047 from chromite.api import api_config
48 from chromite.api import message_util
Mike Frysinger88770ef2021-05-21 11:04:00 -040049
Alex Klein92341cd2020-02-27 14:11:04 -070050MethodData = collections.namedtuple(
Alex Klein1699fab2022-09-08 08:46:06 -060051 "MethodData", ("service_descriptor", "module_name", "method_descriptor")
52)
Alex Klein146d4772019-06-20 13:48:25 -060053
Mike Frysingeref94e4c2020-02-10 23:59:54 -050054
Alex Klein146d4772019-06-20 13:48:25 -060055class Error(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -060056 """Base error class for the module."""
Alex Klein146d4772019-06-20 13:48:25 -060057
58
Alex Kleind3394c22020-06-16 14:05:06 -060059class InvalidSdkError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060060 """Raised when the SDK is invalid or does not exist."""
Alex Kleind3394c22020-06-16 14:05:06 -060061
62
Alex Klein146d4772019-06-20 13:48:25 -060063class CrosSdkNotRunError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060064 """Raised when the cros_sdk command could not be run to enter the chroot."""
Alex Klein146d4772019-06-20 13:48:25 -060065
66
67# API Service Errors.
68class UnknownServiceError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060069 """Error raised when the requested service has not been registered."""
Alex Klein146d4772019-06-20 13:48:25 -060070
71
72class ControllerModuleNotDefinedError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060073 """Error class for when no controller has been defined for a service."""
Alex Klein146d4772019-06-20 13:48:25 -060074
75
76class ServiceControllerNotFoundError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060077 """Error raised when the service's controller cannot be imported."""
Alex Klein146d4772019-06-20 13:48:25 -060078
79
80# API Method Errors.
81class UnknownMethodError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060082 """The service has been defined in the proto, but the method has not."""
Alex Klein146d4772019-06-20 13:48:25 -060083
84
85class MethodNotFoundError(Error):
Alex Klein54c891a2023-01-24 10:45:41 -070086 """The method's implementation cannot be found in the controller."""
Alex Klein146d4772019-06-20 13:48:25 -060087
88
89class Router(object):
Alex Klein1699fab2022-09-08 08:46:06 -060090 """Encapsulates the request dispatching logic."""
Alex Klein146d4772019-06-20 13:48:25 -060091
Alex Klein1699fab2022-09-08 08:46:06 -060092 REEXEC_INPUT_FILE = "input_proto"
93 REEXEC_OUTPUT_FILE = "output_proto"
94 REEXEC_CONFIG_FILE = "config_proto"
Alex Kleinbd6edf82019-07-18 10:30:49 -060095
Alex Klein1699fab2022-09-08 08:46:06 -060096 def __init__(self):
97 self._services = {}
98 self._aliases = {}
99 # All imported generated messages get added to this symbol db.
100 self._sym_db = symbol_database.Default()
Alex Klein146d4772019-06-20 13:48:25 -0600101
Alex Klein1699fab2022-09-08 08:46:06 -0600102 # Save the service and method extension info for looking up
103 # configured extension data.
104 extensions = build_api_pb2.DESCRIPTOR.extensions_by_name
105 self._svc_options_ext = extensions["service_options"]
106 self._method_options_ext = extensions["method_options"]
Alex Klein92341cd2020-02-27 14:11:04 -0700107
Alex Klein1699fab2022-09-08 08:46:06 -0600108 @memoize.Memoize
109 def _get_method_data(self, service_name, method_name):
110 """Get the descriptors and module name for the given Service/Method."""
111 try:
112 svc, module_name = self._services[service_name]
113 except KeyError:
114 raise UnknownServiceError(
115 "The %s service has not been registered." % service_name
116 )
Alex Klein92341cd2020-02-27 14:11:04 -0700117
Alex Klein1699fab2022-09-08 08:46:06 -0600118 try:
119 method_desc = svc.methods_by_name[method_name]
120 except KeyError:
121 raise UnknownMethodError(
122 "The %s method has not been defined in the %s "
123 "service." % (method_name, service_name)
124 )
Alex Klein92341cd2020-02-27 14:11:04 -0700125
Alex Klein1699fab2022-09-08 08:46:06 -0600126 return MethodData(
127 service_descriptor=svc,
128 module_name=module_name,
129 method_descriptor=method_desc,
130 )
Alex Klein92341cd2020-02-27 14:11:04 -0700131
Alex Klein1699fab2022-09-08 08:46:06 -0600132 def get_input_message_instance(self, service_name, method_name):
133 """Get an empty input message instance for the specified method."""
134 method_data = self._get_method_data(service_name, method_name)
135 return self._sym_db.GetPrototype(
136 method_data.method_descriptor.input_type
137 )()
Alex Klein92341cd2020-02-27 14:11:04 -0700138
Alex Klein1699fab2022-09-08 08:46:06 -0600139 def _get_output_message_instance(self, service_name, method_name):
140 """Get an empty output message instance for the specified method."""
141 method_data = self._get_method_data(service_name, method_name)
142 return self._sym_db.GetPrototype(
143 method_data.method_descriptor.output_type
144 )()
Alex Klein92341cd2020-02-27 14:11:04 -0700145
Alex Klein1699fab2022-09-08 08:46:06 -0600146 def _get_module_name(self, service_name, method_name):
147 """Get the name of the module containing the endpoint implementation."""
148 return self._get_method_data(service_name, method_name).module_name
Alex Klein92341cd2020-02-27 14:11:04 -0700149
Alex Klein1699fab2022-09-08 08:46:06 -0600150 def _get_service_options(self, service_name, method_name):
151 """Get the configured service options for the endpoint."""
152 method_data = self._get_method_data(service_name, method_name)
153 svc_extensions = method_data.service_descriptor.GetOptions().Extensions
154 return svc_extensions[self._svc_options_ext]
Alex Klein92341cd2020-02-27 14:11:04 -0700155
Alex Klein1699fab2022-09-08 08:46:06 -0600156 def _get_method_options(self, service_name, method_name):
157 """Get the configured method options for the endpoint."""
158 method_data = self._get_method_data(service_name, method_name)
159 method_extensions = (
160 method_data.method_descriptor.GetOptions().Extensions
161 )
162 return method_extensions[self._method_options_ext]
Alex Klein146d4772019-06-20 13:48:25 -0600163
Alex Klein1699fab2022-09-08 08:46:06 -0600164 def Register(self, proto_module: ModuleType):
165 """Register the services from a generated proto module.
Alex Klein146d4772019-06-20 13:48:25 -0600166
Alex Klein1699fab2022-09-08 08:46:06 -0600167 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600168 proto_module: The generated proto module to register.
Alex Klein146d4772019-06-20 13:48:25 -0600169
Alex Klein1699fab2022-09-08 08:46:06 -0600170 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600171 ServiceModuleNotDefinedError when the service cannot be found in the
172 provided module.
Alex Klein1699fab2022-09-08 08:46:06 -0600173 """
174 services = proto_module.DESCRIPTOR.services_by_name
175 for service_name, svc in services.items():
176 module_name = (
177 svc.GetOptions().Extensions[self._svc_options_ext].module
178 )
Alex Klein146d4772019-06-20 13:48:25 -0600179
Alex Klein1699fab2022-09-08 08:46:06 -0600180 if not module_name:
181 raise ControllerModuleNotDefinedError(
Alex Klein54c891a2023-01-24 10:45:41 -0700182 "The module must be defined in the service definition: "
183 f"{proto_module}.{service_name}"
Alex Klein1699fab2022-09-08 08:46:06 -0600184 )
Alex Klein146d4772019-06-20 13:48:25 -0600185
Alex Klein1699fab2022-09-08 08:46:06 -0600186 self._services[svc.full_name] = (svc, module_name)
Alex Klein146d4772019-06-20 13:48:25 -0600187
Alex Klein1699fab2022-09-08 08:46:06 -0600188 def ListMethods(self):
189 """List all methods registered with the router."""
190 services = []
191 for service_name, (svc, _module) in self._services.items():
192 svc_visibility = getattr(
193 svc.GetOptions().Extensions[self._svc_options_ext],
194 "service_visibility",
195 build_api_pb2.LV_VISIBLE,
196 )
197 if svc_visibility == build_api_pb2.LV_HIDDEN:
198 continue
Alex Klein6cce6f62021-03-02 14:24:05 -0700199
Alex Klein1699fab2022-09-08 08:46:06 -0600200 for method_name in svc.methods_by_name.keys():
201 method_options = self._get_method_options(
202 service_name, method_name
203 )
204 method_visibility = getattr(
205 method_options,
206 "method_visibility",
207 build_api_pb2.LV_VISIBLE,
208 )
209 if method_visibility == build_api_pb2.LV_HIDDEN:
210 continue
211
212 services.append("%s/%s" % (service_name, method_name))
213
214 return sorted(services)
215
216 def Route(
217 self,
218 service_name: str,
219 method_name: str,
220 config: "api_config.ApiConfig",
221 input_handler: "message_util.MessageHandler",
222 output_handlers: List["message_util.MessageHandler"],
223 config_handler: "message_util.MessageHandler",
224 ) -> int:
225 """Dispatch the request.
226
227 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600228 service_name: The fully qualified service name.
229 method_name: The name of the method being called.
230 config: The call configs.
231 input_handler: The request message handler.
232 output_handlers: The response message handlers.
233 config_handler: The config message handler.
Alex Klein1699fab2022-09-08 08:46:06 -0600234
235 Returns:
Alex Kleina0442682022-10-10 13:47:38 -0600236 The return code.
Alex Klein1699fab2022-09-08 08:46:06 -0600237
238 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600239 InvalidInputFileError when the input file cannot be read.
240 InvalidOutputFileError when the output file cannot be written.
241 ServiceModuleNotFoundError when the service module cannot be
242 imported.
243 MethodNotFoundError when the method cannot be retrieved from the
244 module.
Alex Klein1699fab2022-09-08 08:46:06 -0600245 """
246 input_msg = self.get_input_message_instance(service_name, method_name)
247 input_handler.read_into(input_msg)
248
249 # Get an empty output message instance.
250 output_msg = self._get_output_message_instance(
251 service_name, method_name
252 )
253
254 # Fetch the method options for chroot and method name overrides.
Alex Klein6cce6f62021-03-02 14:24:05 -0700255 method_options = self._get_method_options(service_name, method_name)
Alex Klein6cce6f62021-03-02 14:24:05 -0700256
Alex Klein1699fab2022-09-08 08:46:06 -0600257 # Check the chroot settings before running.
258 service_options = self._get_service_options(service_name, method_name)
259 if self._ChrootCheck(service_options, method_options, config):
260 # Run inside the chroot instead.
261 logging.info("Re-executing the endpoint inside the chroot.")
262 return self._ReexecuteInside(
263 input_msg,
264 output_msg,
265 config,
266 input_handler,
267 output_handlers,
268 config_handler,
269 service_name,
270 method_name,
271 )
Alex Klein146d4772019-06-20 13:48:25 -0600272
Alex Klein1699fab2022-09-08 08:46:06 -0600273 # Allow proto-based method name override.
274 if method_options.HasField("implementation_name"):
275 implementation_name = method_options.implementation_name
276 else:
277 implementation_name = method_name
Alex Klein146d4772019-06-20 13:48:25 -0600278
Alex Klein1699fab2022-09-08 08:46:06 -0600279 # Import the module and get the method.
280 module_name = self._get_module_name(service_name, method_name)
281 method_impl = self._GetMethod(module_name, implementation_name)
Alex Klein146d4772019-06-20 13:48:25 -0600282
Alex Klein1699fab2022-09-08 08:46:06 -0600283 # Successfully located; call and return.
284 return_code = method_impl(input_msg, output_msg, config)
285 if return_code is None:
286 return_code = controller.RETURN_CODE_SUCCESS
Alex Klein146d4772019-06-20 13:48:25 -0600287
Alex Klein1699fab2022-09-08 08:46:06 -0600288 for h in output_handlers:
289 h.write_from(output_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600290
Alex Klein1699fab2022-09-08 08:46:06 -0600291 return return_code
Alex Klein146d4772019-06-20 13:48:25 -0600292
Alex Klein1699fab2022-09-08 08:46:06 -0600293 def _ChrootCheck(
294 self,
295 service_options: "google.protobuf.Message",
296 method_options: "google.protobuf.Message",
297 config: "api_config.ApiConfig",
298 ) -> bool:
Alex Kleina0442682022-10-10 13:47:38 -0600299 """Check the chroot options; execute assertion or note reexec as needed.
Alex Klein146d4772019-06-20 13:48:25 -0600300
Alex Klein1699fab2022-09-08 08:46:06 -0600301 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600302 service_options: The service options.
303 method_options: The method options.
304 config: The Build API call config instance.
Alex Klein146d4772019-06-20 13:48:25 -0600305
Alex Klein1699fab2022-09-08 08:46:06 -0600306 Returns:
Alex Kleina0442682022-10-10 13:47:38 -0600307 True iff it needs to be reexeced inside the chroot.
Alex Kleinbd6edf82019-07-18 10:30:49 -0600308
Alex Klein1699fab2022-09-08 08:46:06 -0600309 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600310 cros_build_lib.DieSystemExit when the chroot setting cannot be
311 satisfied.
Alex Klein1699fab2022-09-08 08:46:06 -0600312 """
313 if not config.run_endpoint:
314 # Do not enter the chroot for validate only and mock calls.
315 return False
Alex Klein146d4772019-06-20 13:48:25 -0600316
Alex Klein1699fab2022-09-08 08:46:06 -0600317 chroot_assert = build_api_pb2.NO_ASSERTION
318 if method_options.HasField("method_chroot_assert"):
319 # Prefer the method option when set.
320 chroot_assert = method_options.method_chroot_assert
321 elif service_options.HasField("service_chroot_assert"):
322 # Fall back to the service option.
323 chroot_assert = service_options.service_chroot_assert
Alex Klein146d4772019-06-20 13:48:25 -0600324
Alex Klein1699fab2022-09-08 08:46:06 -0600325 if chroot_assert == build_api_pb2.INSIDE:
326 return not cros_build_lib.IsInsideChroot()
327 elif chroot_assert == build_api_pb2.OUTSIDE:
328 # If it must be run outside we have to already be outside.
329 cros_build_lib.AssertOutsideChroot()
Alex Klein146d4772019-06-20 13:48:25 -0600330
Alex Klein1699fab2022-09-08 08:46:06 -0600331 return False
Alex Klein146d4772019-06-20 13:48:25 -0600332
Alex Klein1699fab2022-09-08 08:46:06 -0600333 def _ReexecuteInside(
334 self,
335 input_msg: "google.protobuf.Message",
336 output_msg: "google.protobuf.Message",
337 config: "api_config.ApiConfig",
338 input_handler: "message_util.MessageHandler",
339 output_handlers: List["message_util.MessageHandler"],
340 config_handler: "message_util.MessageHandler",
341 service_name: str,
342 method_name: str,
343 ):
344 """Re-execute the service inside the chroot.
Alex Klein146d4772019-06-20 13:48:25 -0600345
Alex Klein1699fab2022-09-08 08:46:06 -0600346 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600347 input_msg: The parsed input message.
348 output_msg: The empty output message instance.
349 config: The call configs.
350 input_handler: Input message handler.
351 output_handlers: Output message handlers.
352 config_handler: Config message handler.
353 service_name: The name of the service to run.
354 method_name: The name of the method to run.
Alex Klein1699fab2022-09-08 08:46:06 -0600355 """
356 # Parse the chroot and clear the chroot field in the input message.
357 chroot = field_handler.handle_chroot(input_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600358
Alex Klein1699fab2022-09-08 08:46:06 -0600359 if not chroot.exists():
360 raise InvalidSdkError("Chroot does not exist.")
Alex Klein146d4772019-06-20 13:48:25 -0600361
Alex Klein1699fab2022-09-08 08:46:06 -0600362 # Use a ContextManagerStack to avoid the deep nesting this many
363 # context managers introduces.
364 with cros_build_lib.ContextManagerStack() as stack:
365 # TempDirs setup.
366 tempdir = stack.Add(chroot.tempdir).tempdir
367 sync_tempdir = stack.Add(chroot.tempdir).tempdir
368 # The copy-paths-in context manager to handle Path messages.
369 stack.Add(
370 field_handler.copy_paths_in,
371 input_msg,
372 chroot.tmp,
373 prefix=chroot.path,
374 )
375 # The sync-directories context manager to handle SyncedDir messages.
376 stack.Add(
377 field_handler.sync_dirs,
378 input_msg,
379 sync_tempdir,
380 prefix=chroot.path,
381 )
Alex Klein146d4772019-06-20 13:48:25 -0600382
Alex Klein1699fab2022-09-08 08:46:06 -0600383 # Parse goma.
384 chroot.goma = field_handler.handle_goma(input_msg, chroot.path)
Alex Kleind1e9e5c2020-12-14 12:32:32 -0700385
Alex Klein1699fab2022-09-08 08:46:06 -0600386 # Parse remoteexec.
387 chroot.remoteexec = field_handler.handle_remoteexec(input_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600388
Alex Klein54c891a2023-01-24 10:45:41 -0700389 # Build inside-chroot paths for the input, output, and config
390 # messages.
Alex Klein1699fab2022-09-08 08:46:06 -0600391 new_input = os.path.join(tempdir, self.REEXEC_INPUT_FILE)
392 chroot_input = "/%s" % os.path.relpath(new_input, chroot.path)
393 new_output = os.path.join(tempdir, self.REEXEC_OUTPUT_FILE)
394 chroot_output = "/%s" % os.path.relpath(new_output, chroot.path)
395 new_config = os.path.join(tempdir, self.REEXEC_CONFIG_FILE)
396 chroot_config = "/%s" % os.path.relpath(new_config, chroot.path)
Alex Klein146d4772019-06-20 13:48:25 -0600397
Alex Klein1699fab2022-09-08 08:46:06 -0600398 # Setup the inside-chroot message files.
399 logging.info("Writing input message to: %s", new_input)
400 input_handler.write_from(input_msg, path=new_input)
401 osutils.Touch(new_output)
402 logging.info("Writing config message to: %s", new_config)
403 config_handler.write_from(config.get_proto(), path=new_config)
Alex Klein146d4772019-06-20 13:48:25 -0600404
Alex Klein1699fab2022-09-08 08:46:06 -0600405 # We can use a single output to write the rest of them. Use the
406 # first one as the reexec output and just translate its output in
407 # the rest of the handlers after.
408 output_handler = output_handlers[0]
Alex Klein146d4772019-06-20 13:48:25 -0600409
Alex Klein1699fab2022-09-08 08:46:06 -0600410 cmd = [
411 "build_api",
412 "%s/%s" % (service_name, method_name),
413 input_handler.input_arg,
414 chroot_input,
415 output_handler.output_arg,
416 chroot_output,
417 config_handler.config_arg,
418 chroot_config,
419 "--debug",
420 ]
Alex Klein915cce92019-12-17 14:19:50 -0700421
Alex Klein1699fab2022-09-08 08:46:06 -0600422 try:
423 result = cros_build_lib.run(
424 cmd,
425 enter_chroot=True,
426 chroot_args=chroot.get_enter_args(),
427 check=False,
428 extra_env=chroot.env,
429 )
430 except cros_build_lib.RunCommandError:
431 # A non-zero return code will not result in an error, but one
432 # is still thrown when the command cannot be run in the first
433 # place. This is known to happen at least when the PATH does
434 # not include the chromite bin dir.
435 raise CrosSdkNotRunError("Unable to enter the chroot.")
Alex Kleind3394c22020-06-16 14:05:06 -0600436
Alex Klein1699fab2022-09-08 08:46:06 -0600437 logging.info(
438 "Endpoint execution completed, return code: %d",
439 result.returncode,
440 )
Alex Klein146d4772019-06-20 13:48:25 -0600441
Alex Klein1699fab2022-09-08 08:46:06 -0600442 # Transfer result files out of the chroot.
443 output_handler.read_into(output_msg, path=new_output)
444 field_handler.extract_results(input_msg, output_msg, chroot)
Alex Klein9b7331e2019-12-30 14:37:21 -0700445
Alex Klein54c891a2023-01-24 10:45:41 -0700446 # Write out all the response formats.
Alex Klein1699fab2022-09-08 08:46:06 -0600447 for handler in output_handlers:
448 handler.write_from(output_msg)
Joanna Wang92cad812021-11-03 14:52:08 -0700449
Alex Klein1699fab2022-09-08 08:46:06 -0600450 return result.returncode
Alex Klein9b7331e2019-12-30 14:37:21 -0700451
Alex Klein1699fab2022-09-08 08:46:06 -0600452 def _GetMethod(self, module_name: str, method_name: str) -> Callable:
453 """Get the implementation of the method for the service module.
Alex Klein146d4772019-06-20 13:48:25 -0600454
Alex Klein1699fab2022-09-08 08:46:06 -0600455 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600456 module_name: The name of the service module.
457 method_name: The name of the method.
Alex Kleine191ed62020-02-27 15:59:55 -0700458
Alex Klein1699fab2022-09-08 08:46:06 -0600459 Returns:
Alex Kleina0442682022-10-10 13:47:38 -0600460 The method.
Alex Klein146d4772019-06-20 13:48:25 -0600461
Alex Klein1699fab2022-09-08 08:46:06 -0600462 Raises:
Alex Kleina0442682022-10-10 13:47:38 -0600463 MethodNotFoundError when the method cannot be found in the module.
464 ServiceModuleNotFoundError when the service module cannot be
465 imported.
Alex Klein1699fab2022-09-08 08:46:06 -0600466 """
467 try:
468 module = importlib.import_module(
469 controller.IMPORT_PATTERN % module_name
470 )
471 except ImportError as e:
472 raise ServiceControllerNotFoundError(str(e))
473 try:
474 return getattr(module, method_name)
475 except AttributeError as e:
476 raise MethodNotFoundError(str(e))
Alex Klein146d4772019-06-20 13:48:25 -0600477
478
Tomasz Tylendab4292302021-08-08 18:59:36 +0900479def RegisterServices(router: Router):
Alex Klein1699fab2022-09-08 08:46:06 -0600480 """Register all the services.
Alex Klein146d4772019-06-20 13:48:25 -0600481
Alex Klein1699fab2022-09-08 08:46:06 -0600482 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600483 router: The router.
Alex Klein1699fab2022-09-08 08:46:06 -0600484 """
485 router.Register(android_pb2)
486 router.Register(api_pb2)
487 router.Register(artifacts_pb2)
488 router.Register(binhost_pb2)
489 router.Register(depgraph_pb2)
490 router.Register(firmware_pb2)
491 router.Register(image_pb2)
492 router.Register(metadata_pb2)
Lizzy Preslande723c012022-06-10 05:05:37 +0000493 router.Register(observability_pb2)
Alex Klein1699fab2022-09-08 08:46:06 -0600494 router.Register(packages_pb2)
495 router.Register(payload_pb2)
496 router.Register(portage_explorer_pb2)
497 router.Register(sdk_pb2)
498 router.Register(sysroot_pb2)
499 router.Register(test_pb2)
500 router.Register(toolchain_pb2)
501 logging.debug("Services registered successfully.")
Alex Klein146d4772019-06-20 13:48:25 -0600502
503
504def GetRouter():
Alex Klein54c891a2023-01-24 10:45:41 -0700505 """Get a router that has had all the services registered."""
Alex Klein1699fab2022-09-08 08:46:06 -0600506 router = Router()
507 RegisterServices(router)
Alex Klein146d4772019-06-20 13:48:25 -0600508
Alex Klein1699fab2022-09-08 08:46:06 -0600509 return router