blob: fadf67cf2729f2125b0beafe4afe4258e7b77d6c [file] [log] [blame]
Alex Kleinf4dc4f52018-12-05 13:55:12 -07001# -*- coding: utf-8 -*-
2# Copyright 2018 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Alex Kleind815ca62020-01-10 12:21:30 -07006"""The Build API entry point."""
Alex Kleinf4dc4f52018-12-05 13:55:12 -07007
8from __future__ import print_function
9
Alex Klein5bcb4d22019-03-21 13:51:54 -060010import os
Mike Frysinger898265b2020-02-10 23:49:12 -050011import sys
Alex Kleind815ca62020-01-10 12:21:30 -070012
13from google.protobuf import json_format
Alex Kleinf4dc4f52018-12-05 13:55:12 -070014
Alex Klein69339cc2019-07-22 14:08:35 -060015from chromite.api import api_config as api_config_lib
Alex Klein2008aee2019-08-20 16:25:27 -060016from chromite.api import controller
Alex Klein146d4772019-06-20 13:48:25 -060017from chromite.api import router as router_lib
Alex Kleind815ca62020-01-10 12:21:30 -070018from chromite.api.gen.chromite.api import build_api_config_pb2
Alex Kleinf4dc4f52018-12-05 13:55:12 -070019from chromite.lib import commandline
Alex Klein2bfacb22019-02-04 11:42:17 -070020from chromite.lib import cros_build_lib
Michael Mortensen3e86c1e2019-11-21 15:51:54 -070021from chromite.lib import cros_logging as logging
Alex Kleind815ca62020-01-10 12:21:30 -070022from chromite.lib import osutils
Michael Mortensen3e86c1e2019-11-21 15:51:54 -070023from chromite.lib import tee
Alex Klein00b1f1e2019-02-08 13:53:42 -070024from chromite.utils import matching
Alex Kleinf4dc4f52018-12-05 13:55:12 -070025
26
Mike Frysinger898265b2020-02-10 23:49:12 -050027assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
28
29
Alex Kleinf4dc4f52018-12-05 13:55:12 -070030def GetParser():
Alex Klein00b1f1e2019-02-08 13:53:42 -070031 """Build the argument parser."""
Alex Kleinf4dc4f52018-12-05 13:55:12 -070032 parser = commandline.ArgumentParser(description=__doc__)
33
Alex Kleind815ca62020-01-10 12:21:30 -070034 parser.add_argument(
Alex Klein2008aee2019-08-20 16:25:27 -060035 'service_method',
Alex Klein2008aee2019-08-20 16:25:27 -060036 help='The "chromite.api.Service/Method" that is being called.')
Alex Kleind815ca62020-01-10 12:21:30 -070037 parser.add_argument(
Alex Klein2008aee2019-08-20 16:25:27 -060038 '--input-json',
39 type='path',
Alex Kleinf4dc4f52018-12-05 13:55:12 -070040 help='Path to the JSON serialized input argument protobuf message.')
Alex Kleind815ca62020-01-10 12:21:30 -070041 parser.add_argument(
Alex Klein2008aee2019-08-20 16:25:27 -060042 '--output-json',
43 type='path',
Alex Kleinf4dc4f52018-12-05 13:55:12 -070044 help='The path to which the result protobuf message should be written.')
Alex Kleind815ca62020-01-10 12:21:30 -070045 parser.add_argument(
46 '--config-json',
47 type='path',
48 help='The path to the Build API call configs.')
49 # TODO(crbug.com/1040978): Remove after usages removed.
50 parser.add_argument(
Michael Mortensen3e86c1e2019-11-21 15:51:54 -070051 '--tee-log',
52 type='path',
53 help='The path to which stdout and stderr should be teed to.')
Alex Klein2008aee2019-08-20 16:25:27 -060054
Alex Kleinf4dc4f52018-12-05 13:55:12 -070055 return parser
56
57
Alex Klein00b1f1e2019-02-08 13:53:42 -070058def _ParseArgs(argv, router):
Alex Kleinf4dc4f52018-12-05 13:55:12 -070059 """Parse and validate arguments."""
60 parser = GetParser()
Alex Klein7cc434f2019-12-17 14:58:57 -070061 opts, unknown = parser.parse_known_args(
62 argv, namespace=commandline.ArgumentNamespace())
63 parser.DoPostParseSetup(opts, unknown)
64
65 if unknown:
66 logging.warning('Unknown args ignored: %s', ' '.join(unknown))
Alex Kleinf4dc4f52018-12-05 13:55:12 -070067
Alex Klein00b1f1e2019-02-08 13:53:42 -070068 methods = router.ListMethods()
George Engelbrechtd3de8df2019-09-04 18:15:05 -060069
Alex Klein2008aee2019-08-20 16:25:27 -060070 # Positional service_method argument validation.
George Engelbrechtd3de8df2019-09-04 18:15:05 -060071 parts = opts.service_method.split('/')
72 if len(parts) != 2:
Alex Kleind815ca62020-01-10 12:21:30 -070073 parser.error('Invalid service/method specification format. It should be '
74 'something like chromite.api.SdkService/Create.')
George Engelbrechtd3de8df2019-09-04 18:15:05 -060075
Alex Klein00b1f1e2019-02-08 13:53:42 -070076 if opts.service_method not in methods:
Alex Klein00aa8072019-04-15 16:36:00 -060077 # Unknown method, try to match against known methods and make a suggestion.
78 # This is just for developer sanity, e.g. misspellings when testing.
Alex Klein2008aee2019-08-20 16:25:27 -060079 matched = matching.GetMostLikelyMatchedObject(
80 methods, opts.service_method, matched_score_threshold=0.6)
Alex Klein00b1f1e2019-02-08 13:53:42 -070081 error = 'Unrecognized service name.'
82 if matched:
83 error += '\nDid you mean: \n%s' % '\n'.join(matched)
84 parser.error(error)
85
Alex Kleinf4dc4f52018-12-05 13:55:12 -070086 opts.service = parts[0]
87 opts.method = parts[1]
88
Alex Klein2008aee2019-08-20 16:25:27 -060089 # --input-json and --output-json validation.
90 if not opts.input_json or not opts.output_json:
91 parser.error('--input-json and --output-json are both required.')
92
Alex Klein5bcb4d22019-03-21 13:51:54 -060093 if not os.path.exists(opts.input_json):
94 parser.error('Input file does not exist.')
95
Alex Kleind815ca62020-01-10 12:21:30 -070096 config_msg = build_api_config_pb2.BuildApiConfig()
97 if opts.config_json:
98 try:
99 json_format.Parse(osutils.ReadFile(opts.config_json), config_msg,
100 ignore_unknown_fields=True)
101 except IOError as e:
102 parser.error(e)
103
104 opts.config = api_config_lib.build_config_from_proto(config_msg)
Alex Klein69339cc2019-07-22 14:08:35 -0600105
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700106 opts.Freeze()
107 return opts
108
109
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700110def main(argv):
Michael Mortensen3e86c1e2019-11-21 15:51:54 -0700111 with cros_build_lib.ContextManagerStack() as stack:
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700112
Michael Mortensen3e86c1e2019-11-21 15:51:54 -0700113 router = router_lib.GetRouter()
114 opts = _ParseArgs(argv, router)
Alex Klein00b1f1e2019-02-08 13:53:42 -0700115
Michael Mortensen3e86c1e2019-11-21 15:51:54 -0700116 if opts.tee_log:
117 stack.Add(tee.Tee, opts.tee_log)
118 logging.info('Teeing stdout and stderr to %s', opts.tee_log)
Alex Kleind815ca62020-01-10 12:21:30 -0700119 if opts.config.log_path:
120 stack.Add(tee.Tee, opts.config.log_path)
121 logging.info('Teeing stdout and stderr to %s', opts.config.log_path)
Michael Mortensena0515d92020-01-02 11:39:34 -0700122 tee_log_env_value = os.environ.get('BUILD_API_TEE_LOG_FILE')
123 if tee_log_env_value:
124 stack.Add(tee.Tee, tee_log_env_value)
125 logging.info('Teeing stdout and stderr to env path %s', tee_log_env_value)
Alex Klein2008aee2019-08-20 16:25:27 -0600126
Alex Kleind815ca62020-01-10 12:21:30 -0700127 if opts.config.mock_invalid:
Michael Mortensen3e86c1e2019-11-21 15:51:54 -0700128 # --mock-invalid handling. We print error messages, but no output is ever
129 # set for validation errors, so we can handle it by just giving back the
130 # correct return code here.
131 return controller.RETURN_CODE_INVALID_INPUT
132
133 try:
134 return router.Route(opts.service, opts.method, opts.input_json,
135 opts.output_json, opts.config)
136 except router_lib.Error as e:
137 # Handle router_lib.Error derivatives nicely, but let anything else bubble
138 # up.
139 cros_build_lib.Die(e)