blob: 0e5a43da1c0d08698548e6b19cc20cb811584053 [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 Kleineb77ffa2019-05-28 14:47:44 -060030from chromite.api.gen.chromite.api import packages_pb2
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060031from chromite.api.gen.chromite.api import payload_pb2
Alex Klein146d4772019-06-20 13:48:25 -060032from chromite.api.gen.chromite.api import sdk_pb2
33from chromite.api.gen.chromite.api import sysroot_pb2
34from chromite.api.gen.chromite.api import test_pb2
Tiancong Wangaf050172019-07-10 11:52:03 -070035from chromite.api.gen.chromite.api import toolchain_pb2
Alex Klein146d4772019-06-20 13:48:25 -060036from chromite.lib import cros_build_lib
Alex Klein146d4772019-06-20 13:48:25 -060037from chromite.lib import osutils
Alex Klein92341cd2020-02-27 14:11:04 -070038from chromite.utils import memoize
Alex Klein146d4772019-06-20 13:48:25 -060039
Tomasz Tylendab4292302021-08-08 18:59:36 +090040if TYPE_CHECKING:
41 from chromite.api import api_config
42 from chromite.api import message_util
43 from chromite.third_party import google
Mike Frysinger88770ef2021-05-21 11:04:00 -040044
Alex Klein92341cd2020-02-27 14:11:04 -070045MethodData = collections.namedtuple(
46 'MethodData', ('service_descriptor', 'module_name', 'method_descriptor'))
Alex Klein146d4772019-06-20 13:48:25 -060047
Mike Frysingeref94e4c2020-02-10 23:59:54 -050048
Alex Klein146d4772019-06-20 13:48:25 -060049class Error(Exception):
50 """Base error class for the module."""
51
52
Alex Kleind3394c22020-06-16 14:05:06 -060053class InvalidSdkError(Error):
54 """Raised when the SDK is invalid or does not exist."""
55
56
Alex Klein146d4772019-06-20 13:48:25 -060057class CrosSdkNotRunError(Error):
58 """Raised when the cros_sdk command could not be run to enter the chroot."""
59
60
61# API Service Errors.
62class UnknownServiceError(Error):
63 """Error raised when the requested service has not been registered."""
64
65
66class ControllerModuleNotDefinedError(Error):
Alex Klein92341cd2020-02-27 14:11:04 -070067 """Error class for when no controller has been defined for a service."""
Alex Klein146d4772019-06-20 13:48:25 -060068
69
70class ServiceControllerNotFoundError(Error):
71 """Error raised when the service's controller cannot be imported."""
72
73
74# API Method Errors.
75class UnknownMethodError(Error):
Alex Klein92341cd2020-02-27 14:11:04 -070076 """The service has been defined in the proto, but the method has not."""
Alex Klein146d4772019-06-20 13:48:25 -060077
78
79class MethodNotFoundError(Error):
80 """The method's implementation cannot be found in the service's controller."""
81
82
83class Router(object):
84 """Encapsulates the request dispatching logic."""
85
Alex Kleine191ed62020-02-27 15:59:55 -070086 REEXEC_INPUT_FILE = 'input_proto'
87 REEXEC_OUTPUT_FILE = 'output_proto'
88 REEXEC_CONFIG_FILE = 'config_proto'
Alex Kleinbd6edf82019-07-18 10:30:49 -060089
Alex Klein146d4772019-06-20 13:48:25 -060090 def __init__(self):
91 self._services = {}
92 self._aliases = {}
93 # All imported generated messages get added to this symbol db.
94 self._sym_db = symbol_database.Default()
95
Alex Klein92341cd2020-02-27 14:11:04 -070096 # Save the service and method extension info for looking up
97 # configured extension data.
Alex Klein146d4772019-06-20 13:48:25 -060098 extensions = build_api_pb2.DESCRIPTOR.extensions_by_name
Alex Klein92341cd2020-02-27 14:11:04 -070099 self._svc_options_ext = extensions['service_options']
100 self._method_options_ext = extensions['method_options']
101
102 @memoize.Memoize
103 def _get_method_data(self, service_name, method_name):
104 """Get the descriptors and module name for the given Service/Method."""
105 try:
106 svc, module_name = self._services[service_name]
107 except KeyError:
108 raise UnknownServiceError(
109 'The %s service has not been registered.' % service_name)
110
111 try:
112 method_desc = svc.methods_by_name[method_name]
113 except KeyError:
114 raise UnknownMethodError('The %s method has not been defined in the %s '
115 'service.' % (method_name, service_name))
116
117 return MethodData(
118 service_descriptor=svc,
119 module_name=module_name,
120 method_descriptor=method_desc)
121
122 def _get_input_message_instance(self, service_name, method_name):
123 """Get an empty input message instance for the specified method."""
124 method_data = self._get_method_data(service_name, method_name)
125 return self._sym_db.GetPrototype(method_data.method_descriptor.input_type)()
126
127 def _get_output_message_instance(self, service_name, method_name):
128 """Get an empty output message instance for the specified method."""
129 method_data = self._get_method_data(service_name, method_name)
130 return self._sym_db.GetPrototype(
131 method_data.method_descriptor.output_type)()
132
133 def _get_module_name(self, service_name, method_name):
134 """Get the name of the module containing the endpoint implementation."""
135 return self._get_method_data(service_name, method_name).module_name
136
137 def _get_service_options(self, service_name, method_name):
138 """Get the configured service options for the endpoint."""
139 method_data = self._get_method_data(service_name, method_name)
140 svc_extensions = method_data.service_descriptor.GetOptions().Extensions
141 return svc_extensions[self._svc_options_ext]
142
143 def _get_method_options(self, service_name, method_name):
144 """Get the configured method options for the endpoint."""
145 method_data = self._get_method_data(service_name, method_name)
146 method_extensions = method_data.method_descriptor.GetOptions().Extensions
147 return method_extensions[self._method_options_ext]
Alex Klein146d4772019-06-20 13:48:25 -0600148
Tomasz Tylendab4292302021-08-08 18:59:36 +0900149 def Register(self, proto_module: ModuleType):
Alex Klein146d4772019-06-20 13:48:25 -0600150 """Register the services from a generated proto module.
151
152 Args:
Tomasz Tylendab4292302021-08-08 18:59:36 +0900153 proto_module: The generated proto module to register.
Alex Klein146d4772019-06-20 13:48:25 -0600154
155 Raises:
156 ServiceModuleNotDefinedError when the service cannot be found in the
157 provided module.
158 """
159 services = proto_module.DESCRIPTOR.services_by_name
160 for service_name, svc in services.items():
Alex Klein92341cd2020-02-27 14:11:04 -0700161 module_name = svc.GetOptions().Extensions[self._svc_options_ext].module
Alex Klein146d4772019-06-20 13:48:25 -0600162
163 if not module_name:
164 raise ControllerModuleNotDefinedError(
165 'The module must be defined in the service definition: %s.%s' %
166 (proto_module, service_name))
167
168 self._services[svc.full_name] = (svc, module_name)
169
170 def ListMethods(self):
171 """List all methods registered with the router."""
172 services = []
173 for service_name, (svc, _module) in self._services.items():
Alex Klein6cce6f62021-03-02 14:24:05 -0700174 svc_visibility = getattr(
175 svc.GetOptions().Extensions[self._svc_options_ext],
176 'service_visibility', build_api_pb2.LV_VISIBLE)
177 if svc_visibility == build_api_pb2.LV_HIDDEN:
178 continue
179
Alex Klein146d4772019-06-20 13:48:25 -0600180 for method_name in svc.methods_by_name.keys():
Alex Klein6cce6f62021-03-02 14:24:05 -0700181 method_options = self._get_method_options(service_name, method_name)
182 method_visibility = getattr(method_options, 'method_visibility',
183 build_api_pb2.LV_VISIBLE)
184 if method_visibility == build_api_pb2.LV_HIDDEN:
185 continue
186
Alex Klein146d4772019-06-20 13:48:25 -0600187 services.append('%s/%s' % (service_name, method_name))
188
189 return sorted(services)
190
Tomasz Tylendab4292302021-08-08 18:59:36 +0900191 def Route(self, service_name: str, method_name: str,
192 config: 'api_config.ApiConfig',
193 input_handler: 'message_util.MessageHandler',
194 output_handlers: List['message_util.MessageHandler'],
195 config_handler: 'message_util.MessageHandler') -> int:
Alex Klein146d4772019-06-20 13:48:25 -0600196 """Dispatch the request.
197
198 Args:
Tomasz Tylendab4292302021-08-08 18:59:36 +0900199 service_name: The fully qualified service name.
200 method_name: The name of the method being called.
201 config: The call configs.
202 input_handler: The request message handler.
203 output_handlers: The response message handlers.
204 config_handler: The config message handler.
Alex Klein146d4772019-06-20 13:48:25 -0600205
206 Returns:
Tomasz Tylendab4292302021-08-08 18:59:36 +0900207 The return code.
Alex Klein146d4772019-06-20 13:48:25 -0600208
209 Raises:
210 InvalidInputFileError when the input file cannot be read.
211 InvalidOutputFileError when the output file cannot be written.
212 ServiceModuleNotFoundError when the service module cannot be imported.
213 MethodNotFoundError when the method cannot be retrieved from the module.
214 """
Alex Klein92341cd2020-02-27 14:11:04 -0700215 input_msg = self._get_input_message_instance(service_name, method_name)
Alex Kleine191ed62020-02-27 15:59:55 -0700216 input_handler.read_into(input_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600217
218 # Get an empty output message instance.
Alex Klein92341cd2020-02-27 14:11:04 -0700219 output_msg = self._get_output_message_instance(service_name, method_name)
Alex Klein146d4772019-06-20 13:48:25 -0600220
Alex Kleinbd6edf82019-07-18 10:30:49 -0600221 # Fetch the method options for chroot and method name overrides.
Alex Klein92341cd2020-02-27 14:11:04 -0700222 method_options = self._get_method_options(service_name, method_name)
Alex Klein146d4772019-06-20 13:48:25 -0600223
224 # Check the chroot settings before running.
Alex Klein92341cd2020-02-27 14:11:04 -0700225 service_options = self._get_service_options(service_name, method_name)
Alex Kleind1e9e5c2020-12-14 12:32:32 -0700226 if self._ChrootCheck(service_options, method_options, config):
Alex Klein146d4772019-06-20 13:48:25 -0600227 # Run inside the chroot instead.
228 logging.info('Re-executing the endpoint inside the chroot.')
Alex Kleine191ed62020-02-27 15:59:55 -0700229 return self._ReexecuteInside(input_msg, output_msg, config, input_handler,
230 output_handlers, config_handler,
231 service_name, method_name)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600232
233 # Allow proto-based method name override.
234 if method_options.HasField('implementation_name'):
Alex Klein92341cd2020-02-27 14:11:04 -0700235 implementation_name = method_options.implementation_name
236 else:
237 implementation_name = method_name
Alex Klein146d4772019-06-20 13:48:25 -0600238
239 # Import the module and get the method.
Alex Klein92341cd2020-02-27 14:11:04 -0700240 module_name = self._get_module_name(service_name, method_name)
241 method_impl = self._GetMethod(module_name, implementation_name)
Alex Klein146d4772019-06-20 13:48:25 -0600242
243 # Successfully located; call and return.
Alex Klein69339cc2019-07-22 14:08:35 -0600244 return_code = method_impl(input_msg, output_msg, config)
Alex Klein146d4772019-06-20 13:48:25 -0600245 if return_code is None:
246 return_code = controller.RETURN_CODE_SUCCESS
247
Alex Kleine191ed62020-02-27 15:59:55 -0700248 for h in output_handlers:
249 h.write_from(output_msg)
Alex Klein146d4772019-06-20 13:48:25 -0600250
251 return return_code
252
Tomasz Tylendab4292302021-08-08 18:59:36 +0900253 def _ChrootCheck(self, service_options: 'google.protobuf.Message',
254 method_options: 'google.protobuf.Message',
255 config: 'api_config.ApiConfig') -> bool:
Alex Klein146d4772019-06-20 13:48:25 -0600256 """Check the chroot options, and execute assertion or note reexec as needed.
257
258 Args:
Tomasz Tylendab4292302021-08-08 18:59:36 +0900259 service_options: The service options.
260 method_options: The method options.
Alex Kleind1e9e5c2020-12-14 12:32:32 -0700261 config: The Build API call config instance.
Alex Klein146d4772019-06-20 13:48:25 -0600262
263 Returns:
Tomasz Tylendab4292302021-08-08 18:59:36 +0900264 True iff it needs to be reexeced inside the chroot.
Alex Klein146d4772019-06-20 13:48:25 -0600265
266 Raises:
267 cros_build_lib.DieSystemExit when the chroot setting cannot be satisfied.
268 """
Alex Kleind1e9e5c2020-12-14 12:32:32 -0700269 if not config.run_endpoint:
270 # Do not enter the chroot for validate only and mock calls.
271 return False
272
Alex Klein146d4772019-06-20 13:48:25 -0600273 chroot_assert = build_api_pb2.NO_ASSERTION
274 if method_options.HasField('method_chroot_assert'):
275 # Prefer the method option when set.
276 chroot_assert = method_options.method_chroot_assert
277 elif service_options.HasField('service_chroot_assert'):
278 # Fall back to the service option.
279 chroot_assert = service_options.service_chroot_assert
280
281 if chroot_assert == build_api_pb2.INSIDE:
282 return not cros_build_lib.IsInsideChroot()
283 elif chroot_assert == build_api_pb2.OUTSIDE:
284 # If it must be run outside we have to already be outside.
285 cros_build_lib.AssertOutsideChroot()
286
287 return False
288
Tomasz Tylendab4292302021-08-08 18:59:36 +0900289 def _ReexecuteInside(self, input_msg: 'google.protobuf.Message',
290 output_msg: 'google.protobuf.Message',
291 config: 'api_config.ApiConfig',
292 input_handler: 'message_util.MessageHandler',
293 output_handlers: List['message_util.MessageHandler'],
294 config_handler: 'message_util.MessageHandler',
295 service_name: str,
296 method_name: str):
Alex Klein146d4772019-06-20 13:48:25 -0600297 """Re-execute the service inside the chroot.
298
299 Args:
Tomasz Tylendab4292302021-08-08 18:59:36 +0900300 input_msg: The parsed input message.
301 output_msg: The empty output message instance.
302 config: The call configs.
303 input_handler: Input message handler.
304 output_handlers: Output message handlers.
305 config_handler: Config message handler.
306 service_name: The name of the service to run.
307 method_name: The name of the method to run.
Alex Klein146d4772019-06-20 13:48:25 -0600308 """
Alex Kleinc7d647f2020-01-06 12:00:48 -0700309 # Parse the chroot and clear the chroot field in the input message.
310 chroot = field_handler.handle_chroot(input_msg)
Alex Klein915cce92019-12-17 14:19:50 -0700311
Alex Kleind3394c22020-06-16 14:05:06 -0600312 if not chroot.exists():
313 raise InvalidSdkError('Chroot does not exist.')
314
Alex Kleinc7d647f2020-01-06 12:00:48 -0700315 # Use a ContextManagerStack to avoid the deep nesting this many
316 # context managers introduces.
317 with cros_build_lib.ContextManagerStack() as stack:
318 # TempDirs setup.
319 tempdir = stack.Add(chroot.tempdir).tempdir
320 sync_tempdir = stack.Add(chroot.tempdir).tempdir
321 # The copy-paths-in context manager to handle Path messages.
Alex Klein92341cd2020-02-27 14:11:04 -0700322 stack.Add(
323 field_handler.copy_paths_in,
324 input_msg,
325 chroot.tmp,
326 prefix=chroot.path)
Alex Kleinc7d647f2020-01-06 12:00:48 -0700327 # The sync-directories context manager to handle SyncedDir messages.
Alex Klein92341cd2020-02-27 14:11:04 -0700328 stack.Add(
329 field_handler.sync_dirs, input_msg, sync_tempdir, prefix=chroot.path)
Alex Klein146d4772019-06-20 13:48:25 -0600330
Alex Klein4089a492020-06-30 10:59:36 -0600331 # Parse goma.
Alex Kleinc7d647f2020-01-06 12:00:48 -0700332 chroot.goma = field_handler.handle_goma(input_msg, chroot.path)
Alex Klein9b7331e2019-12-30 14:37:21 -0700333
Joanna Wang92cad812021-11-03 14:52:08 -0700334 # Parse remoteexec.
335 chroot.remoteexec = field_handler.handle_remoteexec(input_msg)
336
Alex Klein4089a492020-06-30 10:59:36 -0600337 # Build inside-chroot paths for the input, output, and config messages.
Alex Kleinc7d647f2020-01-06 12:00:48 -0700338 new_input = os.path.join(tempdir, self.REEXEC_INPUT_FILE)
339 chroot_input = '/%s' % os.path.relpath(new_input, chroot.path)
340 new_output = os.path.join(tempdir, self.REEXEC_OUTPUT_FILE)
341 chroot_output = '/%s' % os.path.relpath(new_output, chroot.path)
Alex Kleind815ca62020-01-10 12:21:30 -0700342 new_config = os.path.join(tempdir, self.REEXEC_CONFIG_FILE)
343 chroot_config = '/%s' % os.path.relpath(new_config, chroot.path)
Alex Klein9b7331e2019-12-30 14:37:21 -0700344
Alex Klein4089a492020-06-30 10:59:36 -0600345 # Setup the inside-chroot message files.
Alex Kleinc7d647f2020-01-06 12:00:48 -0700346 logging.info('Writing input message to: %s', new_input)
Alex Kleine191ed62020-02-27 15:59:55 -0700347 input_handler.write_from(input_msg, path=new_input)
Alex Kleinc7d647f2020-01-06 12:00:48 -0700348 osutils.Touch(new_output)
Alex Kleind815ca62020-01-10 12:21:30 -0700349 logging.info('Writing config message to: %s', new_config)
Alex Kleine191ed62020-02-27 15:59:55 -0700350 config_handler.write_from(config.get_proto(), path=new_config)
Alex Klein146d4772019-06-20 13:48:25 -0600351
Alex Kleine191ed62020-02-27 15:59:55 -0700352 # We can use a single output to write the rest of them. Use the
353 # first one as the reexec output and just translate its output in
354 # the rest of the handlers after.
355 output_handler = output_handlers[0]
356
357 cmd = [
358 'build_api',
359 '%s/%s' % (service_name, method_name),
360 input_handler.input_arg,
361 chroot_input,
362 output_handler.output_arg,
363 chroot_output,
364 config_handler.config_arg,
365 chroot_config,
Alex Klein0b9edda2020-05-20 10:35:01 -0600366 '--debug',
Alex Kleine191ed62020-02-27 15:59:55 -0700367 ]
Alex Klein146d4772019-06-20 13:48:25 -0600368
Alex Kleinc7d647f2020-01-06 12:00:48 -0700369 try:
370 result = cros_build_lib.run(
371 cmd,
372 enter_chroot=True,
373 chroot_args=chroot.get_enter_args(),
374 check=False,
375 extra_env=chroot.env)
376 except cros_build_lib.RunCommandError:
377 # A non-zero return code will not result in an error, but one
378 # is still thrown when the command cannot be run in the first
379 # place. This is known to happen at least when the PATH does
380 # not include the chromite bin dir.
381 raise CrosSdkNotRunError('Unable to enter the chroot.')
Alex Klein69339cc2019-07-22 14:08:35 -0600382
Alex Kleinc7d647f2020-01-06 12:00:48 -0700383 logging.info('Endpoint execution completed, return code: %d',
384 result.returncode)
Alex Klein146d4772019-06-20 13:48:25 -0600385
Alex Kleinc7d647f2020-01-06 12:00:48 -0700386 # Transfer result files out of the chroot.
Alex Kleine191ed62020-02-27 15:59:55 -0700387 output_handler.read_into(output_msg, path=new_output)
388 field_handler.extract_results(input_msg, output_msg, chroot)
Alex Klein146d4772019-06-20 13:48:25 -0600389
Alex Kleine191ed62020-02-27 15:59:55 -0700390 # Write out all of the response formats.
391 for handler in output_handlers:
392 handler.write_from(output_msg)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600393
Alex Kleinc7d647f2020-01-06 12:00:48 -0700394 return result.returncode
Alex Klein146d4772019-06-20 13:48:25 -0600395
Tomasz Tylendab4292302021-08-08 18:59:36 +0900396 def _GetMethod(self, module_name: str, method_name: str) -> Callable:
Alex Klein146d4772019-06-20 13:48:25 -0600397 """Get the implementation of the method for the service module.
398
399 Args:
Tomasz Tylendab4292302021-08-08 18:59:36 +0900400 module_name: The name of the service module.
401 method_name: The name of the method.
Alex Klein146d4772019-06-20 13:48:25 -0600402
403 Returns:
Tomasz Tylendab4292302021-08-08 18:59:36 +0900404 The method.
Alex Klein146d4772019-06-20 13:48:25 -0600405
406 Raises:
407 MethodNotFoundError when the method cannot be found in the module.
408 ServiceModuleNotFoundError when the service module cannot be imported.
409 """
410 try:
411 module = importlib.import_module(controller.IMPORT_PATTERN % module_name)
412 except ImportError as e:
Mike Frysinger6b5c3cd2019-08-27 16:51:00 -0400413 raise ServiceControllerNotFoundError(str(e))
Alex Klein146d4772019-06-20 13:48:25 -0600414 try:
415 return getattr(module, method_name)
416 except AttributeError as e:
Mike Frysinger6b5c3cd2019-08-27 16:51:00 -0400417 raise MethodNotFoundError(str(e))
Alex Klein146d4772019-06-20 13:48:25 -0600418
419
Tomasz Tylendab4292302021-08-08 18:59:36 +0900420def RegisterServices(router: Router):
Alex Klein146d4772019-06-20 13:48:25 -0600421 """Register all the services.
422
423 Args:
Tomasz Tylendab4292302021-08-08 18:59:36 +0900424 router: The router.
Alex Klein146d4772019-06-20 13:48:25 -0600425 """
Alex Klein4de25e82019-08-05 15:58:39 -0600426 router.Register(android_pb2)
Alex Klein54e38e32019-06-21 14:54:17 -0600427 router.Register(api_pb2)
Alex Klein146d4772019-06-20 13:48:25 -0600428 router.Register(artifacts_pb2)
429 router.Register(binhost_pb2)
430 router.Register(depgraph_pb2)
Jett Rink17ed0f52020-09-25 17:14:31 -0600431 router.Register(firmware_pb2)
Alex Klein146d4772019-06-20 13:48:25 -0600432 router.Register(image_pb2)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600433 router.Register(packages_pb2)
George Engelbrechtfe63c8c2019-08-31 22:51:29 -0600434 router.Register(payload_pb2)
Alex Klein146d4772019-06-20 13:48:25 -0600435 router.Register(sdk_pb2)
436 router.Register(sysroot_pb2)
437 router.Register(test_pb2)
Tiancong Wangaf050172019-07-10 11:52:03 -0700438 router.Register(toolchain_pb2)
Alex Klein146d4772019-06-20 13:48:25 -0600439 logging.debug('Services registered successfully.')
440
441
442def GetRouter():
443 """Get a router that has had all of the services registered."""
444 router = Router()
445 RegisterServices(router)
446
447 return router