blob: 5e540e9e3692fe5bd29fa4136d3f403884175dd6 [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
Cindy Lin0abfec02022-09-01 18:25:30 +000018from chromite.lib import namespaces
Alex Klein00b1f1e2019-02-08 13:53:42 -070019from chromite.utils import matching
Alex Kleinf4dc4f52018-12-05 13:55:12 -070020
Mike Frysinger898265b2020-02-10 23:49:12 -050021
Alex Kleinf4dc4f52018-12-05 13:55:12 -070022def GetParser():
Alex Klein1699fab2022-09-08 08:46:06 -060023 """Build the argument parser."""
24 parser = commandline.ArgumentParser(description=__doc__)
Alex Kleinf4dc4f52018-12-05 13:55:12 -070025
Alex Klein1699fab2022-09-08 08:46:06 -060026 parser.add_argument(
27 "service_method",
28 help='The "chromite.api.Service/Method" that is being called.',
29 )
30 # Input arguments.
31 input_args = parser.add_mutually_exclusive_group(required=True)
32 input_args.add_argument(
33 "--input-binary",
34 type="path",
35 help="Path to the protobuf binary serialization of the input message.",
36 )
37 input_args.add_argument(
38 "--input-json",
39 type="path",
40 help="Path to the JSON serialized input argument protobuf message.",
41 )
42 # Output options.
43 parser.add_argument(
44 "--output-binary",
45 type="path",
46 help="The path to which the protobuf binary serialization of the "
47 "response message should be written.",
48 )
49 parser.add_argument(
50 "--output-json",
51 type="path",
52 help="The path to which the JSON serialization of the response message "
53 "should be written.",
54 )
55 # Config options.
56 config_args = parser.add_mutually_exclusive_group()
57 config_args.add_argument(
58 "--config-binary",
59 type="path",
60 help="The path to the protobuf binary serialization of the Build API "
61 "call configs.",
62 )
63 config_args.add_argument(
64 "--config-json",
65 type="path",
66 help="The path to the JSON encoded Build API call configs.",
67 )
Alex Klein2008aee2019-08-20 16:25:27 -060068
Alex Klein1699fab2022-09-08 08:46:06 -060069 return parser
Alex Kleinf4dc4f52018-12-05 13:55:12 -070070
71
Alex Klein00b1f1e2019-02-08 13:53:42 -070072def _ParseArgs(argv, router):
Alex Klein1699fab2022-09-08 08:46:06 -060073 """Parse and validate arguments."""
74 parser = GetParser()
75 opts, unknown = parser.parse_known_args(
76 argv, namespace=commandline.ArgumentNamespace()
77 )
78 parser.DoPostParseSetup(opts, unknown)
Alex Klein7cc434f2019-12-17 14:58:57 -070079
Alex Klein1699fab2022-09-08 08:46:06 -060080 if unknown:
81 logging.warning("Unknown args ignored: %s", " ".join(unknown))
Alex Kleinf4dc4f52018-12-05 13:55:12 -070082
Alex Klein1699fab2022-09-08 08:46:06 -060083 methods = router.ListMethods()
George Engelbrechtd3de8df2019-09-04 18:15:05 -060084
Alex Klein1699fab2022-09-08 08:46:06 -060085 # Positional service_method argument validation.
86 parts = opts.service_method.split("/")
87 if len(parts) != 2:
88 parser.error(
89 "Invalid service/method specification format. It should be "
90 "something like chromite.api.SdkService/Create."
91 )
George Engelbrechtd3de8df2019-09-04 18:15:05 -060092
Alex Klein1699fab2022-09-08 08:46:06 -060093 if opts.service_method not in methods:
Alex Klein9e7b29e2023-04-11 16:10:31 -060094 # Unknown method, try to match against known methods and make a
95 # suggestion. This is just for developer assistance, e.g. misspellings
96 # when testing.
Alex Klein1699fab2022-09-08 08:46:06 -060097 matched = matching.GetMostLikelyMatchedObject(
98 methods, opts.service_method, matched_score_threshold=0.6
99 )
100 error = "Unrecognized service name."
101 if matched:
102 error += "\nDid you mean: \n%s" % "\n".join(matched)
103 parser.error(error)
Alex Klein00b1f1e2019-02-08 13:53:42 -0700104
Alex Klein1699fab2022-09-08 08:46:06 -0600105 opts.service = parts[0]
106 opts.method = parts[1]
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700107
Alex Klein1699fab2022-09-08 08:46:06 -0600108 # Input and output validation.
109 if not opts.output_binary and not opts.output_json:
110 parser.error("At least one output file must be specified.")
Alex Klein2008aee2019-08-20 16:25:27 -0600111
Alex Klein1699fab2022-09-08 08:46:06 -0600112 if not os.path.exists(opts.input_binary or opts.input_json):
113 parser.error("Input file does not exist.")
Alex Klein5bcb4d22019-03-21 13:51:54 -0600114
Alex Klein1699fab2022-09-08 08:46:06 -0600115 config_msg = build_api_config_pb2.BuildApiConfig()
116 if opts.config_json:
117 handler = message_util.get_message_handler(
118 opts.config_json, message_util.FORMAT_JSON
119 )
120 else:
121 handler = message_util.get_message_handler(
122 opts.config_binary, message_util.FORMAT_BINARY
123 )
Alex Kleine191ed62020-02-27 15:59:55 -0700124
Alex Klein1699fab2022-09-08 08:46:06 -0600125 if opts.config_json or opts.config_binary:
126 # We have been given a config, so read it.
127 try:
128 handler.read_into(config_msg)
129 except message_util.Error as e:
130 parser.error(str(e))
Alex Kleind815ca62020-01-10 12:21:30 -0700131
Alex Klein1699fab2022-09-08 08:46:06 -0600132 opts.config = api_config_lib.build_config_from_proto(config_msg)
133 opts.config_handler = handler
Alex Klein69339cc2019-07-22 14:08:35 -0600134
Alex Klein1699fab2022-09-08 08:46:06 -0600135 opts.Freeze()
136 return opts
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700137
138
Alex Kleine191ed62020-02-27 15:59:55 -0700139def _get_io_handlers(opts):
Alex Klein1699fab2022-09-08 08:46:06 -0600140 """Build the input and output handlers."""
141 if opts.input_binary:
142 input_handler = message_util.get_message_handler(
143 opts.input_binary, message_util.FORMAT_BINARY
144 )
145 else:
146 input_handler = message_util.get_message_handler(
147 opts.input_json, message_util.FORMAT_JSON
148 )
Alex Kleine191ed62020-02-27 15:59:55 -0700149
Alex Klein1699fab2022-09-08 08:46:06 -0600150 output_handlers = []
151 if opts.output_binary:
152 handler = message_util.get_message_handler(
153 opts.output_binary, message_util.FORMAT_BINARY
154 )
155 output_handlers.append(handler)
156 if opts.output_json:
157 handler = message_util.get_message_handler(
158 opts.output_json, message_util.FORMAT_JSON
159 )
160 output_handlers.append(handler)
Alex Kleine191ed62020-02-27 15:59:55 -0700161
Alex Klein1699fab2022-09-08 08:46:06 -0600162 return input_handler, output_handlers
Alex Kleine191ed62020-02-27 15:59:55 -0700163
164
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700165def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600166 router = router_lib.GetRouter()
167 opts = _ParseArgs(argv, router)
Alex Klein00b1f1e2019-02-08 13:53:42 -0700168
Alex Klein9e7b29e2023-04-11 16:10:31 -0600169 # For build_image, make sure we run with network disabled to prevent
170 # leakage.
Cindy Lin0abfec02022-09-01 18:25:30 +0000171 if (
172 cros_build_lib.IsInsideChroot()
173 and opts.service_method == "chromite.api.ImageService/Create"
174 ):
175 namespaces.ReExecuteWithNamespace(sys.argv, preserve_env=True)
176
Alex Klein1699fab2022-09-08 08:46:06 -0600177 # We currently don't have any APIs that want to access stdin, so rebind.
Mike Frysinger31fdddd2023-02-24 15:50:55 -0500178 # pylint: disable=consider-using-with
179 sys.stdin = open(os.devnull, "r", encoding="utf-8")
180 # pylint: enable=consider-using-with
Alex Klein1699fab2022-09-08 08:46:06 -0600181 os.dup2(sys.stdin.fileno(), 0)
Mike Frysinger144478e2022-08-31 04:43:47 -0400182
Alex Klein1699fab2022-09-08 08:46:06 -0600183 if opts.config.log_path:
184 logging.warning("Ignoring log_path config option")
185 if "BUILD_API_TEE_LOG_FILE" in os.environ:
186 logging.warning("Ignoring $BUILD_API_TEE_LOG_FILE env var")
Alex Klein2008aee2019-08-20 16:25:27 -0600187
Alex Klein1699fab2022-09-08 08:46:06 -0600188 if opts.config.mock_invalid:
Alex Klein9e7b29e2023-04-11 16:10:31 -0600189 # --mock-invalid handling. We print error messages, but no output is
190 # ever set for validation errors, so we can handle it by just giving
191 # back the correct return code here.
Alex Klein1699fab2022-09-08 08:46:06 -0600192 return controller.RETURN_CODE_INVALID_INPUT
Michael Mortensen3e86c1e2019-11-21 15:51:54 -0700193
Alex Klein1699fab2022-09-08 08:46:06 -0600194 input_handler, output_handlers = _get_io_handlers(opts)
Alex Kleine191ed62020-02-27 15:59:55 -0700195
Alex Klein1699fab2022-09-08 08:46:06 -0600196 try:
197 return router.Route(
198 opts.service,
199 opts.method,
200 opts.config,
201 input_handler,
202 output_handlers,
203 opts.config_handler,
204 )
205 except router_lib.Error as e:
Alex Klein9e7b29e2023-04-11 16:10:31 -0600206 # Handle router_lib.Error derivatives nicely, but let anything else
207 # bubble up.
Alex Klein1699fab2022-09-08 08:46:06 -0600208 cros_build_lib.Die(e)