def RunProxyConnectCommand(args, supports_database=False): """Connects to a Cloud SQL instance through the Cloud SQL Proxy. Args: args: argparse.Namespace, The arguments that this command was invoked with. supports_database: Whether or not the `--database` flag needs to be accounted for. Returns: If no exception is raised this method does not return. A new process is started and the original one is killed. Raises: HttpException: An http error response was received while executing api request. CloudSqlProxyError: Cloud SQL Proxy could not be found. SqlClientNotFoundError: A local SQL client could not be found. ConnectionError: An error occurred while trying to connect to the instance. """ client = api_util.SqlClient(api_util.API_VERSION_DEFAULT) sql_client = client.sql_client sql_messages = client.sql_messages instance_ref = instances_command_util.GetInstanceRef(args, client) instance_info = sql_client.instances.Get( sql_messages.SqlInstancesGetRequest(project=instance_ref.project, instance=instance_ref.instance)) if not instances_api_util.IsInstanceV2(sql_messages, instance_info): # The Cloud SQL Proxy does not support V1 instances. return RunConnectCommand(args, supports_database) # If the instance is V2, keep going with the proxy. exe = files.FindExecutableOnPath('cloud_sql_proxy') if not exe: raise exceptions.CloudSqlProxyError( 'Cloud SQL Proxy could not be found in PATH. See ' 'https://cloud.google.com/sql/docs/mysql/sql-proxy#install for ' 'information on installing.') # Check for the executable based on the db version. db_type = instance_info.databaseVersion.name.split('_')[0] exe_name = constants.DB_EXE.get(db_type, 'mysql') exe = files.FindExecutableOnPath(exe_name) if not exe: raise exceptions.SqlClientNotFoundError( '{0} client not found. Please install a {1} client and make sure ' 'it is in PATH to be able to connect to the database instance.'. format(exe_name.title(), exe_name)) # Start the Cloud SQL Proxy and wait for it to be ready to accept connections. port = six.text_type(args.port) proxy_process = instances_api_util.StartCloudSqlProxy(instance_info, port) atexit.register(proxy_process.kill) # Determine what SQL user to connect with. sql_user = constants.DEFAULT_SQL_USER[exe_name] if args.user: sql_user = args.user # We have everything we need, time to party! flags = constants.EXE_FLAGS[exe_name] sql_args = [exe_name] if exe_name == 'mssql-cli': # mssql-cli merges hostname and port into a single argument hostname = 'tcp:127.0.0.1,{0}'.format(port) sql_args.extend([flags['hostname'], hostname]) else: sql_args.extend([flags['hostname'], '127.0.0.1', flags['port'], port]) sql_args.extend([flags['user'], sql_user]) if 'password' in flags: sql_args.append(flags['password']) if supports_database: sql_args.extend(instances_command_util.GetDatabaseArgs(args, flags)) instances_command_util.ConnectToInstance(sql_args, sql_user) proxy_process.kill()
def RunConnectCommand(args, supports_database=False): """Connects to a Cloud SQL instance directly. Args: args: argparse.Namespace, The arguments that this command was invoked with. supports_database: Whether or not the `--database` flag needs to be accounted for. Returns: If no exception is raised this method does not return. A new process is started and the original one is killed. Raises: HttpException: An http error response was received while executing api request. UpdateError: An error occurred while updating an instance. SqlClientNotFoundError: A local SQL client could not be found. ConnectionError: An error occurred while trying to connect to the instance. """ client = api_util.SqlClient(api_util.API_VERSION_DEFAULT) sql_client = client.sql_client sql_messages = client.sql_messages instance_ref = instances_command_util.GetInstanceRef(args, client) acl_name = _AllowlistClientIP(instance_ref, sql_client, sql_messages, client.resource_parser) # Get the client IP that the server sees. Sadly we can only do this by # checking the name of the authorized network rule. retryer = retry.Retryer(max_retrials=2, exponential_sleep_multiplier=2) try: instance_info, client_ip = retryer.RetryOnResult( _GetClientIP, [instance_ref, sql_client, acl_name], should_retry_if=lambda x, s: x[1] is None, # client_ip is None sleep_ms=500) except retry.RetryException: raise exceptions.UpdateError( 'Could not allowlist client IP. Server did ' 'not reply with the allowlisted IP.') # Check for the mysql or psql executable based on the db version. db_type = instance_info.databaseVersion.name.split('_')[0] exe_name = constants.DB_EXE.get(db_type, 'mysql') exe = files.FindExecutableOnPath(exe_name) if not exe: raise exceptions.SqlClientNotFoundError( '{0} client not found. Please install a {1} client and make sure ' 'it is in PATH to be able to connect to the database instance.'. format(exe_name.title(), exe_name)) # Check the version of IP and decide if we need to add ipv4 support. ip_type = network.GetIpVersion(client_ip) if ip_type == network.IP_VERSION_4: if instance_info.settings.ipConfiguration.ipv4Enabled: ip_address = instance_info.ipAddresses[0].ipAddress else: # TODO(b/36049930): ask user if we should enable ipv4 addressing message = ( 'It seems your client does not have ipv6 connectivity and ' 'the database instance does not have an ipv4 address. ' 'Please request an ipv4 address for this database instance.') raise exceptions.ConnectionError(message) elif ip_type == network.IP_VERSION_6: ip_address = instance_info.ipv6Address else: raise exceptions.ConnectionError('Could not connect to SQL server.') # Determine what SQL user to connect with. sql_user = constants.DEFAULT_SQL_USER[exe_name] if args.user: sql_user = args.user # We have everything we need, time to party! flags = constants.EXE_FLAGS[exe_name] sql_args = [exe_name, flags['hostname'], ip_address] sql_args.extend([flags['user'], sql_user]) if 'password' in flags: sql_args.append(flags['password']) if supports_database: sql_args.extend(instances_command_util.GetDatabaseArgs(args, flags)) instances_command_util.ConnectToInstance(sql_args, sql_user)