blob: e292c58d685440f6ddd3cde8b667dffbfc44b869 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2018 The ChromiumOS Authors
Alex Kleinf4dc4f52018-12-05 13:55:12 -07002# 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 Klein1699fab2022-09-08 08:46:06 -060022 """Build the argument parser."""
23 parser = commandline.ArgumentParser(description=__doc__)
Alex Kleinf4dc4f52018-12-05 13:55:12 -070024
Alex Klein1699fab2022-09-08 08:46:06 -060025 parser.add_argument(
26 "service_method",
27 help='The "chromite.api.Service/Method" that is being called.',
28 )
29 # Input arguments.
30 input_args = parser.add_mutually_exclusive_group(required=True)
31 input_args.add_argument(
32 "--input-binary",
33 type="path",
34 help="Path to the protobuf binary serialization of the input message.",
35 )
36 input_args.add_argument(
37 "--input-json",
38 type="path",
39 help="Path to the JSON serialized input argument protobuf message.",
40 )
41 # Output options.
42 parser.add_argument(
43 "--output-binary",
44 type="path",
45 help="The path to which the protobuf binary serialization of the "
46 "response message should be written.",
47 )
48 parser.add_argument(
49 "--output-json",
50 type="path",
51 help="The path to which the JSON serialization of the response message "
52 "should be written.",
53 )
54 # Config options.
55 config_args = parser.add_mutually_exclusive_group()
56 config_args.add_argument(
57 "--config-binary",
58 type="path",
59 help="The path to the protobuf binary serialization of the Build API "
60 "call configs.",
61 )
62 config_args.add_argument(
63 "--config-json",
64 type="path",
65 help="The path to the JSON encoded Build API call configs.",
66 )
Alex Klein2008aee2019-08-20 16:25:27 -060067
Alex Klein1699fab2022-09-08 08:46:06 -060068 return parser
Alex Kleinf4dc4f52018-12-05 13:55:12 -070069
70
Alex Klein00b1f1e2019-02-08 13:53:42 -070071def _ParseArgs(argv, router):
Alex Klein1699fab2022-09-08 08:46:06 -060072 """Parse and validate arguments."""
73 parser = GetParser()
74 opts, unknown = parser.parse_known_args(
75 argv, namespace=commandline.ArgumentNamespace()
76 )
77 parser.DoPostParseSetup(opts, unknown)
Alex Klein7cc434f2019-12-17 14:58:57 -070078
Alex Klein1699fab2022-09-08 08:46:06 -060079 if unknown:
80 logging.warning("Unknown args ignored: %s", " ".join(unknown))
Alex Kleinf4dc4f52018-12-05 13:55:12 -070081
Alex Klein1699fab2022-09-08 08:46:06 -060082 methods = router.ListMethods()
George Engelbrechtd3de8df2019-09-04 18:15:05 -060083
Alex Klein1699fab2022-09-08 08:46:06 -060084 # Positional service_method argument validation.
85 parts = opts.service_method.split("/")
86 if len(parts) != 2:
87 parser.error(
88 "Invalid service/method specification format. It should be "
89 "something like chromite.api.SdkService/Create."
90 )
George Engelbrechtd3de8df2019-09-04 18:15:05 -060091
Alex Klein1699fab2022-09-08 08:46:06 -060092 if opts.service_method not in methods:
93 # Unknown method, try to match against known methods and make a suggestion.
94 # This is just for developer assistance, e.g. misspellings when testing.
95 matched = matching.GetMostLikelyMatchedObject(
96 methods, opts.service_method, matched_score_threshold=0.6
97 )
98 error = "Unrecognized service name."
99 if matched:
100 error += "\nDid you mean: \n%s" % "\n".join(matched)
101 parser.error(error)
Alex Klein00b1f1e2019-02-08 13:53:42 -0700102
Alex Klein1699fab2022-09-08 08:46:06 -0600103 opts.service = parts[0]
104 opts.method = parts[1]
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700105
Alex Klein1699fab2022-09-08 08:46:06 -0600106 # Input and output validation.
107 if not opts.output_binary and not opts.output_json:
108 parser.error("At least one output file must be specified.")
Alex Klein2008aee2019-08-20 16:25:27 -0600109
Alex Klein1699fab2022-09-08 08:46:06 -0600110 if not os.path.exists(opts.input_binary or opts.input_json):
111 parser.error("Input file does not exist.")
Alex Klein5bcb4d22019-03-21 13:51:54 -0600112
Alex Klein1699fab2022-09-08 08:46:06 -0600113 config_msg = build_api_config_pb2.BuildApiConfig()
114 if opts.config_json:
115 handler = message_util.get_message_handler(
116 opts.config_json, message_util.FORMAT_JSON
117 )
118 else:
119 handler = message_util.get_message_handler(
120 opts.config_binary, message_util.FORMAT_BINARY
121 )
Alex Kleine191ed62020-02-27 15:59:55 -0700122
Alex Klein1699fab2022-09-08 08:46:06 -0600123 if opts.config_json or opts.config_binary:
124 # We have been given a config, so read it.
125 try:
126 handler.read_into(config_msg)
127 except message_util.Error as e:
128 parser.error(str(e))
Alex Kleind815ca62020-01-10 12:21:30 -0700129
Alex Klein1699fab2022-09-08 08:46:06 -0600130 opts.config = api_config_lib.build_config_from_proto(config_msg)
131 opts.config_handler = handler
Alex Klein69339cc2019-07-22 14:08:35 -0600132
Alex Klein1699fab2022-09-08 08:46:06 -0600133 opts.Freeze()
134 return opts
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700135
136
Alex Kleine191ed62020-02-27 15:59:55 -0700137def _get_io_handlers(opts):
Alex Klein1699fab2022-09-08 08:46:06 -0600138 """Build the input and output handlers."""
139 if opts.input_binary:
140 input_handler = message_util.get_message_handler(
141 opts.input_binary, message_util.FORMAT_BINARY
142 )
143 else:
144 input_handler = message_util.get_message_handler(
145 opts.input_json, message_util.FORMAT_JSON
146 )
Alex Kleine191ed62020-02-27 15:59:55 -0700147
Alex Klein1699fab2022-09-08 08:46:06 -0600148 output_handlers = []
149 if opts.output_binary:
150 handler = message_util.get_message_handler(
151 opts.output_binary, message_util.FORMAT_BINARY
152 )
153 output_handlers.append(handler)
154 if opts.output_json:
155 handler = message_util.get_message_handler(
156 opts.output_json, message_util.FORMAT_JSON
157 )
158 output_handlers.append(handler)
Alex Kleine191ed62020-02-27 15:59:55 -0700159
Alex Klein1699fab2022-09-08 08:46:06 -0600160 return input_handler, output_handlers
Alex Kleine191ed62020-02-27 15:59:55 -0700161
162
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700163def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600164 router = router_lib.GetRouter()
165 opts = _ParseArgs(argv, router)
Alex Klein00b1f1e2019-02-08 13:53:42 -0700166
Alex Klein1699fab2022-09-08 08:46:06 -0600167 # We currently don't have any APIs that want to access stdin, so rebind.
168 sys.stdin = open(os.devnull, "r") # pylint: disable=consider-using-with
169 os.dup2(sys.stdin.fileno(), 0)
Mike Frysinger144478e2022-08-31 04:43:47 -0400170
Alex Klein1699fab2022-09-08 08:46:06 -0600171 if opts.config.log_path:
172 logging.warning("Ignoring log_path config option")
173 if "BUILD_API_TEE_LOG_FILE" in os.environ:
174 logging.warning("Ignoring $BUILD_API_TEE_LOG_FILE env var")
Alex Klein2008aee2019-08-20 16:25:27 -0600175
Alex Klein1699fab2022-09-08 08:46:06 -0600176 if opts.config.mock_invalid:
177 # --mock-invalid handling. We print error messages, but no output is ever
178 # set for validation errors, so we can handle it by just giving back the
179 # correct return code here.
180 return controller.RETURN_CODE_INVALID_INPUT
Michael Mortensen3e86c1e2019-11-21 15:51:54 -0700181
Alex Klein1699fab2022-09-08 08:46:06 -0600182 input_handler, output_handlers = _get_io_handlers(opts)
Alex Kleine191ed62020-02-27 15:59:55 -0700183
Alex Klein1699fab2022-09-08 08:46:06 -0600184 try:
185 return router.Route(
186 opts.service,
187 opts.method,
188 opts.config,
189 input_handler,
190 output_handlers,
191 opts.config_handler,
192 )
193 except router_lib.Error as e:
194 # Handle router_lib.Error derivatives nicely, but let anything else bubble
195 # up.
196 cros_build_lib.Die(e)