1# Copyright 2020 gRPC authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Configure Traffic Director for different GRPC Proxyless. 15 16This is intended as a debugging / local development helper and not executed 17as a part of interop test suites. 18 19Typical usage examples: 20 21 # Regular proxyless setup 22 python -m bin.run_td_setup --flagfile=config/local-dev.cfg 23 24 # Additional commands: cleanup, backend management, etc. 25 python -m bin.run_td_setup --flagfile=config/local-dev.cfg --cmd=cleanup 26 27 # PSM security setup options: mtls, tls, etc. 28 python -m bin.run_td_setup --flagfile=config/local-dev.cfg --security=mtls 29 30 # More information and usage options 31 python -m bin.run_td_setup --helpfull 32""" 33import logging 34 35from absl import app 36from absl import flags 37 38from framework import xds_flags 39from framework import xds_k8s_flags 40from framework.helpers import rand 41from framework.infrastructure import gcp 42from framework.infrastructure import k8s 43from framework.infrastructure import traffic_director 44from framework.test_app.runners.k8s import k8s_xds_server_runner 45 46logger = logging.getLogger(__name__) 47# Flags 48_CMD = flags.DEFINE_enum('cmd', 49 default='create', 50 enum_values=[ 51 'cycle', 'create', 'cleanup', 'backends-add', 52 'backends-cleanup', 'unused-xds-port' 53 ], 54 help='Command') 55_SECURITY = flags.DEFINE_enum('security', 56 default=None, 57 enum_values=[ 58 'mtls', 'tls', 'plaintext', 'mtls_error', 59 'server_authz_error' 60 ], 61 help='Configure TD with security') 62flags.adopt_module_key_flags(xds_flags) 63flags.adopt_module_key_flags(xds_k8s_flags) 64# Running outside of a test suite, so require explicit resource_suffix. 65flags.mark_flag_as_required(xds_flags.RESOURCE_SUFFIX.name) 66 67 68@flags.multi_flags_validator((xds_flags.SERVER_XDS_PORT.name, _CMD.name), 69 message="Run outside of a test suite, must provide" 70 " the exact port value (must be greater than 0).") 71def _check_server_xds_port_flag(flags_dict): 72 if flags_dict[_CMD.name] not in ('create', 'cycle'): 73 return True 74 return flags_dict[xds_flags.SERVER_XDS_PORT.name] > 0 75 76 77# Type aliases 78_KubernetesServerRunner = k8s_xds_server_runner.KubernetesServerRunner 79 80 81def main(argv): # pylint: disable=too-many-locals,too-many-branches,too-many-statements 82 if len(argv) > 1: 83 raise app.UsageError('Too many command-line arguments.') 84 85 # Must be called before KubernetesApiManager or GcpApiManager init. 86 xds_flags.set_socket_default_timeout_from_flag() 87 88 command = _CMD.value 89 security_mode = _SECURITY.value 90 91 project: str = xds_flags.PROJECT.value 92 network: str = xds_flags.NETWORK.value 93 94 # Resource names. 95 resource_prefix: str = xds_flags.RESOURCE_PREFIX.value 96 resource_suffix: str = xds_flags.RESOURCE_SUFFIX.value 97 98 # Test server 99 server_name = xds_flags.SERVER_NAME.value 100 server_port = xds_flags.SERVER_PORT.value 101 server_maintenance_port = xds_flags.SERVER_MAINTENANCE_PORT.value 102 server_xds_host = xds_flags.SERVER_XDS_HOST.value 103 server_xds_port = xds_flags.SERVER_XDS_PORT.value 104 server_namespace = _KubernetesServerRunner.make_namespace_name( 105 resource_prefix, resource_suffix) 106 107 gcp_api_manager = gcp.api.GcpApiManager() 108 109 if security_mode is None: 110 td = traffic_director.TrafficDirectorManager( 111 gcp_api_manager, 112 project=project, 113 network=network, 114 resource_prefix=resource_prefix, 115 resource_suffix=resource_suffix) 116 else: 117 td = traffic_director.TrafficDirectorSecureManager( 118 gcp_api_manager, 119 project=project, 120 network=network, 121 resource_prefix=resource_prefix, 122 resource_suffix=resource_suffix) 123 if server_maintenance_port is None: 124 server_maintenance_port = \ 125 _KubernetesServerRunner.DEFAULT_SECURE_MODE_MAINTENANCE_PORT 126 127 try: 128 if command in ('create', 'cycle'): 129 logger.info('Create mode') 130 if security_mode is None: 131 logger.info('No security') 132 td.setup_for_grpc(server_xds_host, 133 server_xds_port, 134 health_check_port=server_maintenance_port) 135 136 elif security_mode == 'mtls': 137 logger.info('Setting up mtls') 138 td.setup_for_grpc(server_xds_host, 139 server_xds_port, 140 health_check_port=server_maintenance_port) 141 td.setup_server_security(server_namespace=server_namespace, 142 server_name=server_name, 143 server_port=server_port, 144 tls=True, 145 mtls=True) 146 td.setup_client_security(server_namespace=server_namespace, 147 server_name=server_name, 148 tls=True, 149 mtls=True) 150 151 elif security_mode == 'tls': 152 logger.info('Setting up tls') 153 td.setup_for_grpc(server_xds_host, 154 server_xds_port, 155 health_check_port=server_maintenance_port) 156 td.setup_server_security(server_namespace=server_namespace, 157 server_name=server_name, 158 server_port=server_port, 159 tls=True, 160 mtls=False) 161 td.setup_client_security(server_namespace=server_namespace, 162 server_name=server_name, 163 tls=True, 164 mtls=False) 165 166 elif security_mode == 'plaintext': 167 logger.info('Setting up plaintext') 168 td.setup_for_grpc(server_xds_host, 169 server_xds_port, 170 health_check_port=server_maintenance_port) 171 td.setup_server_security(server_namespace=server_namespace, 172 server_name=server_name, 173 server_port=server_port, 174 tls=False, 175 mtls=False) 176 td.setup_client_security(server_namespace=server_namespace, 177 server_name=server_name, 178 tls=False, 179 mtls=False) 180 181 elif security_mode == 'mtls_error': 182 # Error case: server expects client mTLS cert, 183 # but client configured only for TLS 184 logger.info('Setting up mtls_error') 185 td.setup_for_grpc(server_xds_host, 186 server_xds_port, 187 health_check_port=server_maintenance_port) 188 td.setup_server_security(server_namespace=server_namespace, 189 server_name=server_name, 190 server_port=server_port, 191 tls=True, 192 mtls=True) 193 td.setup_client_security(server_namespace=server_namespace, 194 server_name=server_name, 195 tls=True, 196 mtls=False) 197 198 elif security_mode == 'server_authz_error': 199 # Error case: client does not authorize server 200 # because of mismatched SAN name. 201 logger.info('Setting up mtls_error') 202 td.setup_for_grpc(server_xds_host, 203 server_xds_port, 204 health_check_port=server_maintenance_port) 205 # Regular TLS setup, but with client policy configured using 206 # intentionality incorrect server_namespace. 207 td.setup_server_security(server_namespace=server_namespace, 208 server_name=server_name, 209 server_port=server_port, 210 tls=True, 211 mtls=False) 212 td.setup_client_security( 213 server_namespace=f'incorrect-namespace-{rand.rand_string()}', 214 server_name=server_name, 215 tls=True, 216 mtls=False) 217 218 logger.info('Works!') 219 except Exception: # noqa pylint: disable=broad-except 220 logger.exception('Got error during creation') 221 222 if command in ('cleanup', 'cycle'): 223 logger.info('Cleaning up') 224 td.cleanup(force=True) 225 226 if command == 'backends-add': 227 logger.info('Adding backends') 228 k8s_api_manager = k8s.KubernetesApiManager( 229 xds_k8s_flags.KUBE_CONTEXT.value) 230 k8s_namespace = k8s.KubernetesNamespace(k8s_api_manager, 231 server_namespace) 232 233 neg_name, neg_zones = k8s_namespace.get_service_neg( 234 server_name, server_port) 235 236 td.load_backend_service() 237 td.backend_service_add_neg_backends(neg_name, neg_zones) 238 td.wait_for_backends_healthy_status() 239 elif command == 'backends-cleanup': 240 td.load_backend_service() 241 td.backend_service_remove_all_backends() 242 elif command == 'unused-xds-port': 243 try: 244 unused_xds_port = td.find_unused_forwarding_rule_port() 245 logger.info('Found unused forwarding rule port: %s', 246 unused_xds_port) 247 except Exception: # noqa pylint: disable=broad-except 248 logger.exception("Couldn't find unused forwarding rule port") 249 250 251if __name__ == '__main__': 252 app.run(main) 253