blob: 2c77a64207959715da73301a5f4e8459773bc83d [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
Chris McDonald59650c32021-07-20 15:29:28 -06007import logging
Alex Klein5bcb4d22019-03-21 13:51:54 -06008import os
Cindy Linc88eba32022-08-10 04:45:03 +00009import sys
Alex Kleind815ca62020-01-10 12:21:30 -070010
Alex Klein69339cc2019-07-22 14:08:35 -060011from chromite.api import api_config as api_config_lib
Alex Klein2008aee2019-08-20 16:25:27 -060012from chromite.api import controller
Alex Kleine191ed62020-02-27 15:59:55 -070013from chromite.api import message_util
Alex Klein146d4772019-06-20 13:48:25 -060014from chromite.api import router as router_lib
Alex Kleind815ca62020-01-10 12:21:30 -070015from chromite.api.gen.chromite.api import build_api_config_pb2
Alex Kleinf4dc4f52018-12-05 13:55:12 -070016from chromite.lib import commandline
Alex Klein2bfacb22019-02-04 11:42:17 -070017from chromite.lib import cros_build_lib
Alex Klein00b1f1e2019-02-08 13:53:42 -070018from chromite.utils import matching
Alex Kleinf4dc4f52018-12-05 13:55:12 -070019
Mike Frysinger898265b2020-02-10 23:49:12 -050020
Alex Kleinf4dc4f52018-12-05 13:55:12 -070021def GetParser():
Alex Klein00b1f1e2019-02-08 13:53:42 -070022 """Build the argument parser."""
Alex Kleinf4dc4f52018-12-05 13:55:12 -070023 parser = commandline.ArgumentParser(description=__doc__)
24
Alex Kleind815ca62020-01-10 12:21:30 -070025 parser.add_argument(
Alex Klein2008aee2019-08-20 16:25:27 -060026 'service_method',
Alex Klein2008aee2019-08-20 16:25:27 -060027 help='The "chromite.api.Service/Method" that is being called.')
Alex Kleine191ed62020-02-27 15:59:55 -070028 # Input arguments.
29 input_args = parser.add_mutually_exclusive_group(required=True)
30 input_args.add_argument(
31 '--input-binary',
32 type='path',
33 help='Path to the protobuf binary serialization of the input message.')
34 input_args.add_argument(
Alex Klein2008aee2019-08-20 16:25:27 -060035 '--input-json',
36 type='path',
Alex Kleinf4dc4f52018-12-05 13:55:12 -070037 help='Path to the JSON serialized input argument protobuf message.')
Alex Kleine191ed62020-02-27 15:59:55 -070038 # Output options.
39 parser.add_argument(
40 '--output-binary',
41 type='path',
42 help='The path to which the protobuf binary serialization of the '
43 'response message should be written.')
Alex Kleind815ca62020-01-10 12:21:30 -070044 parser.add_argument(
Alex Klein2008aee2019-08-20 16:25:27 -060045 '--output-json',
46 type='path',
Alex Kleine191ed62020-02-27 15:59:55 -070047 help='The path to which the JSON serialization of the response message '
48 'should be written.')
49 # Config options.
50 config_args = parser.add_mutually_exclusive_group()
51 config_args.add_argument(
52 '--config-binary',
53 type='path',
54 help='The path to the protobuf binary serialization of the Build API '
55 'call configs.')
56 config_args.add_argument(
Alex Kleind815ca62020-01-10 12:21:30 -070057 '--config-json',
58 type='path',
Alex Kleine191ed62020-02-27 15:59:55 -070059 help='The path to the JSON encoded Build API call configs.')
Alex Klein2008aee2019-08-20 16:25:27 -060060
Alex Kleinf4dc4f52018-12-05 13:55:12 -070061 return parser
62
63
Alex Klein00b1f1e2019-02-08 13:53:42 -070064def _ParseArgs(argv, router):
Alex Kleinf4dc4f52018-12-05 13:55:12 -070065 """Parse and validate arguments."""
66 parser = GetParser()
Alex Klein7cc434f2019-12-17 14:58:57 -070067 opts, unknown = parser.parse_known_args(
68 argv, namespace=commandline.ArgumentNamespace())
69 parser.DoPostParseSetup(opts, unknown)
70
71 if unknown:
72 logging.warning('Unknown args ignored: %s', ' '.join(unknown))
Alex Kleinf4dc4f52018-12-05 13:55:12 -070073
Alex Klein00b1f1e2019-02-08 13:53:42 -070074 methods = router.ListMethods()
George Engelbrechtd3de8df2019-09-04 18:15:05 -060075
Alex Klein2008aee2019-08-20 16:25:27 -060076 # Positional service_method argument validation.
George Engelbrechtd3de8df2019-09-04 18:15:05 -060077 parts = opts.service_method.split('/')
78 if len(parts) != 2:
Alex Kleind815ca62020-01-10 12:21:30 -070079 parser.error('Invalid service/method specification format. It should be '
80 'something like chromite.api.SdkService/Create.')
George Engelbrechtd3de8df2019-09-04 18:15:05 -060081
Alex Klein00b1f1e2019-02-08 13:53:42 -070082 if opts.service_method not in methods:
Alex Klein00aa8072019-04-15 16:36:00 -060083 # Unknown method, try to match against known methods and make a suggestion.
Sloan Johnsona1c89eb2022-06-07 22:49:05 +000084 # This is just for developer assistance, e.g. misspellings when testing.
Alex Klein2008aee2019-08-20 16:25:27 -060085 matched = matching.GetMostLikelyMatchedObject(
86 methods, opts.service_method, matched_score_threshold=0.6)
Alex Klein00b1f1e2019-02-08 13:53:42 -070087 error = 'Unrecognized service name.'
88 if matched:
89 error += '\nDid you mean: \n%s' % '\n'.join(matched)
90 parser.error(error)
91
Alex Kleinf4dc4f52018-12-05 13:55:12 -070092 opts.service = parts[0]
93 opts.method = parts[1]
94
Alex Kleine191ed62020-02-27 15:59:55 -070095 # Input and output validation.
96 if not opts.output_binary and not opts.output_json:
97 parser.error('At least one output file must be specified.')
Alex Klein2008aee2019-08-20 16:25:27 -060098
Alex Kleine191ed62020-02-27 15:59:55 -070099 if not os.path.exists(opts.input_binary or opts.input_json):
Alex Klein5bcb4d22019-03-21 13:51:54 -0600100 parser.error('Input file does not exist.')
101
Alex Kleind815ca62020-01-10 12:21:30 -0700102 config_msg = build_api_config_pb2.BuildApiConfig()
103 if opts.config_json:
Alex Kleine191ed62020-02-27 15:59:55 -0700104 handler = message_util.get_message_handler(opts.config_json,
105 message_util.FORMAT_JSON)
106 else:
107 handler = message_util.get_message_handler(opts.config_binary,
108 message_util.FORMAT_BINARY)
109
110 if opts.config_json or opts.config_binary:
111 # We have been given a config, so read it.
Alex Kleind815ca62020-01-10 12:21:30 -0700112 try:
Alex Kleine191ed62020-02-27 15:59:55 -0700113 handler.read_into(config_msg)
114 except message_util.Error as e:
115 parser.error(str(e))
Alex Kleind815ca62020-01-10 12:21:30 -0700116
117 opts.config = api_config_lib.build_config_from_proto(config_msg)
Alex Kleine191ed62020-02-27 15:59:55 -0700118 opts.config_handler = handler
Alex Klein69339cc2019-07-22 14:08:35 -0600119
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700120 opts.Freeze()
121 return opts
122
123
Alex Kleine191ed62020-02-27 15:59:55 -0700124def _get_io_handlers(opts):
125 """Build the input and output handlers."""
126 if opts.input_binary:
127 input_handler = message_util.get_message_handler(opts.input_binary,
128 message_util.FORMAT_BINARY)
129 else:
130 input_handler = message_util.get_message_handler(opts.input_json,
131 message_util.FORMAT_JSON)
132
133 output_handlers = []
134 if opts.output_binary:
135 handler = message_util.get_message_handler(opts.output_binary,
136 message_util.FORMAT_BINARY)
137 output_handlers.append(handler)
138 if opts.output_json:
139 handler = message_util.get_message_handler(opts.output_json,
140 message_util.FORMAT_JSON)
141 output_handlers.append(handler)
142
143 return input_handler, output_handlers
144
145
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700146def main(argv):
Mike Frysingerda928de2021-06-21 12:49:40 -0400147 router = router_lib.GetRouter()
148 opts = _ParseArgs(argv, router)
Alex Klein00b1f1e2019-02-08 13:53:42 -0700149
Mike Frysinger144478e2022-08-31 04:43:47 -0400150 # We currently don't have any APIs that want to access stdin, so rebind.
151 sys.stdin = open(os.devnull, 'r') # pylint: disable=consider-using-with
152 os.dup2(sys.stdin.fileno(), 0)
153
Mike Frysingerda928de2021-06-21 12:49:40 -0400154 if opts.config.log_path:
155 logging.warning('Ignoring log_path config option')
156 if 'BUILD_API_TEE_LOG_FILE' in os.environ:
157 logging.warning('Ignoring $BUILD_API_TEE_LOG_FILE env var')
Alex Klein2008aee2019-08-20 16:25:27 -0600158
Mike Frysingerda928de2021-06-21 12:49:40 -0400159 if opts.config.mock_invalid:
160 # --mock-invalid handling. We print error messages, but no output is ever
161 # set for validation errors, so we can handle it by just giving back the
162 # correct return code here.
163 return controller.RETURN_CODE_INVALID_INPUT
Michael Mortensen3e86c1e2019-11-21 15:51:54 -0700164
Mike Frysingerda928de2021-06-21 12:49:40 -0400165 input_handler, output_handlers = _get_io_handlers(opts)
Alex Kleine191ed62020-02-27 15:59:55 -0700166
Mike Frysingerda928de2021-06-21 12:49:40 -0400167 try:
168 return router.Route(opts.service, opts.method, opts.config, input_handler,
169 output_handlers, opts.config_handler)
170 except router_lib.Error as e:
171 # Handle router_lib.Error derivatives nicely, but let anything else bubble
172 # up.
173 cros_build_lib.Die(e)