blob: f7efa83bcd437261cd7d2534d6ff3752b3edde5e [file] [log] [blame]
Alex Kleinf4dc4f52018-12-05 13:55:12 -07001# Copyright 2018 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 Kleind815ca62020-01-10 12:21:30 -07005"""The Build API entry point."""
Alex Kleinf4dc4f52018-12-05 13:55:12 -07006
Alex Klein5bcb4d22019-03-21 13:51:54 -06007import os
Alex Kleind815ca62020-01-10 12:21:30 -07008
Alex Klein69339cc2019-07-22 14:08:35 -06009from chromite.api import api_config as api_config_lib
Alex Klein2008aee2019-08-20 16:25:27 -060010from chromite.api import controller
Alex Kleine191ed62020-02-27 15:59:55 -070011from chromite.api import message_util
Alex Klein146d4772019-06-20 13:48:25 -060012from chromite.api import router as router_lib
Alex Kleind815ca62020-01-10 12:21:30 -070013from chromite.api.gen.chromite.api import build_api_config_pb2
Alex Kleinf4dc4f52018-12-05 13:55:12 -070014from chromite.lib import commandline
Alex Klein2bfacb22019-02-04 11:42:17 -070015from chromite.lib import cros_build_lib
Michael Mortensen3e86c1e2019-11-21 15:51:54 -070016from chromite.lib import cros_logging as logging
Alex Klein00b1f1e2019-02-08 13:53:42 -070017from chromite.utils import matching
Alex Kleinf4dc4f52018-12-05 13:55:12 -070018
Mike Frysinger898265b2020-02-10 23:49:12 -050019
Alex Kleinf4dc4f52018-12-05 13:55:12 -070020def GetParser():
Alex Klein00b1f1e2019-02-08 13:53:42 -070021 """Build the argument parser."""
Alex Kleinf4dc4f52018-12-05 13:55:12 -070022 parser = commandline.ArgumentParser(description=__doc__)
23
Alex Kleind815ca62020-01-10 12:21:30 -070024 parser.add_argument(
Alex Klein2008aee2019-08-20 16:25:27 -060025 'service_method',
Alex Klein2008aee2019-08-20 16:25:27 -060026 help='The "chromite.api.Service/Method" that is being called.')
Alex Kleine191ed62020-02-27 15:59:55 -070027 # Input arguments.
28 input_args = parser.add_mutually_exclusive_group(required=True)
29 input_args.add_argument(
30 '--input-binary',
31 type='path',
32 help='Path to the protobuf binary serialization of the input message.')
33 input_args.add_argument(
Alex Klein2008aee2019-08-20 16:25:27 -060034 '--input-json',
35 type='path',
Alex Kleinf4dc4f52018-12-05 13:55:12 -070036 help='Path to the JSON serialized input argument protobuf message.')
Alex Kleine191ed62020-02-27 15:59:55 -070037 # Output options.
38 parser.add_argument(
39 '--output-binary',
40 type='path',
41 help='The path to which the protobuf binary serialization of the '
42 'response message should be written.')
Alex Kleind815ca62020-01-10 12:21:30 -070043 parser.add_argument(
Alex Klein2008aee2019-08-20 16:25:27 -060044 '--output-json',
45 type='path',
Alex Kleine191ed62020-02-27 15:59:55 -070046 help='The path to which the JSON serialization of the response message '
47 'should be written.')
48 # Config options.
49 config_args = parser.add_mutually_exclusive_group()
50 config_args.add_argument(
51 '--config-binary',
52 type='path',
53 help='The path to the protobuf binary serialization of the Build API '
54 'call configs.')
55 config_args.add_argument(
Alex Kleind815ca62020-01-10 12:21:30 -070056 '--config-json',
57 type='path',
Alex Kleine191ed62020-02-27 15:59:55 -070058 help='The path to the JSON encoded Build API call configs.')
Alex Klein2008aee2019-08-20 16:25:27 -060059
Alex Kleinf4dc4f52018-12-05 13:55:12 -070060 return parser
61
62
Alex Klein00b1f1e2019-02-08 13:53:42 -070063def _ParseArgs(argv, router):
Alex Kleinf4dc4f52018-12-05 13:55:12 -070064 """Parse and validate arguments."""
65 parser = GetParser()
Alex Klein7cc434f2019-12-17 14:58:57 -070066 opts, unknown = parser.parse_known_args(
67 argv, namespace=commandline.ArgumentNamespace())
68 parser.DoPostParseSetup(opts, unknown)
69
70 if unknown:
71 logging.warning('Unknown args ignored: %s', ' '.join(unknown))
Alex Kleinf4dc4f52018-12-05 13:55:12 -070072
Alex Klein00b1f1e2019-02-08 13:53:42 -070073 methods = router.ListMethods()
George Engelbrechtd3de8df2019-09-04 18:15:05 -060074
Alex Klein2008aee2019-08-20 16:25:27 -060075 # Positional service_method argument validation.
George Engelbrechtd3de8df2019-09-04 18:15:05 -060076 parts = opts.service_method.split('/')
77 if len(parts) != 2:
Alex Kleind815ca62020-01-10 12:21:30 -070078 parser.error('Invalid service/method specification format. It should be '
79 'something like chromite.api.SdkService/Create.')
George Engelbrechtd3de8df2019-09-04 18:15:05 -060080
Alex Klein00b1f1e2019-02-08 13:53:42 -070081 if opts.service_method not in methods:
Alex Klein00aa8072019-04-15 16:36:00 -060082 # Unknown method, try to match against known methods and make a suggestion.
83 # This is just for developer sanity, e.g. misspellings when testing.
Alex Klein2008aee2019-08-20 16:25:27 -060084 matched = matching.GetMostLikelyMatchedObject(
85 methods, opts.service_method, matched_score_threshold=0.6)
Alex Klein00b1f1e2019-02-08 13:53:42 -070086 error = 'Unrecognized service name.'
87 if matched:
88 error += '\nDid you mean: \n%s' % '\n'.join(matched)
89 parser.error(error)
90
Alex Kleinf4dc4f52018-12-05 13:55:12 -070091 opts.service = parts[0]
92 opts.method = parts[1]
93
Alex Kleine191ed62020-02-27 15:59:55 -070094 # Input and output validation.
95 if not opts.output_binary and not opts.output_json:
96 parser.error('At least one output file must be specified.')
Alex Klein2008aee2019-08-20 16:25:27 -060097
Alex Kleine191ed62020-02-27 15:59:55 -070098 if not os.path.exists(opts.input_binary or opts.input_json):
Alex Klein5bcb4d22019-03-21 13:51:54 -060099 parser.error('Input file does not exist.')
100
Alex Kleind815ca62020-01-10 12:21:30 -0700101 config_msg = build_api_config_pb2.BuildApiConfig()
102 if opts.config_json:
Alex Kleine191ed62020-02-27 15:59:55 -0700103 handler = message_util.get_message_handler(opts.config_json,
104 message_util.FORMAT_JSON)
105 else:
106 handler = message_util.get_message_handler(opts.config_binary,
107 message_util.FORMAT_BINARY)
108
109 if opts.config_json or opts.config_binary:
110 # We have been given a config, so read it.
Alex Kleind815ca62020-01-10 12:21:30 -0700111 try:
Alex Kleine191ed62020-02-27 15:59:55 -0700112 handler.read_into(config_msg)
113 except message_util.Error as e:
114 parser.error(str(e))
Alex Kleind815ca62020-01-10 12:21:30 -0700115
116 opts.config = api_config_lib.build_config_from_proto(config_msg)
Alex Kleine191ed62020-02-27 15:59:55 -0700117 opts.config_handler = handler
Alex Klein69339cc2019-07-22 14:08:35 -0600118
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700119 opts.Freeze()
120 return opts
121
122
Alex Kleine191ed62020-02-27 15:59:55 -0700123def _get_io_handlers(opts):
124 """Build the input and output handlers."""
125 if opts.input_binary:
126 input_handler = message_util.get_message_handler(opts.input_binary,
127 message_util.FORMAT_BINARY)
128 else:
129 input_handler = message_util.get_message_handler(opts.input_json,
130 message_util.FORMAT_JSON)
131
132 output_handlers = []
133 if opts.output_binary:
134 handler = message_util.get_message_handler(opts.output_binary,
135 message_util.FORMAT_BINARY)
136 output_handlers.append(handler)
137 if opts.output_json:
138 handler = message_util.get_message_handler(opts.output_json,
139 message_util.FORMAT_JSON)
140 output_handlers.append(handler)
141
142 return input_handler, output_handlers
143
144
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700145def main(argv):
Mike Frysingerda928de2021-06-21 12:49:40 -0400146 router = router_lib.GetRouter()
147 opts = _ParseArgs(argv, router)
Alex Klein00b1f1e2019-02-08 13:53:42 -0700148
Mike Frysingerda928de2021-06-21 12:49:40 -0400149 if opts.config.log_path:
150 logging.warning('Ignoring log_path config option')
151 if 'BUILD_API_TEE_LOG_FILE' in os.environ:
152 logging.warning('Ignoring $BUILD_API_TEE_LOG_FILE env var')
Alex Klein2008aee2019-08-20 16:25:27 -0600153
Mike Frysingerda928de2021-06-21 12:49:40 -0400154 if opts.config.mock_invalid:
155 # --mock-invalid handling. We print error messages, but no output is ever
156 # set for validation errors, so we can handle it by just giving back the
157 # correct return code here.
158 return controller.RETURN_CODE_INVALID_INPUT
Michael Mortensen3e86c1e2019-11-21 15:51:54 -0700159
Mike Frysingerda928de2021-06-21 12:49:40 -0400160 input_handler, output_handlers = _get_io_handlers(opts)
Alex Kleine191ed62020-02-27 15:59:55 -0700161
Mike Frysingerda928de2021-06-21 12:49:40 -0400162 try:
163 return router.Route(opts.service, opts.method, opts.config, input_handler,
164 output_handlers, opts.config_handler)
165 except router_lib.Error as e:
166 # Handle router_lib.Error derivatives nicely, but let anything else bubble
167 # up.
168 cros_build_lib.Die(e)